Version Description
- Improved plugin API
- Fixed a bug with saving snippets per page option (#)
- Updated CodeMirror to version 3.11
- Allow plugin to be activated on individual sites on multisite (#)
- Slimmed down the description visual editor
- Added icon for the new MP6 admin UI (#)
- Strip PHP tags from the beginning and end of a snippet in case someone forgets
- Changed to MIT license
- Removed HTML, CSS and JavaScript CodeMirror modes that were messing things up
- Made everything leaner, faster, and better
Download this release
Release Info
Developer | bungeshea |
Plugin | Code Snippets |
Version | 1.7 |
Comparing to | |
See all releases |
Code changes from version 1.6.1 to 1.7
- assets/admin-single.css +50 -0
- assets/{util → codemirror/addon}/matchbrackets.js +19 -8
- assets/{util → codemirror/addon}/search.js +25 -13
- assets/{util → codemirror/addon}/searchcursor.js +40 -29
- assets/{codemirror.css → codemirror/lib/codemirror.css} +26 -32
- assets/{codemirror.js → codemirror/lib/codemirror.js} +2097 -1134
- assets/{mode → codemirror/mode}/clike.js +2 -0
- assets/{mode → codemirror/mode}/php.js +1 -1
- assets/icon.css +0 -3
- assets/menu-icon.mp6.css +23 -0
- assets/mode/css.js +0 -465
- assets/mode/htmlmixed.js +0 -84
- assets/mode/javascript.js +0 -411
- assets/mode/xml.js +0 -324
- assets/screen-icon.css +10 -0
- assets/table.css +7 -0
- assets/table.mp6.css +85 -0
- assets/util/dialog.css +0 -32
- assets/util/dialog.js +0 -75
- code-snippets.php +614 -346
- images/{icon16.png → menu-icon.png} +0 -0
- images/{icon32.png → screen-icon.png} +0 -0
- includes/admin/import.php +16 -1
- includes/admin/manage.php +20 -9
- includes/admin/single.php +42 -52
- includes/class-list-table.php +187 -89
- includes/export.php +145 -96
- includes/help/import.php +2 -2
- includes/help/single.php +1 -2
- languages/code-snippets.pot +221 -142
- license.txt +4 -671
- readme.txt +32 -14
- screenshot-1.jpg +0 -0
- screenshot-2.jpg +0 -0
- screenshot-3.jpg +0 -0
- screenshot-4.jpg +0 -0
- screenshot-5.jpg +0 -0
assets/admin-single.css
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Custom styling for the single snippet admin page
|
3 |
+
*
|
4 |
+
* @package Code Snippets
|
5 |
+
* @subpackage Assets
|
6 |
+
*/
|
7 |
+
|
8 |
+
h3 {
|
9 |
+
/* Provide some decent space between the fields and titles */
|
10 |
+
margin: 1em 0;
|
11 |
+
}
|
12 |
+
|
13 |
+
label {
|
14 |
+
/* What's with the pointer mouse on labels? */
|
15 |
+
cursor: auto;
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Customise the CodeMirror editor
|
20 |
+
* to look pretty and support auto-resizing
|
21 |
+
*/
|
22 |
+
.CodeMirror {
|
23 |
+
height: auto;
|
24 |
+
min-height: 300px;
|
25 |
+
border: 1px solid #eee;
|
26 |
+
-moz-border-radius: 3px; -webkit-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px;
|
27 |
+
}
|
28 |
+
|
29 |
+
.CodeMirror-scroll {
|
30 |
+
overflow-y: hidden;
|
31 |
+
overflow-x: auto;
|
32 |
+
}
|
33 |
+
|
34 |
+
.CodeMirror-sizer {
|
35 |
+
min-height: 300px !important;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Tweak things for the MP6 interface
|
40 |
+
*/
|
41 |
+
.admin-color-mp6 .CodeMirror {
|
42 |
+
width: 100%;
|
43 |
+
background-color: #fff;
|
44 |
+
border-color: #dfdfdf;
|
45 |
+
}
|
46 |
+
|
47 |
+
.admin-color-mp6 .CodeMirror * {
|
48 |
+
/* Override Open Sans font */
|
49 |
+
font-family: monospace !important;
|
50 |
+
}
|
assets/{util → codemirror/addon}/matchbrackets.js
RENAMED
@@ -1,11 +1,18 @@
|
|
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(
|
9 |
|
10 |
var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
|
11 |
function scan(line, lineNo, start) {
|
@@ -14,7 +21,7 @@
|
|
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(
|
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};
|
@@ -27,17 +34,21 @@
|
|
27 |
else found = scan(cm.getLineHandle(i), i);
|
28 |
if (found) break;
|
29 |
}
|
30 |
-
return {from:
|
31 |
}
|
32 |
|
33 |
function matchBrackets(cm, autoclear) {
|
34 |
var found = findMatchingBracket(cm);
|
35 |
-
if (!found)
|
|
|
|
|
|
|
36 |
var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
37 |
-
var one = cm.markText(found.from,
|
38 |
-
|
39 |
-
|
40 |
-
|
|
|
41 |
var clear = function() {
|
42 |
cm.operation(function() { one.clear(); two && two.clear(); });
|
43 |
};
|
1 |
(function() {
|
2 |
+
var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
|
3 |
+
(document.documentMode == null || document.documentMode < 8);
|
4 |
+
|
5 |
+
var Pos = CodeMirror.Pos;
|
6 |
+
// Disable brace matching in long lines, since it'll cause hugely slow updates
|
7 |
+
var maxLineLen = 1000;
|
8 |
+
|
9 |
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
|
10 |
function findMatchingBracket(cm) {
|
11 |
var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
|
12 |
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
|
13 |
if (!match) return null;
|
14 |
var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
|
15 |
+
var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type;
|
16 |
|
17 |
var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
|
18 |
function scan(line, lineNo, start) {
|
21 |
if (start != null) pos = start + d;
|
22 |
for (; pos != end; pos += d) {
|
23 |
var ch = line.text.charAt(pos);
|
24 |
+
if (re.test(ch) && cm.getTokenAt(Pos(lineNo, pos + 1)).type == style) {
|
25 |
var match = matching[ch];
|
26 |
if (match.charAt(1) == ">" == forward) stack.push(ch);
|
27 |
else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
|
34 |
else found = scan(cm.getLineHandle(i), i);
|
35 |
if (found) break;
|
36 |
}
|
37 |
+
return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match};
|
38 |
}
|
39 |
|
40 |
function matchBrackets(cm, autoclear) {
|
41 |
var found = findMatchingBracket(cm);
|
42 |
+
if (!found || cm.getLine(found.from.line).length > maxLineLen ||
|
43 |
+
found.to && cm.getLine(found.to.line).length > maxLineLen)
|
44 |
+
return;
|
45 |
+
|
46 |
var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
47 |
+
var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style});
|
48 |
+
var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style});
|
49 |
+
// Kludge to work around the IE bug from issue #1193, where text
|
50 |
+
// input stops going to the textare whever this fires.
|
51 |
+
if (ie_lt8 && cm.state.focused) cm.display.input.focus();
|
52 |
var clear = function() {
|
53 |
cm.operation(function() { one.clear(); two && two.clear(); });
|
54 |
};
|
assets/{util → codemirror/addon}/search.js
RENAMED
@@ -7,9 +7,24 @@
|
|
7 |
// Ctrl-G.
|
8 |
|
9 |
(function() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
function SearchState() {
|
11 |
this.posFrom = this.posTo = this.query = null;
|
12 |
-
this.
|
13 |
}
|
14 |
function getSearchState(cm) {
|
15 |
return cm._searchState || (cm._searchState = new SearchState());
|
@@ -39,11 +54,9 @@
|
|
39 |
cm.operation(function() {
|
40 |
if (!query || state.query) return;
|
41 |
state.query = parseQuery(query);
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
{className: "CodeMirror-searching"}));
|
46 |
-
}
|
47 |
state.posFrom = state.posTo = cm.getCursor();
|
48 |
findNext(cm, rev);
|
49 |
});
|
@@ -53,7 +66,7 @@
|
|
53 |
var state = getSearchState(cm);
|
54 |
var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
|
55 |
if (!cursor.find(rev)) {
|
56 |
-
cursor = getSearchCursor(cm, state.query, rev ?
|
57 |
if (!cursor.find(rev)) return;
|
58 |
}
|
59 |
cm.setSelection(cursor.from(), cursor.to());
|
@@ -63,8 +76,7 @@
|
|
63 |
var state = getSearchState(cm);
|
64 |
if (!state.query) return;
|
65 |
state.query = null;
|
66 |
-
|
67 |
-
state.marked.length = 0;
|
68 |
});}
|
69 |
|
70 |
var replaceQueryDialog =
|
@@ -88,7 +100,7 @@
|
|
88 |
} else {
|
89 |
clearSearch(cm);
|
90 |
var cursor = getSearchCursor(cm, query, cm.getCursor());
|
91 |
-
|
92 |
var start = cursor.from(), match;
|
93 |
if (!(match = cursor.findNext())) {
|
94 |
cursor = getSearchCursor(cm, query);
|
@@ -98,12 +110,12 @@
|
|
98 |
cm.setSelection(cursor.from(), cursor.to());
|
99 |
confirmDialog(cm, doReplaceConfirm, "Replace?",
|
100 |
[function() {doReplace(match);}, advance]);
|
101 |
-
}
|
102 |
-
|
103 |
cursor.replace(typeof query == "string" ? text :
|
104 |
text.replace(/\$(\d)/, function(_, i) {return match[i];}));
|
105 |
advance();
|
106 |
-
}
|
107 |
advance();
|
108 |
}
|
109 |
});
|
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());
|
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 |
});
|
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 ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
|
70 |
if (!cursor.find(rev)) return;
|
71 |
}
|
72 |
cm.setSelection(cursor.from(), cursor.to());
|
76 |
var state = getSearchState(cm);
|
77 |
if (!state.query) return;
|
78 |
state.query = null;
|
79 |
+
cm.removeOverlay(state.overlay);
|
|
|
80 |
});}
|
81 |
|
82 |
var replaceQueryDialog =
|
100 |
} else {
|
101 |
clearSearch(cm);
|
102 |
var cursor = getSearchCursor(cm, query, cm.getCursor());
|
103 |
+
var advance = function() {
|
104 |
var start = cursor.from(), match;
|
105 |
if (!(match = cursor.findNext())) {
|
106 |
cursor = getSearchCursor(cm, query);
|
110 |
cm.setSelection(cursor.from(), cursor.to());
|
111 |
confirmDialog(cm, doReplaceConfirm, "Replace?",
|
112 |
[function() {doReplace(match);}, advance]);
|
113 |
+
};
|
114 |
+
var doReplace = function(match) {
|
115 |
cursor.replace(typeof query == "string" ? text :
|
116 |
text.replace(/\$(\d)/, function(_, i) {return match[i];}));
|
117 |
advance();
|
118 |
+
};
|
119 |
advance();
|
120 |
}
|
121 |
});
|
assets/{util → codemirror/addon}/searchcursor.js
RENAMED
@@ -1,9 +1,11 @@
|
|
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) :
|
7 |
this.pos = {from: pos, to: pos};
|
8 |
|
9 |
// The matches method is filled in based on the type of query.
|
@@ -15,24 +17,23 @@
|
|
15 |
this.matches = function(reverse, pos) {
|
16 |
if (reverse) {
|
17 |
query.lastIndex = 0;
|
18 |
-
var line = cm.getLine(pos.line).slice(0, pos.ch),
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
}
|
27 |
-
start--;
|
28 |
} else {
|
29 |
query.lastIndex = pos.ch;
|
30 |
var line = cm.getLine(pos.line), match = query.exec(line),
|
31 |
start = match && match.index;
|
32 |
}
|
33 |
-
if (match)
|
34 |
-
return {from:
|
35 |
-
to:
|
36 |
match: match};
|
37 |
};
|
38 |
} else { // String query
|
@@ -40,15 +41,21 @@
|
|
40 |
var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
|
41 |
var target = query.split("\n");
|
42 |
// Different methods for single-line and multi-line queries
|
43 |
-
if (target.length == 1)
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
this.matches = function(reverse, pos) {
|
53 |
var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln));
|
54 |
var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
|
@@ -66,10 +73,11 @@
|
|
66 |
var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
|
67 |
if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
|
68 |
return;
|
69 |
-
var start =
|
70 |
return {from: reverse ? end : start, to: reverse ? start : end};
|
71 |
}
|
72 |
};
|
|
|
73 |
}
|
74 |
}
|
75 |
|
@@ -80,7 +88,7 @@
|
|
80 |
find: function(reverse) {
|
81 |
var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
|
82 |
function savePosAndFail(line) {
|
83 |
-
var pos =
|
84 |
self.pos = {from: pos, to: pos};
|
85 |
self.atOccurrence = false;
|
86 |
return false;
|
@@ -88,17 +96,18 @@
|
|
88 |
|
89 |
for (;;) {
|
90 |
if (this.pos = this.matches(reverse, pos)) {
|
|
|
91 |
this.atOccurrence = true;
|
92 |
return this.pos.match || true;
|
93 |
}
|
94 |
if (reverse) {
|
95 |
if (!pos.line) return savePosAndFail(0);
|
96 |
-
pos =
|
97 |
}
|
98 |
else {
|
99 |
var maxLine = this.cm.lineCount();
|
100 |
if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
|
101 |
-
pos =
|
102 |
}
|
103 |
}
|
104 |
},
|
@@ -107,9 +116,11 @@
|
|
107 |
to: function() {if (this.atOccurrence) return this.pos.to;},
|
108 |
|
109 |
replace: function(newText) {
|
110 |
-
|
111 |
-
|
112 |
-
|
|
|
|
|
113 |
}
|
114 |
};
|
115 |
|
1 |
(function(){
|
2 |
+
var Pos = CodeMirror.Pos;
|
3 |
+
|
4 |
function SearchCursor(cm, query, pos, caseFold) {
|
5 |
this.atOccurrence = false; this.cm = cm;
|
6 |
if (caseFold == null && typeof query == "string") caseFold = false;
|
7 |
|
8 |
+
pos = pos ? cm.clipPos(pos) : Pos(0, 0);
|
9 |
this.pos = {from: pos, to: pos};
|
10 |
|
11 |
// The matches method is filled in based on the type of query.
|
17 |
this.matches = function(reverse, pos) {
|
18 |
if (reverse) {
|
19 |
query.lastIndex = 0;
|
20 |
+
var line = cm.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start;
|
21 |
+
for (;;) {
|
22 |
+
query.lastIndex = cutOff;
|
23 |
+
var newMatch = query.exec(line);
|
24 |
+
if (!newMatch) break;
|
25 |
+
match = newMatch;
|
26 |
+
start = match.index;
|
27 |
+
cutOff = match.index + 1;
|
28 |
}
|
|
|
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: Pos(pos.line, start),
|
36 |
+
to: Pos(pos.line, start + match[0].length),
|
37 |
match: match};
|
38 |
};
|
39 |
} else { // String query
|
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: Pos(pos.line, match),
|
55 |
+
to: Pos(pos.line, 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));
|
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 = Pos(pos.line, offsetA), end = Pos(ln, offsetB);
|
77 |
return {from: reverse ? end : start, to: reverse ? start : end};
|
78 |
}
|
79 |
};
|
80 |
+
}
|
81 |
}
|
82 |
}
|
83 |
|
88 |
find: function(reverse) {
|
89 |
var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
|
90 |
function savePosAndFail(line) {
|
91 |
+
var pos = Pos(line, 0);
|
92 |
self.pos = {from: pos, to: pos};
|
93 |
self.atOccurrence = false;
|
94 |
return false;
|
96 |
|
97 |
for (;;) {
|
98 |
if (this.pos = this.matches(reverse, pos)) {
|
99 |
+
if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); }
|
100 |
this.atOccurrence = true;
|
101 |
return this.pos.match || true;
|
102 |
}
|
103 |
if (reverse) {
|
104 |
if (!pos.line) return savePosAndFail(0);
|
105 |
+
pos = Pos(pos.line-1, this.cm.getLine(pos.line-1).length);
|
106 |
}
|
107 |
else {
|
108 |
var maxLine = this.cm.lineCount();
|
109 |
if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
|
110 |
+
pos = Pos(pos.line + 1, 0);
|
111 |
}
|
112 |
}
|
113 |
},
|
116 |
to: function() {if (this.atOccurrence) return this.pos.to;},
|
117 |
|
118 |
replace: function(newText) {
|
119 |
+
if (!this.atOccurrence) return;
|
120 |
+
var lines = CodeMirror.splitLines(newText);
|
121 |
+
this.cm.replaceRange(lines, this.pos.from, this.pos.to);
|
122 |
+
this.pos.to = Pos(this.pos.from.line + lines.length - 1,
|
123 |
+
lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0));
|
124 |
}
|
125 |
};
|
126 |
|
assets/{codemirror.css → codemirror/lib/codemirror.css}
RENAMED
@@ -3,19 +3,11 @@
|
|
3 |
.CodeMirror {
|
4 |
/* Set height, width, borders, and global font properties here */
|
5 |
font-family: monospace;
|
6 |
-
height:
|
7 |
-
min-height: 300px;
|
8 |
-
border: 1px solid #eee;
|
9 |
-
-moz-border-radius: 3px; -webkit-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px;
|
10 |
}
|
11 |
.CodeMirror-scroll {
|
12 |
/* Set scrolling behaviour here */
|
13 |
-
overflow
|
14 |
-
overflow-x: auto;
|
15 |
-
}
|
16 |
-
|
17 |
-
.CodeMirror-sizer {
|
18 |
-
min-height: 300px !important;
|
19 |
}
|
20 |
|
21 |
/* PADDING */
|
@@ -47,26 +39,24 @@
|
|
47 |
|
48 |
/* CURSOR */
|
49 |
|
50 |
-
.CodeMirror
|
51 |
border-left: 1px solid black;
|
|
|
52 |
}
|
53 |
/* Shown when moving in bi-directional text */
|
54 |
-
.CodeMirror
|
55 |
border-left: 1px solid silver;
|
56 |
}
|
57 |
-
.cm-keymap-fat-cursor
|
58 |
width: auto;
|
59 |
border: 0;
|
60 |
-
background:
|
61 |
-
|
62 |
-
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800);
|
63 |
-
}
|
64 |
-
/* Kludge to turn off filter in ie9+, which also accepts rgba */
|
65 |
-
.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) {
|
66 |
-
filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
|
67 |
}
|
68 |
/* Can style cursor different in overwrite (non-insert) mode */
|
69 |
-
.CodeMirror
|
|
|
|
|
70 |
|
71 |
/* DEFAULT THEME */
|
72 |
|
@@ -98,7 +88,6 @@
|
|
98 |
.cm-positive {color: #292;}
|
99 |
.cm-header, .cm-strong {font-weight: bold;}
|
100 |
.cm-em {font-style: italic;}
|
101 |
-
.cm-emstrong {font-style: italic; font-weight: bold;}
|
102 |
.cm-link {text-decoration: underline;}
|
103 |
|
104 |
.cm-invalidchar {color: #f00;}
|
@@ -106,11 +95,6 @@
|
|
106 |
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
|
107 |
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
108 |
|
109 |
-
.activeline { background: #e8f2ff !important; }
|
110 |
-
|
111 |
-
span.CodeMirror-matchhighlight { background: #e9e9e9 }
|
112 |
-
.CodeMirror-focused span.CodeMirror-matchhighlight { background: #e7e4ff; !important }
|
113 |
-
|
114 |
/* STOP */
|
115 |
|
116 |
/* The rest of this file contains styles related to the mechanics of
|
@@ -120,6 +104,8 @@ span.CodeMirror-matchhighlight { background: #e9e9e9 }
|
|
120 |
line-height: 1;
|
121 |
position: relative;
|
122 |
overflow: hidden;
|
|
|
|
|
123 |
}
|
124 |
|
125 |
.CodeMirror-scroll {
|
@@ -161,10 +147,13 @@ span.CodeMirror-matchhighlight { background: #e9e9e9 }
|
|
161 |
.CodeMirror-gutters {
|
162 |
position: absolute; left: 0; top: 0;
|
163 |
height: 100%;
|
|
|
164 |
z-index: 3;
|
165 |
}
|
166 |
.CodeMirror-gutter {
|
167 |
height: 100%;
|
|
|
|
|
168 |
display: inline-block;
|
169 |
/* Hack to make IE7 behave */
|
170 |
*zoom:1;
|
@@ -181,7 +170,7 @@ span.CodeMirror-matchhighlight { background: #e9e9e9 }
|
|
181 |
}
|
182 |
.CodeMirror pre {
|
183 |
/* Reset some styles that the rest of the page might have set */
|
184 |
-
-moz-border-radius: 0; -webkit-border-radius: 0;
|
185 |
border-width: 0;
|
186 |
background: transparent;
|
187 |
font-family: inherit;
|
@@ -209,6 +198,11 @@ span.CodeMirror-matchhighlight { background: #e9e9e9 }
|
|
209 |
.CodeMirror-linewidget {
|
210 |
position: relative;
|
211 |
z-index: 2;
|
|
|
|
|
|
|
|
|
|
|
212 |
}
|
213 |
|
214 |
.CodeMirror-wrap .CodeMirror-scroll {
|
@@ -223,20 +217,20 @@ span.CodeMirror-matchhighlight { background: #e9e9e9 }
|
|
223 |
}
|
224 |
.CodeMirror-measure pre { position: static; }
|
225 |
|
226 |
-
.CodeMirror
|
227 |
position: absolute;
|
228 |
visibility: hidden;
|
229 |
border-right: none;
|
230 |
width: 0;
|
231 |
}
|
232 |
-
.CodeMirror-focused
|
233 |
visibility: visible;
|
234 |
}
|
235 |
|
236 |
.CodeMirror-selected { background: #d9d9d9; }
|
237 |
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
|
238 |
|
239 |
-
.
|
240 |
background: #ffa;
|
241 |
background: rgba(255, 255, 0, .4);
|
242 |
}
|
@@ -246,7 +240,7 @@ span.CodeMirror-matchhighlight { background: #e9e9e9 }
|
|
246 |
|
247 |
@media print {
|
248 |
/* Hide the cursor when printing */
|
249 |
-
.CodeMirror
|
250 |
visibility: hidden;
|
251 |
}
|
252 |
}
|
3 |
.CodeMirror {
|
4 |
/* Set height, width, borders, and global font properties here */
|
5 |
font-family: monospace;
|
6 |
+
height: 300px;
|
|
|
|
|
|
|
7 |
}
|
8 |
.CodeMirror-scroll {
|
9 |
/* Set scrolling behaviour here */
|
10 |
+
overflow: auto;
|
|
|
|
|
|
|
|
|
|
|
11 |
}
|
12 |
|
13 |
/* PADDING */
|
39 |
|
40 |
/* CURSOR */
|
41 |
|
42 |
+
.CodeMirror div.CodeMirror-cursor {
|
43 |
border-left: 1px solid black;
|
44 |
+
z-index: 3;
|
45 |
}
|
46 |
/* Shown when moving in bi-directional text */
|
47 |
+
.CodeMirror div.CodeMirror-secondarycursor {
|
48 |
border-left: 1px solid silver;
|
49 |
}
|
50 |
+
.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
|
51 |
width: auto;
|
52 |
border: 0;
|
53 |
+
background: #7e7;
|
54 |
+
z-index: 1;
|
|
|
|
|
|
|
|
|
|
|
55 |
}
|
56 |
/* Can style cursor different in overwrite (non-insert) mode */
|
57 |
+
.CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
|
58 |
+
|
59 |
+
.cm-tab { display: inline-block; }
|
60 |
|
61 |
/* DEFAULT THEME */
|
62 |
|
88 |
.cm-positive {color: #292;}
|
89 |
.cm-header, .cm-strong {font-weight: bold;}
|
90 |
.cm-em {font-style: italic;}
|
|
|
91 |
.cm-link {text-decoration: underline;}
|
92 |
|
93 |
.cm-invalidchar {color: #f00;}
|
95 |
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
|
96 |
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
97 |
|
|
|
|
|
|
|
|
|
|
|
98 |
/* STOP */
|
99 |
|
100 |
/* The rest of this file contains styles related to the mechanics of
|
104 |
line-height: 1;
|
105 |
position: relative;
|
106 |
overflow: hidden;
|
107 |
+
background: white;
|
108 |
+
color: black;
|
109 |
}
|
110 |
|
111 |
.CodeMirror-scroll {
|
147 |
.CodeMirror-gutters {
|
148 |
position: absolute; left: 0; top: 0;
|
149 |
height: 100%;
|
150 |
+
padding-bottom: 30px;
|
151 |
z-index: 3;
|
152 |
}
|
153 |
.CodeMirror-gutter {
|
154 |
height: 100%;
|
155 |
+
padding-bottom: 30px;
|
156 |
+
margin-bottom: -32px;
|
157 |
display: inline-block;
|
158 |
/* Hack to make IE7 behave */
|
159 |
*zoom:1;
|
170 |
}
|
171 |
.CodeMirror pre {
|
172 |
/* Reset some styles that the rest of the page might have set */
|
173 |
+
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
|
174 |
border-width: 0;
|
175 |
background: transparent;
|
176 |
font-family: inherit;
|
198 |
.CodeMirror-linewidget {
|
199 |
position: relative;
|
200 |
z-index: 2;
|
201 |
+
overflow: auto;
|
202 |
+
}
|
203 |
+
|
204 |
+
.CodeMirror-widget {
|
205 |
+
display: inline-block;
|
206 |
}
|
207 |
|
208 |
.CodeMirror-wrap .CodeMirror-scroll {
|
217 |
}
|
218 |
.CodeMirror-measure pre { position: static; }
|
219 |
|
220 |
+
.CodeMirror div.CodeMirror-cursor {
|
221 |
position: absolute;
|
222 |
visibility: hidden;
|
223 |
border-right: none;
|
224 |
width: 0;
|
225 |
}
|
226 |
+
.CodeMirror-focused div.CodeMirror-cursor {
|
227 |
visibility: visible;
|
228 |
}
|
229 |
|
230 |
.CodeMirror-selected { background: #d9d9d9; }
|
231 |
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
|
232 |
|
233 |
+
.cm-searching {
|
234 |
background: #ffa;
|
235 |
background: rgba(255, 255, 0, .4);
|
236 |
}
|
240 |
|
241 |
@media print {
|
242 |
/* Hide the cursor when printing */
|
243 |
+
.CodeMirror div.CodeMirror-cursor {
|
244 |
visibility: hidden;
|
245 |
}
|
246 |
}
|
assets/{codemirror.js → codemirror/lib/codemirror.js}
RENAMED
@@ -1,4 +1,4 @@
|
|
1 |
-
// CodeMirror version 3.
|
2 |
//
|
3 |
// CodeMirror is the only global var we claim
|
4 |
window.CodeMirror = (function() {
|
@@ -10,8 +10,8 @@ window.CodeMirror = (function() {
|
|
10 |
// bugs and behavior differences.
|
11 |
var gecko = /gecko\/\d/i.test(navigator.userAgent);
|
12 |
var ie = /MSIE \d/.test(navigator.userAgent);
|
13 |
-
var ie_lt8 =
|
14 |
-
var ie_lt9 =
|
15 |
var webkit = /WebKit\//.test(navigator.userAgent);
|
16 |
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
|
17 |
var chrome = /Chrome\//.test(navigator.userAgent);
|
@@ -24,8 +24,15 @@ window.CodeMirror = (function() {
|
|
24 |
|
25 |
var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
|
26 |
// This is woefully incomplete. Suggestions for alternative methods welcome.
|
27 |
-
var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent);
|
28 |
var mac = ios || /Mac/.test(navigator.platform);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
// Optimize some code when these features are not used
|
31 |
var sawReadOnlySpans = false, sawCollapsedSpans = false;
|
@@ -34,31 +41,38 @@ window.CodeMirror = (function() {
|
|
34 |
|
35 |
function CodeMirror(place, options) {
|
36 |
if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
|
37 |
-
|
38 |
this.options = options = options || {};
|
39 |
// Determine effective options based on given values and defaults.
|
40 |
for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
|
41 |
options[opt] = defaults[opt];
|
42 |
setGuttersForLineNumbers(options);
|
43 |
|
44 |
-
var
|
|
|
45 |
display.wrapper.CodeMirror = this;
|
46 |
updateGutters(this);
|
47 |
if (options.autofocus && !mobile) focusInput(this);
|
48 |
|
49 |
-
this.
|
50 |
-
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
52 |
themeChanged(this);
|
53 |
if (options.lineWrapping)
|
54 |
this.display.wrapper.className += " CodeMirror-wrap";
|
55 |
|
56 |
-
|
57 |
-
|
|
|
|
|
58 |
// Override magic textarea content restore that IE sometimes does
|
59 |
// on our hidden textarea on reload
|
60 |
if (ie) setTimeout(bind(resetInput, this, true), 20);
|
61 |
-
this.view.history = makeHistory();
|
62 |
|
63 |
registerEventHandlers(this);
|
64 |
// IE throws unspecified error in certain cases, when
|
@@ -77,10 +91,16 @@ window.CodeMirror = (function() {
|
|
77 |
|
78 |
// DISPLAY CONSTRUCTOR
|
79 |
|
80 |
-
function makeDisplay(place) {
|
81 |
var d = {};
|
|
|
82 |
var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
|
83 |
-
|
|
|
|
|
|
|
|
|
|
|
84 |
// Wraps and hides input textarea
|
85 |
d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
|
86 |
// The actual fake scrollbars.
|
@@ -91,9 +111,9 @@ window.CodeMirror = (function() {
|
|
91 |
d.lineDiv = elt("div");
|
92 |
d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
|
93 |
// Blinky cursor, and element used to ensure cursor fits at the end of a line
|
94 |
-
d.cursor = elt("
|
95 |
// Secondary cursor, shown when on a 'jump' in bi-directional text
|
96 |
-
d.otherCursor = elt("
|
97 |
// Used to measure text size
|
98 |
d.measure = elt("div", null, "CodeMirror-measure");
|
99 |
// Wraps everything that needs to exist inside the vertically-padded coordinate system
|
@@ -104,7 +124,7 @@ window.CodeMirror = (function() {
|
|
104 |
// Set to the height of the text, causes scrolling
|
105 |
d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
|
106 |
// D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
|
107 |
-
d.heightForcer = elt("div",
|
108 |
// Will contain the gutters, if any
|
109 |
d.gutters = elt("div", null, "CodeMirror-gutters");
|
110 |
d.lineGutter = null;
|
@@ -129,7 +149,8 @@ window.CodeMirror = (function() {
|
|
129 |
else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
|
130 |
|
131 |
// Current visible range (may be bigger than the view window).
|
132 |
-
d.viewOffset = d.
|
|
|
133 |
|
134 |
// Used to only resize the line number gutter when necessary (when
|
135 |
// the amount of lines crosses a boundary that makes its width change)
|
@@ -156,35 +177,16 @@ window.CodeMirror = (function() {
|
|
156 |
// string instead of the (large) selection.
|
157 |
d.inaccurateSelection = false;
|
158 |
|
159 |
-
//
|
160 |
-
//
|
161 |
-
d.
|
162 |
-
|
163 |
-
|
164 |
-
}
|
165 |
|
166 |
-
|
|
|
167 |
|
168 |
-
|
169 |
-
var selPos = {line: 0, ch: 0};
|
170 |
-
return {
|
171 |
-
doc: doc,
|
172 |
-
// frontier is the point up to which the content has been parsed,
|
173 |
-
frontier: 0, highlight: new Delayed(),
|
174 |
-
sel: {from: selPos, to: selPos, head: selPos, anchor: selPos, shift: false, extend: false},
|
175 |
-
scrollTop: 0, scrollLeft: 0,
|
176 |
-
overwrite: false, focused: false,
|
177 |
-
// Tracks the maximum line length so that
|
178 |
-
// the horizontal scrollbar can be kept
|
179 |
-
// static when scrolling.
|
180 |
-
maxLine: getLine(doc, 0),
|
181 |
-
maxLineLength: 0,
|
182 |
-
maxLineChanged: false,
|
183 |
-
suppressEdits: false,
|
184 |
-
goalColumn: null,
|
185 |
-
cantEdit: false,
|
186 |
-
keyMaps: []
|
187 |
-
};
|
188 |
}
|
189 |
|
190 |
// STATE UPDATES
|
@@ -192,34 +194,50 @@ window.CodeMirror = (function() {
|
|
192 |
// Used to get the editor into a consistent state again when options change.
|
193 |
|
194 |
function loadMode(cm) {
|
195 |
-
|
196 |
-
cm.
|
197 |
-
|
198 |
-
|
|
|
|
|
199 |
startWorker(cm, 100);
|
|
|
|
|
200 |
}
|
201 |
|
202 |
function wrappingChanged(cm) {
|
203 |
-
var doc = cm.view.doc, th = textHeight(cm.display);
|
204 |
if (cm.options.lineWrapping) {
|
205 |
cm.display.wrapper.className += " CodeMirror-wrap";
|
206 |
-
var perLine = cm.display.scroller.clientWidth / charWidth(cm.display) - 3;
|
207 |
-
doc.iter(0, doc.size, function(line) {
|
208 |
-
if (line.height == 0) return;
|
209 |
-
var guess = Math.ceil(line.text.length / perLine) || 1;
|
210 |
-
if (guess != 1) updateLineHeight(line, guess * th);
|
211 |
-
});
|
212 |
cm.display.sizer.style.minWidth = "";
|
213 |
} else {
|
214 |
cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
|
215 |
-
computeMaxLength(cm
|
216 |
-
doc.iter(0, doc.size, function(line) {
|
217 |
-
if (line.height != 0) updateLineHeight(line, th);
|
218 |
-
});
|
219 |
}
|
220 |
-
|
|
|
221 |
clearCaches(cm);
|
222 |
-
setTimeout(function(){updateScrollbars(cm.display, cm.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
223 |
}
|
224 |
|
225 |
function keyMapChanged(cm) {
|
@@ -236,7 +254,7 @@ window.CodeMirror = (function() {
|
|
236 |
|
237 |
function guttersChanged(cm) {
|
238 |
updateGutters(cm);
|
239 |
-
|
240 |
}
|
241 |
|
242 |
function updateGutters(cm) {
|
@@ -271,15 +289,16 @@ window.CodeMirror = (function() {
|
|
271 |
return len;
|
272 |
}
|
273 |
|
274 |
-
function computeMaxLength(
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
|
|
283 |
}
|
284 |
});
|
285 |
}
|
@@ -303,7 +322,7 @@ window.CodeMirror = (function() {
|
|
303 |
// Re-synchronize the fake scrollbars with the actual size of the
|
304 |
// content. Optionally force a scrollTop.
|
305 |
function updateScrollbars(d /* display */, docHeight) {
|
306 |
-
var totalHeight = docHeight +
|
307 |
d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
|
308 |
var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
|
309 |
var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
|
@@ -311,7 +330,7 @@ window.CodeMirror = (function() {
|
|
311 |
if (needsV) {
|
312 |
d.scrollbarV.style.display = "block";
|
313 |
d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
|
314 |
-
d.scrollbarV.firstChild.style.height =
|
315 |
(scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
|
316 |
} else d.scrollbarV.style.display = "";
|
317 |
if (needsH) {
|
@@ -342,18 +361,19 @@ window.CodeMirror = (function() {
|
|
342 |
|
343 |
function alignHorizontally(cm) {
|
344 |
var display = cm.display;
|
345 |
-
if (!display.alignWidgets && !display.gutters.firstChild) return;
|
346 |
-
var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.
|
347 |
var gutterW = display.gutters.offsetWidth, l = comp + "px";
|
348 |
for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
|
349 |
for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
|
350 |
}
|
351 |
-
|
|
|
352 |
}
|
353 |
|
354 |
function maybeUpdateLineNumberWidth(cm) {
|
355 |
if (!cm.options.lineNumbers) return false;
|
356 |
-
var doc = cm.
|
357 |
if (last.length != display.lineNumChars) {
|
358 |
var test = display.measure.appendChild(elt("div", [elt("div", last)],
|
359 |
"CodeMirror-linenumber CodeMirror-gutter-elt"));
|
@@ -372,7 +392,7 @@ window.CodeMirror = (function() {
|
|
372 |
return String(options.lineNumberFormatter(i + options.firstLineNumber));
|
373 |
}
|
374 |
function compensateForHScroll(display) {
|
375 |
-
return display.scroller
|
376 |
}
|
377 |
|
378 |
// DISPLAY DRAWING
|
@@ -381,12 +401,12 @@ window.CodeMirror = (function() {
|
|
381 |
var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
|
382 |
var updated = updateDisplayInner(cm, changes, viewPort);
|
383 |
if (updated) {
|
384 |
-
signalLater(cm,
|
385 |
if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
|
386 |
-
signalLater(cm,
|
387 |
}
|
388 |
updateSelection(cm);
|
389 |
-
updateScrollbars(cm.display, cm.
|
390 |
|
391 |
return updated;
|
392 |
}
|
@@ -395,9 +415,10 @@ window.CodeMirror = (function() {
|
|
395 |
// determine which DOM updates have to be made, and makes the
|
396 |
// updates.
|
397 |
function updateDisplayInner(cm, changes, viewPort) {
|
398 |
-
var display = cm.display, doc = cm.
|
399 |
if (!display.wrapper.clientWidth) {
|
400 |
-
display.showingFrom = display.showingTo =
|
|
|
401 |
return;
|
402 |
}
|
403 |
|
@@ -406,45 +427,50 @@ window.CodeMirror = (function() {
|
|
406 |
// to render instead of the current scrollbar position.
|
407 |
var visible = visibleLines(display, doc, viewPort);
|
408 |
// Bail out if the visible area is already rendered and nothing changed.
|
409 |
-
if (changes
|
410 |
visible.from > display.showingFrom && visible.to < display.showingTo)
|
411 |
return;
|
412 |
|
413 |
-
if (
|
414 |
-
changes =
|
415 |
-
|
416 |
-
|
417 |
-
// When merged lines are present, the line that needs to be
|
418 |
-
// redrawn might not be the one that was changed.
|
419 |
-
if (changes !== true && sawCollapsedSpans)
|
420 |
-
for (var i = 0; i < changes.length; ++i) {
|
421 |
-
var ch = changes[i], merged;
|
422 |
-
while (merged = collapsedSpanAtStart(getLine(doc, ch.from))) {
|
423 |
-
var from = merged.find().from.line;
|
424 |
-
if (ch.diff) ch.diff -= ch.from - from;
|
425 |
-
ch.from = from;
|
426 |
-
}
|
427 |
-
}
|
428 |
|
429 |
// Used to determine which lines need their line numbers updated
|
430 |
-
var positionsChangedFrom =
|
431 |
-
if (cm.options.lineNumbers
|
432 |
for (var i = 0; i < changes.length; ++i)
|
433 |
if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
|
434 |
|
435 |
-
var
|
436 |
-
var
|
437 |
-
|
438 |
-
if (display.
|
|
|
439 |
if (sawCollapsedSpans) {
|
440 |
from = lineNo(visualLine(doc, getLine(doc, from)));
|
441 |
-
while (to <
|
442 |
}
|
443 |
|
444 |
// Create a range of theoretically intact lines, and punch holes
|
445 |
// in that using the change info.
|
446 |
-
var intact =
|
447 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
448 |
// Clip off the parts that won't be visible
|
449 |
var intactLines = 0;
|
450 |
for (var i = 0; i < intact.length; ++i) {
|
@@ -454,13 +480,17 @@ window.CodeMirror = (function() {
|
|
454 |
if (range.from >= range.to) intact.splice(i--, 1);
|
455 |
else intactLines += range.to - range.from;
|
456 |
}
|
457 |
-
if (intactLines == to - from && from == display.showingFrom && to == display.showingTo)
|
|
|
458 |
return;
|
|
|
459 |
intact.sort(function(a, b) {return a.from - b.from;});
|
460 |
|
|
|
461 |
if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
|
462 |
patchDisplay(cm, from, to, intact, positionsChangedFrom);
|
463 |
display.lineDiv.style.display = "";
|
|
|
464 |
|
465 |
var different = from != display.showingFrom || to != display.showingTo ||
|
466 |
display.lastSizeC != display.wrapper.clientHeight;
|
@@ -477,20 +507,31 @@ window.CodeMirror = (function() {
|
|
477 |
height = bot - prevBottom;
|
478 |
prevBottom = bot;
|
479 |
} else {
|
480 |
-
var box = node
|
481 |
height = box.bottom - box.top;
|
482 |
}
|
483 |
var diff = node.lineObj.height - height;
|
484 |
if (height < 2) height = textHeight(display);
|
485 |
-
if (diff > .001 || diff < -.001)
|
486 |
updateLineHeight(node.lineObj, height);
|
|
|
|
|
|
|
|
|
487 |
}
|
488 |
-
|
489 |
-
|
490 |
-
display
|
|
|
491 |
return true;
|
492 |
}
|
493 |
|
|
|
|
|
|
|
|
|
|
|
|
|
494 |
function computeIntact(intact, changes) {
|
495 |
for (var i = 0, l = changes.length || 0; i < l; ++i) {
|
496 |
var change = changes[i], intact2 = [], diff = change.diff || 0;
|
@@ -528,9 +569,7 @@ window.CodeMirror = (function() {
|
|
528 |
function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
|
529 |
var dims = getDimensions(cm);
|
530 |
var display = cm.display, lineNumbers = cm.options.lineNumbers;
|
531 |
-
|
532 |
-
// we still need widgets and markers intact to add back to the new content later
|
533 |
-
if (!intact.length && !ie && (!webkit || !cm.display.currentWheelTarget))
|
534 |
removeChildren(display.lineDiv);
|
535 |
var container = display.lineDiv, cur = container.firstChild;
|
536 |
|
@@ -540,50 +579,106 @@ window.CodeMirror = (function() {
|
|
540 |
node.style.display = "none";
|
541 |
node.lineObj = null;
|
542 |
} else {
|
543 |
-
|
544 |
}
|
545 |
return next;
|
546 |
}
|
547 |
|
548 |
-
var nextIntact = intact.shift(),
|
549 |
-
cm.
|
550 |
-
if (nextIntact && nextIntact.to ==
|
551 |
-
if (lineIsHidden(line)) {
|
552 |
if (line.height != 0) updateLineHeight(line, 0);
|
553 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
554 |
// This line is intact. Skip to the actual node. Update its
|
555 |
// line number if needed.
|
556 |
while (cur.lineObj != line) cur = rm(cur);
|
557 |
-
if (lineNumbers && updateNumbersFrom <=
|
558 |
-
setTextContent(cur.lineNumber, lineNumberFor(cm.options,
|
559 |
cur = cur.nextSibling;
|
560 |
} else {
|
|
|
|
|
|
|
|
|
|
|
561 |
// This line needs to be generated.
|
562 |
-
var lineNode = buildLineElement(cm, line,
|
563 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
564 |
lineNode.lineObj = line;
|
565 |
}
|
566 |
-
++
|
567 |
});
|
568 |
while (cur) cur = rm(cur);
|
569 |
}
|
570 |
|
571 |
-
function buildLineElement(cm, line, lineNo, dims) {
|
572 |
var lineElement = lineContent(cm, line);
|
573 |
-
var markers = line.gutterMarkers, display = cm.display;
|
574 |
-
|
575 |
-
if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass &&
|
576 |
-
|
577 |
-
|
578 |
-
// Lines with gutter elements or a background class need
|
579 |
-
// to be wrapped again, and have the extra elements added
|
580 |
-
//
|
581 |
-
|
582 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
583 |
if (cm.options.lineNumbers || markers) {
|
584 |
-
var gutterWrap = wrap.
|
585 |
-
|
586 |
-
|
|
|
587 |
if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
|
588 |
wrap.lineNumber = gutterWrap.appendChild(
|
589 |
elt("div", lineNumberFor(cm.options, lineNo),
|
@@ -598,44 +693,42 @@ window.CodeMirror = (function() {
|
|
598 |
dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
|
599 |
}
|
600 |
}
|
601 |
-
// Kludge to make sure the styled element lies behind the selection (by z-index)
|
602 |
-
if (line.bgClass)
|
603 |
-
wrap.appendChild(elt("div", "\u00a0", line.bgClass + " CodeMirror-linebackground"));
|
604 |
-
wrap.appendChild(lineElement);
|
605 |
-
if (line.widgets)
|
606 |
-
for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
|
607 |
-
var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
|
608 |
-
node.widget = widget;
|
609 |
-
if (widget.noHScroll) {
|
610 |
-
(wrap.alignable || (wrap.alignable = [])).push(node);
|
611 |
-
var width = dims.wrapperWidth;
|
612 |
-
node.style.left = dims.fixedPos + "px";
|
613 |
-
if (!widget.coverGutter) {
|
614 |
-
width -= dims.gutterTotalWidth;
|
615 |
-
node.style.paddingLeft = dims.gutterTotalWidth + "px";
|
616 |
-
}
|
617 |
-
node.style.width = width + "px";
|
618 |
-
}
|
619 |
-
if (widget.coverGutter) {
|
620 |
-
node.style.zIndex = 5;
|
621 |
-
node.style.position = "relative";
|
622 |
-
if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
|
623 |
-
}
|
624 |
-
if (widget.above)
|
625 |
-
wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
|
626 |
-
else
|
627 |
-
wrap.appendChild(node);
|
628 |
-
}
|
629 |
-
|
630 |
if (ie_lt8) wrap.style.zIndex = 2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
631 |
return wrap;
|
632 |
}
|
633 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
634 |
// SELECTION / CURSOR
|
635 |
|
636 |
function updateSelection(cm) {
|
637 |
var display = cm.display;
|
638 |
-
var collapsed = posEq(cm.
|
639 |
if (collapsed || cm.options.showCursorWhenSelecting)
|
640 |
updateSelectionCursor(cm);
|
641 |
else
|
@@ -646,8 +739,8 @@ window.CodeMirror = (function() {
|
|
646 |
display.selectionDiv.style.display = "none";
|
647 |
|
648 |
// Move the hidden textarea near the cursor to prevent scrolling artifacts
|
649 |
-
var headPos = cursorCoords(cm, cm.
|
650 |
-
var wrapOff = display.wrapper
|
651 |
display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
|
652 |
headPos.top + lineOff.top - wrapOff.top)) + "px";
|
653 |
display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
|
@@ -656,7 +749,7 @@ window.CodeMirror = (function() {
|
|
656 |
|
657 |
// No selection, plain cursor
|
658 |
function updateSelectionCursor(cm) {
|
659 |
-
var display = cm.display, pos = cursorCoords(cm, cm.
|
660 |
display.cursor.style.left = pos.left + "px";
|
661 |
display.cursor.style.top = pos.top + "px";
|
662 |
display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
|
@@ -672,7 +765,7 @@ window.CodeMirror = (function() {
|
|
672 |
|
673 |
// Highlight selection
|
674 |
function updateSelectionRange(cm) {
|
675 |
-
var display = cm.display, doc = cm.
|
676 |
var fragment = document.createDocumentFragment();
|
677 |
var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
|
678 |
|
@@ -687,7 +780,7 @@ window.CodeMirror = (function() {
|
|
687 |
var lineObj = getLine(doc, line);
|
688 |
var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
|
689 |
function coords(ch) {
|
690 |
-
return charCoords(cm,
|
691 |
}
|
692 |
|
693 |
iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
|
@@ -765,28 +858,33 @@ window.CodeMirror = (function() {
|
|
765 |
// HIGHLIGHT WORKER
|
766 |
|
767 |
function startWorker(cm, time) {
|
768 |
-
if (cm.
|
769 |
-
cm.
|
770 |
}
|
771 |
|
772 |
function highlightWorker(cm) {
|
773 |
-
var
|
774 |
-
if (
|
|
|
775 |
var end = +new Date + cm.options.workTime;
|
776 |
-
var state = copyState(
|
777 |
var changed = [], prevChange;
|
778 |
-
doc.iter(
|
779 |
-
if (
|
780 |
-
|
781 |
-
|
782 |
-
|
|
|
|
|
|
|
|
|
783 |
}
|
784 |
-
line.stateAfter = copyState(
|
785 |
} else {
|
786 |
processLine(cm, line, state);
|
787 |
-
line.stateAfter =
|
788 |
}
|
789 |
-
++
|
790 |
if (+new Date > end) {
|
791 |
startWorker(cm, cm.options.workDelay);
|
792 |
return true;
|
@@ -805,10 +903,10 @@ window.CodeMirror = (function() {
|
|
805 |
// smallest indentation, which tends to need the least context to
|
806 |
// parse correctly.
|
807 |
function findStartLine(cm, n) {
|
808 |
-
var minindent, minline, doc = cm.
|
809 |
for (var search = n, lim = n - 100; search > lim; --search) {
|
810 |
-
if (search
|
811 |
-
var line = getLine(doc, search-1);
|
812 |
if (line.stateAfter) return search;
|
813 |
var indented = countColumn(line.text, null, cm.options.tabSize);
|
814 |
if (minline == null || minindent > indented) {
|
@@ -820,29 +918,33 @@ window.CodeMirror = (function() {
|
|
820 |
}
|
821 |
|
822 |
function getStateBefore(cm, n) {
|
823 |
-
var
|
824 |
-
|
825 |
-
|
826 |
-
|
827 |
-
|
|
|
828 |
processLine(cm, line, state);
|
829 |
-
var save = pos == n - 1 || pos % 5 == 0 || pos >=
|
830 |
-
line.stateAfter = save ? copyState(
|
831 |
++pos;
|
832 |
});
|
833 |
return state;
|
834 |
}
|
835 |
|
836 |
// POSITION MEASUREMENT
|
837 |
-
|
838 |
function paddingTop(display) {return display.lineSpace.offsetTop;}
|
|
|
839 |
function paddingLeft(display) {
|
840 |
-
var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x"));
|
841 |
return e.offsetLeft;
|
842 |
}
|
843 |
|
844 |
function measureChar(cm, line, ch, data) {
|
845 |
-
var
|
|
|
|
|
846 |
for (var pos = ch;; pos += dir) {
|
847 |
var r = data[pos];
|
848 |
if (r) break;
|
@@ -853,22 +955,30 @@ window.CodeMirror = (function() {
|
|
853 |
top: r.top, bottom: r.bottom};
|
854 |
}
|
855 |
|
856 |
-
function
|
857 |
-
|
858 |
-
var display = cm.display, cache = cm.display.measureLineCache;
|
859 |
for (var i = 0; i < cache.length; ++i) {
|
860 |
var memo = cache[i];
|
861 |
if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
|
862 |
-
display.scroller.clientWidth == memo.width
|
|
|
863 |
return memo.measure;
|
864 |
}
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
if (
|
871 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
872 |
return measure;
|
873 |
}
|
874 |
|
@@ -903,10 +1013,15 @@ window.CodeMirror = (function() {
|
|
903 |
|
904 |
removeChildrenAndAdd(display.measure, pre);
|
905 |
|
906 |
-
var outer = display.lineDiv
|
907 |
var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
|
|
|
|
|
|
|
|
|
|
|
908 |
for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
|
909 |
-
var size = cur
|
910 |
var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
|
911 |
for (var j = 0; j < vranges.length; j += 2) {
|
912 |
var rtop = vranges[j], rbot = vranges[j+1];
|
@@ -920,25 +1035,44 @@ window.CodeMirror = (function() {
|
|
920 |
}
|
921 |
}
|
922 |
if (j == vranges.length) vranges.push(top, bot);
|
923 |
-
|
|
|
|
|
924 |
}
|
925 |
for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
|
926 |
var vr = cur.top;
|
927 |
cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
|
928 |
}
|
|
|
929 |
return data;
|
930 |
}
|
931 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
932 |
function clearCaches(cm) {
|
933 |
cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
|
934 |
cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
|
935 |
-
cm.
|
|
|
936 |
}
|
937 |
|
938 |
// Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
|
939 |
function intoCoordSystem(cm, lineObj, rect, context) {
|
940 |
if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
|
941 |
-
var size = lineObj.widgets[i]
|
942 |
rect.top += size; rect.bottom += size;
|
943 |
}
|
944 |
if (context == "line") return rect;
|
@@ -946,7 +1080,7 @@ window.CodeMirror = (function() {
|
|
946 |
var yOff = heightAtLine(cm, lineObj);
|
947 |
if (context != "local") yOff -= cm.display.viewOffset;
|
948 |
if (context == "page") {
|
949 |
-
var lOff = cm.display.lineSpace
|
950 |
yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
|
951 |
var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
|
952 |
rect.left += xOff; rect.right += xOff;
|
@@ -955,13 +1089,33 @@ window.CodeMirror = (function() {
|
|
955 |
return rect;
|
956 |
}
|
957 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
958 |
function charCoords(cm, pos, context, lineObj) {
|
959 |
-
if (!lineObj) lineObj = getLine(cm.
|
960 |
return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
|
961 |
}
|
962 |
|
963 |
function cursorCoords(cm, pos, context, lineObj, measurement) {
|
964 |
-
lineObj = lineObj || getLine(cm.
|
965 |
if (!measurement) measurement = measureLine(cm, lineObj);
|
966 |
function get(ch, right) {
|
967 |
var m = measureChar(cm, lineObj, ch, measurement);
|
@@ -976,10 +1130,9 @@ window.CodeMirror = (function() {
|
|
976 |
if (part.from < ch && part.to > ch) return get(ch, rtl);
|
977 |
var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
|
978 |
if (left == ch) {
|
979 |
-
//
|
980 |
-
//
|
981 |
-
//
|
982 |
-
// level.
|
983 |
if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
|
984 |
else here = get(rtl && part.from != part.to ? ch - 1 : ch);
|
985 |
if (rtl == linedir) main = here; else other = here;
|
@@ -997,21 +1150,29 @@ window.CodeMirror = (function() {
|
|
997 |
return main;
|
998 |
}
|
999 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1000 |
// Coords must be lineSpace-local
|
1001 |
function coordsChar(cm, x, y) {
|
1002 |
-
var doc = cm.
|
1003 |
y += cm.display.viewOffset;
|
1004 |
-
if (y < 0) return
|
1005 |
-
var lineNo = lineAtHeight(doc, y);
|
1006 |
-
if (lineNo
|
|
|
1007 |
if (x < 0) x = 0;
|
1008 |
|
1009 |
for (;;) {
|
1010 |
var lineObj = getLine(doc, lineNo);
|
1011 |
var found = coordsCharInner(cm, lineObj, lineNo, x, y);
|
1012 |
var merged = collapsedSpanAtEnd(lineObj);
|
1013 |
-
|
1014 |
-
|
|
|
1015 |
else
|
1016 |
return found;
|
1017 |
}
|
@@ -1019,30 +1180,32 @@ window.CodeMirror = (function() {
|
|
1019 |
|
1020 |
function coordsCharInner(cm, lineObj, lineNo, x, y) {
|
1021 |
var innerOff = y - heightAtLine(cm, lineObj);
|
1022 |
-
var wrongLine = false,
|
1023 |
var measurement = measureLine(cm, lineObj);
|
1024 |
|
1025 |
function getX(ch) {
|
1026 |
-
var sp = cursorCoords(cm,
|
1027 |
lineObj, measurement);
|
1028 |
wrongLine = true;
|
1029 |
-
if (innerOff > sp.bottom) return
|
1030 |
-
else if (innerOff < sp.top) return sp.left +
|
1031 |
else wrongLine = false;
|
1032 |
return sp.left;
|
1033 |
}
|
1034 |
|
1035 |
var bidi = getOrder(lineObj), dist = lineObj.text.length;
|
1036 |
var from = lineLeft(lineObj), to = lineRight(lineObj);
|
1037 |
-
var fromX =
|
1038 |
|
1039 |
-
if (x > toX) return
|
1040 |
// Do a binary search between these bounds.
|
1041 |
for (;;) {
|
1042 |
if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
|
1043 |
var after = x - fromX < toX - x, ch = after ? from : to;
|
1044 |
while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
|
1045 |
-
|
|
|
|
|
1046 |
}
|
1047 |
var step = Math.ceil(dist / 2), middle = from + step;
|
1048 |
if (bidi) {
|
@@ -1050,8 +1213,8 @@ window.CodeMirror = (function() {
|
|
1050 |
for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
|
1051 |
}
|
1052 |
var middleX = getX(middle);
|
1053 |
-
if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;}
|
1054 |
-
else {from = middle; fromX = middleX; dist = step;}
|
1055 |
}
|
1056 |
}
|
1057 |
|
@@ -1092,78 +1255,117 @@ window.CodeMirror = (function() {
|
|
1092 |
// be awkward, slow, and error-prone), but instead updates are
|
1093 |
// batched and then all combined and executed at once.
|
1094 |
|
|
|
1095 |
function startOperation(cm) {
|
1096 |
-
|
1097 |
-
else cm.curOp = {
|
1098 |
-
// Nested operations delay update until the outermost one
|
1099 |
-
// finishes.
|
1100 |
-
depth: 1,
|
1101 |
// An array of ranges of lines that have to be updated. See
|
1102 |
// updateDisplay.
|
1103 |
changes: [],
|
1104 |
-
delayedCallbacks: [],
|
1105 |
updateInput: null,
|
1106 |
userSelChange: null,
|
1107 |
textChanged: null,
|
1108 |
selectionChanged: false,
|
1109 |
updateMaxLine: false,
|
1110 |
-
|
|
|
1111 |
};
|
|
|
1112 |
}
|
1113 |
|
1114 |
function endOperation(cm) {
|
1115 |
-
var op = cm.curOp;
|
1116 |
-
if (--op.depth) return;
|
1117 |
cm.curOp = null;
|
1118 |
-
|
1119 |
-
if (op.updateMaxLine) computeMaxLength(
|
1120 |
-
if (
|
1121 |
-
var width =
|
1122 |
-
display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px";
|
1123 |
-
|
|
|
|
|
|
|
1124 |
}
|
1125 |
var newScrollPos, updated;
|
1126 |
-
if (op.
|
1127 |
-
|
|
|
|
|
1128 |
newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
|
1129 |
}
|
1130 |
-
if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null)
|
1131 |
updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
|
|
|
|
|
1132 |
if (!updated && op.selectionChanged) updateSelection(cm);
|
1133 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
1134 |
if (op.selectionChanged) restartBlink(cm);
|
1135 |
|
1136 |
-
if (
|
1137 |
resetInput(cm, op.userSelChange);
|
1138 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1139 |
if (op.textChanged)
|
1140 |
signal(cm, "change", cm, op.textChanged);
|
1141 |
if (op.selectionChanged) signal(cm, "cursorActivity", cm);
|
1142 |
-
for (var i = 0; i <
|
1143 |
}
|
1144 |
|
1145 |
// Wraps a function in an operation. Returns the wrapped function.
|
1146 |
function operation(cm1, f) {
|
1147 |
return function() {
|
1148 |
-
var cm = cm1 || this;
|
1149 |
-
startOperation(cm);
|
1150 |
-
try {var result = f.apply(cm, arguments);}
|
1151 |
-
finally {endOperation(cm);}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1152 |
return result;
|
1153 |
};
|
1154 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1155 |
|
1156 |
function regChange(cm, from, to, lendiff) {
|
|
|
|
|
1157 |
cm.curOp.changes.push({from: from, to: to, diff: lendiff});
|
1158 |
}
|
1159 |
|
1160 |
// INPUT HANDLING
|
1161 |
|
1162 |
function slowPoll(cm) {
|
1163 |
-
if (cm.
|
1164 |
cm.display.poll.set(cm.options.pollInterval, function() {
|
1165 |
readInput(cm);
|
1166 |
-
if (cm.
|
1167 |
});
|
1168 |
}
|
1169 |
|
@@ -1184,50 +1386,59 @@ window.CodeMirror = (function() {
|
|
1184 |
// events that indicate IME taking place, but these are not widely
|
1185 |
// supported or compatible enough yet to rely on.)
|
1186 |
function readInput(cm) {
|
1187 |
-
var input = cm.display.input, prevInput = cm.display.prevInput,
|
1188 |
-
if (!
|
1189 |
var text = input.value;
|
1190 |
if (text == prevInput && posEq(sel.from, sel.to)) return false;
|
1191 |
-
|
1192 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1193 |
var same = 0, l = Math.min(prevInput.length, text.length);
|
1194 |
while (same < l && prevInput[same] == text[same]) ++same;
|
1195 |
var from = sel.from, to = sel.to;
|
1196 |
if (same < prevInput.length)
|
1197 |
-
from =
|
1198 |
-
else if (
|
1199 |
-
to =
|
1200 |
var updateInput = cm.curOp.updateInput;
|
1201 |
-
|
1202 |
-
|
|
|
1203 |
cm.curOp.updateInput = updateInput;
|
1204 |
-
if (text.length > 1000) input.value = cm.display.prevInput = "";
|
1205 |
else cm.display.prevInput = text;
|
1206 |
-
endOperation(cm);
|
1207 |
-
cm.
|
1208 |
return true;
|
1209 |
}
|
1210 |
|
1211 |
function resetInput(cm, user) {
|
1212 |
-
var
|
1213 |
-
if (!posEq(
|
1214 |
cm.display.prevInput = "";
|
1215 |
minimal = hasCopyEvent &&
|
1216 |
-
(
|
1217 |
if (minimal) cm.display.input.value = "-";
|
1218 |
else cm.display.input.value = selected || cm.getSelection();
|
1219 |
-
if (
|
1220 |
} else if (user) cm.display.prevInput = cm.display.input.value = "";
|
1221 |
cm.display.inaccurateSelection = minimal;
|
1222 |
}
|
1223 |
|
1224 |
function focusInput(cm) {
|
1225 |
-
if (cm.options.readOnly != "nocursor" && (
|
1226 |
cm.display.input.focus();
|
1227 |
}
|
1228 |
|
1229 |
function isReadOnly(cm) {
|
1230 |
-
return cm.options.readOnly || cm.
|
1231 |
}
|
1232 |
|
1233 |
// EVENT HANDLERS
|
@@ -1237,44 +1448,56 @@ window.CodeMirror = (function() {
|
|
1237 |
on(d.scroller, "mousedown", operation(cm, onMouseDown));
|
1238 |
on(d.scroller, "dblclick", operation(cm, e_preventDefault));
|
1239 |
on(d.lineSpace, "selectstart", function(e) {
|
1240 |
-
if (!
|
1241 |
});
|
1242 |
// Gecko browsers fire contextmenu *after* opening the menu, at
|
1243 |
// which point we can't mess with it anymore. Context menu is
|
1244 |
// handled in onMouseDown for Gecko.
|
1245 |
-
if (!
|
1246 |
|
1247 |
on(d.scroller, "scroll", function() {
|
1248 |
-
|
1249 |
-
|
1250 |
-
|
|
|
|
|
1251 |
});
|
1252 |
on(d.scrollbarV, "scroll", function() {
|
1253 |
-
setScrollTop(cm, d.scrollbarV.scrollTop);
|
1254 |
});
|
1255 |
on(d.scrollbarH, "scroll", function() {
|
1256 |
-
setScrollLeft(cm, d.scrollbarH.scrollLeft);
|
1257 |
});
|
1258 |
|
1259 |
on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
|
1260 |
on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
|
1261 |
|
1262 |
-
function reFocus() { if (cm.
|
1263 |
on(d.scrollbarH, "mousedown", reFocus);
|
1264 |
on(d.scrollbarV, "mousedown", reFocus);
|
1265 |
// Prevent wrapper from ever scrolling
|
1266 |
on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
|
1267 |
-
|
|
|
1268 |
// Might be a text scaling operation, clear size caches.
|
1269 |
d.cachedCharWidth = d.cachedTextHeight = null;
|
1270 |
clearCaches(cm);
|
1271 |
-
|
1272 |
-
|
1273 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1274 |
|
1275 |
on(d.input, "keyup", operation(cm, function(e) {
|
1276 |
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
1277 |
-
if (
|
1278 |
}));
|
1279 |
on(d.input, "input", bind(fastPoll, cm));
|
1280 |
on(d.input, "keydown", operation(cm, onKeyDown));
|
@@ -1292,9 +1515,13 @@ window.CodeMirror = (function() {
|
|
1292 |
on(d.scroller, "dragover", drag_);
|
1293 |
on(d.scroller, "drop", operation(cm, onDrop));
|
1294 |
}
|
1295 |
-
on(d.scroller, "paste", function(){
|
|
|
|
|
|
|
|
|
1296 |
on(d.input, "paste", function() {
|
1297 |
-
|
1298 |
fastPoll(cm);
|
1299 |
});
|
1300 |
|
@@ -1316,10 +1543,12 @@ window.CodeMirror = (function() {
|
|
1316 |
});
|
1317 |
}
|
1318 |
|
1319 |
-
function
|
1320 |
-
for (var n = e_target(e); n != display.wrapper; n = n.parentNode)
|
|
|
1321 |
if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
|
1322 |
n.parentNode == display.sizer && n != display.mover) return true;
|
|
|
1323 |
}
|
1324 |
|
1325 |
function posFromMouse(cm, e, liberal) {
|
@@ -1330,7 +1559,7 @@ window.CodeMirror = (function() {
|
|
1330 |
target == display.scrollbarV || target == display.scrollbarV.firstChild ||
|
1331 |
target == display.scrollbarFiller) return null;
|
1332 |
}
|
1333 |
-
var x, y, space = display.lineSpace
|
1334 |
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
|
1335 |
try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
|
1336 |
return coordsChar(cm, x - space.left, y - space.top);
|
@@ -1338,10 +1567,10 @@ window.CodeMirror = (function() {
|
|
1338 |
|
1339 |
var lastClick, lastDoubleClick;
|
1340 |
function onMouseDown(e) {
|
1341 |
-
var cm = this, display = cm.display,
|
1342 |
-
sel.shift =
|
1343 |
|
1344 |
-
if (
|
1345 |
if (!webkit) {
|
1346 |
display.scroller.draggable = false;
|
1347 |
setTimeout(function(){display.scroller.draggable = true;}, 100);
|
@@ -1353,10 +1582,10 @@ window.CodeMirror = (function() {
|
|
1353 |
|
1354 |
switch (e_button(e)) {
|
1355 |
case 3:
|
1356 |
-
if (
|
1357 |
return;
|
1358 |
case 2:
|
1359 |
-
if (start) extendSelection(cm, start);
|
1360 |
setTimeout(bind(focusInput, cm), 20);
|
1361 |
e_preventDefault(e);
|
1362 |
return;
|
@@ -1366,7 +1595,7 @@ window.CodeMirror = (function() {
|
|
1366 |
// selection.
|
1367 |
if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
|
1368 |
|
1369 |
-
if (!
|
1370 |
|
1371 |
var now = +new Date, type = "single";
|
1372 |
if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
|
@@ -1379,7 +1608,7 @@ window.CodeMirror = (function() {
|
|
1379 |
lastDoubleClick = {time: now, pos: start};
|
1380 |
e_preventDefault(e);
|
1381 |
var word = findWordAt(getLine(doc, start.line).text, start);
|
1382 |
-
extendSelection(cm, word.from, word.to);
|
1383 |
} else { lastClick = {time: now, pos: start}; }
|
1384 |
|
1385 |
var last = start;
|
@@ -1387,18 +1616,18 @@ window.CodeMirror = (function() {
|
|
1387 |
!posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
|
1388 |
var dragEnd = operation(cm, function(e2) {
|
1389 |
if (webkit) display.scroller.draggable = false;
|
1390 |
-
|
1391 |
off(document, "mouseup", dragEnd);
|
1392 |
off(display.scroller, "drop", dragEnd);
|
1393 |
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
|
1394 |
e_preventDefault(e2);
|
1395 |
-
extendSelection(cm, start);
|
1396 |
focusInput(cm);
|
1397 |
}
|
1398 |
});
|
1399 |
// Let the drag handler handle this.
|
1400 |
if (webkit) display.scroller.draggable = true;
|
1401 |
-
|
1402 |
// IE's approach to draggable
|
1403 |
if (display.scroller.dragDrop) display.scroller.dragDrop();
|
1404 |
on(document, "mouseup", dragEnd);
|
@@ -1406,13 +1635,13 @@ window.CodeMirror = (function() {
|
|
1406 |
return;
|
1407 |
}
|
1408 |
e_preventDefault(e);
|
1409 |
-
if (type == "single") extendSelection(cm, clipPos(doc, start));
|
1410 |
|
1411 |
var startstart = sel.from, startend = sel.to;
|
1412 |
|
1413 |
function doSelect(cur) {
|
1414 |
if (type == "single") {
|
1415 |
-
extendSelection(cm, clipPos(doc, start), cur);
|
1416 |
return;
|
1417 |
}
|
1418 |
|
@@ -1420,15 +1649,15 @@ window.CodeMirror = (function() {
|
|
1420 |
startend = clipPos(doc, startend);
|
1421 |
if (type == "double") {
|
1422 |
var word = findWordAt(getLine(doc, cur.line).text, cur);
|
1423 |
-
if (posLess(cur, startstart)) extendSelection(cm, word.from, startend);
|
1424 |
-
else extendSelection(cm, startstart, word.to);
|
1425 |
} else if (type == "triple") {
|
1426 |
-
if (posLess(cur, startstart)) extendSelection(cm, startend, clipPos(doc,
|
1427 |
-
else extendSelection(cm, startstart, clipPos(doc,
|
1428 |
}
|
1429 |
}
|
1430 |
|
1431 |
-
var editorSize = display.wrapper
|
1432 |
// Used to ensure timeout re-tries don't fire when another extend
|
1433 |
// happened in the meantime (clearTimeout isn't reliable -- at
|
1434 |
// least on Chrome, the timeouts still happen even when cleared,
|
@@ -1440,7 +1669,7 @@ window.CodeMirror = (function() {
|
|
1440 |
var cur = posFromMouse(cm, e, true);
|
1441 |
if (!cur) return;
|
1442 |
if (!posEq(cur, last)) {
|
1443 |
-
if (!
|
1444 |
last = cur;
|
1445 |
doSelect(cur);
|
1446 |
var visible = visibleLines(display, doc);
|
@@ -1477,7 +1706,8 @@ window.CodeMirror = (function() {
|
|
1477 |
|
1478 |
function onDrop(e) {
|
1479 |
var cm = this;
|
1480 |
-
if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e)))
|
|
|
1481 |
e_preventDefault(e);
|
1482 |
var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
|
1483 |
if (!pos || isReadOnly(cm)) return;
|
@@ -1488,11 +1718,8 @@ window.CodeMirror = (function() {
|
|
1488 |
reader.onload = function() {
|
1489 |
text[i] = reader.result;
|
1490 |
if (++read == n) {
|
1491 |
-
pos = clipPos(cm.
|
1492 |
-
|
1493 |
-
var end = replaceRange(cm, text.join(""), pos, pos, "paste");
|
1494 |
-
setSelection(cm, pos, end);
|
1495 |
-
})();
|
1496 |
}
|
1497 |
};
|
1498 |
reader.readAsText(file);
|
@@ -1500,17 +1727,18 @@ window.CodeMirror = (function() {
|
|
1500 |
for (var i = 0; i < n; ++i) loadFile(files[i], i);
|
1501 |
} else {
|
1502 |
// Don't do a replace if the drop happened inside of the selected text.
|
1503 |
-
if (cm.
|
1504 |
-
cm.
|
1505 |
-
|
|
|
1506 |
return;
|
1507 |
}
|
1508 |
try {
|
1509 |
var text = e.dataTransfer.getData("Text");
|
1510 |
if (text) {
|
1511 |
-
var curFrom = cm.
|
1512 |
-
setSelection(cm, pos, pos);
|
1513 |
-
if (cm.
|
1514 |
cm.replaceSelection(text, null, "paste");
|
1515 |
focusInput(cm);
|
1516 |
onFocus(cm);
|
@@ -1525,20 +1753,20 @@ window.CodeMirror = (function() {
|
|
1525 |
try { var mX = e.clientX, mY = e.clientY; }
|
1526 |
catch(e) { return false; }
|
1527 |
|
1528 |
-
if (mX >= Math.floor(display.gutters
|
1529 |
e_preventDefault(e);
|
1530 |
if (!hasHandler(cm, "gutterClick")) return true;
|
1531 |
|
1532 |
-
var lineBox = display.lineDiv
|
1533 |
if (mY > lineBox.bottom) return true;
|
1534 |
mY -= lineBox.top - display.viewOffset;
|
1535 |
|
1536 |
for (var i = 0; i < cm.options.gutters.length; ++i) {
|
1537 |
var g = display.gutters.childNodes[i];
|
1538 |
-
if (g && g
|
1539 |
-
var line = lineAtHeight(cm.
|
1540 |
var gutter = cm.options.gutters[i];
|
1541 |
-
signalLater(cm,
|
1542 |
break;
|
1543 |
}
|
1544 |
}
|
@@ -1546,26 +1774,47 @@ window.CodeMirror = (function() {
|
|
1546 |
}
|
1547 |
|
1548 |
function onDragStart(cm, e) {
|
|
|
|
|
1549 |
var txt = cm.getSelection();
|
1550 |
e.dataTransfer.setData("Text", txt);
|
1551 |
|
1552 |
// Use dummy image instead of default browsers image.
|
1553 |
// Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
|
1554 |
-
if (e.dataTransfer.setDragImage
|
1555 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1556 |
}
|
1557 |
|
1558 |
function setScrollTop(cm, val) {
|
1559 |
-
if (Math.abs(cm.
|
1560 |
-
cm.
|
1561 |
if (!gecko) updateDisplay(cm, [], val);
|
1562 |
if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
|
1563 |
if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
|
1564 |
if (gecko) updateDisplay(cm, []);
|
1565 |
}
|
1566 |
function setScrollLeft(cm, val, isScroller) {
|
1567 |
-
if (isScroller ? val == cm.
|
1568 |
-
cm.
|
|
|
1569 |
alignHorizontally(cm);
|
1570 |
if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
|
1571 |
if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
|
@@ -1582,7 +1831,7 @@ window.CodeMirror = (function() {
|
|
1582 |
// is that it gives us a chance to update the display before the
|
1583 |
// actual scrolling happens, reducing flickering.
|
1584 |
|
1585 |
-
var wheelSamples = 0,
|
1586 |
// Fill in a browser-detected starting value on browsers where we
|
1587 |
// know one. These don't have to be accurate -- the result of them
|
1588 |
// being wrong would just be a slight flicker on the first wheel
|
@@ -1611,7 +1860,7 @@ window.CodeMirror = (function() {
|
|
1611 |
}
|
1612 |
}
|
1613 |
|
1614 |
-
var
|
1615 |
// On some browsers, horizontal scrolling will cause redraws to
|
1616 |
// happen before the gutter has been realigned, causing it to
|
1617 |
// wriggle around in a most unseemly way. When we have an
|
@@ -1623,35 +1872,35 @@ window.CodeMirror = (function() {
|
|
1623 |
setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
|
1624 |
setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
|
1625 |
e_preventDefault(e);
|
1626 |
-
wheelStartX = null; // Abort measurement, if in progress
|
1627 |
return;
|
1628 |
}
|
1629 |
|
1630 |
if (dy && wheelPixelsPerUnit != null) {
|
1631 |
var pixels = dy * wheelPixelsPerUnit;
|
1632 |
-
var top = cm.
|
1633 |
if (pixels < 0) top = Math.max(0, top + pixels - 50);
|
1634 |
-
else bot = Math.min(cm.
|
1635 |
updateDisplay(cm, [], {top: top, bottom: bot});
|
1636 |
}
|
1637 |
|
1638 |
if (wheelSamples < 20) {
|
1639 |
-
if (wheelStartX == null) {
|
1640 |
-
wheelStartX = scroll.scrollLeft; wheelStartY = scroll.scrollTop;
|
1641 |
-
wheelDX = dx; wheelDY = dy;
|
1642 |
setTimeout(function() {
|
1643 |
-
if (wheelStartX == null) return;
|
1644 |
-
var movedX = scroll.scrollLeft - wheelStartX;
|
1645 |
-
var movedY = scroll.scrollTop - wheelStartY;
|
1646 |
-
var sample = (movedY && wheelDY && movedY / wheelDY) ||
|
1647 |
-
(movedX && wheelDX && movedX / wheelDX);
|
1648 |
-
wheelStartX = wheelStartY = null;
|
1649 |
if (!sample) return;
|
1650 |
wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
|
1651 |
++wheelSamples;
|
1652 |
}, 200);
|
1653 |
} else {
|
1654 |
-
wheelDX += dx; wheelDY += dy;
|
1655 |
}
|
1656 |
}
|
1657 |
}
|
@@ -1664,25 +1913,22 @@ window.CodeMirror = (function() {
|
|
1664 |
// Ensure previous input has been read, so that the handler sees a
|
1665 |
// consistent view of the document
|
1666 |
if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
|
1667 |
-
var
|
1668 |
try {
|
1669 |
-
if (isReadOnly(cm))
|
1670 |
-
if (dropShift)
|
1671 |
-
bound(cm);
|
1672 |
-
} catch(e) {
|
1673 |
-
if (e != Pass) throw e;
|
1674 |
-
return false;
|
1675 |
} finally {
|
1676 |
-
|
1677 |
-
|
1678 |
}
|
1679 |
-
return
|
1680 |
}
|
1681 |
|
1682 |
function allKeyMaps(cm) {
|
1683 |
-
var maps = cm.
|
|
|
1684 |
maps.push(cm.options.keyMap);
|
1685 |
-
if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys);
|
1686 |
return maps;
|
1687 |
}
|
1688 |
|
@@ -1696,28 +1942,23 @@ window.CodeMirror = (function() {
|
|
1696 |
cm.options.keyMap = (next.call ? next.call(null, cm) : next);
|
1697 |
}, 50);
|
1698 |
|
1699 |
-
var name =
|
1700 |
-
|
1701 |
-
if (name == null || e.altGraphKey) return false;
|
1702 |
-
if (e_prop(e, "altKey")) name = "Alt-" + name;
|
1703 |
-
if (e_prop(e, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name;
|
1704 |
-
if (e_prop(e, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name;
|
1705 |
-
|
1706 |
-
var stopped = false;
|
1707 |
-
function stop() { stopped = true; }
|
1708 |
var keymaps = allKeyMaps(cm);
|
1709 |
|
1710 |
-
if (
|
1711 |
-
|
1712 |
-
|
1713 |
-
|
1714 |
-
|
1715 |
-
|
|
|
|
|
1716 |
} else {
|
1717 |
-
handled = lookupKey(name, keymaps,
|
1718 |
-
function(b) { return doHandleBinding(cm, b); }, stop);
|
1719 |
}
|
1720 |
-
if (
|
|
|
1721 |
if (handled) {
|
1722 |
e_preventDefault(e);
|
1723 |
restartBlink(cm);
|
@@ -1739,18 +1980,18 @@ window.CodeMirror = (function() {
|
|
1739 |
var lastStoppedKey = null;
|
1740 |
function onKeyDown(e) {
|
1741 |
var cm = this;
|
1742 |
-
if (!cm.
|
1743 |
if (ie && e.keyCode == 27) { e.returnValue = false; }
|
1744 |
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
1745 |
-
var code =
|
1746 |
// IE does strange things with escape.
|
1747 |
-
cm.
|
1748 |
// First give onKeyEvent option a chance to handle this.
|
1749 |
var handled = handleKeyBinding(cm, e);
|
1750 |
if (opera) {
|
1751 |
lastStoppedKey = handled ? code : null;
|
1752 |
// Opera has no cut event... we try to at least catch the key combo
|
1753 |
-
if (!handled && code == 88 && !hasCopyEvent &&
|
1754 |
cm.replaceSelection("");
|
1755 |
}
|
1756 |
}
|
@@ -1758,53 +1999,55 @@ window.CodeMirror = (function() {
|
|
1758 |
function onKeyPress(e) {
|
1759 |
var cm = this;
|
1760 |
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
1761 |
-
var keyCode =
|
1762 |
if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
|
1763 |
if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
|
1764 |
var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
|
1765 |
-
if (this.options.electricChars && this.
|
1766 |
this.options.smartIndent && !isReadOnly(this) &&
|
1767 |
-
this.
|
1768 |
-
setTimeout(operation(cm, function() {indentLine(cm, cm.
|
1769 |
if (handleCharBinding(cm, e, ch)) return;
|
1770 |
fastPoll(cm);
|
1771 |
}
|
1772 |
|
1773 |
function onFocus(cm) {
|
1774 |
if (cm.options.readOnly == "nocursor") return;
|
1775 |
-
if (!cm.
|
1776 |
signal(cm, "focus", cm);
|
1777 |
-
cm.
|
1778 |
-
if (cm.display.
|
1779 |
-
cm.display.
|
1780 |
resetInput(cm, true);
|
1781 |
}
|
1782 |
slowPoll(cm);
|
1783 |
restartBlink(cm);
|
1784 |
}
|
1785 |
function onBlur(cm) {
|
1786 |
-
if (cm.
|
1787 |
signal(cm, "blur", cm);
|
1788 |
-
cm.
|
1789 |
-
cm.display.
|
1790 |
}
|
1791 |
clearInterval(cm.display.blinker);
|
1792 |
-
setTimeout(function() {if (!cm.
|
1793 |
}
|
1794 |
|
1795 |
var detectingSelectAll;
|
1796 |
function onContextMenu(cm, e) {
|
1797 |
-
var display = cm.display, sel = cm.
|
|
|
|
|
1798 |
var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
|
1799 |
if (!pos || opera) return; // Opera is difficult.
|
1800 |
if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
|
1801 |
-
operation(cm, setSelection)(cm, pos, pos);
|
1802 |
|
1803 |
var oldCSS = display.input.style.cssText;
|
1804 |
display.inputDiv.style.position = "absolute";
|
1805 |
display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
|
1806 |
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
|
1807 |
-
"border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
|
1808 |
focusInput(cm);
|
1809 |
resetInput(cm, true);
|
1810 |
// Adds "Select all" to context menu in FF
|
@@ -1816,27 +2059,29 @@ window.CodeMirror = (function() {
|
|
1816 |
if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
|
1817 |
slowPoll(cm);
|
1818 |
|
1819 |
-
// Try to detect the user choosing select-all
|
1820 |
-
if (display.input.selectionStart != null) {
|
1821 |
clearTimeout(detectingSelectAll);
|
1822 |
var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
|
1823 |
display.prevInput = " ";
|
1824 |
display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
|
1825 |
-
|
1826 |
if (display.prevInput == " " && display.input.selectionStart == 0)
|
1827 |
operation(cm, commands.selectAll)(cm);
|
1828 |
else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
|
1829 |
else resetInput(cm);
|
1830 |
-
}
|
|
|
1831 |
}
|
1832 |
}
|
1833 |
|
1834 |
-
if (
|
1835 |
e_stop(e);
|
1836 |
-
|
1837 |
off(window, "mouseup", mouseup);
|
1838 |
setTimeout(rehide, 20);
|
1839 |
-
}
|
|
|
1840 |
} else {
|
1841 |
setTimeout(rehide, 50);
|
1842 |
}
|
@@ -1844,124 +2089,208 @@ window.CodeMirror = (function() {
|
|
1844 |
|
1845 |
// UPDATING
|
1846 |
|
1847 |
-
|
1848 |
-
|
1849 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1850 |
// Possibly split or suppress the update based on the presence
|
1851 |
// of read-only spans in its range.
|
1852 |
-
var split = sawReadOnlySpans &&
|
1853 |
-
removeReadOnlyRanges(cm.view.doc, from, to);
|
1854 |
if (split) {
|
1855 |
for (var i = split.length - 1; i >= 1; --i)
|
1856 |
-
|
1857 |
if (split.length)
|
1858 |
-
|
1859 |
} else {
|
1860 |
-
|
1861 |
}
|
1862 |
}
|
1863 |
|
1864 |
-
function
|
1865 |
-
|
|
|
1866 |
|
1867 |
-
|
1868 |
-
|
1869 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1870 |
});
|
1871 |
-
|
1872 |
-
|
1873 |
-
|
1874 |
-
if (
|
1875 |
-
|
1876 |
-
|
1877 |
-
|
1878 |
-
|
1879 |
-
|
1880 |
-
|
1881 |
-
var
|
1882 |
-
|
1883 |
-
var anti = {events: [], fromBefore: set.fromAfter, toBefore: set.toAfter,
|
1884 |
-
fromAfter: set.fromBefore, toAfter: set.toBefore};
|
1885 |
-
for (var i = set.events.length - 1; i >= 0; i -= 1) {
|
1886 |
-
hist.dirtyCounter += type == "undo" ? -1 : 1;
|
1887 |
-
var change = set.events[i];
|
1888 |
-
var replaced = [], end = change.start + change.added;
|
1889 |
-
doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); });
|
1890 |
-
anti.events.push({start: change.start, added: change.old.length, old: replaced});
|
1891 |
-
var selPos = i ? null : {from: set.fromBefore, to: set.toBefore};
|
1892 |
-
updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length},
|
1893 |
-
change.old, selPos, type);
|
1894 |
-
}
|
1895 |
(type == "undo" ? hist.undone : hist.done).push(anti);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1896 |
}
|
1897 |
|
1898 |
-
function
|
1899 |
-
var
|
1900 |
-
if (view.suppressEdits) return;
|
1901 |
|
1902 |
-
var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
|
1903 |
var recomputeMaxLength = false, checkWidthStart = from.line;
|
1904 |
if (!cm.options.lineWrapping) {
|
1905 |
-
checkWidthStart = lineNo(visualLine(doc,
|
1906 |
doc.iter(checkWidthStart, to.line + 1, function(line) {
|
1907 |
-
if (
|
1908 |
recomputeMaxLength = true;
|
1909 |
return true;
|
1910 |
}
|
1911 |
});
|
1912 |
}
|
1913 |
|
1914 |
-
|
1915 |
-
|
1916 |
-
// First adjust the line structure
|
1917 |
-
if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") {
|
1918 |
-
// This is a whole-line replace. Treated specially to make
|
1919 |
-
// sure line objects move the way they are supposed to.
|
1920 |
-
var added = [];
|
1921 |
-
for (var i = 0, e = lines.length - 1; i < e; ++i)
|
1922 |
-
added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
|
1923 |
-
updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL));
|
1924 |
-
if (nlines) doc.remove(from.line, nlines, cm);
|
1925 |
-
if (added.length) doc.insert(from.line, added);
|
1926 |
-
} else if (firstLine == lastLine) {
|
1927 |
-
if (lines.length == 1) {
|
1928 |
-
updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
|
1929 |
-
firstLine.text.slice(to.ch), hlSpans(lines[0]));
|
1930 |
-
} else {
|
1931 |
-
for (var added = [], i = 1, e = lines.length - 1; i < e; ++i)
|
1932 |
-
added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
|
1933 |
-
added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th));
|
1934 |
-
updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
|
1935 |
-
doc.insert(from.line + 1, added);
|
1936 |
-
}
|
1937 |
-
} else if (lines.length == 1) {
|
1938 |
-
updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) +
|
1939 |
-
lastLine.text.slice(to.ch), hlSpans(lines[0]));
|
1940 |
-
doc.remove(from.line + 1, nlines, cm);
|
1941 |
-
} else {
|
1942 |
-
var added = [];
|
1943 |
-
updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]));
|
1944 |
-
updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL));
|
1945 |
-
for (var i = 1, e = lines.length - 1; i < e; ++i)
|
1946 |
-
added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th));
|
1947 |
-
if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm);
|
1948 |
-
doc.insert(from.line + 1, added);
|
1949 |
-
}
|
1950 |
|
1951 |
-
if (cm.options.lineWrapping) {
|
1952 |
-
|
1953 |
-
doc.iter(from.line, from.line + lines.length, function(line) {
|
1954 |
-
if (line.height == 0) return;
|
1955 |
-
var guess = (Math.ceil(line.text.length / perLine) || 1) * th;
|
1956 |
-
if (guess != line.height) updateLineHeight(line, guess);
|
1957 |
-
});
|
1958 |
-
} else {
|
1959 |
-
doc.iter(checkWidthStart, from.line + lines.length, function(line) {
|
1960 |
var len = lineLength(doc, line);
|
1961 |
-
if (len >
|
1962 |
-
|
1963 |
-
|
1964 |
-
|
1965 |
recomputeMaxLength = false;
|
1966 |
}
|
1967 |
});
|
@@ -1969,82 +2298,66 @@ window.CodeMirror = (function() {
|
|
1969 |
}
|
1970 |
|
1971 |
// Adjust frontier, schedule worker
|
1972 |
-
|
1973 |
startWorker(cm, 400);
|
1974 |
|
1975 |
-
var lendiff =
|
1976 |
// Remember that these lines changed, for updating the display
|
1977 |
regChange(cm, from.line, to.line + 1, lendiff);
|
|
|
1978 |
if (hasHandler(cm, "change")) {
|
1979 |
-
|
1980 |
-
|
1981 |
-
|
1982 |
-
|
1983 |
-
var changeObj = {from: from, to: to, text: lines, origin: origin};
|
1984 |
if (cm.curOp.textChanged) {
|
1985 |
for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
|
1986 |
cur.next = changeObj;
|
1987 |
} else cm.curOp.textChanged = changeObj;
|
1988 |
}
|
1989 |
-
|
1990 |
-
// Update the selection
|
1991 |
-
var newSelFrom, newSelTo, end = {line: from.line + lines.length - 1,
|
1992 |
-
ch: hlText(lastHL).length + (lines.length == 1 ? from.ch : 0)};
|
1993 |
-
if (selUpdate && typeof selUpdate != "string") {
|
1994 |
-
if (selUpdate.from) { newSelFrom = selUpdate.from; newSelTo = selUpdate.to; }
|
1995 |
-
else newSelFrom = newSelTo = selUpdate;
|
1996 |
-
} else if (selUpdate == "end") {
|
1997 |
-
newSelFrom = newSelTo = end;
|
1998 |
-
} else if (selUpdate == "start") {
|
1999 |
-
newSelFrom = newSelTo = from;
|
2000 |
-
} else if (selUpdate == "around") {
|
2001 |
-
newSelFrom = from; newSelTo = end;
|
2002 |
-
} else {
|
2003 |
-
var adjustPos = function(pos) {
|
2004 |
-
if (posLess(pos, from)) return pos;
|
2005 |
-
if (!posLess(to, pos)) return end;
|
2006 |
-
var line = pos.line + lendiff;
|
2007 |
-
var ch = pos.ch;
|
2008 |
-
if (pos.line == to.line)
|
2009 |
-
ch += hlText(lastHL).length - (to.ch - (to.line == from.line ? from.ch : 0));
|
2010 |
-
return {line: line, ch: ch};
|
2011 |
-
};
|
2012 |
-
newSelFrom = adjustPos(view.sel.from);
|
2013 |
-
newSelTo = adjustPos(view.sel.to);
|
2014 |
-
}
|
2015 |
-
setSelection(cm, newSelFrom, newSelTo, null, true);
|
2016 |
-
return end;
|
2017 |
}
|
2018 |
|
2019 |
-
function replaceRange(
|
2020 |
if (!to) to = from;
|
2021 |
if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
|
2022 |
-
|
|
|
2023 |
}
|
2024 |
|
2025 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
2026 |
|
2027 |
function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
|
2028 |
function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
|
2029 |
-
function copyPos(x) {return
|
|
|
|
|
2030 |
|
2031 |
-
function clipLine(doc, n) {return Math.max(
|
2032 |
function clipPos(doc, pos) {
|
2033 |
-
if (pos.line <
|
2034 |
-
|
2035 |
-
|
2036 |
-
|
2037 |
-
|
|
|
|
|
|
|
|
|
2038 |
else return pos;
|
2039 |
}
|
2040 |
-
function isLine(doc, l) {return l >=
|
2041 |
|
2042 |
// If shift is held, this will move the selection anchor. Otherwise,
|
2043 |
// it'll set the whole selection.
|
2044 |
-
function extendSelection(
|
2045 |
-
|
2046 |
-
|
2047 |
-
var anchor = sel.anchor;
|
2048 |
if (other) {
|
2049 |
var posBefore = posLess(pos, anchor);
|
2050 |
if (posBefore != posLess(other, anchor)) {
|
@@ -2054,24 +2367,38 @@ window.CodeMirror = (function() {
|
|
2054 |
pos = other;
|
2055 |
}
|
2056 |
}
|
2057 |
-
setSelection(
|
2058 |
} else {
|
2059 |
-
setSelection(
|
2060 |
}
|
2061 |
-
cm.curOp.userSelChange = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2062 |
}
|
2063 |
|
2064 |
// Update the selection. Last two args are only used by
|
2065 |
// updateDoc, since they have to be expressed in the line
|
2066 |
// numbers before the update.
|
2067 |
-
function setSelection(
|
2068 |
-
cm.
|
2069 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
2070 |
// Skip over atomic spans.
|
2071 |
if (checkAtomic || !posEq(anchor, sel.anchor))
|
2072 |
-
anchor = skipAtomic(
|
2073 |
if (checkAtomic || !posEq(head, sel.head))
|
2074 |
-
head = skipAtomic(
|
2075 |
|
2076 |
if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
|
2077 |
|
@@ -2080,47 +2407,53 @@ window.CodeMirror = (function() {
|
|
2080 |
sel.from = inv ? head : anchor;
|
2081 |
sel.to = inv ? anchor : head;
|
2082 |
|
2083 |
-
cm
|
2084 |
-
|
|
|
|
|
2085 |
}
|
2086 |
|
2087 |
function reCheckSelection(cm) {
|
2088 |
-
setSelection(cm, cm.
|
2089 |
}
|
2090 |
|
2091 |
-
function skipAtomic(
|
2092 |
-
var
|
2093 |
var dir = bias || 1;
|
2094 |
-
|
2095 |
search: for (;;) {
|
2096 |
-
var line = getLine(doc, curPos.line)
|
2097 |
if (line.markedSpans) {
|
2098 |
for (var i = 0; i < line.markedSpans.length; ++i) {
|
2099 |
var sp = line.markedSpans[i], m = sp.marker;
|
2100 |
if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
|
2101 |
(sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
|
2102 |
-
if (mayClear
|
2103 |
-
(
|
2104 |
-
|
2105 |
-
|
|
|
|
|
|
|
|
|
2106 |
var newPos = m.find()[dir < 0 ? "from" : "to"];
|
2107 |
if (posEq(newPos, curPos)) {
|
2108 |
newPos.ch += dir;
|
2109 |
if (newPos.ch < 0) {
|
2110 |
-
if (newPos.line) newPos = clipPos(doc,
|
2111 |
else newPos = null;
|
2112 |
} else if (newPos.ch > line.text.length) {
|
2113 |
-
if (newPos.line < doc.size - 1) newPos =
|
2114 |
else newPos = null;
|
2115 |
}
|
2116 |
if (!newPos) {
|
2117 |
if (flipped) {
|
2118 |
// Driven in a corner -- no valid cursor position found at all
|
2119 |
// -- try again *with* clearing, if we didn't already
|
2120 |
-
if (!mayClear) return skipAtomic(
|
2121 |
// Otherwise, turn off editing until further notice, and return the start of the doc
|
2122 |
-
|
2123 |
-
return
|
2124 |
}
|
2125 |
flipped = true; newPos = pos; dir = -dir;
|
2126 |
}
|
@@ -2129,7 +2462,6 @@ window.CodeMirror = (function() {
|
|
2129 |
continue search;
|
2130 |
}
|
2131 |
}
|
2132 |
-
if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear();
|
2133 |
}
|
2134 |
return curPos;
|
2135 |
}
|
@@ -2138,12 +2470,11 @@ window.CodeMirror = (function() {
|
|
2138 |
// SCROLLING
|
2139 |
|
2140 |
function scrollCursorIntoView(cm) {
|
2141 |
-
var
|
2142 |
-
|
2143 |
-
|
2144 |
-
|
2145 |
-
if (coords.
|
2146 |
-
else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
|
2147 |
if (doScroll != null && !phantom) {
|
2148 |
var hidden = display.cursor.style.display == "none";
|
2149 |
if (hidden) {
|
@@ -2156,18 +2487,19 @@ window.CodeMirror = (function() {
|
|
2156 |
}
|
2157 |
}
|
2158 |
|
2159 |
-
function scrollPosIntoView(cm, pos) {
|
|
|
2160 |
for (;;) {
|
2161 |
var changed = false, coords = cursorCoords(cm, pos);
|
2162 |
-
var scrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
|
2163 |
-
var startTop = cm.
|
2164 |
if (scrollPos.scrollTop != null) {
|
2165 |
setScrollTop(cm, scrollPos.scrollTop);
|
2166 |
-
if (Math.abs(cm.
|
2167 |
}
|
2168 |
if (scrollPos.scrollLeft != null) {
|
2169 |
setScrollLeft(cm, scrollPos.scrollLeft);
|
2170 |
-
if (Math.abs(cm.
|
2171 |
}
|
2172 |
if (!changed) return coords;
|
2173 |
}
|
@@ -2183,7 +2515,7 @@ window.CodeMirror = (function() {
|
|
2183 |
var display = cm.display, pt = paddingTop(display);
|
2184 |
y1 += pt; y2 += pt;
|
2185 |
var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
|
2186 |
-
var docBottom = cm.
|
2187 |
var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
|
2188 |
if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
|
2189 |
else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
|
@@ -2201,13 +2533,24 @@ window.CodeMirror = (function() {
|
|
2201 |
return result;
|
2202 |
}
|
2203 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2204 |
// API UTILITIES
|
2205 |
|
2206 |
function indentLine(cm, n, how, aggressive) {
|
2207 |
-
var doc = cm.
|
2208 |
if (!how) how = "add";
|
2209 |
if (how == "smart") {
|
2210 |
-
if (!cm.
|
2211 |
else var state = getStateBefore(cm, n);
|
2212 |
}
|
2213 |
|
@@ -2215,18 +2558,20 @@ window.CodeMirror = (function() {
|
|
2215 |
var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
|
2216 |
var curSpaceString = line.text.match(/^\s*/)[0], indentation;
|
2217 |
if (how == "smart") {
|
2218 |
-
indentation = cm.
|
2219 |
if (indentation == Pass) {
|
2220 |
if (!aggressive) return;
|
2221 |
how = "prev";
|
2222 |
}
|
2223 |
}
|
2224 |
if (how == "prev") {
|
2225 |
-
if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
|
2226 |
else indentation = 0;
|
|
|
|
|
|
|
|
|
2227 |
}
|
2228 |
-
else if (how == "add") indentation = curSpace + cm.options.indentUnit;
|
2229 |
-
else if (how == "subtract") indentation = curSpace - cm.options.indentUnit;
|
2230 |
indentation = Math.max(0, indentation);
|
2231 |
|
2232 |
var indentString = "", pos = 0;
|
@@ -2235,12 +2580,12 @@ window.CodeMirror = (function() {
|
|
2235 |
if (pos < indentation) indentString += spaceStr(indentation - pos);
|
2236 |
|
2237 |
if (indentString != curSpaceString)
|
2238 |
-
replaceRange(cm, indentString,
|
2239 |
line.stateAfter = null;
|
2240 |
}
|
2241 |
|
2242 |
function changeLine(cm, handle, op) {
|
2243 |
-
var no = handle, line = handle, doc = cm.
|
2244 |
if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
|
2245 |
else no = lineNo(handle);
|
2246 |
if (no == null) return null;
|
@@ -2249,12 +2594,13 @@ window.CodeMirror = (function() {
|
|
2249 |
return line;
|
2250 |
}
|
2251 |
|
2252 |
-
function findPosH(
|
2253 |
-
var
|
2254 |
var lineObj = getLine(doc, line);
|
|
|
2255 |
function findNextLine() {
|
2256 |
var l = line + dir;
|
2257 |
-
if (l <
|
2258 |
line = l;
|
2259 |
return lineObj = getLine(doc, l);
|
2260 |
}
|
@@ -2264,22 +2610,50 @@ window.CodeMirror = (function() {
|
|
2264 |
if (!boundToLine && findNextLine()) {
|
2265 |
if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
|
2266 |
else ch = dir < 0 ? lineObj.text.length : 0;
|
2267 |
-
} else return false;
|
2268 |
} else ch = next;
|
2269 |
return true;
|
2270 |
}
|
|
|
2271 |
if (unit == "char") moveOnce();
|
2272 |
else if (unit == "column") moveOnce(true);
|
2273 |
-
else if (unit == "word") {
|
2274 |
-
var
|
2275 |
-
for (;;) {
|
2276 |
-
if (dir < 0
|
2277 |
-
|
2278 |
-
|
2279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2280 |
}
|
2281 |
}
|
2282 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2283 |
}
|
2284 |
|
2285 |
function findWordAt(line, pos) {
|
@@ -2287,17 +2661,17 @@ window.CodeMirror = (function() {
|
|
2287 |
if (line) {
|
2288 |
if (pos.after === false || end == line.length) --start; else ++end;
|
2289 |
var startChar = line.charAt(start);
|
2290 |
-
var check = isWordChar(startChar) ? isWordChar
|
2291 |
-
/\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
|
2292 |
-
|
2293 |
while (start > 0 && check(line.charAt(start - 1))) --start;
|
2294 |
while (end < line.length && check(line.charAt(end))) ++end;
|
2295 |
}
|
2296 |
-
return {from:
|
2297 |
}
|
2298 |
|
2299 |
function selectLine(cm, line) {
|
2300 |
-
extendSelection(cm,
|
2301 |
}
|
2302 |
|
2303 |
// PROTOTYPE
|
@@ -2306,24 +2680,6 @@ window.CodeMirror = (function() {
|
|
2306 |
// 'wrap f in an operation, performed on its `this` parameter'
|
2307 |
|
2308 |
CodeMirror.prototype = {
|
2309 |
-
getValue: function(lineSep) {
|
2310 |
-
var text = [], doc = this.view.doc;
|
2311 |
-
doc.iter(0, doc.size, function(line) { text.push(line.text); });
|
2312 |
-
return text.join(lineSep || "\n");
|
2313 |
-
},
|
2314 |
-
|
2315 |
-
setValue: operation(null, function(code) {
|
2316 |
-
var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length;
|
2317 |
-
updateDocInner(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top, "setValue");
|
2318 |
-
}),
|
2319 |
-
|
2320 |
-
getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); },
|
2321 |
-
|
2322 |
-
replaceSelection: operation(null, function(code, collapse, origin) {
|
2323 |
-
var sel = this.view.sel;
|
2324 |
-
updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around", origin);
|
2325 |
-
}),
|
2326 |
-
|
2327 |
focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
|
2328 |
|
2329 |
setOption: function(option, value) {
|
@@ -2335,15 +2691,13 @@ window.CodeMirror = (function() {
|
|
2335 |
},
|
2336 |
|
2337 |
getOption: function(option) {return this.options[option];},
|
|
|
2338 |
|
2339 |
-
|
2340 |
-
|
2341 |
-
addKeyMap: function(map) {
|
2342 |
-
this.view.keyMaps.push(map);
|
2343 |
},
|
2344 |
-
|
2345 |
removeKeyMap: function(map) {
|
2346 |
-
var maps = this.
|
2347 |
for (var i = 0; i < maps.length; ++i)
|
2348 |
if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
|
2349 |
maps.splice(i, 1);
|
@@ -2351,68 +2705,45 @@ window.CodeMirror = (function() {
|
|
2351 |
}
|
2352 |
},
|
2353 |
|
2354 |
-
|
2355 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2356 |
|
2357 |
indentLine: operation(null, function(n, dir, aggressive) {
|
2358 |
if (typeof dir != "string") {
|
2359 |
if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
|
2360 |
else dir = dir ? "add" : "subtract";
|
2361 |
}
|
2362 |
-
if (isLine(this.
|
2363 |
}),
|
2364 |
-
|
2365 |
indentSelection: operation(null, function(how) {
|
2366 |
-
var sel = this.
|
2367 |
if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
|
2368 |
var e = sel.to.line - (sel.to.ch ? 0 : 1);
|
2369 |
for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
|
2370 |
}),
|
2371 |
|
2372 |
-
historySize: function() {
|
2373 |
-
var hist = this.view.history;
|
2374 |
-
return {undo: hist.done.length, redo: hist.undone.length};
|
2375 |
-
},
|
2376 |
-
|
2377 |
-
clearHistory: function() {this.view.history = makeHistory();},
|
2378 |
-
|
2379 |
-
markClean: function() {
|
2380 |
-
this.view.history.dirtyCounter = 0;
|
2381 |
-
this.view.history.lastOp = this.view.history.lastOrigin = null;
|
2382 |
-
},
|
2383 |
-
|
2384 |
-
isClean: function () {return this.view.history.dirtyCounter == 0;},
|
2385 |
-
|
2386 |
-
getHistory: function() {
|
2387 |
-
var hist = this.view.history;
|
2388 |
-
function cp(arr) {
|
2389 |
-
for (var i = 0, nw = [], nwelt; i < arr.length; ++i) {
|
2390 |
-
var set = arr[i];
|
2391 |
-
nw.push({events: nwelt = [], fromBefore: set.fromBefore, toBefore: set.toBefore,
|
2392 |
-
fromAfter: set.fromAfter, toAfter: set.toAfter});
|
2393 |
-
for (var j = 0, elt = set.events; j < elt.length; ++j) {
|
2394 |
-
var old = [], cur = elt[j];
|
2395 |
-
nwelt.push({start: cur.start, added: cur.added, old: old});
|
2396 |
-
for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k]));
|
2397 |
-
}
|
2398 |
-
}
|
2399 |
-
return nw;
|
2400 |
-
}
|
2401 |
-
return {done: cp(hist.done), undone: cp(hist.undone)};
|
2402 |
-
},
|
2403 |
-
|
2404 |
-
setHistory: function(histData) {
|
2405 |
-
var hist = this.view.history = makeHistory();
|
2406 |
-
hist.done = histData.done;
|
2407 |
-
hist.undone = histData.undone;
|
2408 |
-
},
|
2409 |
-
|
2410 |
// Fetch the parser token for a given character. Useful for hacks
|
2411 |
// that want to inspect the mode state (say, for completion).
|
2412 |
getTokenAt: function(pos) {
|
2413 |
-
var doc = this.
|
2414 |
pos = clipPos(doc, pos);
|
2415 |
-
var state = getStateBefore(this, pos.line), mode = this.
|
2416 |
var line = getLine(doc, pos.line);
|
2417 |
var stream = new StringStream(line.text, this.options.tabSize);
|
2418 |
while (stream.pos < pos.ch && !stream.eol()) {
|
@@ -2428,52 +2759,30 @@ window.CodeMirror = (function() {
|
|
2428 |
},
|
2429 |
|
2430 |
getStateAfter: function(line) {
|
2431 |
-
var doc = this.
|
2432 |
-
line = clipLine(doc, line == null ? doc.size - 1: line);
|
2433 |
return getStateBefore(this, line + 1);
|
2434 |
},
|
2435 |
|
2436 |
cursorCoords: function(start, mode) {
|
2437 |
-
var pos, sel = this.
|
2438 |
if (start == null) pos = sel.head;
|
2439 |
-
else if (typeof start == "object") pos = clipPos(this.
|
2440 |
else pos = start ? sel.from : sel.to;
|
2441 |
return cursorCoords(this, pos, mode || "page");
|
2442 |
},
|
2443 |
|
2444 |
charCoords: function(pos, mode) {
|
2445 |
-
return charCoords(this, clipPos(this.
|
2446 |
},
|
2447 |
|
2448 |
-
coordsChar: function(coords) {
|
2449 |
-
|
2450 |
-
return coordsChar(this, coords.left
|
2451 |
},
|
2452 |
|
2453 |
defaultTextHeight: function() { return textHeight(this.display); },
|
2454 |
-
|
2455 |
-
markText: operation(null, function(from, to, options) {
|
2456 |
-
return markText(this, clipPos(this.view.doc, from), clipPos(this.view.doc, to),
|
2457 |
-
options, "range");
|
2458 |
-
}),
|
2459 |
-
|
2460 |
-
setBookmark: operation(null, function(pos, widget) {
|
2461 |
-
pos = clipPos(this.view.doc, pos);
|
2462 |
-
return markText(this, pos, pos, widget ? {replacedWith: widget} : {}, "bookmark");
|
2463 |
-
}),
|
2464 |
-
|
2465 |
-
findMarksAt: function(pos) {
|
2466 |
-
var doc = this.view.doc;
|
2467 |
-
pos = clipPos(doc, pos);
|
2468 |
-
var markers = [], spans = getLine(doc, pos.line).markedSpans;
|
2469 |
-
if (spans) for (var i = 0; i < spans.length; ++i) {
|
2470 |
-
var span = spans[i];
|
2471 |
-
if ((span.from == null || span.from <= pos.ch) &&
|
2472 |
-
(span.to == null || span.to >= pos.ch))
|
2473 |
-
markers.push(span.marker);
|
2474 |
-
}
|
2475 |
-
return markers;
|
2476 |
-
},
|
2477 |
|
2478 |
setGutterMarker: operation(null, function(line, gutterID, value) {
|
2479 |
return changeLine(this, line, function(line) {
|
@@ -2485,8 +2794,8 @@ window.CodeMirror = (function() {
|
|
2485 |
}),
|
2486 |
|
2487 |
clearGutter: operation(null, function(gutterID) {
|
2488 |
-
var
|
2489 |
-
doc.iter(
|
2490 |
if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
|
2491 |
line.gutterMarkers[gutterID] = null;
|
2492 |
regChange(cm, i, i + 1);
|
@@ -2522,29 +2831,16 @@ window.CodeMirror = (function() {
|
|
2522 |
}),
|
2523 |
|
2524 |
addLineWidget: operation(null, function(handle, node, options) {
|
2525 |
-
|
2526 |
-
widget.node = node;
|
2527 |
-
if (widget.noHScroll) this.display.alignWidgets = true;
|
2528 |
-
changeLine(this, handle, function(line) {
|
2529 |
-
(line.widgets || (line.widgets = [])).push(widget);
|
2530 |
-
widget.line = line;
|
2531 |
-
return true;
|
2532 |
-
});
|
2533 |
-
return widget;
|
2534 |
}),
|
2535 |
|
2536 |
-
removeLineWidget:
|
2537 |
-
var ws = widget.line.widgets, no = lineNo(widget.line);
|
2538 |
-
if (no == null) return;
|
2539 |
-
for (var i = 0; i < ws.length; ++i) if (ws[i] == widget) ws.splice(i--, 1);
|
2540 |
-
regChange(this, no, no + 1);
|
2541 |
-
}),
|
2542 |
|
2543 |
lineInfo: function(line) {
|
2544 |
if (typeof line == "number") {
|
2545 |
-
if (!isLine(this.
|
2546 |
var n = line;
|
2547 |
-
line = getLine(this.
|
2548 |
if (!line) return null;
|
2549 |
} else {
|
2550 |
var n = lineNo(line);
|
@@ -2559,16 +2855,20 @@ window.CodeMirror = (function() {
|
|
2559 |
|
2560 |
addWidget: function(pos, node, scroll, vert, horiz) {
|
2561 |
var display = this.display;
|
2562 |
-
pos = cursorCoords(this, clipPos(this.
|
2563 |
-
var top = pos.
|
2564 |
node.style.position = "absolute";
|
2565 |
display.sizer.appendChild(node);
|
2566 |
-
if (vert == "over")
|
2567 |
-
|
2568 |
-
|
|
|
2569 |
hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
|
2570 |
-
|
|
|
2571 |
top = pos.top - node.offsetHeight;
|
|
|
|
|
2572 |
if (left + node.offsetWidth > hspace)
|
2573 |
left = hspace - node.offsetWidth;
|
2574 |
}
|
@@ -2586,146 +2886,71 @@ window.CodeMirror = (function() {
|
|
2586 |
scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
|
2587 |
},
|
2588 |
|
2589 |
-
lineCount: function() {return this.view.doc.size;},
|
2590 |
-
|
2591 |
-
clipPos: function(pos) {return clipPos(this.view.doc, pos);},
|
2592 |
-
|
2593 |
-
getCursor: function(start) {
|
2594 |
-
var sel = this.view.sel, pos;
|
2595 |
-
if (start == null || start == "head") pos = sel.head;
|
2596 |
-
else if (start == "anchor") pos = sel.anchor;
|
2597 |
-
else if (start == "end" || start === false) pos = sel.to;
|
2598 |
-
else pos = sel.from;
|
2599 |
-
return copyPos(pos);
|
2600 |
-
},
|
2601 |
-
|
2602 |
-
somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);},
|
2603 |
-
|
2604 |
-
setCursor: operation(null, function(line, ch, extend) {
|
2605 |
-
var pos = clipPos(this.view.doc, typeof line == "number" ? {line: line, ch: ch || 0} : line);
|
2606 |
-
if (extend) extendSelection(this, pos);
|
2607 |
-
else setSelection(this, pos, pos);
|
2608 |
-
}),
|
2609 |
-
|
2610 |
-
setSelection: operation(null, function(anchor, head) {
|
2611 |
-
var doc = this.view.doc;
|
2612 |
-
setSelection(this, clipPos(doc, anchor), clipPos(doc, head || anchor));
|
2613 |
-
}),
|
2614 |
-
|
2615 |
-
extendSelection: operation(null, function(from, to) {
|
2616 |
-
var doc = this.view.doc;
|
2617 |
-
extendSelection(this, clipPos(doc, from), to && clipPos(doc, to));
|
2618 |
-
}),
|
2619 |
-
|
2620 |
-
setExtending: function(val) {this.view.sel.extend = val;},
|
2621 |
-
|
2622 |
-
getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
|
2623 |
-
|
2624 |
-
getLineHandle: function(line) {
|
2625 |
-
var doc = this.view.doc;
|
2626 |
-
if (isLine(doc, line)) return getLine(doc, line);
|
2627 |
-
},
|
2628 |
-
|
2629 |
-
getLineNumber: function(line) {return lineNo(line);},
|
2630 |
-
|
2631 |
-
setLine: operation(null, function(line, text) {
|
2632 |
-
if (isLine(this.view.doc, line))
|
2633 |
-
replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length});
|
2634 |
-
}),
|
2635 |
-
|
2636 |
-
removeLine: operation(null, function(line) {
|
2637 |
-
if (isLine(this.view.doc, line))
|
2638 |
-
replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0}));
|
2639 |
-
}),
|
2640 |
-
|
2641 |
-
replaceRange: operation(null, function(code, from, to) {
|
2642 |
-
var doc = this.view.doc;
|
2643 |
-
from = clipPos(doc, from);
|
2644 |
-
to = to ? clipPos(doc, to) : from;
|
2645 |
-
return replaceRange(this, code, from, to);
|
2646 |
-
}),
|
2647 |
-
|
2648 |
-
getRange: function(from, to, lineSep) {
|
2649 |
-
var doc = this.view.doc;
|
2650 |
-
from = clipPos(doc, from); to = clipPos(doc, to);
|
2651 |
-
var l1 = from.line, l2 = to.line;
|
2652 |
-
if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch);
|
2653 |
-
var code = [getLine(doc, l1).text.slice(from.ch)];
|
2654 |
-
doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
|
2655 |
-
code.push(getLine(doc, l2).text.slice(0, to.ch));
|
2656 |
-
return code.join(lineSep || "\n");
|
2657 |
-
},
|
2658 |
-
|
2659 |
triggerOnKeyDown: operation(null, onKeyDown),
|
2660 |
|
2661 |
execCommand: function(cmd) {return commands[cmd](this);},
|
2662 |
|
2663 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2664 |
moveH: operation(null, function(dir, unit) {
|
2665 |
-
var sel = this.
|
2666 |
-
if (sel.shift || sel.extend || posEq(sel.from, sel.to))
|
2667 |
-
|
|
|
|
|
|
|
2668 |
}),
|
2669 |
|
2670 |
deleteH: operation(null, function(dir, unit) {
|
2671 |
-
var sel = this.
|
2672 |
-
if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to, "delete");
|
2673 |
-
else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false), "delete");
|
2674 |
this.curOp.userSelChange = true;
|
2675 |
}),
|
2676 |
|
2677 |
-
|
2678 |
-
var
|
2679 |
-
|
2680 |
-
var
|
2681 |
-
|
2682 |
-
|
2683 |
-
|
2684 |
-
|
2685 |
-
|
2686 |
-
y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
|
2687 |
}
|
2688 |
-
|
2689 |
-
|
2690 |
-
y += dir * 5;
|
2691 |
-
} while (target.outside && (dir < 0 ? y > 0 : y < doc.height));
|
2692 |
|
2693 |
-
|
2694 |
-
|
2695 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
2696 |
}),
|
2697 |
|
2698 |
toggleOverwrite: function() {
|
2699 |
-
if (this.
|
2700 |
this.display.cursor.className += " CodeMirror-overwrite";
|
2701 |
else
|
2702 |
this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
|
2703 |
},
|
|
|
2704 |
|
2705 |
-
|
2706 |
-
|
2707 |
-
|
2708 |
-
var sz = line.text.length + 1;
|
2709 |
-
if (sz > off) { ch = off; return true; }
|
2710 |
-
off -= sz;
|
2711 |
-
++lineNo;
|
2712 |
-
});
|
2713 |
-
return clipPos(doc, {line: lineNo, ch: ch});
|
2714 |
-
},
|
2715 |
-
indexFromPos: function (coords) {
|
2716 |
-
if (coords.line < 0 || coords.ch < 0) return 0;
|
2717 |
-
var index = coords.ch;
|
2718 |
-
this.view.doc.iter(0, coords.line, function (line) {
|
2719 |
-
index += line.text.length + 1;
|
2720 |
-
});
|
2721 |
-
return index;
|
2722 |
-
},
|
2723 |
-
|
2724 |
-
scrollTo: function(x, y) {
|
2725 |
-
if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x;
|
2726 |
-
if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y;
|
2727 |
-
updateDisplay(this, []);
|
2728 |
-
},
|
2729 |
getScrollInfo: function() {
|
2730 |
var scroller = this.display.scroller, co = scrollerCutOff;
|
2731 |
return {left: scroller.scrollLeft, top: scroller.scrollTop,
|
@@ -2733,10 +2958,14 @@ window.CodeMirror = (function() {
|
|
2733 |
clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
|
2734 |
},
|
2735 |
|
2736 |
-
scrollIntoView: function(pos) {
|
2737 |
-
if (typeof pos == "number") pos =
|
2738 |
-
pos
|
2739 |
-
|
|
|
|
|
|
|
|
|
2740 |
},
|
2741 |
|
2742 |
setSize: function(width, height) {
|
@@ -2751,14 +2980,22 @@ window.CodeMirror = (function() {
|
|
2751 |
on: function(type, f) {on(this, type, f);},
|
2752 |
off: function(type, f) {off(this, type, f);},
|
2753 |
|
2754 |
-
operation: function(f){return
|
2755 |
|
2756 |
-
refresh: function() {
|
2757 |
clearCaches(this);
|
2758 |
-
|
2759 |
-
|
2760 |
-
|
2761 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2762 |
|
2763 |
getInputField: function(){return this.display.input;},
|
2764 |
getWrapperElement: function(){return this.display.wrapper;},
|
@@ -2783,8 +3020,13 @@ window.CodeMirror = (function() {
|
|
2783 |
|
2784 |
// These two are, on init, called from the constructor because they
|
2785 |
// have to be initialized before the editor can start at all.
|
2786 |
-
option("value", "", function(cm, val) {
|
2787 |
-
|
|
|
|
|
|
|
|
|
|
|
2788 |
|
2789 |
option("indentUnit", 2, loadMode, true);
|
2790 |
option("indentWithTabs", false);
|
@@ -2792,9 +3034,10 @@ window.CodeMirror = (function() {
|
|
2792 |
option("tabSize", 4, function(cm) {
|
2793 |
loadMode(cm);
|
2794 |
clearCaches(cm);
|
2795 |
-
|
2796 |
}, true);
|
2797 |
option("electricChars", true);
|
|
|
2798 |
|
2799 |
option("theme", "default", function(cm) {
|
2800 |
themeChanged(cm);
|
@@ -2811,6 +3054,10 @@ window.CodeMirror = (function() {
|
|
2811 |
setGuttersForLineNumbers(cm.options);
|
2812 |
guttersChanged(cm);
|
2813 |
}, true);
|
|
|
|
|
|
|
|
|
2814 |
option("lineNumbers", false, function(cm) {
|
2815 |
setGuttersForLineNumbers(cm.options);
|
2816 |
guttersChanged(cm);
|
@@ -2818,7 +3065,7 @@ window.CodeMirror = (function() {
|
|
2818 |
option("firstLineNumber", 1, guttersChanged, true);
|
2819 |
option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
|
2820 |
option("showCursorWhenSelecting", false, updateSelection, true);
|
2821 |
-
|
2822 |
option("readOnly", false, function(cm, val) {
|
2823 |
if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
|
2824 |
else if (!val) resetInput(cm, true);
|
@@ -2831,7 +3078,7 @@ window.CodeMirror = (function() {
|
|
2831 |
option("workDelay", 100);
|
2832 |
option("flattenSpans", true);
|
2833 |
option("pollInterval", 100);
|
2834 |
-
option("undoDepth", 40);
|
2835 |
option("viewportMargin", 10, function(cm){cm.refresh();}, true);
|
2836 |
|
2837 |
option("tabindex", null, function(cm, val) {
|
@@ -2867,7 +3114,7 @@ window.CodeMirror = (function() {
|
|
2867 |
};
|
2868 |
|
2869 |
CodeMirror.getMode = function(options, spec) {
|
2870 |
-
|
2871 |
var mfactory = modes[spec.name];
|
2872 |
if (!mfactory) return CodeMirror.getMode(options, "text/plain");
|
2873 |
var modeObj = mfactory(options, spec);
|
@@ -2891,8 +3138,7 @@ window.CodeMirror = (function() {
|
|
2891 |
var modeExtensions = CodeMirror.modeExtensions = {};
|
2892 |
CodeMirror.extendMode = function(mode, properties) {
|
2893 |
var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
|
2894 |
-
|
2895 |
-
exts[prop] = properties[prop];
|
2896 |
};
|
2897 |
|
2898 |
// EXTENSIONS
|
@@ -2940,21 +3186,21 @@ window.CodeMirror = (function() {
|
|
2940 |
// STANDARD COMMANDS
|
2941 |
|
2942 |
var commands = CodeMirror.commands = {
|
2943 |
-
selectAll: function(cm) {cm.setSelection(
|
2944 |
killLine: function(cm) {
|
2945 |
var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
|
2946 |
if (!sel && cm.getLine(from.line).length == from.ch)
|
2947 |
-
cm.replaceRange("", from,
|
2948 |
-
else cm.replaceRange("", from, sel ? to :
|
2949 |
},
|
2950 |
deleteLine: function(cm) {
|
2951 |
var l = cm.getCursor().line;
|
2952 |
-
cm.replaceRange("",
|
2953 |
},
|
2954 |
undo: function(cm) {cm.undo();},
|
2955 |
redo: function(cm) {cm.redo();},
|
2956 |
-
goDocStart: function(cm) {cm.extendSelection(
|
2957 |
-
goDocEnd: function(cm) {cm.extendSelection(
|
2958 |
goLineStart: function(cm) {
|
2959 |
cm.extendSelection(lineStart(cm, cm.getCursor().line));
|
2960 |
},
|
@@ -2965,12 +3211,20 @@ window.CodeMirror = (function() {
|
|
2965 |
if (!order || order[0].level == 0) {
|
2966 |
var firstNonWS = Math.max(0, line.text.search(/\S/));
|
2967 |
var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
|
2968 |
-
cm.extendSelection(
|
2969 |
} else cm.extendSelection(start);
|
2970 |
},
|
2971 |
goLineEnd: function(cm) {
|
2972 |
cm.extendSelection(lineEnd(cm, cm.getCursor().line));
|
2973 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2974 |
goLineUp: function(cm) {cm.moveV(-1, "line");},
|
2975 |
goLineDown: function(cm) {cm.moveV(1, "line");},
|
2976 |
goPageUp: function(cm) {cm.moveV(-1, "page");},
|
@@ -2980,28 +3234,32 @@ window.CodeMirror = (function() {
|
|
2980 |
goColumnLeft: function(cm) {cm.moveH(-1, "column");},
|
2981 |
goColumnRight: function(cm) {cm.moveH(1, "column");},
|
2982 |
goWordLeft: function(cm) {cm.moveH(-1, "word");},
|
|
|
|
|
2983 |
goWordRight: function(cm) {cm.moveH(1, "word");},
|
2984 |
delCharBefore: function(cm) {cm.deleteH(-1, "char");},
|
2985 |
delCharAfter: function(cm) {cm.deleteH(1, "char");},
|
2986 |
delWordBefore: function(cm) {cm.deleteH(-1, "word");},
|
2987 |
delWordAfter: function(cm) {cm.deleteH(1, "word");},
|
|
|
|
|
2988 |
indentAuto: function(cm) {cm.indentSelection("smart");},
|
2989 |
indentMore: function(cm) {cm.indentSelection("add");},
|
2990 |
indentLess: function(cm) {cm.indentSelection("subtract");},
|
2991 |
-
insertTab: function(cm) {cm.replaceSelection("\t", "end", "input");},
|
2992 |
defaultTab: function(cm) {
|
2993 |
if (cm.somethingSelected()) cm.indentSelection("add");
|
2994 |
-
else cm.replaceSelection("\t", "end", "input");
|
2995 |
},
|
2996 |
transposeChars: function(cm) {
|
2997 |
var cur = cm.getCursor(), line = cm.getLine(cur.line);
|
2998 |
if (cur.ch > 0 && cur.ch < line.length - 1)
|
2999 |
cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
|
3000 |
-
|
3001 |
},
|
3002 |
newlineAndIndent: function(cm) {
|
3003 |
operation(cm, function() {
|
3004 |
-
cm.replaceSelection("\n", "end", "input");
|
3005 |
cm.indentLine(cm.getCursor().line, null, true);
|
3006 |
})();
|
3007 |
},
|
@@ -3022,17 +3280,17 @@ window.CodeMirror = (function() {
|
|
3022 |
keyMap.pcDefault = {
|
3023 |
"Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
|
3024 |
"Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
|
3025 |
-
"Ctrl-Left": "
|
3026 |
-
"Ctrl-Backspace": "
|
3027 |
"Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
|
3028 |
"Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
|
3029 |
fallthrough: "basic"
|
3030 |
};
|
3031 |
keyMap.macDefault = {
|
3032 |
"Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
|
3033 |
-
"Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "
|
3034 |
-
"Alt-Right": "
|
3035 |
-
"Ctrl-Alt-Backspace": "
|
3036 |
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
|
3037 |
"Cmd-[": "indentLess", "Cmd-]": "indentMore",
|
3038 |
fallthrough: ["basic", "emacsy"]
|
@@ -3052,37 +3310,46 @@ window.CodeMirror = (function() {
|
|
3052 |
else return val;
|
3053 |
}
|
3054 |
|
3055 |
-
function lookupKey(name, maps, handle
|
3056 |
function lookup(map) {
|
3057 |
map = getKeyMap(map);
|
3058 |
var found = map[name];
|
3059 |
-
if (found === false)
|
3060 |
-
if (stop) stop();
|
3061 |
-
return true;
|
3062 |
-
}
|
3063 |
if (found != null && handle(found)) return true;
|
3064 |
-
if (map.nofallthrough)
|
3065 |
-
|
3066 |
-
return true;
|
3067 |
-
}
|
3068 |
var fallthrough = map.fallthrough;
|
3069 |
if (fallthrough == null) return false;
|
3070 |
if (Object.prototype.toString.call(fallthrough) != "[object Array]")
|
3071 |
return lookup(fallthrough);
|
3072 |
for (var i = 0, e = fallthrough.length; i < e; ++i) {
|
3073 |
-
|
|
|
3074 |
}
|
3075 |
return false;
|
3076 |
}
|
3077 |
|
3078 |
-
for (var i = 0; i < maps.length; ++i)
|
3079 |
-
|
|
|
|
|
3080 |
}
|
3081 |
function isModifierKey(event) {
|
3082 |
-
var name = keyNames[
|
3083 |
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
|
3084 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3085 |
CodeMirror.isModifierKey = isModifierKey;
|
|
|
3086 |
|
3087 |
// FROMTEXTAREA
|
3088 |
|
@@ -3091,6 +3358,8 @@ window.CodeMirror = (function() {
|
|
3091 |
options.value = textarea.value;
|
3092 |
if (!options.tabindex && textarea.tabindex)
|
3093 |
options.tabindex = textarea.tabindex;
|
|
|
|
|
3094 |
// Set autofocus to true if this textarea is focused, or if it has
|
3095 |
// autofocus and no other element is focused.
|
3096 |
if (options.autofocus == null) {
|
@@ -3103,17 +3372,19 @@ window.CodeMirror = (function() {
|
|
3103 |
|
3104 |
function save() {textarea.value = cm.getValue();}
|
3105 |
if (textarea.form) {
|
3106 |
-
// Deplorable hack to make the submit method do the right thing.
|
3107 |
on(textarea.form, "submit", save);
|
3108 |
-
|
3109 |
-
|
3110 |
-
form
|
3111 |
-
|
3112 |
-
form.submit =
|
3113 |
-
|
3114 |
-
|
3115 |
-
|
3116 |
-
|
|
|
|
|
|
|
3117 |
}
|
3118 |
|
3119 |
textarea.style.display = "none";
|
@@ -3145,6 +3416,7 @@ window.CodeMirror = (function() {
|
|
3145 |
this.pos = this.start = 0;
|
3146 |
this.string = string;
|
3147 |
this.tabSize = tabSize || 8;
|
|
|
3148 |
}
|
3149 |
|
3150 |
StringStream.prototype = {
|
@@ -3177,12 +3449,19 @@ window.CodeMirror = (function() {
|
|
3177 |
if (found > -1) {this.pos = found; return true;}
|
3178 |
},
|
3179 |
backUp: function(n) {this.pos -= n;},
|
3180 |
-
column: function() {
|
|
|
|
|
|
|
|
|
|
|
|
|
3181 |
indentation: function() {return countColumn(this.string, null, this.tabSize);},
|
3182 |
match: function(pattern, consume, caseInsensitive) {
|
3183 |
if (typeof pattern == "string") {
|
3184 |
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
|
3185 |
-
|
|
|
3186 |
if (consume !== false) this.pos += pattern.length;
|
3187 |
return true;
|
3188 |
}
|
@@ -3199,15 +3478,17 @@ window.CodeMirror = (function() {
|
|
3199 |
|
3200 |
// TEXTMARKERS
|
3201 |
|
3202 |
-
function TextMarker(
|
3203 |
this.lines = [];
|
3204 |
this.type = type;
|
3205 |
-
this.
|
3206 |
}
|
|
|
3207 |
|
3208 |
TextMarker.prototype.clear = function() {
|
3209 |
if (this.explicitlyCleared) return;
|
3210 |
-
|
|
|
3211 |
var min = null, max = null;
|
3212 |
for (var i = 0; i < this.lines.length; ++i) {
|
3213 |
var line = this.lines[i];
|
@@ -3216,18 +3497,27 @@ window.CodeMirror = (function() {
|
|
3216 |
line.markedSpans = removeMarkedSpan(line.markedSpans, span);
|
3217 |
if (span.from != null)
|
3218 |
min = lineNo(line);
|
3219 |
-
else if (this.collapsed && !lineIsHidden(line))
|
3220 |
-
updateLineHeight(line, textHeight(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3221 |
}
|
3222 |
-
|
|
|
3223 |
this.lines.length = 0;
|
3224 |
this.explicitlyCleared = true;
|
3225 |
-
if (this.collapsed && this.
|
3226 |
-
this.
|
3227 |
-
reCheckSelection(
|
3228 |
}
|
3229 |
-
endOperation(
|
3230 |
-
signalLater(this
|
3231 |
};
|
3232 |
|
3233 |
TextMarker.prototype.find = function() {
|
@@ -3237,28 +3527,58 @@ window.CodeMirror = (function() {
|
|
3237 |
var span = getMarkedSpanFor(line.markedSpans, this);
|
3238 |
if (span.from != null || span.to != null) {
|
3239 |
var found = lineNo(line);
|
3240 |
-
if (span.from != null) from =
|
3241 |
-
if (span.to != null) to =
|
3242 |
}
|
3243 |
}
|
3244 |
if (this.type == "bookmark") return from;
|
3245 |
return from && {from: from, to: to};
|
3246 |
};
|
3247 |
|
3248 |
-
function
|
3249 |
-
var
|
3250 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3251 |
if (type == "range" && !posLess(from, to)) return marker;
|
3252 |
-
if (options)
|
3253 |
-
marker[opt] = options[opt];
|
3254 |
if (marker.replacedWith) {
|
3255 |
marker.collapsed = true;
|
3256 |
marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
|
3257 |
}
|
3258 |
if (marker.collapsed) sawCollapsedSpans = true;
|
3259 |
|
3260 |
-
var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd;
|
3261 |
doc.iter(curLine, to.line + 1, function(line) {
|
|
|
|
|
3262 |
var span = {from: null, to: null, marker: marker};
|
3263 |
size += line.text.length;
|
3264 |
if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
|
@@ -3269,15 +3589,18 @@ window.CodeMirror = (function() {
|
|
3269 |
else updateLineHeight(line, 0);
|
3270 |
}
|
3271 |
addMarkedSpan(line, span);
|
3272 |
-
if (marker.collapsed && curLine == from.line && lineIsHidden(line))
|
3273 |
-
updateLineHeight(line, 0);
|
3274 |
++curLine;
|
3275 |
});
|
|
|
|
|
|
|
|
|
|
|
3276 |
|
3277 |
if (marker.readOnly) {
|
3278 |
sawReadOnlySpans = true;
|
3279 |
-
if (
|
3280 |
-
|
3281 |
}
|
3282 |
if (marker.collapsed) {
|
3283 |
if (collapsedAtStart != collapsedAtEnd)
|
@@ -3285,12 +3608,58 @@ window.CodeMirror = (function() {
|
|
3285 |
marker.size = size;
|
3286 |
marker.atomic = true;
|
3287 |
}
|
3288 |
-
if (
|
3289 |
-
|
3290 |
-
|
|
|
|
|
|
|
3291 |
return marker;
|
3292 |
}
|
3293 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3294 |
// TEXTMARKER SPANS
|
3295 |
|
3296 |
function getMarkedSpanFor(spans, marker) {
|
@@ -3306,14 +3675,14 @@ window.CodeMirror = (function() {
|
|
3306 |
}
|
3307 |
function addMarkedSpan(line, span) {
|
3308 |
line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
|
3309 |
-
span.marker.
|
3310 |
}
|
3311 |
|
3312 |
-
function markedSpansBefore(old, startCh) {
|
3313 |
if (old) for (var i = 0, nw; i < old.length; ++i) {
|
3314 |
var span = old[i], marker = span.marker;
|
3315 |
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
|
3316 |
-
if (startsBefore || marker.type == "bookmark" && span.from == startCh) {
|
3317 |
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
|
3318 |
(nw || (nw = [])).push({from: span.from,
|
3319 |
to: endsAfter ? null : span.to,
|
@@ -3323,11 +3692,11 @@ window.CodeMirror = (function() {
|
|
3323 |
return nw;
|
3324 |
}
|
3325 |
|
3326 |
-
function markedSpansAfter(old,
|
3327 |
if (old) for (var i = 0, nw; i < old.length; ++i) {
|
3328 |
var span = old[i], marker = span.marker;
|
3329 |
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
|
3330 |
-
if (endsAfter || marker.type == "bookmark" && span.from == endCh && span.
|
3331 |
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
|
3332 |
(nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
|
3333 |
to: span.to == null ? null : span.to - endCh,
|
@@ -3337,14 +3706,18 @@ window.CodeMirror = (function() {
|
|
3337 |
return nw;
|
3338 |
}
|
3339 |
|
3340 |
-
function
|
3341 |
-
|
|
|
|
|
|
|
|
|
3342 |
// Get the spans that 'stick out' on both sides
|
3343 |
-
var first = markedSpansBefore(oldFirst, startCh);
|
3344 |
-
var last = markedSpansAfter(oldLast,
|
3345 |
|
3346 |
// Next, merge those two ends
|
3347 |
-
var sameLine =
|
3348 |
if (first) {
|
3349 |
// Fix up .to properties of first
|
3350 |
for (var i = 0; i < first.length; ++i) {
|
@@ -3374,21 +3747,43 @@ window.CodeMirror = (function() {
|
|
3374 |
}
|
3375 |
}
|
3376 |
|
3377 |
-
var newMarkers = [
|
3378 |
if (!sameLine) {
|
3379 |
// Fill gap with whole-line-spans
|
3380 |
-
var gap =
|
3381 |
if (gap > 0 && first)
|
3382 |
for (var i = 0; i < first.length; ++i)
|
3383 |
if (first[i].to == null)
|
3384 |
(gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
|
3385 |
for (var i = 0; i < gap; ++i)
|
3386 |
-
newMarkers.push(
|
3387 |
-
newMarkers.push(
|
3388 |
}
|
3389 |
return newMarkers;
|
3390 |
}
|
3391 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3392 |
function removeReadOnlyRanges(doc, from, to) {
|
3393 |
var markers = null;
|
3394 |
doc.iter(from.line, to.line + 1, function(line) {
|
@@ -3401,13 +3796,15 @@ window.CodeMirror = (function() {
|
|
3401 |
if (!markers) return null;
|
3402 |
var parts = [{from: from, to: to}];
|
3403 |
for (var i = 0; i < markers.length; ++i) {
|
3404 |
-
var
|
3405 |
for (var j = 0; j < parts.length; ++j) {
|
3406 |
var p = parts[j];
|
3407 |
-
if (
|
3408 |
var newParts = [j, 1];
|
3409 |
-
if (posLess(p.from, m.from)
|
3410 |
-
|
|
|
|
|
3411 |
parts.splice.apply(parts, newParts);
|
3412 |
j += newParts.length - 1;
|
3413 |
}
|
@@ -3437,80 +3834,124 @@ window.CodeMirror = (function() {
|
|
3437 |
return line;
|
3438 |
}
|
3439 |
|
3440 |
-
function lineIsHidden(line) {
|
3441 |
var sps = sawCollapsedSpans && line.markedSpans;
|
3442 |
if (sps) for (var sp, i = 0; i < sps.length; ++i) {
|
3443 |
sp = sps[i];
|
3444 |
if (!sp.marker.collapsed) continue;
|
3445 |
if (sp.from == null) return true;
|
3446 |
-
if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(line, sp))
|
3447 |
return true;
|
3448 |
}
|
3449 |
}
|
3450 |
-
|
3451 |
-
|
3452 |
-
|
|
|
|
|
|
|
3453 |
return true;
|
3454 |
for (var sp, i = 0; i < line.markedSpans.length; ++i) {
|
3455 |
sp = line.markedSpans[i];
|
3456 |
if (sp.marker.collapsed && sp.from == span.to &&
|
3457 |
(sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
|
3458 |
-
lineIsHiddenInner(line, sp)) return true;
|
3459 |
}
|
3460 |
}
|
3461 |
|
3462 |
-
// hl stands for history-line, a data structure that can be either a
|
3463 |
-
// string (line without markers) or a {text, markedSpans} object.
|
3464 |
-
function hlText(val) { return typeof val == "string" ? val : val.text; }
|
3465 |
-
function hlSpans(val) {
|
3466 |
-
if (typeof val == "string") return null;
|
3467 |
-
var spans = val.markedSpans, out = null;
|
3468 |
-
for (var i = 0; i < spans.length; ++i) {
|
3469 |
-
if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
|
3470 |
-
else if (out) out.push(spans[i]);
|
3471 |
-
}
|
3472 |
-
return !out ? spans : out.length ? out : null;
|
3473 |
-
}
|
3474 |
-
function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; }
|
3475 |
-
|
3476 |
function detachMarkedSpans(line) {
|
3477 |
var spans = line.markedSpans;
|
3478 |
if (!spans) return;
|
3479 |
-
for (var i = 0; i < spans.length; ++i)
|
3480 |
-
|
3481 |
-
var ix = indexOf(lines, line);
|
3482 |
-
lines.splice(ix, 1);
|
3483 |
-
}
|
3484 |
line.markedSpans = null;
|
3485 |
}
|
3486 |
|
3487 |
function attachMarkedSpans(line, spans) {
|
3488 |
if (!spans) return;
|
3489 |
for (var i = 0; i < spans.length; ++i)
|
3490 |
-
spans[i].marker.
|
3491 |
line.markedSpans = spans;
|
3492 |
}
|
3493 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3494 |
// LINE DATA STRUCTURE
|
3495 |
|
3496 |
// Line objects. These hold state related to a line, including
|
3497 |
// highlighting info (the styles array).
|
3498 |
-
function makeLine(text, markedSpans,
|
3499 |
-
var line = {text: text
|
3500 |
attachMarkedSpans(line, markedSpans);
|
3501 |
-
|
3502 |
return line;
|
3503 |
}
|
3504 |
|
3505 |
-
function updateLine(
|
3506 |
line.text = text;
|
3507 |
-
line.stateAfter
|
|
|
3508 |
if (line.order != null) line.order = null;
|
3509 |
detachMarkedSpans(line);
|
3510 |
attachMarkedSpans(line, markedSpans);
|
3511 |
-
|
3512 |
-
|
3513 |
-
signalLater(cm, line, "change");
|
3514 |
}
|
3515 |
|
3516 |
function cleanUpLine(line) {
|
@@ -3521,37 +3962,79 @@ window.CodeMirror = (function() {
|
|
3521 |
// Run the given mode's parser over a line, update the styles
|
3522 |
// array, which contains alternating fragments of text and CSS
|
3523 |
// classes.
|
3524 |
-
function
|
3525 |
-
var
|
3526 |
-
|
3527 |
-
var
|
3528 |
-
|
|
|
3529 |
while (!stream.eol()) {
|
3530 |
-
var style = mode.token(stream, state)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3531 |
stream.start = stream.pos;
|
3532 |
if (!flattenSpans || curStyle != style) {
|
3533 |
-
if (curText)
|
3534 |
-
changed = changed || pos >= st.length || curText != st[pos] || curStyle != st[pos+1];
|
3535 |
-
st[pos++] = curText; st[pos++] = curStyle;
|
3536 |
-
}
|
3537 |
curText = substr; curStyle = style;
|
3538 |
} else curText = curText + substr;
|
3539 |
-
// Give up when line is ridiculously long
|
3540 |
-
if (stream.pos > 5000) break;
|
3541 |
}
|
3542 |
-
if (curText)
|
3543 |
-
|
3544 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3545 |
}
|
3546 |
-
|
3547 |
-
|
3548 |
-
|
|
|
|
|
|
|
|
|
|
|
3549 |
}
|
3550 |
|
3551 |
// Lightweight form of highlight -- proceed over this line and
|
3552 |
// update state, but don't save a style array.
|
3553 |
function processLine(cm, line, state) {
|
3554 |
-
var mode = cm.
|
3555 |
var stream = new StringStream(line.text, cm.options.tabSize);
|
3556 |
if (line.text == "" && mode.blankLine) mode.blankLine(state);
|
3557 |
while (!stream.eol() && stream.pos <= 5000) {
|
@@ -3571,7 +4054,7 @@ window.CodeMirror = (function() {
|
|
3571 |
var merged, line = realLine, lineBefore, sawBefore, simple = true;
|
3572 |
while (merged = collapsedSpanAtStart(line)) {
|
3573 |
simple = false;
|
3574 |
-
line = getLine(cm.
|
3575 |
if (!lineBefore) lineBefore = line;
|
3576 |
}
|
3577 |
|
@@ -3580,32 +4063,48 @@ window.CodeMirror = (function() {
|
|
3580 |
if (line.textClass) builder.pre.className = line.textClass;
|
3581 |
|
3582 |
do {
|
3583 |
-
if (!line.styles)
|
3584 |
-
highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
|
3585 |
builder.measure = line == realLine && measure;
|
3586 |
builder.pos = 0;
|
3587 |
builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
|
|
|
|
|
3588 |
if (measure && sawBefore && line != realLine && !builder.addedOne) {
|
3589 |
measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
|
3590 |
builder.addedOne = true;
|
3591 |
}
|
3592 |
-
var next = insertLineContent(line, builder);
|
3593 |
sawBefore = line == lineBefore;
|
3594 |
if (next) {
|
3595 |
-
line = getLine(cm.
|
3596 |
simple = false;
|
3597 |
}
|
3598 |
} while (next);
|
3599 |
|
3600 |
if (measure && !builder.addedOne)
|
3601 |
measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
|
3602 |
-
if (!builder.pre.firstChild && !lineIsHidden(realLine))
|
3603 |
builder.pre.appendChild(document.createTextNode("\u00a0"));
|
3604 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3605 |
return builder.pre;
|
3606 |
}
|
3607 |
|
3608 |
-
var tokenSpecialChars = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g;
|
3609 |
function buildToken(builder, text, style, startStyle, endStyle) {
|
3610 |
if (!text) return;
|
3611 |
if (!tokenSpecialChars.test(text)) {
|
@@ -3645,18 +4144,42 @@ window.CodeMirror = (function() {
|
|
3645 |
}
|
3646 |
|
3647 |
function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
|
|
|
3648 |
for (var i = 0; i < text.length; ++i) {
|
3649 |
-
|
3650 |
-
|
3651 |
-
|
|
|
|
|
|
|
3652 |
builder.pre.appendChild(elt("wbr"));
|
3653 |
-
|
3654 |
-
|
3655 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3656 |
}
|
3657 |
if (text.length) builder.addedOne = true;
|
3658 |
}
|
3659 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3660 |
function buildCollapsedSpan(builder, size, widget) {
|
3661 |
if (widget) {
|
3662 |
if (!builder.display) widget = widget.cloneNode(true);
|
@@ -3671,16 +4194,16 @@ window.CodeMirror = (function() {
|
|
3671 |
|
3672 |
// Outputs a number of spans to make up a line, taking highlighting
|
3673 |
// and marked text into account.
|
3674 |
-
function insertLineContent(line, builder) {
|
3675 |
-
var
|
3676 |
if (!spans) {
|
3677 |
-
for (var i =
|
3678 |
-
builder.addToken(builder,
|
3679 |
return;
|
3680 |
}
|
3681 |
|
3682 |
var allText = line.text, len = allText.length;
|
3683 |
-
var pos = 0, i =
|
3684 |
var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
|
3685 |
for (;;) {
|
3686 |
if (nextChange == pos) { // Update current marker set
|
@@ -3717,20 +4240,66 @@ window.CodeMirror = (function() {
|
|
3717 |
var end = pos + text.length;
|
3718 |
if (!collapsed) {
|
3719 |
var tokenText = end > upto ? text.slice(0, upto - pos) : text;
|
3720 |
-
builder.addToken(builder, tokenText, style + spanStyle,
|
3721 |
spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
|
3722 |
}
|
3723 |
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
|
3724 |
pos = end;
|
3725 |
spanStartStyle = "";
|
3726 |
}
|
3727 |
-
text =
|
3728 |
}
|
3729 |
}
|
3730 |
}
|
3731 |
|
3732 |
// DOCUMENT DATA STRUCTURE
|
3733 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3734 |
function LeafChunk(lines) {
|
3735 |
this.lines = lines;
|
3736 |
this.parent = null;
|
@@ -3743,19 +4312,19 @@ window.CodeMirror = (function() {
|
|
3743 |
|
3744 |
LeafChunk.prototype = {
|
3745 |
chunkSize: function() { return this.lines.length; },
|
3746 |
-
|
3747 |
for (var i = at, e = at + n; i < e; ++i) {
|
3748 |
var line = this.lines[i];
|
3749 |
this.height -= line.height;
|
3750 |
cleanUpLine(line);
|
3751 |
-
signalLater(
|
3752 |
}
|
3753 |
this.lines.splice(at, n);
|
3754 |
},
|
3755 |
collapse: function(lines) {
|
3756 |
lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
|
3757 |
},
|
3758 |
-
|
3759 |
this.height += height;
|
3760 |
this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
|
3761 |
for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
|
@@ -3781,13 +4350,13 @@ window.CodeMirror = (function() {
|
|
3781 |
|
3782 |
BranchChunk.prototype = {
|
3783 |
chunkSize: function() { return this.size; },
|
3784 |
-
|
3785 |
this.size -= n;
|
3786 |
for (var i = 0; i < this.children.length; ++i) {
|
3787 |
var child = this.children[i], sz = child.chunkSize();
|
3788 |
if (at < sz) {
|
3789 |
var rm = Math.min(n, sz - at), oldHeight = child.height;
|
3790 |
-
child.
|
3791 |
this.height -= oldHeight - child.height;
|
3792 |
if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
|
3793 |
if ((n -= rm) == 0) break;
|
@@ -3804,18 +4373,13 @@ window.CodeMirror = (function() {
|
|
3804 |
collapse: function(lines) {
|
3805 |
for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
|
3806 |
},
|
3807 |
-
|
3808 |
-
var height = 0;
|
3809 |
-
for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
|
3810 |
-
this.insertHeight(at, lines, height);
|
3811 |
-
},
|
3812 |
-
insertHeight: function(at, lines, height) {
|
3813 |
this.size += lines.length;
|
3814 |
this.height += height;
|
3815 |
for (var i = 0, e = this.children.length; i < e; ++i) {
|
3816 |
var child = this.children[i], sz = child.chunkSize();
|
3817 |
if (at <= sz) {
|
3818 |
-
child.
|
3819 |
if (child.lines && child.lines.length > 50) {
|
3820 |
while (child.lines.length > 50) {
|
3821 |
var spilled = child.lines.splice(child.lines.length - 25, 25);
|
@@ -3852,7 +4416,6 @@ window.CodeMirror = (function() {
|
|
3852 |
} while (me.children.length > 10);
|
3853 |
me.parent.maybeSpill();
|
3854 |
},
|
3855 |
-
iter: function(from, to, op) { this.iterN(from, to - from, op); },
|
3856 |
iterN: function(at, n, op) {
|
3857 |
for (var i = 0, e = this.children.length; i < e; ++i) {
|
3858 |
var child = this.children[i], sz = child.chunkSize();
|
@@ -3866,9 +4429,268 @@ window.CodeMirror = (function() {
|
|
3866 |
}
|
3867 |
};
|
3868 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3869 |
// LINE UTILITIES
|
3870 |
|
3871 |
function getLine(chunk, n) {
|
|
|
3872 |
while (!chunk.lines) {
|
3873 |
for (var i = 0;; ++i) {
|
3874 |
var child = chunk.children[i], sz = child.chunkSize();
|
@@ -3879,6 +4701,23 @@ window.CodeMirror = (function() {
|
|
3879 |
return chunk.lines[n];
|
3880 |
}
|
3881 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3882 |
function updateLineHeight(line, height) {
|
3883 |
var diff = height - line.height;
|
3884 |
for (var n = line; n; n = n.parent) n.height += diff;
|
@@ -3893,11 +4732,11 @@ window.CodeMirror = (function() {
|
|
3893 |
no += chunk.children[i].chunkSize();
|
3894 |
}
|
3895 |
}
|
3896 |
-
return no;
|
3897 |
}
|
3898 |
|
3899 |
function lineAtHeight(chunk, h) {
|
3900 |
-
var n =
|
3901 |
outer: do {
|
3902 |
for (var i = 0, e = chunk.children.length; i < e; ++i) {
|
3903 |
var child = chunk.children[i], ch = child.height;
|
@@ -3916,7 +4755,7 @@ window.CodeMirror = (function() {
|
|
3916 |
}
|
3917 |
|
3918 |
function heightAtLine(cm, lineObj) {
|
3919 |
-
lineObj = visualLine(cm.
|
3920 |
|
3921 |
var h = 0, chunk = lineObj.parent;
|
3922 |
for (var i = 0; i < chunk.lines.length; ++i) {
|
@@ -3947,7 +4786,7 @@ window.CodeMirror = (function() {
|
|
3947 |
// Arrays of history events. Doing something adds an event to
|
3948 |
// done and clears undo. Undoing moves events from done to
|
3949 |
// undone, redoing moves them in the other direction.
|
3950 |
-
done: [], undone: [],
|
3951 |
// Used to track when changes can be merged into a single undo
|
3952 |
// event
|
3953 |
lastTime: 0, lastOp: null, lastOrigin: null,
|
@@ -3956,47 +4795,151 @@ window.CodeMirror = (function() {
|
|
3956 |
};
|
3957 |
}
|
3958 |
|
3959 |
-
function
|
3960 |
-
var
|
3961 |
-
|
3962 |
-
|
3963 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3964 |
if (cur &&
|
3965 |
-
(
|
3966 |
-
|
3967 |
-
|
3968 |
// Merge this change into the last event
|
3969 |
-
var last = lst(cur.
|
3970 |
-
if (
|
3971 |
-
//
|
3972 |
-
|
|
|
3973 |
} else {
|
3974 |
-
//
|
3975 |
-
|
3976 |
-
endAfter = Math.max(0, (start + old.length) - (last.start + last.added));
|
3977 |
-
for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]);
|
3978 |
-
for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]);
|
3979 |
-
if (startBefore) last.start = start;
|
3980 |
-
last.added += added - (old.length - startBefore - endAfter);
|
3981 |
}
|
3982 |
-
cur.
|
3983 |
} else {
|
3984 |
// Can not be merged, start a new event.
|
3985 |
-
cur = {
|
3986 |
-
|
3987 |
-
|
3988 |
-
|
3989 |
-
|
3990 |
-
|
3991 |
-
|
3992 |
-
|
3993 |
-
|
|
|
3994 |
else
|
3995 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3996 |
}
|
3997 |
-
|
3998 |
-
|
3999 |
-
|
|
|
|
|
|
|
4000 |
}
|
4001 |
|
4002 |
// EVENT OPERATORS
|
@@ -4033,13 +4976,6 @@ window.CodeMirror = (function() {
|
|
4033 |
return b;
|
4034 |
}
|
4035 |
|
4036 |
-
// Allow 3rd-party code to override event properties by adding an override
|
4037 |
-
// object to an event object.
|
4038 |
-
function e_prop(e, prop) {
|
4039 |
-
var overridden = e.override && e.override.hasOwnProperty(prop);
|
4040 |
-
return overridden ? e.override[prop] : e[prop];
|
4041 |
-
}
|
4042 |
-
|
4043 |
// EVENT HANDLING
|
4044 |
|
4045 |
function on(emitter, type, f) {
|
@@ -4074,14 +5010,26 @@ window.CodeMirror = (function() {
|
|
4074 |
for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
|
4075 |
}
|
4076 |
|
4077 |
-
|
|
|
4078 |
var arr = emitter._handlers && emitter._handlers[type];
|
4079 |
if (!arr) return;
|
4080 |
-
var args = Array.prototype.slice.call(arguments,
|
|
|
|
|
|
|
|
|
|
|
4081 |
function bnd(f) {return function(){f.apply(null, args);};};
|
4082 |
for (var i = 0; i < arr.length; ++i)
|
4083 |
-
|
4084 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
4085 |
}
|
4086 |
|
4087 |
function hasHandler(emitter, type) {
|
@@ -4105,12 +5053,12 @@ window.CodeMirror = (function() {
|
|
4105 |
|
4106 |
// Counts the column offset in a string, taking tabs into account.
|
4107 |
// Used mostly to find indentation.
|
4108 |
-
function countColumn(string, end, tabSize) {
|
4109 |
if (end == null) {
|
4110 |
end = string.search(/[^\s\u00a0]/);
|
4111 |
if (end == -1) end = string.length;
|
4112 |
}
|
4113 |
-
for (var i = 0, n = 0; i < end; ++i) {
|
4114 |
if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
|
4115 |
else ++n;
|
4116 |
}
|
@@ -4141,6 +5089,20 @@ window.CodeMirror = (function() {
|
|
4141 |
return -1;
|
4142 |
}
|
4143 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4144 |
function emptyArray(size) {
|
4145 |
for (var a = [], i = 0; i < size; ++i) a.push(undefined);
|
4146 |
return a;
|
@@ -4158,12 +5120,11 @@ window.CodeMirror = (function() {
|
|
4158 |
}
|
4159 |
|
4160 |
function isEmpty(obj) {
|
4161 |
-
var
|
4162 |
-
|
4163 |
-
return !c;
|
4164 |
}
|
4165 |
|
4166 |
-
var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/;
|
4167 |
|
4168 |
// DOM UTILITIES
|
4169 |
|
@@ -4177,7 +5138,8 @@ window.CodeMirror = (function() {
|
|
4177 |
}
|
4178 |
|
4179 |
function removeChildren(e) {
|
4180 |
-
e.
|
|
|
4181 |
return e;
|
4182 |
}
|
4183 |
|
@@ -4192,6 +5154,11 @@ window.CodeMirror = (function() {
|
|
4192 |
} else e.textContent = str;
|
4193 |
}
|
4194 |
|
|
|
|
|
|
|
|
|
|
|
4195 |
// FEATURE DETECTION
|
4196 |
|
4197 |
// Detect drag-and-drop
|
@@ -4212,8 +5179,8 @@ window.CodeMirror = (function() {
|
|
4212 |
// various browsers.
|
4213 |
var spanAffectsWrapping = /^$/; // Won't match any two-character string
|
4214 |
if (gecko) spanAffectsWrapping = /$'/;
|
4215 |
-
else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
|
4216 |
-
else if (
|
4217 |
|
4218 |
var knownScrollbarWidth;
|
4219 |
function scrollbarWidth(measure) {
|
@@ -4300,7 +5267,7 @@ window.CodeMirror = (function() {
|
|
4300 |
if (!order) return f(from, to, "ltr");
|
4301 |
for (var i = 0; i < order.length; ++i) {
|
4302 |
var part = order[i];
|
4303 |
-
if (part.from < to && part.to > from)
|
4304 |
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
|
4305 |
}
|
4306 |
}
|
@@ -4316,20 +5283,20 @@ window.CodeMirror = (function() {
|
|
4316 |
}
|
4317 |
|
4318 |
function lineStart(cm, lineN) {
|
4319 |
-
var line = getLine(cm.
|
4320 |
-
var visual = visualLine(cm.
|
4321 |
if (visual != line) lineN = lineNo(visual);
|
4322 |
var order = getOrder(visual);
|
4323 |
var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
|
4324 |
-
return
|
4325 |
}
|
4326 |
-
function lineEnd(cm,
|
4327 |
var merged, line;
|
4328 |
-
while (merged = collapsedSpanAtEnd(line = getLine(cm.
|
4329 |
-
|
4330 |
var order = getOrder(line);
|
4331 |
var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
|
4332 |
-
return
|
4333 |
}
|
4334 |
|
4335 |
// This is somewhat involved. It is needed in order to move
|
@@ -4418,24 +5385,20 @@ window.CodeMirror = (function() {
|
|
4418 |
|
4419 |
var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
|
4420 |
var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
|
|
|
|
|
4421 |
|
4422 |
-
return function
|
4423 |
if (!bidiRE.test(str)) return false;
|
4424 |
-
var len = str.length, types = []
|
4425 |
-
for (var i = 0, type; i < len; ++i)
|
4426 |
types.push(type = charType(str.charCodeAt(i)));
|
4427 |
-
if (startType == null) {
|
4428 |
-
if (type == "L") startType = "L";
|
4429 |
-
else if (type == "R" || type == "r") startType = "R";
|
4430 |
-
}
|
4431 |
-
}
|
4432 |
-
if (startType == null) startType = "L";
|
4433 |
|
4434 |
// W1. Examine each non-spacing mark (NSM) in the level run, and
|
4435 |
// change the type of the NSM to the type of the previous
|
4436 |
// character. If the NSM is at the start of the level run, it will
|
4437 |
// get the type of sor.
|
4438 |
-
for (var i = 0, prev =
|
4439 |
var type = types[i];
|
4440 |
if (type == "m") types[i] = prev;
|
4441 |
else prev = type;
|
@@ -4446,7 +5409,7 @@ window.CodeMirror = (function() {
|
|
4446 |
// AL is found, change the type of the European number to Arabic
|
4447 |
// number.
|
4448 |
// W3. Change all ALs to R.
|
4449 |
-
for (var i = 0, cur =
|
4450 |
var type = types[i];
|
4451 |
if (type == "1" && cur == "r") types[i] = "n";
|
4452 |
else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
|
@@ -4481,7 +5444,7 @@ window.CodeMirror = (function() {
|
|
4481 |
// W7. Search backwards from each instance of a European number
|
4482 |
// until the first strong type (R, L, or sor) is found. If an L is
|
4483 |
// found, then change the type of the European number to L.
|
4484 |
-
for (var i = 0, cur =
|
4485 |
var type = types[i];
|
4486 |
if (cur == "L" && type == "1") types[i] = "L";
|
4487 |
else if (isStrong.test(type)) cur = type;
|
@@ -4496,8 +5459,8 @@ window.CodeMirror = (function() {
|
|
4496 |
for (var i = 0; i < len; ++i) {
|
4497 |
if (isNeutral.test(types[i])) {
|
4498 |
for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
|
4499 |
-
var before = (i ? types[i-1] :
|
4500 |
-
var after = (end < len - 1 ? types[end] :
|
4501 |
var replace = before || after ? "L" : "R";
|
4502 |
for (var j = i; j < end; ++j) types[j] = replace;
|
4503 |
i = end - 1;
|
@@ -4547,7 +5510,7 @@ window.CodeMirror = (function() {
|
|
4547 |
|
4548 |
// THE END
|
4549 |
|
4550 |
-
CodeMirror.version = "3.
|
4551 |
|
4552 |
return CodeMirror;
|
4553 |
})();
|
1 |
+
// CodeMirror version 3.11
|
2 |
//
|
3 |
// CodeMirror is the only global var we claim
|
4 |
window.CodeMirror = (function() {
|
10 |
// bugs and behavior differences.
|
11 |
var gecko = /gecko\/\d/i.test(navigator.userAgent);
|
12 |
var ie = /MSIE \d/.test(navigator.userAgent);
|
13 |
+
var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8);
|
14 |
+
var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
|
15 |
var webkit = /WebKit\//.test(navigator.userAgent);
|
16 |
var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent);
|
17 |
var chrome = /Chrome\//.test(navigator.userAgent);
|
24 |
|
25 |
var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
|
26 |
// This is woefully incomplete. Suggestions for alternative methods welcome.
|
27 |
+
var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent);
|
28 |
var mac = ios || /Mac/.test(navigator.platform);
|
29 |
+
var windows = /windows/i.test(navigator.platform);
|
30 |
+
|
31 |
+
var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/);
|
32 |
+
if (opera_version) opera_version = Number(opera_version[1]);
|
33 |
+
// Some browsers use the wrong event properties to signal cmd/ctrl on OS X
|
34 |
+
var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11));
|
35 |
+
var captureMiddleClick = gecko || (ie && !ie_lt9);
|
36 |
|
37 |
// Optimize some code when these features are not used
|
38 |
var sawReadOnlySpans = false, sawCollapsedSpans = false;
|
41 |
|
42 |
function CodeMirror(place, options) {
|
43 |
if (!(this instanceof CodeMirror)) return new CodeMirror(place, options);
|
44 |
+
|
45 |
this.options = options = options || {};
|
46 |
// Determine effective options based on given values and defaults.
|
47 |
for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt))
|
48 |
options[opt] = defaults[opt];
|
49 |
setGuttersForLineNumbers(options);
|
50 |
|
51 |
+
var docStart = typeof options.value == "string" ? 0 : options.value.first;
|
52 |
+
var display = this.display = makeDisplay(place, docStart);
|
53 |
display.wrapper.CodeMirror = this;
|
54 |
updateGutters(this);
|
55 |
if (options.autofocus && !mobile) focusInput(this);
|
56 |
|
57 |
+
this.state = {keyMaps: [],
|
58 |
+
overlays: [],
|
59 |
+
modeGen: 0,
|
60 |
+
overwrite: false, focused: false,
|
61 |
+
suppressEdits: false, pasteIncoming: false,
|
62 |
+
draggingText: false,
|
63 |
+
highlight: new Delayed()};
|
64 |
+
|
65 |
themeChanged(this);
|
66 |
if (options.lineWrapping)
|
67 |
this.display.wrapper.className += " CodeMirror-wrap";
|
68 |
|
69 |
+
var doc = options.value;
|
70 |
+
if (typeof doc == "string") doc = new Doc(options.value, options.mode);
|
71 |
+
operation(this, attachDoc)(this, doc);
|
72 |
+
|
73 |
// Override magic textarea content restore that IE sometimes does
|
74 |
// on our hidden textarea on reload
|
75 |
if (ie) setTimeout(bind(resetInput, this, true), 20);
|
|
|
76 |
|
77 |
registerEventHandlers(this);
|
78 |
// IE throws unspecified error in certain cases, when
|
91 |
|
92 |
// DISPLAY CONSTRUCTOR
|
93 |
|
94 |
+
function makeDisplay(place, docStart) {
|
95 |
var d = {};
|
96 |
+
|
97 |
var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;");
|
98 |
+
if (webkit) input.style.width = "1000px";
|
99 |
+
else input.setAttribute("wrap", "off");
|
100 |
+
// if border: 0; -- iOS fails to open keyboard (issue #1287)
|
101 |
+
if (ios) input.style.border = "1px solid black";
|
102 |
+
input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
|
103 |
+
|
104 |
// Wraps and hides input textarea
|
105 |
d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
|
106 |
// The actual fake scrollbars.
|
111 |
d.lineDiv = elt("div");
|
112 |
d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
|
113 |
// Blinky cursor, and element used to ensure cursor fits at the end of a line
|
114 |
+
d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
|
115 |
// Secondary cursor, shown when on a 'jump' in bi-directional text
|
116 |
+
d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor");
|
117 |
// Used to measure text size
|
118 |
d.measure = elt("div", null, "CodeMirror-measure");
|
119 |
// Wraps everything that needs to exist inside the vertically-padded coordinate system
|
124 |
// Set to the height of the text, causes scrolling
|
125 |
d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
|
126 |
// D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers
|
127 |
+
d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;");
|
128 |
// Will contain the gutters, if any
|
129 |
d.gutters = elt("div", null, "CodeMirror-gutters");
|
130 |
d.lineGutter = null;
|
149 |
else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px";
|
150 |
|
151 |
// Current visible range (may be bigger than the view window).
|
152 |
+
d.viewOffset = d.lastSizeC = 0;
|
153 |
+
d.showingFrom = d.showingTo = docStart;
|
154 |
|
155 |
// Used to only resize the line number gutter when necessary (when
|
156 |
// the amount of lines crosses a boundary that makes its width change)
|
177 |
// string instead of the (large) selection.
|
178 |
d.inaccurateSelection = false;
|
179 |
|
180 |
+
// Tracks the maximum line length so that the horizontal scrollbar
|
181 |
+
// can be kept static when scrolling.
|
182 |
+
d.maxLine = null;
|
183 |
+
d.maxLineLength = 0;
|
184 |
+
d.maxLineChanged = false;
|
|
|
185 |
|
186 |
+
// Used for measuring wheel scrolling granularity
|
187 |
+
d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
|
188 |
|
189 |
+
return d;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
190 |
}
|
191 |
|
192 |
// STATE UPDATES
|
194 |
// Used to get the editor into a consistent state again when options change.
|
195 |
|
196 |
function loadMode(cm) {
|
197 |
+
cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption);
|
198 |
+
cm.doc.iter(function(line) {
|
199 |
+
if (line.stateAfter) line.stateAfter = null;
|
200 |
+
if (line.styles) line.styles = null;
|
201 |
+
});
|
202 |
+
cm.doc.frontier = cm.doc.first;
|
203 |
startWorker(cm, 100);
|
204 |
+
cm.state.modeGen++;
|
205 |
+
if (cm.curOp) regChange(cm);
|
206 |
}
|
207 |
|
208 |
function wrappingChanged(cm) {
|
|
|
209 |
if (cm.options.lineWrapping) {
|
210 |
cm.display.wrapper.className += " CodeMirror-wrap";
|
|
|
|
|
|
|
|
|
|
|
|
|
211 |
cm.display.sizer.style.minWidth = "";
|
212 |
} else {
|
213 |
cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", "");
|
214 |
+
computeMaxLength(cm);
|
|
|
|
|
|
|
215 |
}
|
216 |
+
estimateLineHeights(cm);
|
217 |
+
regChange(cm);
|
218 |
clearCaches(cm);
|
219 |
+
setTimeout(function(){updateScrollbars(cm.display, cm.doc.height);}, 100);
|
220 |
+
}
|
221 |
+
|
222 |
+
function estimateHeight(cm) {
|
223 |
+
var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
|
224 |
+
var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
|
225 |
+
return function(line) {
|
226 |
+
if (lineIsHidden(cm.doc, line))
|
227 |
+
return 0;
|
228 |
+
else if (wrapping)
|
229 |
+
return (Math.ceil(line.text.length / perLine) || 1) * th;
|
230 |
+
else
|
231 |
+
return th;
|
232 |
+
};
|
233 |
+
}
|
234 |
+
|
235 |
+
function estimateLineHeights(cm) {
|
236 |
+
var doc = cm.doc, est = estimateHeight(cm);
|
237 |
+
doc.iter(function(line) {
|
238 |
+
var estHeight = est(line);
|
239 |
+
if (estHeight != line.height) updateLineHeight(line, estHeight);
|
240 |
+
});
|
241 |
}
|
242 |
|
243 |
function keyMapChanged(cm) {
|
254 |
|
255 |
function guttersChanged(cm) {
|
256 |
updateGutters(cm);
|
257 |
+
regChange(cm);
|
258 |
}
|
259 |
|
260 |
function updateGutters(cm) {
|
289 |
return len;
|
290 |
}
|
291 |
|
292 |
+
function computeMaxLength(cm) {
|
293 |
+
var d = cm.display, doc = cm.doc;
|
294 |
+
d.maxLine = getLine(doc, doc.first);
|
295 |
+
d.maxLineLength = lineLength(doc, d.maxLine);
|
296 |
+
d.maxLineChanged = true;
|
297 |
+
doc.iter(function(line) {
|
298 |
+
var len = lineLength(doc, line);
|
299 |
+
if (len > d.maxLineLength) {
|
300 |
+
d.maxLineLength = len;
|
301 |
+
d.maxLine = line;
|
302 |
}
|
303 |
});
|
304 |
}
|
322 |
// Re-synchronize the fake scrollbars with the actual size of the
|
323 |
// content. Optionally force a scrollTop.
|
324 |
function updateScrollbars(d /* display */, docHeight) {
|
325 |
+
var totalHeight = docHeight + paddingVert(d);
|
326 |
d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
|
327 |
var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
|
328 |
var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
|
330 |
if (needsV) {
|
331 |
d.scrollbarV.style.display = "block";
|
332 |
d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0";
|
333 |
+
d.scrollbarV.firstChild.style.height =
|
334 |
(scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px";
|
335 |
} else d.scrollbarV.style.display = "";
|
336 |
if (needsH) {
|
361 |
|
362 |
function alignHorizontally(cm) {
|
363 |
var display = cm.display;
|
364 |
+
if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return;
|
365 |
+
var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
|
366 |
var gutterW = display.gutters.offsetWidth, l = comp + "px";
|
367 |
for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) {
|
368 |
for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l;
|
369 |
}
|
370 |
+
if (cm.options.fixedGutter)
|
371 |
+
display.gutters.style.left = (comp + gutterW) + "px";
|
372 |
}
|
373 |
|
374 |
function maybeUpdateLineNumberWidth(cm) {
|
375 |
if (!cm.options.lineNumbers) return false;
|
376 |
+
var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
|
377 |
if (last.length != display.lineNumChars) {
|
378 |
var test = display.measure.appendChild(elt("div", [elt("div", last)],
|
379 |
"CodeMirror-linenumber CodeMirror-gutter-elt"));
|
392 |
return String(options.lineNumberFormatter(i + options.firstLineNumber));
|
393 |
}
|
394 |
function compensateForHScroll(display) {
|
395 |
+
return getRect(display.scroller).left - getRect(display.sizer).left;
|
396 |
}
|
397 |
|
398 |
// DISPLAY DRAWING
|
401 |
var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo;
|
402 |
var updated = updateDisplayInner(cm, changes, viewPort);
|
403 |
if (updated) {
|
404 |
+
signalLater(cm, "update", cm);
|
405 |
if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
|
406 |
+
signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
|
407 |
}
|
408 |
updateSelection(cm);
|
409 |
+
updateScrollbars(cm.display, cm.doc.height);
|
410 |
|
411 |
return updated;
|
412 |
}
|
415 |
// determine which DOM updates have to be made, and makes the
|
416 |
// updates.
|
417 |
function updateDisplayInner(cm, changes, viewPort) {
|
418 |
+
var display = cm.display, doc = cm.doc;
|
419 |
if (!display.wrapper.clientWidth) {
|
420 |
+
display.showingFrom = display.showingTo = doc.first;
|
421 |
+
display.viewOffset = 0;
|
422 |
return;
|
423 |
}
|
424 |
|
427 |
// to render instead of the current scrollbar position.
|
428 |
var visible = visibleLines(display, doc, viewPort);
|
429 |
// Bail out if the visible area is already rendered and nothing changed.
|
430 |
+
if (changes.length == 0 &&
|
431 |
visible.from > display.showingFrom && visible.to < display.showingTo)
|
432 |
return;
|
433 |
|
434 |
+
if (maybeUpdateLineNumberWidth(cm))
|
435 |
+
changes = [{from: doc.first, to: doc.first + doc.size}];
|
436 |
+
var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px";
|
437 |
+
display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
438 |
|
439 |
// Used to determine which lines need their line numbers updated
|
440 |
+
var positionsChangedFrom = Infinity;
|
441 |
+
if (cm.options.lineNumbers)
|
442 |
for (var i = 0; i < changes.length; ++i)
|
443 |
if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; }
|
444 |
|
445 |
+
var end = doc.first + doc.size;
|
446 |
+
var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
|
447 |
+
var to = Math.min(end, visible.to + cm.options.viewportMargin);
|
448 |
+
if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom);
|
449 |
+
if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo);
|
450 |
if (sawCollapsedSpans) {
|
451 |
from = lineNo(visualLine(doc, getLine(doc, from)));
|
452 |
+
while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to;
|
453 |
}
|
454 |
|
455 |
// Create a range of theoretically intact lines, and punch holes
|
456 |
// in that using the change info.
|
457 |
+
var intact = [{from: Math.max(display.showingFrom, doc.first),
|
458 |
+
to: Math.min(display.showingTo, end)}];
|
459 |
+
if (intact[0].from >= intact[0].to) intact = [];
|
460 |
+
else intact = computeIntact(intact, changes);
|
461 |
+
// When merged lines are present, we might have to reduce the
|
462 |
+
// intact ranges because changes in continued fragments of the
|
463 |
+
// intact lines do require the lines to be redrawn.
|
464 |
+
if (sawCollapsedSpans)
|
465 |
+
for (var i = 0; i < intact.length; ++i) {
|
466 |
+
var range = intact[i], merged;
|
467 |
+
while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) {
|
468 |
+
var newTo = merged.find().from.line;
|
469 |
+
if (newTo > range.from) range.to = newTo;
|
470 |
+
else { intact.splice(i--, 1); break; }
|
471 |
+
}
|
472 |
+
}
|
473 |
+
|
474 |
// Clip off the parts that won't be visible
|
475 |
var intactLines = 0;
|
476 |
for (var i = 0; i < intact.length; ++i) {
|
480 |
if (range.from >= range.to) intact.splice(i--, 1);
|
481 |
else intactLines += range.to - range.from;
|
482 |
}
|
483 |
+
if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) {
|
484 |
+
updateViewOffset(cm);
|
485 |
return;
|
486 |
+
}
|
487 |
intact.sort(function(a, b) {return a.from - b.from;});
|
488 |
|
489 |
+
var focused = document.activeElement;
|
490 |
if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none";
|
491 |
patchDisplay(cm, from, to, intact, positionsChangedFrom);
|
492 |
display.lineDiv.style.display = "";
|
493 |
+
if (document.activeElement != focused && focused.offsetHeight) focused.focus();
|
494 |
|
495 |
var different = from != display.showingFrom || to != display.showingTo ||
|
496 |
display.lastSizeC != display.wrapper.clientHeight;
|
507 |
height = bot - prevBottom;
|
508 |
prevBottom = bot;
|
509 |
} else {
|
510 |
+
var box = getRect(node);
|
511 |
height = box.bottom - box.top;
|
512 |
}
|
513 |
var diff = node.lineObj.height - height;
|
514 |
if (height < 2) height = textHeight(display);
|
515 |
+
if (diff > .001 || diff < -.001) {
|
516 |
updateLineHeight(node.lineObj, height);
|
517 |
+
var widgets = node.lineObj.widgets;
|
518 |
+
if (widgets) for (var i = 0; i < widgets.length; ++i)
|
519 |
+
widgets[i].height = widgets[i].node.offsetHeight;
|
520 |
+
}
|
521 |
}
|
522 |
+
updateViewOffset(cm);
|
523 |
+
|
524 |
+
if (visibleLines(display, doc, viewPort).to > to)
|
525 |
+
updateDisplayInner(cm, [], viewPort);
|
526 |
return true;
|
527 |
}
|
528 |
|
529 |
+
function updateViewOffset(cm) {
|
530 |
+
var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom));
|
531 |
+
// Position the mover div to align with the current virtual scroll position
|
532 |
+
cm.display.mover.style.top = off + "px";
|
533 |
+
}
|
534 |
+
|
535 |
function computeIntact(intact, changes) {
|
536 |
for (var i = 0, l = changes.length || 0; i < l; ++i) {
|
537 |
var change = changes[i], intact2 = [], diff = change.diff || 0;
|
569 |
function patchDisplay(cm, from, to, intact, updateNumbersFrom) {
|
570 |
var dims = getDimensions(cm);
|
571 |
var display = cm.display, lineNumbers = cm.options.lineNumbers;
|
572 |
+
if (!intact.length && (!webkit || !cm.display.currentWheelTarget))
|
|
|
|
|
573 |
removeChildren(display.lineDiv);
|
574 |
var container = display.lineDiv, cur = container.firstChild;
|
575 |
|
579 |
node.style.display = "none";
|
580 |
node.lineObj = null;
|
581 |
} else {
|
582 |
+
node.parentNode.removeChild(node);
|
583 |
}
|
584 |
return next;
|
585 |
}
|
586 |
|
587 |
+
var nextIntact = intact.shift(), lineN = from;
|
588 |
+
cm.doc.iter(from, to, function(line) {
|
589 |
+
if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift();
|
590 |
+
if (lineIsHidden(cm.doc, line)) {
|
591 |
if (line.height != 0) updateLineHeight(line, 0);
|
592 |
+
if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i)
|
593 |
+
if (line.widgets[i].showIfHidden) {
|
594 |
+
var prev = cur.previousSibling;
|
595 |
+
if (/pre/i.test(prev.nodeName)) {
|
596 |
+
var wrap = elt("div", null, null, "position: relative");
|
597 |
+
prev.parentNode.replaceChild(wrap, prev);
|
598 |
+
wrap.appendChild(prev);
|
599 |
+
prev = wrap;
|
600 |
+
}
|
601 |
+
var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget"));
|
602 |
+
positionLineWidget(line.widgets[i], wnode, prev, dims);
|
603 |
+
}
|
604 |
+
} else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) {
|
605 |
// This line is intact. Skip to the actual node. Update its
|
606 |
// line number if needed.
|
607 |
while (cur.lineObj != line) cur = rm(cur);
|
608 |
+
if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber)
|
609 |
+
setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN));
|
610 |
cur = cur.nextSibling;
|
611 |
} else {
|
612 |
+
// For lines with widgets, make an attempt to find and reuse
|
613 |
+
// the existing element, so that widgets aren't needlessly
|
614 |
+
// removed and re-inserted into the dom
|
615 |
+
if (line.widgets) for (var j = 0, search = cur, reuse; search && j < 20; ++j, search = search.nextSibling)
|
616 |
+
if (search.lineObj == line && /div/i.test(search.nodeName)) { reuse = search; break; }
|
617 |
// This line needs to be generated.
|
618 |
+
var lineNode = buildLineElement(cm, line, lineN, dims, reuse);
|
619 |
+
if (lineNode != reuse) {
|
620 |
+
container.insertBefore(lineNode, cur);
|
621 |
+
} else {
|
622 |
+
while (cur != reuse) cur = rm(cur);
|
623 |
+
cur = cur.nextSibling;
|
624 |
+
}
|
625 |
+
|
626 |
lineNode.lineObj = line;
|
627 |
}
|
628 |
+
++lineN;
|
629 |
});
|
630 |
while (cur) cur = rm(cur);
|
631 |
}
|
632 |
|
633 |
+
function buildLineElement(cm, line, lineNo, dims, reuse) {
|
634 |
var lineElement = lineContent(cm, line);
|
635 |
+
var markers = line.gutterMarkers, display = cm.display, wrap;
|
636 |
+
|
637 |
+
if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets)
|
638 |
+
return lineElement;
|
639 |
+
|
640 |
+
// Lines with gutter elements, widgets or a background class need
|
641 |
+
// to be wrapped again, and have the extra elements added to the
|
642 |
+
// wrapper div
|
643 |
+
|
644 |
+
if (reuse) {
|
645 |
+
reuse.alignable = null;
|
646 |
+
var isOk = true, widgetsSeen = 0;
|
647 |
+
for (var n = reuse.firstChild, next; n; n = next) {
|
648 |
+
next = n.nextSibling;
|
649 |
+
if (!/\bCodeMirror-linewidget\b/.test(n.className)) {
|
650 |
+
reuse.removeChild(n);
|
651 |
+
} else {
|
652 |
+
for (var i = 0, first = true; i < line.widgets.length; ++i) {
|
653 |
+
var widget = line.widgets[i], isFirst = false;
|
654 |
+
if (!widget.above) { isFirst = first; first = false; }
|
655 |
+
if (widget.node == n.firstChild) {
|
656 |
+
positionLineWidget(widget, n, reuse, dims);
|
657 |
+
++widgetsSeen;
|
658 |
+
if (isFirst) reuse.insertBefore(lineElement, n);
|
659 |
+
break;
|
660 |
+
}
|
661 |
+
}
|
662 |
+
if (i == line.widgets.length) { isOk = false; break; }
|
663 |
+
}
|
664 |
+
}
|
665 |
+
if (isOk && widgetsSeen == line.widgets.length) {
|
666 |
+
wrap = reuse;
|
667 |
+
reuse.className = line.wrapClass || "";
|
668 |
+
}
|
669 |
+
}
|
670 |
+
if (!wrap) {
|
671 |
+
wrap = elt("div", null, line.wrapClass, "position: relative");
|
672 |
+
wrap.appendChild(lineElement);
|
673 |
+
}
|
674 |
+
// Kludge to make sure the styled element lies behind the selection (by z-index)
|
675 |
+
if (line.bgClass)
|
676 |
+
wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild);
|
677 |
if (cm.options.lineNumbers || markers) {
|
678 |
+
var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " +
|
679 |
+
(cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"),
|
680 |
+
wrap.firstChild);
|
681 |
+
if (cm.options.fixedGutter) (wrap.alignable || (wrap.alignable = [])).push(gutterWrap);
|
682 |
if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
|
683 |
wrap.lineNumber = gutterWrap.appendChild(
|
684 |
elt("div", lineNumberFor(cm.options, lineNo),
|
693 |
dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px"));
|
694 |
}
|
695 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
696 |
if (ie_lt8) wrap.style.zIndex = 2;
|
697 |
+
if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
|
698 |
+
var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget");
|
699 |
+
positionLineWidget(widget, node, wrap, dims);
|
700 |
+
if (widget.above)
|
701 |
+
wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement);
|
702 |
+
else
|
703 |
+
wrap.appendChild(node);
|
704 |
+
signalLater(widget, "redraw");
|
705 |
+
}
|
706 |
return wrap;
|
707 |
}
|
708 |
|
709 |
+
function positionLineWidget(widget, node, wrap, dims) {
|
710 |
+
if (widget.noHScroll) {
|
711 |
+
(wrap.alignable || (wrap.alignable = [])).push(node);
|
712 |
+
var width = dims.wrapperWidth;
|
713 |
+
node.style.left = dims.fixedPos + "px";
|
714 |
+
if (!widget.coverGutter) {
|
715 |
+
width -= dims.gutterTotalWidth;
|
716 |
+
node.style.paddingLeft = dims.gutterTotalWidth + "px";
|
717 |
+
}
|
718 |
+
node.style.width = width + "px";
|
719 |
+
}
|
720 |
+
if (widget.coverGutter) {
|
721 |
+
node.style.zIndex = 5;
|
722 |
+
node.style.position = "relative";
|
723 |
+
if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px";
|
724 |
+
}
|
725 |
+
}
|
726 |
+
|
727 |
// SELECTION / CURSOR
|
728 |
|
729 |
function updateSelection(cm) {
|
730 |
var display = cm.display;
|
731 |
+
var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to);
|
732 |
if (collapsed || cm.options.showCursorWhenSelecting)
|
733 |
updateSelectionCursor(cm);
|
734 |
else
|
739 |
display.selectionDiv.style.display = "none";
|
740 |
|
741 |
// Move the hidden textarea near the cursor to prevent scrolling artifacts
|
742 |
+
var headPos = cursorCoords(cm, cm.doc.sel.head, "div");
|
743 |
+
var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv);
|
744 |
display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
|
745 |
headPos.top + lineOff.top - wrapOff.top)) + "px";
|
746 |
display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
|
749 |
|
750 |
// No selection, plain cursor
|
751 |
function updateSelectionCursor(cm) {
|
752 |
+
var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div");
|
753 |
display.cursor.style.left = pos.left + "px";
|
754 |
display.cursor.style.top = pos.top + "px";
|
755 |
display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
|
765 |
|
766 |
// Highlight selection
|
767 |
function updateSelectionRange(cm) {
|
768 |
+
var display = cm.display, doc = cm.doc, sel = cm.doc.sel;
|
769 |
var fragment = document.createDocumentFragment();
|
770 |
var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display);
|
771 |
|
780 |
var lineObj = getLine(doc, line);
|
781 |
var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity;
|
782 |
function coords(ch) {
|
783 |
+
return charCoords(cm, Pos(line, ch), "div", lineObj);
|
784 |
}
|
785 |
|
786 |
iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) {
|
858 |
// HIGHLIGHT WORKER
|
859 |
|
860 |
function startWorker(cm, time) {
|
861 |
+
if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo)
|
862 |
+
cm.state.highlight.set(time, bind(highlightWorker, cm));
|
863 |
}
|
864 |
|
865 |
function highlightWorker(cm) {
|
866 |
+
var doc = cm.doc;
|
867 |
+
if (doc.frontier < doc.first) doc.frontier = doc.first;
|
868 |
+
if (doc.frontier >= cm.display.showingTo) return;
|
869 |
var end = +new Date + cm.options.workTime;
|
870 |
+
var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
|
871 |
var changed = [], prevChange;
|
872 |
+
doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) {
|
873 |
+
if (doc.frontier >= cm.display.showingFrom) { // Visible
|
874 |
+
var oldStyles = line.styles;
|
875 |
+
line.styles = highlightLine(cm, line, state);
|
876 |
+
var ischange = !oldStyles || oldStyles.length != line.styles.length;
|
877 |
+
for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
|
878 |
+
if (ischange) {
|
879 |
+
if (prevChange && prevChange.end == doc.frontier) prevChange.end++;
|
880 |
+
else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1});
|
881 |
}
|
882 |
+
line.stateAfter = copyState(doc.mode, state);
|
883 |
} else {
|
884 |
processLine(cm, line, state);
|
885 |
+
line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null;
|
886 |
}
|
887 |
+
++doc.frontier;
|
888 |
if (+new Date > end) {
|
889 |
startWorker(cm, cm.options.workDelay);
|
890 |
return true;
|
903 |
// smallest indentation, which tends to need the least context to
|
904 |
// parse correctly.
|
905 |
function findStartLine(cm, n) {
|
906 |
+
var minindent, minline, doc = cm.doc;
|
907 |
for (var search = n, lim = n - 100; search > lim; --search) {
|
908 |
+
if (search <= doc.first) return doc.first;
|
909 |
+
var line = getLine(doc, search - 1);
|
910 |
if (line.stateAfter) return search;
|
911 |
var indented = countColumn(line.text, null, cm.options.tabSize);
|
912 |
if (minline == null || minindent > indented) {
|
918 |
}
|
919 |
|
920 |
function getStateBefore(cm, n) {
|
921 |
+
var doc = cm.doc, display = cm.display;
|
922 |
+
if (!doc.mode.startState) return true;
|
923 |
+
var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter;
|
924 |
+
if (!state) state = startState(doc.mode);
|
925 |
+
else state = copyState(doc.mode, state);
|
926 |
+
doc.iter(pos, n, function(line) {
|
927 |
processLine(cm, line, state);
|
928 |
+
var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo;
|
929 |
+
line.stateAfter = save ? copyState(doc.mode, state) : null;
|
930 |
++pos;
|
931 |
});
|
932 |
return state;
|
933 |
}
|
934 |
|
935 |
// POSITION MEASUREMENT
|
936 |
+
|
937 |
function paddingTop(display) {return display.lineSpace.offsetTop;}
|
938 |
+
function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;}
|
939 |
function paddingLeft(display) {
|
940 |
+
var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x"));
|
941 |
return e.offsetLeft;
|
942 |
}
|
943 |
|
944 |
function measureChar(cm, line, ch, data) {
|
945 |
+
var dir = -1;
|
946 |
+
data = data || measureLine(cm, line);
|
947 |
+
|
948 |
for (var pos = ch;; pos += dir) {
|
949 |
var r = data[pos];
|
950 |
if (r) break;
|
955 |
top: r.top, bottom: r.bottom};
|
956 |
}
|
957 |
|
958 |
+
function findCachedMeasurement(cm, line) {
|
959 |
+
var cache = cm.display.measureLineCache;
|
|
|
960 |
for (var i = 0; i < cache.length; ++i) {
|
961 |
var memo = cache[i];
|
962 |
if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
|
963 |
+
cm.display.scroller.clientWidth == memo.width &&
|
964 |
+
memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
|
965 |
return memo.measure;
|
966 |
}
|
967 |
+
}
|
968 |
+
|
969 |
+
function measureLine(cm, line) {
|
970 |
+
// First look in the cache
|
971 |
+
var measure = findCachedMeasurement(cm, line);
|
972 |
+
if (!measure) {
|
973 |
+
// Failing that, recompute and store result in cache
|
974 |
+
measure = measureLineInner(cm, line);
|
975 |
+
var cache = cm.display.measureLineCache;
|
976 |
+
var memo = {text: line.text, width: cm.display.scroller.clientWidth,
|
977 |
+
markedSpans: line.markedSpans, measure: measure,
|
978 |
+
classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
|
979 |
+
if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
|
980 |
+
else cache.push(memo);
|
981 |
+
}
|
982 |
return measure;
|
983 |
}
|
984 |
|
1013 |
|
1014 |
removeChildrenAndAdd(display.measure, pre);
|
1015 |
|
1016 |
+
var outer = getRect(display.lineDiv);
|
1017 |
var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight;
|
1018 |
+
// Work around an IE7/8 bug where it will sometimes have randomly
|
1019 |
+
// replaced our pre with a clone at this point.
|
1020 |
+
if (ie_lt9 && display.measure.first != pre)
|
1021 |
+
removeChildrenAndAdd(display.measure, pre);
|
1022 |
+
|
1023 |
for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) {
|
1024 |
+
var size = getRect(cur);
|
1025 |
var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot);
|
1026 |
for (var j = 0; j < vranges.length; j += 2) {
|
1027 |
var rtop = vranges[j], rbot = vranges[j+1];
|
1035 |
}
|
1036 |
}
|
1037 |
if (j == vranges.length) vranges.push(top, bot);
|
1038 |
+
var right = size.right;
|
1039 |
+
if (cur.measureRight) right = getRect(cur.measureRight).left;
|
1040 |
+
data[i] = {left: size.left - outer.left, right: right - outer.left, top: j};
|
1041 |
}
|
1042 |
for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) {
|
1043 |
var vr = cur.top;
|
1044 |
cur.top = vranges[vr]; cur.bottom = vranges[vr+1];
|
1045 |
}
|
1046 |
+
|
1047 |
return data;
|
1048 |
}
|
1049 |
|
1050 |
+
function measureLineWidth(cm, line) {
|
1051 |
+
var hasBadSpan = false;
|
1052 |
+
if (line.markedSpans) for (var i = 0; i < line.markedSpans; ++i) {
|
1053 |
+
var sp = line.markedSpans[i];
|
1054 |
+
if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
|
1055 |
+
}
|
1056 |
+
var cached = !hasBadSpan && findCachedMeasurement(cm, line);
|
1057 |
+
if (cached) return measureChar(cm, line, line.text.length, cached).right;
|
1058 |
+
|
1059 |
+
var pre = lineContent(cm, line);
|
1060 |
+
var end = pre.appendChild(zeroWidthElement(cm.display.measure));
|
1061 |
+
removeChildrenAndAdd(cm.display.measure, pre);
|
1062 |
+
return getRect(end).right - getRect(cm.display.lineDiv).left;
|
1063 |
+
}
|
1064 |
+
|
1065 |
function clearCaches(cm) {
|
1066 |
cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0;
|
1067 |
cm.display.cachedCharWidth = cm.display.cachedTextHeight = null;
|
1068 |
+
cm.display.maxLineChanged = true;
|
1069 |
+
cm.display.lineNumChars = null;
|
1070 |
}
|
1071 |
|
1072 |
// Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page"
|
1073 |
function intoCoordSystem(cm, lineObj, rect, context) {
|
1074 |
if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) {
|
1075 |
+
var size = widgetHeight(lineObj.widgets[i]);
|
1076 |
rect.top += size; rect.bottom += size;
|
1077 |
}
|
1078 |
if (context == "line") return rect;
|
1080 |
var yOff = heightAtLine(cm, lineObj);
|
1081 |
if (context != "local") yOff -= cm.display.viewOffset;
|
1082 |
if (context == "page") {
|
1083 |
+
var lOff = getRect(cm.display.lineSpace);
|
1084 |
yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop);
|
1085 |
var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft);
|
1086 |
rect.left += xOff; rect.right += xOff;
|
1089 |
return rect;
|
1090 |
}
|
1091 |
|
1092 |
+
// Context may be "window", "page", "div", or "local"/null
|
1093 |
+
// Result is in local coords
|
1094 |
+
function fromCoordSystem(cm, coords, context) {
|
1095 |
+
if (context == "div") return coords;
|
1096 |
+
var left = coords.left, top = coords.top;
|
1097 |
+
if (context == "page") {
|
1098 |
+
left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft;
|
1099 |
+
top -= window.pageYOffset || (document.documentElement || document.body).scrollTop;
|
1100 |
+
}
|
1101 |
+
var lineSpaceBox = getRect(cm.display.lineSpace);
|
1102 |
+
left -= lineSpaceBox.left;
|
1103 |
+
top -= lineSpaceBox.top;
|
1104 |
+
if (context == "local" || !context) {
|
1105 |
+
var editorBox = getRect(cm.display.wrapper);
|
1106 |
+
left -= editorBox.left;
|
1107 |
+
top -= editorBox.top;
|
1108 |
+
}
|
1109 |
+
return {left: left, top: top};
|
1110 |
+
}
|
1111 |
+
|
1112 |
function charCoords(cm, pos, context, lineObj) {
|
1113 |
+
if (!lineObj) lineObj = getLine(cm.doc, pos.line);
|
1114 |
return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context);
|
1115 |
}
|
1116 |
|
1117 |
function cursorCoords(cm, pos, context, lineObj, measurement) {
|
1118 |
+
lineObj = lineObj || getLine(cm.doc, pos.line);
|
1119 |
if (!measurement) measurement = measureLine(cm, lineObj);
|
1120 |
function get(ch, right) {
|
1121 |
var m = measureChar(cm, lineObj, ch, measurement);
|
1130 |
if (part.from < ch && part.to > ch) return get(ch, rtl);
|
1131 |
var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
|
1132 |
if (left == ch) {
|
1133 |
+
// IE returns bogus offsets and widths for edges where the
|
1134 |
+
// direction flips, but only for the side with the lower
|
1135 |
+
// level. So we try to use the side with the higher level.
|
|
|
1136 |
if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
|
1137 |
else here = get(rtl && part.from != part.to ? ch - 1 : ch);
|
1138 |
if (rtl == linedir) main = here; else other = here;
|
1150 |
return main;
|
1151 |
}
|
1152 |
|
1153 |
+
function PosMaybeOutside(line, ch, outside) {
|
1154 |
+
var pos = new Pos(line, ch);
|
1155 |
+
if (outside) pos.outside = true;
|
1156 |
+
return pos;
|
1157 |
+
}
|
1158 |
+
|
1159 |
// Coords must be lineSpace-local
|
1160 |
function coordsChar(cm, x, y) {
|
1161 |
+
var doc = cm.doc;
|
1162 |
y += cm.display.viewOffset;
|
1163 |
+
if (y < 0) return PosMaybeOutside(doc.first, 0, true);
|
1164 |
+
var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
|
1165 |
+
if (lineNo > last)
|
1166 |
+
return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true);
|
1167 |
if (x < 0) x = 0;
|
1168 |
|
1169 |
for (;;) {
|
1170 |
var lineObj = getLine(doc, lineNo);
|
1171 |
var found = coordsCharInner(cm, lineObj, lineNo, x, y);
|
1172 |
var merged = collapsedSpanAtEnd(lineObj);
|
1173 |
+
var mergedPos = merged && merged.find();
|
1174 |
+
if (merged && found.ch >= mergedPos.from.ch)
|
1175 |
+
lineNo = mergedPos.to.line;
|
1176 |
else
|
1177 |
return found;
|
1178 |
}
|
1180 |
|
1181 |
function coordsCharInner(cm, lineObj, lineNo, x, y) {
|
1182 |
var innerOff = y - heightAtLine(cm, lineObj);
|
1183 |
+
var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth;
|
1184 |
var measurement = measureLine(cm, lineObj);
|
1185 |
|
1186 |
function getX(ch) {
|
1187 |
+
var sp = cursorCoords(cm, Pos(lineNo, ch), "line",
|
1188 |
lineObj, measurement);
|
1189 |
wrongLine = true;
|
1190 |
+
if (innerOff > sp.bottom) return sp.left - adjust;
|
1191 |
+
else if (innerOff < sp.top) return sp.left + adjust;
|
1192 |
else wrongLine = false;
|
1193 |
return sp.left;
|
1194 |
}
|
1195 |
|
1196 |
var bidi = getOrder(lineObj), dist = lineObj.text.length;
|
1197 |
var from = lineLeft(lineObj), to = lineRight(lineObj);
|
1198 |
+
var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine;
|
1199 |
|
1200 |
+
if (x > toX) return PosMaybeOutside(lineNo, to, toOutside);
|
1201 |
// Do a binary search between these bounds.
|
1202 |
for (;;) {
|
1203 |
if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) {
|
1204 |
var after = x - fromX < toX - x, ch = after ? from : to;
|
1205 |
while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch;
|
1206 |
+
var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside);
|
1207 |
+
pos.after = after;
|
1208 |
+
return pos;
|
1209 |
}
|
1210 |
var step = Math.ceil(dist / 2), middle = from + step;
|
1211 |
if (bidi) {
|
1213 |
for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1);
|
1214 |
}
|
1215 |
var middleX = getX(middle);
|
1216 |
+
if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist -= step;}
|
1217 |
+
else {from = middle; fromX = middleX; fromOutside = wrongLine; dist = step;}
|
1218 |
}
|
1219 |
}
|
1220 |
|
1255 |
// be awkward, slow, and error-prone), but instead updates are
|
1256 |
// batched and then all combined and executed at once.
|
1257 |
|
1258 |
+
var nextOpId = 0;
|
1259 |
function startOperation(cm) {
|
1260 |
+
cm.curOp = {
|
|
|
|
|
|
|
|
|
1261 |
// An array of ranges of lines that have to be updated. See
|
1262 |
// updateDisplay.
|
1263 |
changes: [],
|
|
|
1264 |
updateInput: null,
|
1265 |
userSelChange: null,
|
1266 |
textChanged: null,
|
1267 |
selectionChanged: false,
|
1268 |
updateMaxLine: false,
|
1269 |
+
updateScrollPos: false,
|
1270 |
+
id: ++nextOpId
|
1271 |
};
|
1272 |
+
if (!delayedCallbackDepth++) delayedCallbacks = [];
|
1273 |
}
|
1274 |
|
1275 |
function endOperation(cm) {
|
1276 |
+
var op = cm.curOp, doc = cm.doc, display = cm.display;
|
|
|
1277 |
cm.curOp = null;
|
1278 |
+
|
1279 |
+
if (op.updateMaxLine) computeMaxLength(cm);
|
1280 |
+
if (display.maxLineChanged && !cm.options.lineWrapping) {
|
1281 |
+
var width = measureLineWidth(cm, display.maxLine);
|
1282 |
+
display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px";
|
1283 |
+
display.maxLineChanged = false;
|
1284 |
+
var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth);
|
1285 |
+
if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos)
|
1286 |
+
setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
|
1287 |
}
|
1288 |
var newScrollPos, updated;
|
1289 |
+
if (op.updateScrollPos) {
|
1290 |
+
newScrollPos = op.updateScrollPos;
|
1291 |
+
} else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible
|
1292 |
+
var coords = cursorCoords(cm, doc.sel.head);
|
1293 |
newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom);
|
1294 |
}
|
1295 |
+
if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) {
|
1296 |
updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop);
|
1297 |
+
if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
|
1298 |
+
}
|
1299 |
if (!updated && op.selectionChanged) updateSelection(cm);
|
1300 |
+
if (op.updateScrollPos) {
|
1301 |
+
display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop;
|
1302 |
+
display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
|
1303 |
+
alignHorizontally(cm);
|
1304 |
+
} else if (newScrollPos) {
|
1305 |
+
scrollCursorIntoView(cm);
|
1306 |
+
}
|
1307 |
if (op.selectionChanged) restartBlink(cm);
|
1308 |
|
1309 |
+
if (cm.state.focused && op.updateInput)
|
1310 |
resetInput(cm, op.userSelChange);
|
1311 |
|
1312 |
+
var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
|
1313 |
+
if (hidden) for (var i = 0; i < hidden.length; ++i)
|
1314 |
+
if (!hidden[i].lines.length) signal(hidden[i], "hide");
|
1315 |
+
if (unhidden) for (var i = 0; i < unhidden.length; ++i)
|
1316 |
+
if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
|
1317 |
+
|
1318 |
+
var delayed;
|
1319 |
+
if (!--delayedCallbackDepth) {
|
1320 |
+
delayed = delayedCallbacks;
|
1321 |
+
delayedCallbacks = null;
|
1322 |
+
}
|
1323 |
if (op.textChanged)
|
1324 |
signal(cm, "change", cm, op.textChanged);
|
1325 |
if (op.selectionChanged) signal(cm, "cursorActivity", cm);
|
1326 |
+
if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
|
1327 |
}
|
1328 |
|
1329 |
// Wraps a function in an operation. Returns the wrapped function.
|
1330 |
function operation(cm1, f) {
|
1331 |
return function() {
|
1332 |
+
var cm = cm1 || this, withOp = !cm.curOp;
|
1333 |
+
if (withOp) startOperation(cm);
|
1334 |
+
try { var result = f.apply(cm, arguments); }
|
1335 |
+
finally { if (withOp) endOperation(cm); }
|
1336 |
+
return result;
|
1337 |
+
};
|
1338 |
+
}
|
1339 |
+
function docOperation(f) {
|
1340 |
+
return function() {
|
1341 |
+
var withOp = this.cm && !this.cm.curOp, result;
|
1342 |
+
if (withOp) startOperation(this.cm);
|
1343 |
+
try { result = f.apply(this, arguments); }
|
1344 |
+
finally { if (withOp) endOperation(this.cm); }
|
1345 |
return result;
|
1346 |
};
|
1347 |
}
|
1348 |
+
function runInOp(cm, f) {
|
1349 |
+
var withOp = !cm.curOp, result;
|
1350 |
+
if (withOp) startOperation(cm);
|
1351 |
+
try { result = f(); }
|
1352 |
+
finally { if (withOp) endOperation(cm); }
|
1353 |
+
return result;
|
1354 |
+
}
|
1355 |
|
1356 |
function regChange(cm, from, to, lendiff) {
|
1357 |
+
if (from == null) from = cm.doc.first;
|
1358 |
+
if (to == null) to = cm.doc.first + cm.doc.size;
|
1359 |
cm.curOp.changes.push({from: from, to: to, diff: lendiff});
|
1360 |
}
|
1361 |
|
1362 |
// INPUT HANDLING
|
1363 |
|
1364 |
function slowPoll(cm) {
|
1365 |
+
if (cm.display.pollingFast) return;
|
1366 |
cm.display.poll.set(cm.options.pollInterval, function() {
|
1367 |
readInput(cm);
|
1368 |
+
if (cm.state.focused) slowPoll(cm);
|
1369 |
});
|
1370 |
}
|
1371 |
|
1386 |
// events that indicate IME taking place, but these are not widely
|
1387 |
// supported or compatible enough yet to rely on.)
|
1388 |
function readInput(cm) {
|
1389 |
+
var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel;
|
1390 |
+
if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false;
|
1391 |
var text = input.value;
|
1392 |
if (text == prevInput && posEq(sel.from, sel.to)) return false;
|
1393 |
+
// IE enjoys randomly deselecting our input's text when
|
1394 |
+
// re-focusing. If the selection is gone but the cursor is at the
|
1395 |
+
// start of the input, that's probably what happened.
|
1396 |
+
if (ie && text && input.selectionStart === 0) {
|
1397 |
+
resetInput(cm, true);
|
1398 |
+
return false;
|
1399 |
+
}
|
1400 |
+
var withOp = !cm.curOp;
|
1401 |
+
if (withOp) startOperation(cm);
|
1402 |
+
sel.shift = false;
|
1403 |
var same = 0, l = Math.min(prevInput.length, text.length);
|
1404 |
while (same < l && prevInput[same] == text[same]) ++same;
|
1405 |
var from = sel.from, to = sel.to;
|
1406 |
if (same < prevInput.length)
|
1407 |
+
from = Pos(from.line, from.ch - (prevInput.length - same));
|
1408 |
+
else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming)
|
1409 |
+
to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same)));
|
1410 |
var updateInput = cm.curOp.updateInput;
|
1411 |
+
makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)),
|
1412 |
+
origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end");
|
1413 |
+
|
1414 |
cm.curOp.updateInput = updateInput;
|
1415 |
+
if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = "";
|
1416 |
else cm.display.prevInput = text;
|
1417 |
+
if (withOp) endOperation(cm);
|
1418 |
+
cm.state.pasteIncoming = false;
|
1419 |
return true;
|
1420 |
}
|
1421 |
|
1422 |
function resetInput(cm, user) {
|
1423 |
+
var minimal, selected, doc = cm.doc;
|
1424 |
+
if (!posEq(doc.sel.from, doc.sel.to)) {
|
1425 |
cm.display.prevInput = "";
|
1426 |
minimal = hasCopyEvent &&
|
1427 |
+
(doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000);
|
1428 |
if (minimal) cm.display.input.value = "-";
|
1429 |
else cm.display.input.value = selected || cm.getSelection();
|
1430 |
+
if (cm.state.focused) selectInput(cm.display.input);
|
1431 |
} else if (user) cm.display.prevInput = cm.display.input.value = "";
|
1432 |
cm.display.inaccurateSelection = minimal;
|
1433 |
}
|
1434 |
|
1435 |
function focusInput(cm) {
|
1436 |
+
if (cm.options.readOnly != "nocursor" && (!mobile || document.activeElement != cm.display.input))
|
1437 |
cm.display.input.focus();
|
1438 |
}
|
1439 |
|
1440 |
function isReadOnly(cm) {
|
1441 |
+
return cm.options.readOnly || cm.doc.cantEdit;
|
1442 |
}
|
1443 |
|
1444 |
// EVENT HANDLERS
|
1448 |
on(d.scroller, "mousedown", operation(cm, onMouseDown));
|
1449 |
on(d.scroller, "dblclick", operation(cm, e_preventDefault));
|
1450 |
on(d.lineSpace, "selectstart", function(e) {
|
1451 |
+
if (!eventInWidget(d, e)) e_preventDefault(e);
|
1452 |
});
|
1453 |
// Gecko browsers fire contextmenu *after* opening the menu, at
|
1454 |
// which point we can't mess with it anymore. Context menu is
|
1455 |
// handled in onMouseDown for Gecko.
|
1456 |
+
if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);});
|
1457 |
|
1458 |
on(d.scroller, "scroll", function() {
|
1459 |
+
if (d.scroller.clientHeight) {
|
1460 |
+
setScrollTop(cm, d.scroller.scrollTop);
|
1461 |
+
setScrollLeft(cm, d.scroller.scrollLeft, true);
|
1462 |
+
signal(cm, "scroll", cm);
|
1463 |
+
}
|
1464 |
});
|
1465 |
on(d.scrollbarV, "scroll", function() {
|
1466 |
+
if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop);
|
1467 |
});
|
1468 |
on(d.scrollbarH, "scroll", function() {
|
1469 |
+
if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft);
|
1470 |
});
|
1471 |
|
1472 |
on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
|
1473 |
on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
|
1474 |
|
1475 |
+
function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
|
1476 |
on(d.scrollbarH, "mousedown", reFocus);
|
1477 |
on(d.scrollbarV, "mousedown", reFocus);
|
1478 |
// Prevent wrapper from ever scrolling
|
1479 |
on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
|
1480 |
+
|
1481 |
+
function onResize() {
|
1482 |
// Might be a text scaling operation, clear size caches.
|
1483 |
d.cachedCharWidth = d.cachedTextHeight = null;
|
1484 |
clearCaches(cm);
|
1485 |
+
runInOp(cm, bind(regChange, cm));
|
1486 |
+
}
|
1487 |
+
on(window, "resize", onResize);
|
1488 |
+
// Above handler holds on to the editor and its data structures.
|
1489 |
+
// Here we poll to unregister it when the editor is no longer in
|
1490 |
+
// the document, so that it can be garbage-collected.
|
1491 |
+
function unregister() {
|
1492 |
+
for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {}
|
1493 |
+
if (p) setTimeout(unregister, 5000);
|
1494 |
+
else off(window, "resize", onResize);
|
1495 |
+
}
|
1496 |
+
setTimeout(unregister, 5000);
|
1497 |
|
1498 |
on(d.input, "keyup", operation(cm, function(e) {
|
1499 |
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
1500 |
+
if (e.keyCode == 16) cm.doc.sel.shift = false;
|
1501 |
}));
|
1502 |
on(d.input, "input", bind(fastPoll, cm));
|
1503 |
on(d.input, "keydown", operation(cm, onKeyDown));
|
1515 |
on(d.scroller, "dragover", drag_);
|
1516 |
on(d.scroller, "drop", operation(cm, onDrop));
|
1517 |
}
|
1518 |
+
on(d.scroller, "paste", function(e){
|
1519 |
+
if (eventInWidget(d, e)) return;
|
1520 |
+
focusInput(cm);
|
1521 |
+
fastPoll(cm);
|
1522 |
+
});
|
1523 |
on(d.input, "paste", function() {
|
1524 |
+
cm.state.pasteIncoming = true;
|
1525 |
fastPoll(cm);
|
1526 |
});
|
1527 |
|
1543 |
});
|
1544 |
}
|
1545 |
|
1546 |
+
function eventInWidget(display, e) {
|
1547 |
+
for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
|
1548 |
+
if (!n) return true;
|
1549 |
if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) ||
|
1550 |
n.parentNode == display.sizer && n != display.mover) return true;
|
1551 |
+
}
|
1552 |
}
|
1553 |
|
1554 |
function posFromMouse(cm, e, liberal) {
|
1559 |
target == display.scrollbarV || target == display.scrollbarV.firstChild ||
|
1560 |
target == display.scrollbarFiller) return null;
|
1561 |
}
|
1562 |
+
var x, y, space = getRect(display.lineSpace);
|
1563 |
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
|
1564 |
try { x = e.clientX; y = e.clientY; } catch (e) { return null; }
|
1565 |
return coordsChar(cm, x - space.left, y - space.top);
|
1567 |
|
1568 |
var lastClick, lastDoubleClick;
|
1569 |
function onMouseDown(e) {
|
1570 |
+
var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel;
|
1571 |
+
sel.shift = e.shiftKey;
|
1572 |
|
1573 |
+
if (eventInWidget(display, e)) {
|
1574 |
if (!webkit) {
|
1575 |
display.scroller.draggable = false;
|
1576 |
setTimeout(function(){display.scroller.draggable = true;}, 100);
|
1582 |
|
1583 |
switch (e_button(e)) {
|
1584 |
case 3:
|
1585 |
+
if (captureMiddleClick) onContextMenu.call(cm, cm, e);
|
1586 |
return;
|
1587 |
case 2:
|
1588 |
+
if (start) extendSelection(cm.doc, start);
|
1589 |
setTimeout(bind(focusInput, cm), 20);
|
1590 |
e_preventDefault(e);
|
1591 |
return;
|
1595 |
// selection.
|
1596 |
if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;}
|
1597 |
|
1598 |
+
if (!cm.state.focused) onFocus(cm);
|
1599 |
|
1600 |
var now = +new Date, type = "single";
|
1601 |
if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
|
1608 |
lastDoubleClick = {time: now, pos: start};
|
1609 |
e_preventDefault(e);
|
1610 |
var word = findWordAt(getLine(doc, start.line).text, start);
|
1611 |
+
extendSelection(cm.doc, word.from, word.to);
|
1612 |
} else { lastClick = {time: now, pos: start}; }
|
1613 |
|
1614 |
var last = start;
|
1616 |
!posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
|
1617 |
var dragEnd = operation(cm, function(e2) {
|
1618 |
if (webkit) display.scroller.draggable = false;
|
1619 |
+
cm.state.draggingText = false;
|
1620 |
off(document, "mouseup", dragEnd);
|
1621 |
off(display.scroller, "drop", dragEnd);
|
1622 |
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
|
1623 |
e_preventDefault(e2);
|
1624 |
+
extendSelection(cm.doc, start);
|
1625 |
focusInput(cm);
|
1626 |
}
|
1627 |
});
|
1628 |
// Let the drag handler handle this.
|
1629 |
if (webkit) display.scroller.draggable = true;
|
1630 |
+
cm.state.draggingText = dragEnd;
|
1631 |
// IE's approach to draggable
|
1632 |
if (display.scroller.dragDrop) display.scroller.dragDrop();
|
1633 |
on(document, "mouseup", dragEnd);
|
1635 |
return;
|
1636 |
}
|
1637 |
e_preventDefault(e);
|
1638 |
+
if (type == "single") extendSelection(cm.doc, clipPos(doc, start));
|
1639 |
|
1640 |
var startstart = sel.from, startend = sel.to;
|
1641 |
|
1642 |
function doSelect(cur) {
|
1643 |
if (type == "single") {
|
1644 |
+
extendSelection(cm.doc, clipPos(doc, start), cur);
|
1645 |
return;
|
1646 |
}
|
1647 |
|
1649 |
startend = clipPos(doc, startend);
|
1650 |
if (type == "double") {
|
1651 |
var word = findWordAt(getLine(doc, cur.line).text, cur);
|
1652 |
+
if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend);
|
1653 |
+
else extendSelection(cm.doc, startstart, word.to);
|
1654 |
} else if (type == "triple") {
|
1655 |
+
if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0)));
|
1656 |
+
else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0)));
|
1657 |
}
|
1658 |
}
|
1659 |
|
1660 |
+
var editorSize = getRect(display.wrapper);
|
1661 |
// Used to ensure timeout re-tries don't fire when another extend
|
1662 |
// happened in the meantime (clearTimeout isn't reliable -- at
|
1663 |
// least on Chrome, the timeouts still happen even when cleared,
|
1669 |
var cur = posFromMouse(cm, e, true);
|
1670 |
if (!cur) return;
|
1671 |
if (!posEq(cur, last)) {
|
1672 |
+
if (!cm.state.focused) onFocus(cm);
|
1673 |
last = cur;
|
1674 |
doSelect(cur);
|
1675 |
var visible = visibleLines(display, doc);
|
1706 |
|
1707 |
function onDrop(e) {
|
1708 |
var cm = this;
|
1709 |
+
if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))))
|
1710 |
+
return;
|
1711 |
e_preventDefault(e);
|
1712 |
var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
|
1713 |
if (!pos || isReadOnly(cm)) return;
|
1718 |
reader.onload = function() {
|
1719 |
text[i] = reader.result;
|
1720 |
if (++read == n) {
|
1721 |
+
pos = clipPos(cm.doc, pos);
|
1722 |
+
makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around");
|
|
|
|
|
|
|
1723 |
}
|
1724 |
};
|
1725 |
reader.readAsText(file);
|
1727 |
for (var i = 0; i < n; ++i) loadFile(files[i], i);
|
1728 |
} else {
|
1729 |
// Don't do a replace if the drop happened inside of the selected text.
|
1730 |
+
if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) {
|
1731 |
+
cm.state.draggingText(e);
|
1732 |
+
// Ensure the editor is re-focused
|
1733 |
+
setTimeout(bind(focusInput, cm), 20);
|
1734 |
return;
|
1735 |
}
|
1736 |
try {
|
1737 |
var text = e.dataTransfer.getData("Text");
|
1738 |
if (text) {
|
1739 |
+
var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to;
|
1740 |
+
setSelection(cm.doc, pos, pos);
|
1741 |
+
if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste");
|
1742 |
cm.replaceSelection(text, null, "paste");
|
1743 |
focusInput(cm);
|
1744 |
onFocus(cm);
|
1753 |
try { var mX = e.clientX, mY = e.clientY; }
|
1754 |
catch(e) { return false; }
|
1755 |
|
1756 |
+
if (mX >= Math.floor(getRect(display.gutters).right)) return false;
|
1757 |
e_preventDefault(e);
|
1758 |
if (!hasHandler(cm, "gutterClick")) return true;
|
1759 |
|
1760 |
+
var lineBox = getRect(display.lineDiv);
|
1761 |
if (mY > lineBox.bottom) return true;
|
1762 |
mY -= lineBox.top - display.viewOffset;
|
1763 |
|
1764 |
for (var i = 0; i < cm.options.gutters.length; ++i) {
|
1765 |
var g = display.gutters.childNodes[i];
|
1766 |
+
if (g && getRect(g).right >= mX) {
|
1767 |
+
var line = lineAtHeight(cm.doc, mY);
|
1768 |
var gutter = cm.options.gutters[i];
|
1769 |
+
signalLater(cm, "gutterClick", cm, line, gutter, e);
|
1770 |
break;
|
1771 |
}
|
1772 |
}
|
1774 |
}
|
1775 |
|
1776 |
function onDragStart(cm, e) {
|
1777 |
+
if (eventInWidget(cm.display, e)) return;
|
1778 |
+
|
1779 |
var txt = cm.getSelection();
|
1780 |
e.dataTransfer.setData("Text", txt);
|
1781 |
|
1782 |
// Use dummy image instead of default browsers image.
|
1783 |
// Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
|
1784 |
+
if (e.dataTransfer.setDragImage) {
|
1785 |
+
var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
|
1786 |
+
if (opera) {
|
1787 |
+
img.width = img.height = 1;
|
1788 |
+
cm.display.wrapper.appendChild(img);
|
1789 |
+
// Force a relayout, or Opera won't use our image for some obscure reason
|
1790 |
+
img._top = img.offsetTop;
|
1791 |
+
}
|
1792 |
+
if (safari) {
|
1793 |
+
if (cm.display.dragImg) {
|
1794 |
+
img = cm.display.dragImg;
|
1795 |
+
} else {
|
1796 |
+
cm.display.dragImg = img;
|
1797 |
+
img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
1798 |
+
cm.display.wrapper.appendChild(img);
|
1799 |
+
}
|
1800 |
+
}
|
1801 |
+
e.dataTransfer.setDragImage(img, 0, 0);
|
1802 |
+
if (opera) img.parentNode.removeChild(img);
|
1803 |
+
}
|
1804 |
}
|
1805 |
|
1806 |
function setScrollTop(cm, val) {
|
1807 |
+
if (Math.abs(cm.doc.scrollTop - val) < 2) return;
|
1808 |
+
cm.doc.scrollTop = val;
|
1809 |
if (!gecko) updateDisplay(cm, [], val);
|
1810 |
if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
|
1811 |
if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
|
1812 |
if (gecko) updateDisplay(cm, []);
|
1813 |
}
|
1814 |
function setScrollLeft(cm, val, isScroller) {
|
1815 |
+
if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return;
|
1816 |
+
val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth);
|
1817 |
+
cm.doc.scrollLeft = val;
|
1818 |
alignHorizontally(cm);
|
1819 |
if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
|
1820 |
if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val;
|
1831 |
// is that it gives us a chance to update the display before the
|
1832 |
// actual scrolling happens, reducing flickering.
|
1833 |
|
1834 |
+
var wheelSamples = 0, wheelPixelsPerUnit = null;
|
1835 |
// Fill in a browser-detected starting value on browsers where we
|
1836 |
// know one. These don't have to be accurate -- the result of them
|
1837 |
// being wrong would just be a slight flicker on the first wheel
|
1860 |
}
|
1861 |
}
|
1862 |
|
1863 |
+
var display = cm.display, scroll = display.scroller;
|
1864 |
// On some browsers, horizontal scrolling will cause redraws to
|
1865 |
// happen before the gutter has been realigned, causing it to
|
1866 |
// wriggle around in a most unseemly way. When we have an
|
1872 |
setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight)));
|
1873 |
setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth)));
|
1874 |
e_preventDefault(e);
|
1875 |
+
display.wheelStartX = null; // Abort measurement, if in progress
|
1876 |
return;
|
1877 |
}
|
1878 |
|
1879 |
if (dy && wheelPixelsPerUnit != null) {
|
1880 |
var pixels = dy * wheelPixelsPerUnit;
|
1881 |
+
var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
|
1882 |
if (pixels < 0) top = Math.max(0, top + pixels - 50);
|
1883 |
+
else bot = Math.min(cm.doc.height, bot + pixels + 50);
|
1884 |
updateDisplay(cm, [], {top: top, bottom: bot});
|
1885 |
}
|
1886 |
|
1887 |
if (wheelSamples < 20) {
|
1888 |
+
if (display.wheelStartX == null) {
|
1889 |
+
display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
|
1890 |
+
display.wheelDX = dx; display.wheelDY = dy;
|
1891 |
setTimeout(function() {
|
1892 |
+
if (display.wheelStartX == null) return;
|
1893 |
+
var movedX = scroll.scrollLeft - display.wheelStartX;
|
1894 |
+
var movedY = scroll.scrollTop - display.wheelStartY;
|
1895 |
+
var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
|
1896 |
+
(movedX && display.wheelDX && movedX / display.wheelDX);
|
1897 |
+
display.wheelStartX = display.wheelStartY = null;
|
1898 |
if (!sample) return;
|
1899 |
wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
|
1900 |
++wheelSamples;
|
1901 |
}, 200);
|
1902 |
} else {
|
1903 |
+
display.wheelDX += dx; display.wheelDY += dy;
|
1904 |
}
|
1905 |
}
|
1906 |
}
|
1913 |
// Ensure previous input has been read, so that the handler sees a
|
1914 |
// consistent view of the document
|
1915 |
if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false;
|
1916 |
+
var doc = cm.doc, prevShift = doc.sel.shift, done = false;
|
1917 |
try {
|
1918 |
+
if (isReadOnly(cm)) cm.state.suppressEdits = true;
|
1919 |
+
if (dropShift) doc.sel.shift = false;
|
1920 |
+
done = bound(cm) != Pass;
|
|
|
|
|
|
|
1921 |
} finally {
|
1922 |
+
doc.sel.shift = prevShift;
|
1923 |
+
cm.state.suppressEdits = false;
|
1924 |
}
|
1925 |
+
return done;
|
1926 |
}
|
1927 |
|
1928 |
function allKeyMaps(cm) {
|
1929 |
+
var maps = cm.state.keyMaps.slice(0);
|
1930 |
+
if (cm.options.extraKeys) maps.push(cm.options.extraKeys);
|
1931 |
maps.push(cm.options.keyMap);
|
|
|
1932 |
return maps;
|
1933 |
}
|
1934 |
|
1942 |
cm.options.keyMap = (next.call ? next.call(null, cm) : next);
|
1943 |
}, 50);
|
1944 |
|
1945 |
+
var name = keyName(e, true), handled = false;
|
1946 |
+
if (!name) return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1947 |
var keymaps = allKeyMaps(cm);
|
1948 |
|
1949 |
+
if (e.shiftKey) {
|
1950 |
+
// First try to resolve full name (including 'Shift-'). Failing
|
1951 |
+
// that, see if there is a cursor-motion command (starting with
|
1952 |
+
// 'go') bound to the keyname without 'Shift-'.
|
1953 |
+
handled = lookupKey("Shift-" + name, keymaps, function(b) {return doHandleBinding(cm, b, true);})
|
1954 |
+
|| lookupKey(name, keymaps, function(b) {
|
1955 |
+
if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b);
|
1956 |
+
});
|
1957 |
} else {
|
1958 |
+
handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); });
|
|
|
1959 |
}
|
1960 |
+
if (handled == "stop") handled = false;
|
1961 |
+
|
1962 |
if (handled) {
|
1963 |
e_preventDefault(e);
|
1964 |
restartBlink(cm);
|
1980 |
var lastStoppedKey = null;
|
1981 |
function onKeyDown(e) {
|
1982 |
var cm = this;
|
1983 |
+
if (!cm.state.focused) onFocus(cm);
|
1984 |
if (ie && e.keyCode == 27) { e.returnValue = false; }
|
1985 |
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
1986 |
+
var code = e.keyCode;
|
1987 |
// IE does strange things with escape.
|
1988 |
+
cm.doc.sel.shift = code == 16 || e.shiftKey;
|
1989 |
// First give onKeyEvent option a chance to handle this.
|
1990 |
var handled = handleKeyBinding(cm, e);
|
1991 |
if (opera) {
|
1992 |
lastStoppedKey = handled ? code : null;
|
1993 |
// Opera has no cut event... we try to at least catch the key combo
|
1994 |
+
if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
|
1995 |
cm.replaceSelection("");
|
1996 |
}
|
1997 |
}
|
1999 |
function onKeyPress(e) {
|
2000 |
var cm = this;
|
2001 |
if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return;
|
2002 |
+
var keyCode = e.keyCode, charCode = e.charCode;
|
2003 |
if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
|
2004 |
if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
|
2005 |
var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
|
2006 |
+
if (this.options.electricChars && this.doc.mode.electricChars &&
|
2007 |
this.options.smartIndent && !isReadOnly(this) &&
|
2008 |
+
this.doc.mode.electricChars.indexOf(ch) > -1)
|
2009 |
+
setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75);
|
2010 |
if (handleCharBinding(cm, e, ch)) return;
|
2011 |
fastPoll(cm);
|
2012 |
}
|
2013 |
|
2014 |
function onFocus(cm) {
|
2015 |
if (cm.options.readOnly == "nocursor") return;
|
2016 |
+
if (!cm.state.focused) {
|
2017 |
signal(cm, "focus", cm);
|
2018 |
+
cm.state.focused = true;
|
2019 |
+
if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
|
2020 |
+
cm.display.wrapper.className += " CodeMirror-focused";
|
2021 |
resetInput(cm, true);
|
2022 |
}
|
2023 |
slowPoll(cm);
|
2024 |
restartBlink(cm);
|
2025 |
}
|
2026 |
function onBlur(cm) {
|
2027 |
+
if (cm.state.focused) {
|
2028 |
signal(cm, "blur", cm);
|
2029 |
+
cm.state.focused = false;
|
2030 |
+
cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", "");
|
2031 |
}
|
2032 |
clearInterval(cm.display.blinker);
|
2033 |
+
setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150);
|
2034 |
}
|
2035 |
|
2036 |
var detectingSelectAll;
|
2037 |
function onContextMenu(cm, e) {
|
2038 |
+
var display = cm.display, sel = cm.doc.sel;
|
2039 |
+
if (eventInWidget(display, e)) return;
|
2040 |
+
|
2041 |
var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
|
2042 |
if (!pos || opera) return; // Opera is difficult.
|
2043 |
if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
|
2044 |
+
operation(cm, setSelection)(cm.doc, pos, pos);
|
2045 |
|
2046 |
var oldCSS = display.input.style.cssText;
|
2047 |
display.inputDiv.style.position = "absolute";
|
2048 |
display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) +
|
2049 |
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" +
|
2050 |
+
"border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);";
|
2051 |
focusInput(cm);
|
2052 |
resetInput(cm, true);
|
2053 |
// Adds "Select all" to context menu in FF
|
2059 |
if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos;
|
2060 |
slowPoll(cm);
|
2061 |
|
2062 |
+
// Try to detect the user choosing select-all
|
2063 |
+
if (display.input.selectionStart != null && (!ie || ie_lt9)) {
|
2064 |
clearTimeout(detectingSelectAll);
|
2065 |
var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
|
2066 |
display.prevInput = " ";
|
2067 |
display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
|
2068 |
+
var poll = function(){
|
2069 |
if (display.prevInput == " " && display.input.selectionStart == 0)
|
2070 |
operation(cm, commands.selectAll)(cm);
|
2071 |
else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
|
2072 |
else resetInput(cm);
|
2073 |
+
};
|
2074 |
+
detectingSelectAll = setTimeout(poll, 200);
|
2075 |
}
|
2076 |
}
|
2077 |
|
2078 |
+
if (captureMiddleClick) {
|
2079 |
e_stop(e);
|
2080 |
+
var mouseup = function() {
|
2081 |
off(window, "mouseup", mouseup);
|
2082 |
setTimeout(rehide, 20);
|
2083 |
+
};
|
2084 |
+
on(window, "mouseup", mouseup);
|
2085 |
} else {
|
2086 |
setTimeout(rehide, 50);
|
2087 |
}
|
2089 |
|
2090 |
// UPDATING
|
2091 |
|
2092 |
+
function changeEnd(change) {
|
2093 |
+
return Pos(change.from.line + change.text.length - 1,
|
2094 |
+
lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0));
|
2095 |
+
}
|
2096 |
+
|
2097 |
+
// Make sure a position will be valid after the given change.
|
2098 |
+
function clipPostChange(doc, change, pos) {
|
2099 |
+
if (!posLess(change.from, pos)) return clipPos(doc, pos);
|
2100 |
+
var diff = (change.text.length - 1) - (change.to.line - change.from.line);
|
2101 |
+
if (pos.line > change.to.line + diff) {
|
2102 |
+
var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1;
|
2103 |
+
if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length);
|
2104 |
+
return clipToLen(pos, getLine(doc, preLine).text.length);
|
2105 |
+
}
|
2106 |
+
if (pos.line == change.to.line + diff)
|
2107 |
+
return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) +
|
2108 |
+
getLine(doc, change.to.line).text.length - change.to.ch);
|
2109 |
+
var inside = pos.line - change.from.line;
|
2110 |
+
return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch));
|
2111 |
+
}
|
2112 |
+
|
2113 |
+
// Hint can be null|"end"|"start"|"around"|{anchor,head}
|
2114 |
+
function computeSelAfterChange(doc, change, hint) {
|
2115 |
+
if (hint && typeof hint == "object") // Assumed to be {anchor, head} object
|
2116 |
+
return {anchor: clipPostChange(doc, change, hint.anchor),
|
2117 |
+
head: clipPostChange(doc, change, hint.head)};
|
2118 |
+
|
2119 |
+
if (hint == "start") return {anchor: change.from, head: change.from};
|
2120 |
+
|
2121 |
+
var end = changeEnd(change);
|
2122 |
+
if (hint == "around") return {anchor: change.from, head: end};
|
2123 |
+
if (hint == "end") return {anchor: end, head: end};
|
2124 |
+
|
2125 |
+
// hint is null, leave the selection alone as much as possible
|
2126 |
+
var adjustPos = function(pos) {
|
2127 |
+
if (posLess(pos, change.from)) return pos;
|
2128 |
+
if (!posLess(change.to, pos)) return end;
|
2129 |
+
|
2130 |
+
var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
|
2131 |
+
if (pos.line == change.to.line) ch += end.ch - change.to.ch;
|
2132 |
+
return Pos(line, ch);
|
2133 |
+
};
|
2134 |
+
return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)};
|
2135 |
+
}
|
2136 |
+
|
2137 |
+
function filterChange(doc, change) {
|
2138 |
+
var obj = {
|
2139 |
+
canceled: false,
|
2140 |
+
from: change.from,
|
2141 |
+
to: change.to,
|
2142 |
+
text: change.text,
|
2143 |
+
origin: change.origin,
|
2144 |
+
update: function(from, to, text, origin) {
|
2145 |
+
if (from) this.from = clipPos(doc, from);
|
2146 |
+
if (to) this.to = clipPos(doc, to);
|
2147 |
+
if (text) this.text = text;
|
2148 |
+
if (origin !== undefined) this.origin = origin;
|
2149 |
+
},
|
2150 |
+
cancel: function() { this.canceled = true; }
|
2151 |
+
};
|
2152 |
+
signal(doc, "beforeChange", doc, obj);
|
2153 |
+
if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj);
|
2154 |
+
|
2155 |
+
if (obj.canceled) return null;
|
2156 |
+
return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin};
|
2157 |
+
}
|
2158 |
+
|
2159 |
+
// Replace the range from from to to by the strings in replacement.
|
2160 |
+
// change is a {from, to, text [, origin]} object
|
2161 |
+
function makeChange(doc, change, selUpdate, ignoreReadOnly) {
|
2162 |
+
if (doc.cm) {
|
2163 |
+
if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly);
|
2164 |
+
if (doc.cm.state.suppressEdits) return;
|
2165 |
+
}
|
2166 |
+
|
2167 |
+
if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
|
2168 |
+
change = filterChange(doc, change);
|
2169 |
+
if (!change) return;
|
2170 |
+
}
|
2171 |
+
|
2172 |
// Possibly split or suppress the update based on the presence
|
2173 |
// of read-only spans in its range.
|
2174 |
+
var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
|
|
|
2175 |
if (split) {
|
2176 |
for (var i = split.length - 1; i >= 1; --i)
|
2177 |
+
makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]});
|
2178 |
if (split.length)
|
2179 |
+
makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate);
|
2180 |
} else {
|
2181 |
+
makeChangeNoReadonly(doc, change, selUpdate);
|
2182 |
}
|
2183 |
}
|
2184 |
|
2185 |
+
function makeChangeNoReadonly(doc, change, selUpdate) {
|
2186 |
+
var selAfter = computeSelAfterChange(doc, change, selUpdate);
|
2187 |
+
addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
|
2188 |
|
2189 |
+
makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
|
2190 |
+
var rebased = [];
|
2191 |
+
|
2192 |
+
linkedDocs(doc, function(doc, sharedHist) {
|
2193 |
+
if (!sharedHist && indexOf(rebased, doc.history) == -1) {
|
2194 |
+
rebaseHist(doc.history, change);
|
2195 |
+
rebased.push(doc.history);
|
2196 |
+
}
|
2197 |
+
makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
|
2198 |
});
|
2199 |
+
}
|
2200 |
+
|
2201 |
+
function makeChangeFromHistory(doc, type) {
|
2202 |
+
if (doc.cm && doc.cm.state.suppressEdits) return;
|
2203 |
+
|
2204 |
+
var hist = doc.history;
|
2205 |
+
var event = (type == "undo" ? hist.done : hist.undone).pop();
|
2206 |
+
if (!event) return;
|
2207 |
+
hist.dirtyCounter += type == "undo" ? -1 : 1;
|
2208 |
+
|
2209 |
+
var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter,
|
2210 |
+
anchorAfter: event.anchorBefore, headAfter: event.headBefore};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2211 |
(type == "undo" ? hist.undone : hist.done).push(anti);
|
2212 |
+
|
2213 |
+
for (var i = event.changes.length - 1; i >= 0; --i) {
|
2214 |
+
var change = event.changes[i];
|
2215 |
+
change.origin = type;
|
2216 |
+
anti.changes.push(historyChangeFromChange(doc, change));
|
2217 |
+
|
2218 |
+
var after = i ? computeSelAfterChange(doc, change, null)
|
2219 |
+
: {anchor: event.anchorBefore, head: event.headBefore};
|
2220 |
+
makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
|
2221 |
+
var rebased = [];
|
2222 |
+
|
2223 |
+
linkedDocs(doc, function(doc, sharedHist) {
|
2224 |
+
if (!sharedHist && indexOf(rebased, doc.history) == -1) {
|
2225 |
+
rebaseHist(doc.history, change);
|
2226 |
+
rebased.push(doc.history);
|
2227 |
+
}
|
2228 |
+
makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
|
2229 |
+
});
|
2230 |
+
}
|
2231 |
+
}
|
2232 |
+
|
2233 |
+
function shiftDoc(doc, distance) {
|
2234 |
+
function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);}
|
2235 |
+
doc.first += distance;
|
2236 |
+
if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance);
|
2237 |
+
doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor);
|
2238 |
+
doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to);
|
2239 |
+
}
|
2240 |
+
|
2241 |
+
function makeChangeSingleDoc(doc, change, selAfter, spans) {
|
2242 |
+
if (doc.cm && !doc.cm.curOp)
|
2243 |
+
return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans);
|
2244 |
+
|
2245 |
+
if (change.to.line < doc.first) {
|
2246 |
+
shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
|
2247 |
+
return;
|
2248 |
+
}
|
2249 |
+
if (change.from.line > doc.lastLine()) return;
|
2250 |
+
|
2251 |
+
// Clip the change to the size of this doc
|
2252 |
+
if (change.from.line < doc.first) {
|
2253 |
+
var shift = change.text.length - 1 - (doc.first - change.from.line);
|
2254 |
+
shiftDoc(doc, shift);
|
2255 |
+
change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
|
2256 |
+
text: [lst(change.text)], origin: change.origin};
|
2257 |
+
}
|
2258 |
+
var last = doc.lastLine();
|
2259 |
+
if (change.to.line > last) {
|
2260 |
+
change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
|
2261 |
+
text: [change.text[0]], origin: change.origin};
|
2262 |
+
}
|
2263 |
+
|
2264 |
+
change.removed = getBetween(doc, change.from, change.to);
|
2265 |
+
|
2266 |
+
if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
|
2267 |
+
if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter);
|
2268 |
+
else updateDoc(doc, change, spans, selAfter);
|
2269 |
}
|
2270 |
|
2271 |
+
function makeChangeSingleDocInEditor(cm, change, spans, selAfter) {
|
2272 |
+
var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
|
|
|
2273 |
|
|
|
2274 |
var recomputeMaxLength = false, checkWidthStart = from.line;
|
2275 |
if (!cm.options.lineWrapping) {
|
2276 |
+
checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line)));
|
2277 |
doc.iter(checkWidthStart, to.line + 1, function(line) {
|
2278 |
+
if (line == display.maxLine) {
|
2279 |
recomputeMaxLength = true;
|
2280 |
return true;
|
2281 |
}
|
2282 |
});
|
2283 |
}
|
2284 |
|
2285 |
+
updateDoc(doc, change, spans, selAfter, estimateHeight(cm));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2286 |
|
2287 |
+
if (!cm.options.lineWrapping) {
|
2288 |
+
doc.iter(checkWidthStart, from.line + change.text.length, function(line) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2289 |
var len = lineLength(doc, line);
|
2290 |
+
if (len > display.maxLineLength) {
|
2291 |
+
display.maxLine = line;
|
2292 |
+
display.maxLineLength = len;
|
2293 |
+
display.maxLineChanged = true;
|
2294 |
recomputeMaxLength = false;
|
2295 |
}
|
2296 |
});
|
2298 |
}
|
2299 |
|
2300 |
// Adjust frontier, schedule worker
|
2301 |
+
doc.frontier = Math.min(doc.frontier, from.line);
|
2302 |
startWorker(cm, 400);
|
2303 |
|
2304 |
+
var lendiff = change.text.length - (to.line - from.line) - 1;
|
2305 |
// Remember that these lines changed, for updating the display
|
2306 |
regChange(cm, from.line, to.line + 1, lendiff);
|
2307 |
+
|
2308 |
if (hasHandler(cm, "change")) {
|
2309 |
+
var changeObj = {from: from, to: to,
|
2310 |
+
text: change.text,
|
2311 |
+
removed: change.removed,
|
2312 |
+
origin: change.origin};
|
|
|
2313 |
if (cm.curOp.textChanged) {
|
2314 |
for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {}
|
2315 |
cur.next = changeObj;
|
2316 |
} else cm.curOp.textChanged = changeObj;
|
2317 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2318 |
}
|
2319 |
|
2320 |
+
function replaceRange(doc, code, from, to, origin) {
|
2321 |
if (!to) to = from;
|
2322 |
if (posLess(to, from)) { var tmp = to; to = from; from = tmp; }
|
2323 |
+
if (typeof code == "string") code = splitLines(code);
|
2324 |
+
makeChange(doc, {from: from, to: to, text: code, origin: origin}, null);
|
2325 |
}
|
2326 |
|
2327 |
+
// POSITION OBJECT
|
2328 |
+
|
2329 |
+
function Pos(line, ch) {
|
2330 |
+
if (!(this instanceof Pos)) return new Pos(line, ch);
|
2331 |
+
this.line = line; this.ch = ch;
|
2332 |
+
}
|
2333 |
+
CodeMirror.Pos = Pos;
|
2334 |
|
2335 |
function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
|
2336 |
function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
|
2337 |
+
function copyPos(x) {return Pos(x.line, x.ch);}
|
2338 |
+
|
2339 |
+
// SELECTION
|
2340 |
|
2341 |
+
function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));}
|
2342 |
function clipPos(doc, pos) {
|
2343 |
+
if (pos.line < doc.first) return Pos(doc.first, 0);
|
2344 |
+
var last = doc.first + doc.size - 1;
|
2345 |
+
if (pos.line > last) return Pos(last, getLine(doc, last).text.length);
|
2346 |
+
return clipToLen(pos, getLine(doc, pos.line).text.length);
|
2347 |
+
}
|
2348 |
+
function clipToLen(pos, linelen) {
|
2349 |
+
var ch = pos.ch;
|
2350 |
+
if (ch == null || ch > linelen) return Pos(pos.line, linelen);
|
2351 |
+
else if (ch < 0) return Pos(pos.line, 0);
|
2352 |
else return pos;
|
2353 |
}
|
2354 |
+
function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;}
|
2355 |
|
2356 |
// If shift is held, this will move the selection anchor. Otherwise,
|
2357 |
// it'll set the whole selection.
|
2358 |
+
function extendSelection(doc, pos, other, bias) {
|
2359 |
+
if (doc.sel.shift || doc.sel.extend) {
|
2360 |
+
var anchor = doc.sel.anchor;
|
|
|
2361 |
if (other) {
|
2362 |
var posBefore = posLess(pos, anchor);
|
2363 |
if (posBefore != posLess(other, anchor)) {
|
2367 |
pos = other;
|
2368 |
}
|
2369 |
}
|
2370 |
+
setSelection(doc, anchor, pos, bias);
|
2371 |
} else {
|
2372 |
+
setSelection(doc, pos, other || pos, bias);
|
2373 |
}
|
2374 |
+
if (doc.cm) doc.cm.curOp.userSelChange = true;
|
2375 |
+
}
|
2376 |
+
|
2377 |
+
function filterSelectionChange(doc, anchor, head) {
|
2378 |
+
var obj = {anchor: anchor, head: head};
|
2379 |
+
signal(doc, "beforeSelectionChange", doc, obj);
|
2380 |
+
if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj);
|
2381 |
+
obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head);
|
2382 |
+
return obj;
|
2383 |
}
|
2384 |
|
2385 |
// Update the selection. Last two args are only used by
|
2386 |
// updateDoc, since they have to be expressed in the line
|
2387 |
// numbers before the update.
|
2388 |
+
function setSelection(doc, anchor, head, bias, checkAtomic) {
|
2389 |
+
if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) {
|
2390 |
+
var filtered = filterSelectionChange(doc, anchor, head);
|
2391 |
+
head = filtered.head;
|
2392 |
+
anchor = filtered.anchor;
|
2393 |
+
}
|
2394 |
+
|
2395 |
+
var sel = doc.sel;
|
2396 |
+
sel.goalColumn = null;
|
2397 |
// Skip over atomic spans.
|
2398 |
if (checkAtomic || !posEq(anchor, sel.anchor))
|
2399 |
+
anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push");
|
2400 |
if (checkAtomic || !posEq(head, sel.head))
|
2401 |
+
head = skipAtomic(doc, head, bias, checkAtomic != "push");
|
2402 |
|
2403 |
if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return;
|
2404 |
|
2407 |
sel.from = inv ? head : anchor;
|
2408 |
sel.to = inv ? anchor : head;
|
2409 |
|
2410 |
+
if (doc.cm)
|
2411 |
+
doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true;
|
2412 |
+
|
2413 |
+
signalLater(doc, "cursorActivity", doc);
|
2414 |
}
|
2415 |
|
2416 |
function reCheckSelection(cm) {
|
2417 |
+
setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push");
|
2418 |
}
|
2419 |
|
2420 |
+
function skipAtomic(doc, pos, bias, mayClear) {
|
2421 |
+
var flipped = false, curPos = pos;
|
2422 |
var dir = bias || 1;
|
2423 |
+
doc.cantEdit = false;
|
2424 |
search: for (;;) {
|
2425 |
+
var line = getLine(doc, curPos.line);
|
2426 |
if (line.markedSpans) {
|
2427 |
for (var i = 0; i < line.markedSpans.length; ++i) {
|
2428 |
var sp = line.markedSpans[i], m = sp.marker;
|
2429 |
if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) &&
|
2430 |
(sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) {
|
2431 |
+
if (mayClear) {
|
2432 |
+
signal(m, "beforeCursorEnter");
|
2433 |
+
if (m.explicitlyCleared) {
|
2434 |
+
if (!line.markedSpans) break;
|
2435 |
+
else {--i; continue;}
|
2436 |
+
}
|
2437 |
+
}
|
2438 |
+
if (!m.atomic) continue;
|
2439 |
var newPos = m.find()[dir < 0 ? "from" : "to"];
|
2440 |
if (posEq(newPos, curPos)) {
|
2441 |
newPos.ch += dir;
|
2442 |
if (newPos.ch < 0) {
|
2443 |
+
if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1));
|
2444 |
else newPos = null;
|
2445 |
} else if (newPos.ch > line.text.length) {
|
2446 |
+
if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0);
|
2447 |
else newPos = null;
|
2448 |
}
|
2449 |
if (!newPos) {
|
2450 |
if (flipped) {
|
2451 |
// Driven in a corner -- no valid cursor position found at all
|
2452 |
// -- try again *with* clearing, if we didn't already
|
2453 |
+
if (!mayClear) return skipAtomic(doc, pos, bias, true);
|
2454 |
// Otherwise, turn off editing until further notice, and return the start of the doc
|
2455 |
+
doc.cantEdit = true;
|
2456 |
+
return Pos(doc.first, 0);
|
2457 |
}
|
2458 |
flipped = true; newPos = pos; dir = -dir;
|
2459 |
}
|
2462 |
continue search;
|
2463 |
}
|
2464 |
}
|
|
|
2465 |
}
|
2466 |
return curPos;
|
2467 |
}
|
2470 |
// SCROLLING
|
2471 |
|
2472 |
function scrollCursorIntoView(cm) {
|
2473 |
+
var coords = scrollPosIntoView(cm, cm.doc.sel.head);
|
2474 |
+
if (!cm.state.focused) return;
|
2475 |
+
var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
|
2476 |
+
if (coords.top + pTop + box.top < 0) doScroll = true;
|
2477 |
+
else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false;
|
|
|
2478 |
if (doScroll != null && !phantom) {
|
2479 |
var hidden = display.cursor.style.display == "none";
|
2480 |
if (hidden) {
|
2487 |
}
|
2488 |
}
|
2489 |
|
2490 |
+
function scrollPosIntoView(cm, pos, margin) {
|
2491 |
+
if (margin == null) margin = 0;
|
2492 |
for (;;) {
|
2493 |
var changed = false, coords = cursorCoords(cm, pos);
|
2494 |
+
var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
|
2495 |
+
var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
|
2496 |
if (scrollPos.scrollTop != null) {
|
2497 |
setScrollTop(cm, scrollPos.scrollTop);
|
2498 |
+
if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true;
|
2499 |
}
|
2500 |
if (scrollPos.scrollLeft != null) {
|
2501 |
setScrollLeft(cm, scrollPos.scrollLeft);
|
2502 |
+
if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true;
|
2503 |
}
|
2504 |
if (!changed) return coords;
|
2505 |
}
|
2515 |
var display = cm.display, pt = paddingTop(display);
|
2516 |
y1 += pt; y2 += pt;
|
2517 |
var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {};
|
2518 |
+
var docBottom = cm.doc.height + paddingVert(display);
|
2519 |
var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;
|
2520 |
if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
|
2521 |
else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
|
2533 |
return result;
|
2534 |
}
|
2535 |
|
2536 |
+
function updateScrollPos(cm, left, top) {
|
2537 |
+
cm.curOp.updateScrollPos = {scrollLeft: left, scrollTop: top};
|
2538 |
+
}
|
2539 |
+
|
2540 |
+
function addToScrollPos(cm, left, top) {
|
2541 |
+
var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop});
|
2542 |
+
var scroll = cm.display.scroller;
|
2543 |
+
pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top));
|
2544 |
+
pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left));
|
2545 |
+
}
|
2546 |
+
|
2547 |
// API UTILITIES
|
2548 |
|
2549 |
function indentLine(cm, n, how, aggressive) {
|
2550 |
+
var doc = cm.doc;
|
2551 |
if (!how) how = "add";
|
2552 |
if (how == "smart") {
|
2553 |
+
if (!cm.doc.mode.indent) how = "prev";
|
2554 |
else var state = getStateBefore(cm, n);
|
2555 |
}
|
2556 |
|
2558 |
var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
|
2559 |
var curSpaceString = line.text.match(/^\s*/)[0], indentation;
|
2560 |
if (how == "smart") {
|
2561 |
+
indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
|
2562 |
if (indentation == Pass) {
|
2563 |
if (!aggressive) return;
|
2564 |
how = "prev";
|
2565 |
}
|
2566 |
}
|
2567 |
if (how == "prev") {
|
2568 |
+
if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
|
2569 |
else indentation = 0;
|
2570 |
+
} else if (how == "add") {
|
2571 |
+
indentation = curSpace + cm.options.indentUnit;
|
2572 |
+
} else if (how == "subtract") {
|
2573 |
+
indentation = curSpace - cm.options.indentUnit;
|
2574 |
}
|
|
|
|
|
2575 |
indentation = Math.max(0, indentation);
|
2576 |
|
2577 |
var indentString = "", pos = 0;
|
2580 |
if (pos < indentation) indentString += spaceStr(indentation - pos);
|
2581 |
|
2582 |
if (indentString != curSpaceString)
|
2583 |
+
replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
|
2584 |
line.stateAfter = null;
|
2585 |
}
|
2586 |
|
2587 |
function changeLine(cm, handle, op) {
|
2588 |
+
var no = handle, line = handle, doc = cm.doc;
|
2589 |
if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle));
|
2590 |
else no = lineNo(handle);
|
2591 |
if (no == null) return null;
|
2594 |
return line;
|
2595 |
}
|
2596 |
|
2597 |
+
function findPosH(doc, pos, dir, unit, visually) {
|
2598 |
+
var line = pos.line, ch = pos.ch;
|
2599 |
var lineObj = getLine(doc, line);
|
2600 |
+
var possible = true;
|
2601 |
function findNextLine() {
|
2602 |
var l = line + dir;
|
2603 |
+
if (l < doc.first || l >= doc.first + doc.size) return (possible = false);
|
2604 |
line = l;
|
2605 |
return lineObj = getLine(doc, l);
|
2606 |
}
|
2610 |
if (!boundToLine && findNextLine()) {
|
2611 |
if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj);
|
2612 |
else ch = dir < 0 ? lineObj.text.length : 0;
|
2613 |
+
} else return (possible = false);
|
2614 |
} else ch = next;
|
2615 |
return true;
|
2616 |
}
|
2617 |
+
|
2618 |
if (unit == "char") moveOnce();
|
2619 |
else if (unit == "column") moveOnce(true);
|
2620 |
+
else if (unit == "word" || unit == "group") {
|
2621 |
+
var sawType = null, group = unit == "group";
|
2622 |
+
for (var first = true;; first = false) {
|
2623 |
+
if (dir < 0 && !moveOnce(!first)) break;
|
2624 |
+
var cur = lineObj.text.charAt(ch) || "\n";
|
2625 |
+
var type = isWordChar(cur) ? "w"
|
2626 |
+
: !group ? null
|
2627 |
+
: /\s/.test(cur) ? null
|
2628 |
+
: "p";
|
2629 |
+
if (sawType && sawType != type) {
|
2630 |
+
if (dir < 0) {dir = 1; moveOnce();}
|
2631 |
+
break;
|
2632 |
+
}
|
2633 |
+
if (type) sawType = type;
|
2634 |
+
if (dir > 0 && !moveOnce(!first)) break;
|
2635 |
}
|
2636 |
}
|
2637 |
+
var result = skipAtomic(doc, Pos(line, ch), dir, true);
|
2638 |
+
if (!possible) result.hitSide = true;
|
2639 |
+
return result;
|
2640 |
+
}
|
2641 |
+
|
2642 |
+
function findPosV(cm, pos, dir, unit) {
|
2643 |
+
var doc = cm.doc, x = pos.left, y;
|
2644 |
+
if (unit == "page") {
|
2645 |
+
var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
|
2646 |
+
y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display));
|
2647 |
+
} else if (unit == "line") {
|
2648 |
+
y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
|
2649 |
+
}
|
2650 |
+
for (;;) {
|
2651 |
+
var target = coordsChar(cm, x, y);
|
2652 |
+
if (!target.outside) break;
|
2653 |
+
if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; }
|
2654 |
+
y += dir * 5;
|
2655 |
+
}
|
2656 |
+
return target;
|
2657 |
}
|
2658 |
|
2659 |
function findWordAt(line, pos) {
|
2661 |
if (line) {
|
2662 |
if (pos.after === false || end == line.length) --start; else ++end;
|
2663 |
var startChar = line.charAt(start);
|
2664 |
+
var check = isWordChar(startChar) ? isWordChar
|
2665 |
+
: /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);}
|
2666 |
+
: function(ch) {return !/\s/.test(ch) && !isWordChar(ch);};
|
2667 |
while (start > 0 && check(line.charAt(start - 1))) --start;
|
2668 |
while (end < line.length && check(line.charAt(end))) ++end;
|
2669 |
}
|
2670 |
+
return {from: Pos(pos.line, start), to: Pos(pos.line, end)};
|
2671 |
}
|
2672 |
|
2673 |
function selectLine(cm, line) {
|
2674 |
+
extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0)));
|
2675 |
}
|
2676 |
|
2677 |
// PROTOTYPE
|
2680 |
// 'wrap f in an operation, performed on its `this` parameter'
|
2681 |
|
2682 |
CodeMirror.prototype = {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2683 |
focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
|
2684 |
|
2685 |
setOption: function(option, value) {
|
2691 |
},
|
2692 |
|
2693 |
getOption: function(option) {return this.options[option];},
|
2694 |
+
getDoc: function() {return this.doc;},
|
2695 |
|
2696 |
+
addKeyMap: function(map, bottom) {
|
2697 |
+
this.state.keyMaps[bottom ? "push" : "unshift"](map);
|
|
|
|
|
2698 |
},
|
|
|
2699 |
removeKeyMap: function(map) {
|
2700 |
+
var maps = this.state.keyMaps;
|
2701 |
for (var i = 0; i < maps.length; ++i)
|
2702 |
if ((typeof map == "string" ? maps[i].name : maps[i]) == map) {
|
2703 |
maps.splice(i, 1);
|
2705 |
}
|
2706 |
},
|
2707 |
|
2708 |
+
addOverlay: operation(null, function(spec, options) {
|
2709 |
+
var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
|
2710 |
+
if (mode.startState) throw new Error("Overlays may not be stateful.");
|
2711 |
+
this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque});
|
2712 |
+
this.state.modeGen++;
|
2713 |
+
regChange(this);
|
2714 |
+
}),
|
2715 |
+
removeOverlay: operation(null, function(spec) {
|
2716 |
+
var overlays = this.state.overlays;
|
2717 |
+
for (var i = 0; i < overlays.length; ++i) {
|
2718 |
+
if (overlays[i].modeSpec == spec) {
|
2719 |
+
overlays.splice(i, 1);
|
2720 |
+
this.state.modeGen++;
|
2721 |
+
regChange(this);
|
2722 |
+
return;
|
2723 |
+
}
|
2724 |
+
}
|
2725 |
+
}),
|
2726 |
|
2727 |
indentLine: operation(null, function(n, dir, aggressive) {
|
2728 |
if (typeof dir != "string") {
|
2729 |
if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
|
2730 |
else dir = dir ? "add" : "subtract";
|
2731 |
}
|
2732 |
+
if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
|
2733 |
}),
|
|
|
2734 |
indentSelection: operation(null, function(how) {
|
2735 |
+
var sel = this.doc.sel;
|
2736 |
if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how);
|
2737 |
var e = sel.to.line - (sel.to.ch ? 0 : 1);
|
2738 |
for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how);
|
2739 |
}),
|
2740 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2741 |
// Fetch the parser token for a given character. Useful for hacks
|
2742 |
// that want to inspect the mode state (say, for completion).
|
2743 |
getTokenAt: function(pos) {
|
2744 |
+
var doc = this.doc;
|
2745 |
pos = clipPos(doc, pos);
|
2746 |
+
var state = getStateBefore(this, pos.line), mode = this.doc.mode;
|
2747 |
var line = getLine(doc, pos.line);
|
2748 |
var stream = new StringStream(line.text, this.options.tabSize);
|
2749 |
while (stream.pos < pos.ch && !stream.eol()) {
|
2759 |
},
|
2760 |
|
2761 |
getStateAfter: function(line) {
|
2762 |
+
var doc = this.doc;
|
2763 |
+
line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
|
2764 |
return getStateBefore(this, line + 1);
|
2765 |
},
|
2766 |
|
2767 |
cursorCoords: function(start, mode) {
|
2768 |
+
var pos, sel = this.doc.sel;
|
2769 |
if (start == null) pos = sel.head;
|
2770 |
+
else if (typeof start == "object") pos = clipPos(this.doc, start);
|
2771 |
else pos = start ? sel.from : sel.to;
|
2772 |
return cursorCoords(this, pos, mode || "page");
|
2773 |
},
|
2774 |
|
2775 |
charCoords: function(pos, mode) {
|
2776 |
+
return charCoords(this, clipPos(this.doc, pos), mode || "page");
|
2777 |
},
|
2778 |
|
2779 |
+
coordsChar: function(coords, mode) {
|
2780 |
+
coords = fromCoordSystem(this, coords, mode || "page");
|
2781 |
+
return coordsChar(this, coords.left, coords.top);
|
2782 |
},
|
2783 |
|
2784 |
defaultTextHeight: function() { return textHeight(this.display); },
|
2785 |
+
defaultCharWidth: function() { return charWidth(this.display); },
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2786 |
|
2787 |
setGutterMarker: operation(null, function(line, gutterID, value) {
|
2788 |
return changeLine(this, line, function(line) {
|
2794 |
}),
|
2795 |
|
2796 |
clearGutter: operation(null, function(gutterID) {
|
2797 |
+
var cm = this, doc = cm.doc, i = doc.first;
|
2798 |
+
doc.iter(function(line) {
|
2799 |
if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
|
2800 |
line.gutterMarkers[gutterID] = null;
|
2801 |
regChange(cm, i, i + 1);
|
2831 |
}),
|
2832 |
|
2833 |
addLineWidget: operation(null, function(handle, node, options) {
|
2834 |
+
return addLineWidget(this, handle, node, options);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2835 |
}),
|
2836 |
|
2837 |
+
removeLineWidget: function(widget) { widget.clear(); },
|
|
|
|
|
|
|
|
|
|
|
2838 |
|
2839 |
lineInfo: function(line) {
|
2840 |
if (typeof line == "number") {
|
2841 |
+
if (!isLine(this.doc, line)) return null;
|
2842 |
var n = line;
|
2843 |
+
line = getLine(this.doc, line);
|
2844 |
if (!line) return null;
|
2845 |
} else {
|
2846 |
var n = lineNo(line);
|
2855 |
|
2856 |
addWidget: function(pos, node, scroll, vert, horiz) {
|
2857 |
var display = this.display;
|
2858 |
+
pos = cursorCoords(this, clipPos(this.doc, pos));
|
2859 |
+
var top = pos.bottom, left = pos.left;
|
2860 |
node.style.position = "absolute";
|
2861 |
display.sizer.appendChild(node);
|
2862 |
+
if (vert == "over") {
|
2863 |
+
top = pos.top;
|
2864 |
+
} else if (vert == "above" || vert == "near") {
|
2865 |
+
var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
|
2866 |
hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
|
2867 |
+
// Default to positioning above (if specified and possible); otherwise default to positioning below
|
2868 |
+
if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
|
2869 |
top = pos.top - node.offsetHeight;
|
2870 |
+
else if (pos.bottom + node.offsetHeight <= vspace)
|
2871 |
+
top = pos.bottom;
|
2872 |
if (left + node.offsetWidth > hspace)
|
2873 |
left = hspace - node.offsetWidth;
|
2874 |
}
|
2886 |
scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight);
|
2887 |
},
|
2888 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2889 |
triggerOnKeyDown: operation(null, onKeyDown),
|
2890 |
|
2891 |
execCommand: function(cmd) {return commands[cmd](this);},
|
2892 |
|
2893 |
+
findPosH: function(from, amount, unit, visually) {
|
2894 |
+
var dir = 1;
|
2895 |
+
if (amount < 0) { dir = -1; amount = -amount; }
|
2896 |
+
for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
|
2897 |
+
cur = findPosH(this.doc, cur, dir, unit, visually);
|
2898 |
+
if (cur.hitSide) break;
|
2899 |
+
}
|
2900 |
+
return cur;
|
2901 |
+
},
|
2902 |
+
|
2903 |
moveH: operation(null, function(dir, unit) {
|
2904 |
+
var sel = this.doc.sel, pos;
|
2905 |
+
if (sel.shift || sel.extend || posEq(sel.from, sel.to))
|
2906 |
+
pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually);
|
2907 |
+
else
|
2908 |
+
pos = dir < 0 ? sel.from : sel.to;
|
2909 |
+
extendSelection(this.doc, pos, pos, dir);
|
2910 |
}),
|
2911 |
|
2912 |
deleteH: operation(null, function(dir, unit) {
|
2913 |
+
var sel = this.doc.sel;
|
2914 |
+
if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete");
|
2915 |
+
else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete");
|
2916 |
this.curOp.userSelChange = true;
|
2917 |
}),
|
2918 |
|
2919 |
+
findPosV: function(from, amount, unit, goalColumn) {
|
2920 |
+
var dir = 1, x = goalColumn;
|
2921 |
+
if (amount < 0) { dir = -1; amount = -amount; }
|
2922 |
+
for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
|
2923 |
+
var coords = cursorCoords(this, cur, "div");
|
2924 |
+
if (x == null) x = coords.left;
|
2925 |
+
else coords.left = x;
|
2926 |
+
cur = findPosV(this, coords, dir, unit);
|
2927 |
+
if (cur.hitSide) break;
|
|
|
2928 |
}
|
2929 |
+
return cur;
|
2930 |
+
},
|
|
|
|
|
2931 |
|
2932 |
+
moveV: operation(null, function(dir, unit) {
|
2933 |
+
var sel = this.doc.sel;
|
2934 |
+
var pos = cursorCoords(this, sel.head, "div");
|
2935 |
+
if (sel.goalColumn != null) pos.left = sel.goalColumn;
|
2936 |
+
var target = findPosV(this, pos, dir, unit);
|
2937 |
+
|
2938 |
+
if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top);
|
2939 |
+
extendSelection(this.doc, target, target, dir);
|
2940 |
+
sel.goalColumn = pos.left;
|
2941 |
}),
|
2942 |
|
2943 |
toggleOverwrite: function() {
|
2944 |
+
if (this.state.overwrite = !this.state.overwrite)
|
2945 |
this.display.cursor.className += " CodeMirror-overwrite";
|
2946 |
else
|
2947 |
this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", "");
|
2948 |
},
|
2949 |
+
hasFocus: function() { return this.state.focused; },
|
2950 |
|
2951 |
+
scrollTo: operation(null, function(x, y) {
|
2952 |
+
updateScrollPos(this, x, y);
|
2953 |
+
}),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2954 |
getScrollInfo: function() {
|
2955 |
var scroller = this.display.scroller, co = scrollerCutOff;
|
2956 |
return {left: scroller.scrollLeft, top: scroller.scrollTop,
|
2958 |
clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
|
2959 |
},
|
2960 |
|
2961 |
+
scrollIntoView: function(pos, margin) {
|
2962 |
+
if (typeof pos == "number") pos = Pos(pos, 0);
|
2963 |
+
if (!pos || pos.line != null) {
|
2964 |
+
pos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
|
2965 |
+
scrollPosIntoView(this, pos, margin);
|
2966 |
+
} else {
|
2967 |
+
scrollIntoView(this, pos.left, pos.top - margin, pos.right, pos.bottom + margin);
|
2968 |
+
}
|
2969 |
},
|
2970 |
|
2971 |
setSize: function(width, height) {
|
2980 |
on: function(type, f) {on(this, type, f);},
|
2981 |
off: function(type, f) {off(this, type, f);},
|
2982 |
|
2983 |
+
operation: function(f){return runInOp(this, f);},
|
2984 |
|
2985 |
+
refresh: operation(null, function() {
|
2986 |
clearCaches(this);
|
2987 |
+
updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop);
|
2988 |
+
regChange(this);
|
2989 |
+
}),
|
2990 |
+
|
2991 |
+
swapDoc: operation(null, function(doc) {
|
2992 |
+
var old = this.doc;
|
2993 |
+
old.cm = null;
|
2994 |
+
attachDoc(this, doc);
|
2995 |
+
clearCaches(this);
|
2996 |
+
updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
|
2997 |
+
return old;
|
2998 |
+
}),
|
2999 |
|
3000 |
getInputField: function(){return this.display.input;},
|
3001 |
getWrapperElement: function(){return this.display.wrapper;},
|
3020 |
|
3021 |
// These two are, on init, called from the constructor because they
|
3022 |
// have to be initialized before the editor can start at all.
|
3023 |
+
option("value", "", function(cm, val) {
|
3024 |
+
cm.setValue(val);
|
3025 |
+
}, true);
|
3026 |
+
option("mode", null, function(cm, val) {
|
3027 |
+
cm.doc.modeOption = val;
|
3028 |
+
loadMode(cm);
|
3029 |
+
}, true);
|
3030 |
|
3031 |
option("indentUnit", 2, loadMode, true);
|
3032 |
option("indentWithTabs", false);
|
3034 |
option("tabSize", 4, function(cm) {
|
3035 |
loadMode(cm);
|
3036 |
clearCaches(cm);
|
3037 |
+
regChange(cm);
|
3038 |
}, true);
|
3039 |
option("electricChars", true);
|
3040 |
+
option("rtlMoveVisually", !windows);
|
3041 |
|
3042 |
option("theme", "default", function(cm) {
|
3043 |
themeChanged(cm);
|
3054 |
setGuttersForLineNumbers(cm.options);
|
3055 |
guttersChanged(cm);
|
3056 |
}, true);
|
3057 |
+
option("fixedGutter", true, function(cm, val) {
|
3058 |
+
cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
|
3059 |
+
cm.refresh();
|
3060 |
+
}, true);
|
3061 |
option("lineNumbers", false, function(cm) {
|
3062 |
setGuttersForLineNumbers(cm.options);
|
3063 |
guttersChanged(cm);
|
3065 |
option("firstLineNumber", 1, guttersChanged, true);
|
3066 |
option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
|
3067 |
option("showCursorWhenSelecting", false, updateSelection, true);
|
3068 |
+
|
3069 |
option("readOnly", false, function(cm, val) {
|
3070 |
if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
|
3071 |
else if (!val) resetInput(cm, true);
|
3078 |
option("workDelay", 100);
|
3079 |
option("flattenSpans", true);
|
3080 |
option("pollInterval", 100);
|
3081 |
+
option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;});
|
3082 |
option("viewportMargin", 10, function(cm){cm.refresh();}, true);
|
3083 |
|
3084 |
option("tabindex", null, function(cm, val) {
|
3114 |
};
|
3115 |
|
3116 |
CodeMirror.getMode = function(options, spec) {
|
3117 |
+
spec = CodeMirror.resolveMode(spec);
|
3118 |
var mfactory = modes[spec.name];
|
3119 |
if (!mfactory) return CodeMirror.getMode(options, "text/plain");
|
3120 |
var modeObj = mfactory(options, spec);
|
3138 |
var modeExtensions = CodeMirror.modeExtensions = {};
|
3139 |
CodeMirror.extendMode = function(mode, properties) {
|
3140 |
var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
|
3141 |
+
copyObj(properties, exts);
|
|
|
3142 |
};
|
3143 |
|
3144 |
// EXTENSIONS
|
3186 |
// STANDARD COMMANDS
|
3187 |
|
3188 |
var commands = CodeMirror.commands = {
|
3189 |
+
selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));},
|
3190 |
killLine: function(cm) {
|
3191 |
var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
|
3192 |
if (!sel && cm.getLine(from.line).length == from.ch)
|
3193 |
+
cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete");
|
3194 |
+
else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete");
|
3195 |
},
|
3196 |
deleteLine: function(cm) {
|
3197 |
var l = cm.getCursor().line;
|
3198 |
+
cm.replaceRange("", Pos(l, 0), Pos(l), "+delete");
|
3199 |
},
|
3200 |
undo: function(cm) {cm.undo();},
|
3201 |
redo: function(cm) {cm.redo();},
|
3202 |
+
goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
|
3203 |
+
goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
|
3204 |
goLineStart: function(cm) {
|
3205 |
cm.extendSelection(lineStart(cm, cm.getCursor().line));
|
3206 |
},
|
3211 |
if (!order || order[0].level == 0) {
|
3212 |
var firstNonWS = Math.max(0, line.text.search(/\S/));
|
3213 |
var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch;
|
3214 |
+
cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS));
|
3215 |
} else cm.extendSelection(start);
|
3216 |
},
|
3217 |
goLineEnd: function(cm) {
|
3218 |
cm.extendSelection(lineEnd(cm, cm.getCursor().line));
|
3219 |
},
|
3220 |
+
goLineRight: function(cm) {
|
3221 |
+
var top = cm.charCoords(cm.getCursor(), "div").top + 5;
|
3222 |
+
cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"));
|
3223 |
+
},
|
3224 |
+
goLineLeft: function(cm) {
|
3225 |
+
var top = cm.charCoords(cm.getCursor(), "div").top + 5;
|
3226 |
+
cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div"));
|
3227 |
+
},
|
3228 |
goLineUp: function(cm) {cm.moveV(-1, "line");},
|
3229 |
goLineDown: function(cm) {cm.moveV(1, "line");},
|
3230 |
goPageUp: function(cm) {cm.moveV(-1, "page");},
|
3234 |
goColumnLeft: function(cm) {cm.moveH(-1, "column");},
|
3235 |
goColumnRight: function(cm) {cm.moveH(1, "column");},
|
3236 |
goWordLeft: function(cm) {cm.moveH(-1, "word");},
|
3237 |
+
goGroupRight: function(cm) {cm.moveH(1, "group");},
|
3238 |
+
goGroupLeft: function(cm) {cm.moveH(-1, "group");},
|
3239 |
goWordRight: function(cm) {cm.moveH(1, "word");},
|
3240 |
delCharBefore: function(cm) {cm.deleteH(-1, "char");},
|
3241 |
delCharAfter: function(cm) {cm.deleteH(1, "char");},
|
3242 |
delWordBefore: function(cm) {cm.deleteH(-1, "word");},
|
3243 |
delWordAfter: function(cm) {cm.deleteH(1, "word");},
|
3244 |
+
delGroupBefore: function(cm) {cm.deleteH(-1, "group");},
|
3245 |
+
delGroupAfter: function(cm) {cm.deleteH(1, "group");},
|
3246 |
indentAuto: function(cm) {cm.indentSelection("smart");},
|
3247 |
indentMore: function(cm) {cm.indentSelection("add");},
|
3248 |
indentLess: function(cm) {cm.indentSelection("subtract");},
|
3249 |
+
insertTab: function(cm) {cm.replaceSelection("\t", "end", "+input");},
|
3250 |
defaultTab: function(cm) {
|
3251 |
if (cm.somethingSelected()) cm.indentSelection("add");
|
3252 |
+
else cm.replaceSelection("\t", "end", "+input");
|
3253 |
},
|
3254 |
transposeChars: function(cm) {
|
3255 |
var cur = cm.getCursor(), line = cm.getLine(cur.line);
|
3256 |
if (cur.ch > 0 && cur.ch < line.length - 1)
|
3257 |
cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1),
|
3258 |
+
Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
|
3259 |
},
|
3260 |
newlineAndIndent: function(cm) {
|
3261 |
operation(cm, function() {
|
3262 |
+
cm.replaceSelection("\n", "end", "+input");
|
3263 |
cm.indentLine(cm.getCursor().line, null, true);
|
3264 |
})();
|
3265 |
},
|
3280 |
keyMap.pcDefault = {
|
3281 |
"Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
|
3282 |
"Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
|
3283 |
+
"Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
|
3284 |
+
"Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
|
3285 |
"Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
|
3286 |
"Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
|
3287 |
fallthrough: "basic"
|
3288 |
};
|
3289 |
keyMap.macDefault = {
|
3290 |
"Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
|
3291 |
+
"Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
|
3292 |
+
"Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
|
3293 |
+
"Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
|
3294 |
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
|
3295 |
"Cmd-[": "indentLess", "Cmd-]": "indentMore",
|
3296 |
fallthrough: ["basic", "emacsy"]
|
3310 |
else return val;
|
3311 |
}
|
3312 |
|
3313 |
+
function lookupKey(name, maps, handle) {
|
3314 |
function lookup(map) {
|
3315 |
map = getKeyMap(map);
|
3316 |
var found = map[name];
|
3317 |
+
if (found === false) return "stop";
|
|
|
|
|
|
|
3318 |
if (found != null && handle(found)) return true;
|
3319 |
+
if (map.nofallthrough) return "stop";
|
3320 |
+
|
|
|
|
|
3321 |
var fallthrough = map.fallthrough;
|
3322 |
if (fallthrough == null) return false;
|
3323 |
if (Object.prototype.toString.call(fallthrough) != "[object Array]")
|
3324 |
return lookup(fallthrough);
|
3325 |
for (var i = 0, e = fallthrough.length; i < e; ++i) {
|
3326 |
+
var done = lookup(fallthrough[i]);
|
3327 |
+
if (done) return done;
|
3328 |
}
|
3329 |
return false;
|
3330 |
}
|
3331 |
|
3332 |
+
for (var i = 0; i < maps.length; ++i) {
|
3333 |
+
var done = lookup(maps[i]);
|
3334 |
+
if (done) return done;
|
3335 |
+
}
|
3336 |
}
|
3337 |
function isModifierKey(event) {
|
3338 |
+
var name = keyNames[event.keyCode];
|
3339 |
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod";
|
3340 |
}
|
3341 |
+
function keyName(event, noShift) {
|
3342 |
+
var name = keyNames[event.keyCode];
|
3343 |
+
if (name == null || event.altGraphKey) return false;
|
3344 |
+
if (event.altKey) name = "Alt-" + name;
|
3345 |
+
if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name;
|
3346 |
+
if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name;
|
3347 |
+
if (!noShift && event.shiftKey) name = "Shift-" + name;
|
3348 |
+
return name;
|
3349 |
+
}
|
3350 |
+
CodeMirror.lookupKey = lookupKey;
|
3351 |
CodeMirror.isModifierKey = isModifierKey;
|
3352 |
+
CodeMirror.keyName = keyName;
|
3353 |
|
3354 |
// FROMTEXTAREA
|
3355 |
|
3358 |
options.value = textarea.value;
|
3359 |
if (!options.tabindex && textarea.tabindex)
|
3360 |
options.tabindex = textarea.tabindex;
|
3361 |
+
if (!options.placeholder && textarea.placeholder)
|
3362 |
+
options.placeholder = textarea.placeholder;
|
3363 |
// Set autofocus to true if this textarea is focused, or if it has
|
3364 |
// autofocus and no other element is focused.
|
3365 |
if (options.autofocus == null) {
|
3372 |
|
3373 |
function save() {textarea.value = cm.getValue();}
|
3374 |
if (textarea.form) {
|
|
|
3375 |
on(textarea.form, "submit", save);
|
3376 |
+
// Deplorable hack to make the submit method do the right thing.
|
3377 |
+
if (!options.leaveSubmitMethodAlone) {
|
3378 |
+
var form = textarea.form, realSubmit = form.submit;
|
3379 |
+
try {
|
3380 |
+
var wrappedSubmit = form.submit = function() {
|
3381 |
+
save();
|
3382 |
+
form.submit = realSubmit;
|
3383 |
+
form.submit();
|
3384 |
+
form.submit = wrappedSubmit;
|
3385 |
+
};
|
3386 |
+
} catch(e) {}
|
3387 |
+
}
|
3388 |
}
|
3389 |
|
3390 |
textarea.style.display = "none";
|
3416 |
this.pos = this.start = 0;
|
3417 |
this.string = string;
|
3418 |
this.tabSize = tabSize || 8;
|
3419 |
+
this.lastColumnPos = this.lastColumnValue = 0;
|
3420 |
}
|
3421 |
|
3422 |
StringStream.prototype = {
|
3449 |
if (found > -1) {this.pos = found; return true;}
|
3450 |
},
|
3451 |
backUp: function(n) {this.pos -= n;},
|
3452 |
+
column: function() {
|
3453 |
+
if (this.lastColumnPos < this.start) {
|
3454 |
+
this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
|
3455 |
+
this.lastColumnPos = this.start;
|
3456 |
+
}
|
3457 |
+
return this.lastColumnValue;
|
3458 |
+
},
|
3459 |
indentation: function() {return countColumn(this.string, null, this.tabSize);},
|
3460 |
match: function(pattern, consume, caseInsensitive) {
|
3461 |
if (typeof pattern == "string") {
|
3462 |
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
|
3463 |
+
var substr = this.string.substr(this.pos, pattern.length);
|
3464 |
+
if (cased(substr) == cased(pattern)) {
|
3465 |
if (consume !== false) this.pos += pattern.length;
|
3466 |
return true;
|
3467 |
}
|
3478 |
|
3479 |
// TEXTMARKERS
|
3480 |
|
3481 |
+
function TextMarker(doc, type) {
|
3482 |
this.lines = [];
|
3483 |
this.type = type;
|
3484 |
+
this.doc = doc;
|
3485 |
}
|
3486 |
+
CodeMirror.TextMarker = TextMarker;
|
3487 |
|
3488 |
TextMarker.prototype.clear = function() {
|
3489 |
if (this.explicitlyCleared) return;
|
3490 |
+
var cm = this.doc.cm, withOp = cm && !cm.curOp;
|
3491 |
+
if (withOp) startOperation(cm);
|
3492 |
var min = null, max = null;
|
3493 |
for (var i = 0; i < this.lines.length; ++i) {
|
3494 |
var line = this.lines[i];
|
3497 |
line.markedSpans = removeMarkedSpan(line.markedSpans, span);
|
3498 |
if (span.from != null)
|
3499 |
min = lineNo(line);
|
3500 |
+
else if (this.collapsed && !lineIsHidden(this.doc, line) && cm)
|
3501 |
+
updateLineHeight(line, textHeight(cm.display));
|
3502 |
+
}
|
3503 |
+
if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) {
|
3504 |
+
var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual);
|
3505 |
+
if (len > cm.display.maxLineLength) {
|
3506 |
+
cm.display.maxLine = visual;
|
3507 |
+
cm.display.maxLineLength = len;
|
3508 |
+
cm.display.maxLineChanged = true;
|
3509 |
+
}
|
3510 |
}
|
3511 |
+
|
3512 |
+
if (min != null && cm) regChange(cm, min, max + 1);
|
3513 |
this.lines.length = 0;
|
3514 |
this.explicitlyCleared = true;
|
3515 |
+
if (this.collapsed && this.doc.cantEdit) {
|
3516 |
+
this.doc.cantEdit = false;
|
3517 |
+
if (cm) reCheckSelection(cm);
|
3518 |
}
|
3519 |
+
if (withOp) endOperation(cm);
|
3520 |
+
signalLater(this, "clear");
|
3521 |
};
|
3522 |
|
3523 |
TextMarker.prototype.find = function() {
|
3527 |
var span = getMarkedSpanFor(line.markedSpans, this);
|
3528 |
if (span.from != null || span.to != null) {
|
3529 |
var found = lineNo(line);
|
3530 |
+
if (span.from != null) from = Pos(found, span.from);
|
3531 |
+
if (span.to != null) to = Pos(found, span.to);
|
3532 |
}
|
3533 |
}
|
3534 |
if (this.type == "bookmark") return from;
|
3535 |
return from && {from: from, to: to};
|
3536 |
};
|
3537 |
|
3538 |
+
TextMarker.prototype.getOptions = function(copyWidget) {
|
3539 |
+
var repl = this.replacedWith;
|
3540 |
+
return {className: this.className,
|
3541 |
+
inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
|
3542 |
+
atomic: this.atomic,
|
3543 |
+
collapsed: this.collapsed,
|
3544 |
+
replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
|
3545 |
+
readOnly: this.readOnly,
|
3546 |
+
startStyle: this.startStyle, endStyle: this.endStyle};
|
3547 |
+
};
|
3548 |
+
|
3549 |
+
TextMarker.prototype.attachLine = function(line) {
|
3550 |
+
if (!this.lines.length && this.doc.cm) {
|
3551 |
+
var op = this.doc.cm.curOp;
|
3552 |
+
if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
|
3553 |
+
(op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this);
|
3554 |
+
}
|
3555 |
+
this.lines.push(line);
|
3556 |
+
};
|
3557 |
+
TextMarker.prototype.detachLine = function(line) {
|
3558 |
+
this.lines.splice(indexOf(this.lines, line), 1);
|
3559 |
+
if (!this.lines.length && this.doc.cm) {
|
3560 |
+
var op = this.doc.cm.curOp;
|
3561 |
+
(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
|
3562 |
+
}
|
3563 |
+
};
|
3564 |
+
|
3565 |
+
function markText(doc, from, to, options, type) {
|
3566 |
+
if (options && options.shared) return markTextShared(doc, from, to, options, type);
|
3567 |
+
if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type);
|
3568 |
+
|
3569 |
+
var marker = new TextMarker(doc, type);
|
3570 |
if (type == "range" && !posLess(from, to)) return marker;
|
3571 |
+
if (options) copyObj(options, marker);
|
|
|
3572 |
if (marker.replacedWith) {
|
3573 |
marker.collapsed = true;
|
3574 |
marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget");
|
3575 |
}
|
3576 |
if (marker.collapsed) sawCollapsedSpans = true;
|
3577 |
|
3578 |
+
var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine;
|
3579 |
doc.iter(curLine, to.line + 1, function(line) {
|
3580 |
+
if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine)
|
3581 |
+
updateMaxLine = true;
|
3582 |
var span = {from: null, to: null, marker: marker};
|
3583 |
size += line.text.length;
|
3584 |
if (curLine == from.line) {span.from = from.ch; size -= from.ch;}
|
3589 |
else updateLineHeight(line, 0);
|
3590 |
}
|
3591 |
addMarkedSpan(line, span);
|
|
|
|
|
3592 |
++curLine;
|
3593 |
});
|
3594 |
+
if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) {
|
3595 |
+
if (lineIsHidden(doc, line)) updateLineHeight(line, 0);
|
3596 |
+
});
|
3597 |
+
|
3598 |
+
if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); });
|
3599 |
|
3600 |
if (marker.readOnly) {
|
3601 |
sawReadOnlySpans = true;
|
3602 |
+
if (doc.history.done.length || doc.history.undone.length)
|
3603 |
+
doc.clearHistory();
|
3604 |
}
|
3605 |
if (marker.collapsed) {
|
3606 |
if (collapsedAtStart != collapsedAtEnd)
|
3608 |
marker.size = size;
|
3609 |
marker.atomic = true;
|
3610 |
}
|
3611 |
+
if (cm) {
|
3612 |
+
if (updateMaxLine) cm.curOp.updateMaxLine = true;
|
3613 |
+
if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed)
|
3614 |
+
regChange(cm, from.line, to.line + 1);
|
3615 |
+
if (marker.atomic) reCheckSelection(cm);
|
3616 |
+
}
|
3617 |
return marker;
|
3618 |
}
|
3619 |
|
3620 |
+
// SHARED TEXTMARKERS
|
3621 |
+
|
3622 |
+
function SharedTextMarker(markers, primary) {
|
3623 |
+
this.markers = markers;
|
3624 |
+
this.primary = primary;
|
3625 |
+
for (var i = 0, me = this; i < markers.length; ++i) {
|
3626 |
+
markers[i].parent = this;
|
3627 |
+
on(markers[i], "clear", function(){me.clear();});
|
3628 |
+
}
|
3629 |
+
}
|
3630 |
+
CodeMirror.SharedTextMarker = SharedTextMarker;
|
3631 |
+
|
3632 |
+
SharedTextMarker.prototype.clear = function() {
|
3633 |
+
if (this.explicitlyCleared) return;
|
3634 |
+
this.explicitlyCleared = true;
|
3635 |
+
for (var i = 0; i < this.markers.length; ++i)
|
3636 |
+
this.markers[i].clear();
|
3637 |
+
signalLater(this, "clear");
|
3638 |
+
};
|
3639 |
+
SharedTextMarker.prototype.find = function() {
|
3640 |
+
return this.primary.find();
|
3641 |
+
};
|
3642 |
+
SharedTextMarker.prototype.getOptions = function(copyWidget) {
|
3643 |
+
var inner = this.primary.getOptions(copyWidget);
|
3644 |
+
inner.shared = true;
|
3645 |
+
return inner;
|
3646 |
+
};
|
3647 |
+
|
3648 |
+
function markTextShared(doc, from, to, options, type) {
|
3649 |
+
options = copyObj(options);
|
3650 |
+
options.shared = false;
|
3651 |
+
var markers = [markText(doc, from, to, options, type)], primary = markers[0];
|
3652 |
+
var widget = options.replacedWith;
|
3653 |
+
linkedDocs(doc, function(doc) {
|
3654 |
+
if (widget) options.replacedWith = widget.cloneNode(true);
|
3655 |
+
markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
|
3656 |
+
for (var i = 0; i < doc.linked.length; ++i)
|
3657 |
+
if (doc.linked[i].isParent) return;
|
3658 |
+
primary = lst(markers);
|
3659 |
+
});
|
3660 |
+
return new SharedTextMarker(markers, primary);
|
3661 |
+
}
|
3662 |
+
|
3663 |
// TEXTMARKER SPANS
|
3664 |
|
3665 |
function getMarkedSpanFor(spans, marker) {
|
3675 |
}
|
3676 |
function addMarkedSpan(line, span) {
|
3677 |
line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
|
3678 |
+
span.marker.attachLine(line);
|
3679 |
}
|
3680 |
|
3681 |
+
function markedSpansBefore(old, startCh, isInsert) {
|
3682 |
if (old) for (var i = 0, nw; i < old.length; ++i) {
|
3683 |
var span = old[i], marker = span.marker;
|
3684 |
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
|
3685 |
+
if (startsBefore || marker.type == "bookmark" && span.from == startCh && (!isInsert || !span.marker.insertLeft)) {
|
3686 |
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);
|
3687 |
(nw || (nw = [])).push({from: span.from,
|
3688 |
to: endsAfter ? null : span.to,
|
3692 |
return nw;
|
3693 |
}
|
3694 |
|
3695 |
+
function markedSpansAfter(old, endCh, isInsert) {
|
3696 |
if (old) for (var i = 0, nw; i < old.length; ++i) {
|
3697 |
var span = old[i], marker = span.marker;
|
3698 |
var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
|
3699 |
+
if (endsAfter || marker.type == "bookmark" && span.from == endCh && (!isInsert || span.marker.insertLeft)) {
|
3700 |
var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);
|
3701 |
(nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh,
|
3702 |
to: span.to == null ? null : span.to - endCh,
|
3706 |
return nw;
|
3707 |
}
|
3708 |
|
3709 |
+
function stretchSpansOverChange(doc, change) {
|
3710 |
+
var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
|
3711 |
+
var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
|
3712 |
+
if (!oldFirst && !oldLast) return null;
|
3713 |
+
|
3714 |
+
var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to);
|
3715 |
// Get the spans that 'stick out' on both sides
|
3716 |
+
var first = markedSpansBefore(oldFirst, startCh, isInsert);
|
3717 |
+
var last = markedSpansAfter(oldLast, endCh, isInsert);
|
3718 |
|
3719 |
// Next, merge those two ends
|
3720 |
+
var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
|
3721 |
if (first) {
|
3722 |
// Fix up .to properties of first
|
3723 |
for (var i = 0; i < first.length; ++i) {
|
3747 |
}
|
3748 |
}
|
3749 |
|
3750 |
+
var newMarkers = [first];
|
3751 |
if (!sameLine) {
|
3752 |
// Fill gap with whole-line-spans
|
3753 |
+
var gap = change.text.length - 2, gapMarkers;
|
3754 |
if (gap > 0 && first)
|
3755 |
for (var i = 0; i < first.length; ++i)
|
3756 |
if (first[i].to == null)
|
3757 |
(gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker});
|
3758 |
for (var i = 0; i < gap; ++i)
|
3759 |
+
newMarkers.push(gapMarkers);
|
3760 |
+
newMarkers.push(last);
|
3761 |
}
|
3762 |
return newMarkers;
|
3763 |
}
|
3764 |
|
3765 |
+
function mergeOldSpans(doc, change) {
|
3766 |
+
var old = getOldSpans(doc, change);
|
3767 |
+
var stretched = stretchSpansOverChange(doc, change);
|
3768 |
+
if (!old) return stretched;
|
3769 |
+
if (!stretched) return old;
|
3770 |
+
|
3771 |
+
for (var i = 0; i < old.length; ++i) {
|
3772 |
+
var oldCur = old[i], stretchCur = stretched[i];
|
3773 |
+
if (oldCur && stretchCur) {
|
3774 |
+
spans: for (var j = 0; j < stretchCur.length; ++j) {
|
3775 |
+
var span = stretchCur[j];
|
3776 |
+
for (var k = 0; k < oldCur.length; ++k)
|
3777 |
+
if (oldCur[k].marker == span.marker) continue spans;
|
3778 |
+
oldCur.push(span);
|
3779 |
+
}
|
3780 |
+
} else if (stretchCur) {
|
3781 |
+
old[i] = stretchCur;
|
3782 |
+
}
|
3783 |
+
}
|
3784 |
+
return old;
|
3785 |
+
}
|
3786 |
+
|
3787 |
function removeReadOnlyRanges(doc, from, to) {
|
3788 |
var markers = null;
|
3789 |
doc.iter(from.line, to.line + 1, function(line) {
|
3796 |
if (!markers) return null;
|
3797 |
var parts = [{from: from, to: to}];
|
3798 |
for (var i = 0; i < markers.length; ++i) {
|
3799 |
+
var mk = markers[i], m = mk.find();
|
3800 |
for (var j = 0; j < parts.length; ++j) {
|
3801 |
var p = parts[j];
|
3802 |
+
if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue;
|
3803 |
var newParts = [j, 1];
|
3804 |
+
if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from))
|
3805 |
+
newParts.push({from: p.from, to: m.from});
|
3806 |
+
if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to))
|
3807 |
+
newParts.push({from: m.to, to: p.to});
|
3808 |
parts.splice.apply(parts, newParts);
|
3809 |
j += newParts.length - 1;
|
3810 |
}
|
3834 |
return line;
|
3835 |
}
|
3836 |
|
3837 |
+
function lineIsHidden(doc, line) {
|
3838 |
var sps = sawCollapsedSpans && line.markedSpans;
|
3839 |
if (sps) for (var sp, i = 0; i < sps.length; ++i) {
|
3840 |
sp = sps[i];
|
3841 |
if (!sp.marker.collapsed) continue;
|
3842 |
if (sp.from == null) return true;
|
3843 |
+
if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
|
3844 |
return true;
|
3845 |
}
|
3846 |
}
|
3847 |
+
function lineIsHiddenInner(doc, line, span) {
|
3848 |
+
if (span.to == null) {
|
3849 |
+
var end = span.marker.find().to, endLine = getLine(doc, end.line);
|
3850 |
+
return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker));
|
3851 |
+
}
|
3852 |
+
if (span.marker.inclusiveRight && span.to == line.text.length)
|
3853 |
return true;
|
3854 |
for (var sp, i = 0; i < line.markedSpans.length; ++i) {
|
3855 |
sp = line.markedSpans[i];
|
3856 |
if (sp.marker.collapsed && sp.from == span.to &&
|
3857 |
(sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
|
3858 |
+
lineIsHiddenInner(doc, line, sp)) return true;
|
3859 |
}
|
3860 |
}
|
3861 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3862 |
function detachMarkedSpans(line) {
|
3863 |
var spans = line.markedSpans;
|
3864 |
if (!spans) return;
|
3865 |
+
for (var i = 0; i < spans.length; ++i)
|
3866 |
+
spans[i].marker.detachLine(line);
|
|
|
|
|
|
|
3867 |
line.markedSpans = null;
|
3868 |
}
|
3869 |
|
3870 |
function attachMarkedSpans(line, spans) {
|
3871 |
if (!spans) return;
|
3872 |
for (var i = 0; i < spans.length; ++i)
|
3873 |
+
spans[i].marker.attachLine(line);
|
3874 |
line.markedSpans = spans;
|
3875 |
}
|
3876 |
|
3877 |
+
// LINE WIDGETS
|
3878 |
+
|
3879 |
+
var LineWidget = CodeMirror.LineWidget = function(cm, node, options) {
|
3880 |
+
for (var opt in options) if (options.hasOwnProperty(opt))
|
3881 |
+
this[opt] = options[opt];
|
3882 |
+
this.cm = cm;
|
3883 |
+
this.node = node;
|
3884 |
+
};
|
3885 |
+
function widgetOperation(f) {
|
3886 |
+
return function() {
|
3887 |
+
var withOp = !this.cm.curOp;
|
3888 |
+
if (withOp) startOperation(this.cm);
|
3889 |
+
try {var result = f.apply(this, arguments);}
|
3890 |
+
finally {if (withOp) endOperation(this.cm);}
|
3891 |
+
return result;
|
3892 |
+
};
|
3893 |
+
}
|
3894 |
+
LineWidget.prototype.clear = widgetOperation(function() {
|
3895 |
+
var ws = this.line.widgets, no = lineNo(this.line);
|
3896 |
+
if (no == null || !ws) return;
|
3897 |
+
for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1);
|
3898 |
+
if (!ws.length) this.line.widgets = null;
|
3899 |
+
updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this)));
|
3900 |
+
regChange(this.cm, no, no + 1);
|
3901 |
+
});
|
3902 |
+
LineWidget.prototype.changed = widgetOperation(function() {
|
3903 |
+
var oldH = this.height;
|
3904 |
+
this.height = null;
|
3905 |
+
var diff = widgetHeight(this) - oldH;
|
3906 |
+
if (!diff) return;
|
3907 |
+
updateLineHeight(this.line, this.line.height + diff);
|
3908 |
+
var no = lineNo(this.line);
|
3909 |
+
regChange(this.cm, no, no + 1);
|
3910 |
+
});
|
3911 |
+
|
3912 |
+
function widgetHeight(widget) {
|
3913 |
+
if (widget.height != null) return widget.height;
|
3914 |
+
if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1)
|
3915 |
+
removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative"));
|
3916 |
+
return widget.height = widget.node.offsetHeight;
|
3917 |
+
}
|
3918 |
+
|
3919 |
+
function addLineWidget(cm, handle, node, options) {
|
3920 |
+
var widget = new LineWidget(cm, node, options);
|
3921 |
+
if (widget.noHScroll) cm.display.alignWidgets = true;
|
3922 |
+
changeLine(cm, handle, function(line) {
|
3923 |
+
(line.widgets || (line.widgets = [])).push(widget);
|
3924 |
+
widget.line = line;
|
3925 |
+
if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) {
|
3926 |
+
var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop;
|
3927 |
+
updateLineHeight(line, line.height + widgetHeight(widget));
|
3928 |
+
if (aboveVisible) addToScrollPos(cm, 0, widget.height);
|
3929 |
+
}
|
3930 |
+
return true;
|
3931 |
+
});
|
3932 |
+
return widget;
|
3933 |
+
}
|
3934 |
+
|
3935 |
// LINE DATA STRUCTURE
|
3936 |
|
3937 |
// Line objects. These hold state related to a line, including
|
3938 |
// highlighting info (the styles array).
|
3939 |
+
function makeLine(text, markedSpans, estimateHeight) {
|
3940 |
+
var line = {text: text};
|
3941 |
attachMarkedSpans(line, markedSpans);
|
3942 |
+
line.height = estimateHeight ? estimateHeight(line) : 1;
|
3943 |
return line;
|
3944 |
}
|
3945 |
|
3946 |
+
function updateLine(line, text, markedSpans, estimateHeight) {
|
3947 |
line.text = text;
|
3948 |
+
if (line.stateAfter) line.stateAfter = null;
|
3949 |
+
if (line.styles) line.styles = null;
|
3950 |
if (line.order != null) line.order = null;
|
3951 |
detachMarkedSpans(line);
|
3952 |
attachMarkedSpans(line, markedSpans);
|
3953 |
+
var estHeight = estimateHeight ? estimateHeight(line) : 1;
|
3954 |
+
if (estHeight != line.height) updateLineHeight(line, estHeight);
|
|
|
3955 |
}
|
3956 |
|
3957 |
function cleanUpLine(line) {
|
3962 |
// Run the given mode's parser over a line, update the styles
|
3963 |
// array, which contains alternating fragments of text and CSS
|
3964 |
// classes.
|
3965 |
+
function runMode(cm, text, mode, state, f) {
|
3966 |
+
var flattenSpans = mode.flattenSpans;
|
3967 |
+
if (flattenSpans == null) flattenSpans = cm.options.flattenSpans;
|
3968 |
+
var curText = "", curStyle = null;
|
3969 |
+
var stream = new StringStream(text, cm.options.tabSize);
|
3970 |
+
if (text == "" && mode.blankLine) mode.blankLine(state);
|
3971 |
while (!stream.eol()) {
|
3972 |
+
var style = mode.token(stream, state);
|
3973 |
+
if (stream.pos > 5000) {
|
3974 |
+
flattenSpans = false;
|
3975 |
+
// Webkit seems to refuse to render text nodes longer than 57444 characters
|
3976 |
+
stream.pos = Math.min(text.length, stream.start + 50000);
|
3977 |
+
style = null;
|
3978 |
+
}
|
3979 |
+
var substr = stream.current();
|
3980 |
stream.start = stream.pos;
|
3981 |
if (!flattenSpans || curStyle != style) {
|
3982 |
+
if (curText) f(curText, curStyle);
|
|
|
|
|
|
|
3983 |
curText = substr; curStyle = style;
|
3984 |
} else curText = curText + substr;
|
|
|
|
|
3985 |
}
|
3986 |
+
if (curText) f(curText, curStyle);
|
3987 |
+
}
|
3988 |
+
|
3989 |
+
function highlightLine(cm, line, state) {
|
3990 |
+
// A styles array always starts with a number identifying the
|
3991 |
+
// mode/overlays that it is based on (for easy invalidation).
|
3992 |
+
var st = [cm.state.modeGen];
|
3993 |
+
// Compute the base array of styles
|
3994 |
+
runMode(cm, line.text, cm.doc.mode, state, function(txt, style) {st.push(txt, style);});
|
3995 |
+
|
3996 |
+
// Run overlays, adjust style array.
|
3997 |
+
for (var o = 0; o < cm.state.overlays.length; ++o) {
|
3998 |
+
var overlay = cm.state.overlays[o], i = 1;
|
3999 |
+
runMode(cm, line.text, overlay.mode, true, function(txt, style) {
|
4000 |
+
var start = i, len = txt.length;
|
4001 |
+
// Ensure there's a token end at the current position, and that i points at it
|
4002 |
+
while (len) {
|
4003 |
+
var cur = st[i], len_ = cur.length;
|
4004 |
+
if (len_ <= len) {
|
4005 |
+
len -= len_;
|
4006 |
+
} else {
|
4007 |
+
st.splice(i, 1, cur.slice(0, len), st[i+1], cur.slice(len));
|
4008 |
+
len = 0;
|
4009 |
+
}
|
4010 |
+
i += 2;
|
4011 |
+
}
|
4012 |
+
if (!style) return;
|
4013 |
+
if (overlay.opaque) {
|
4014 |
+
st.splice(start, i - start, txt, style);
|
4015 |
+
i = start + 2;
|
4016 |
+
} else {
|
4017 |
+
for (; start < i; start += 2) {
|
4018 |
+
var cur = st[start+1];
|
4019 |
+
st[start+1] = cur ? cur + " " + style : style;
|
4020 |
+
}
|
4021 |
+
}
|
4022 |
+
});
|
4023 |
}
|
4024 |
+
|
4025 |
+
return st;
|
4026 |
+
}
|
4027 |
+
|
4028 |
+
function getLineStyles(cm, line) {
|
4029 |
+
if (!line.styles || line.styles[0] != cm.state.modeGen)
|
4030 |
+
line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line)));
|
4031 |
+
return line.styles;
|
4032 |
}
|
4033 |
|
4034 |
// Lightweight form of highlight -- proceed over this line and
|
4035 |
// update state, but don't save a style array.
|
4036 |
function processLine(cm, line, state) {
|
4037 |
+
var mode = cm.doc.mode;
|
4038 |
var stream = new StringStream(line.text, cm.options.tabSize);
|
4039 |
if (line.text == "" && mode.blankLine) mode.blankLine(state);
|
4040 |
while (!stream.eol() && stream.pos <= 5000) {
|
4054 |
var merged, line = realLine, lineBefore, sawBefore, simple = true;
|
4055 |
while (merged = collapsedSpanAtStart(line)) {
|
4056 |
simple = false;
|
4057 |
+
line = getLine(cm.doc, merged.find().from.line);
|
4058 |
if (!lineBefore) lineBefore = line;
|
4059 |
}
|
4060 |
|
4063 |
if (line.textClass) builder.pre.className = line.textClass;
|
4064 |
|
4065 |
do {
|
|
|
|
|
4066 |
builder.measure = line == realLine && measure;
|
4067 |
builder.pos = 0;
|
4068 |
builder.addToken = builder.measure ? buildTokenMeasure : buildToken;
|
4069 |
+
if ((ie || webkit) && cm.getOption("lineWrapping"))
|
4070 |
+
builder.addToken = buildTokenSplitSpaces(builder.addToken);
|
4071 |
if (measure && sawBefore && line != realLine && !builder.addedOne) {
|
4072 |
measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure));
|
4073 |
builder.addedOne = true;
|
4074 |
}
|
4075 |
+
var next = insertLineContent(line, builder, getLineStyles(cm, line));
|
4076 |
sawBefore = line == lineBefore;
|
4077 |
if (next) {
|
4078 |
+
line = getLine(cm.doc, next.to.line);
|
4079 |
simple = false;
|
4080 |
}
|
4081 |
} while (next);
|
4082 |
|
4083 |
if (measure && !builder.addedOne)
|
4084 |
measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure));
|
4085 |
+
if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine))
|
4086 |
builder.pre.appendChild(document.createTextNode("\u00a0"));
|
4087 |
|
4088 |
+
var order;
|
4089 |
+
// Work around problem with the reported dimensions of single-char
|
4090 |
+
// direction spans on IE (issue #1129). See also the comment in
|
4091 |
+
// cursorCoords.
|
4092 |
+
if (measure && ie && (order = getOrder(line))) {
|
4093 |
+
var l = order.length - 1;
|
4094 |
+
if (order[l].from == order[l].to) --l;
|
4095 |
+
var last = order[l], prev = order[l - 1];
|
4096 |
+
if (last.from + 1 == last.to && prev && last.level < prev.level) {
|
4097 |
+
var span = measure[builder.pos - 1];
|
4098 |
+
if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure),
|
4099 |
+
span.nextSibling);
|
4100 |
+
}
|
4101 |
+
}
|
4102 |
+
|
4103 |
+
signal(cm, "renderLine", cm, realLine, builder.pre);
|
4104 |
return builder.pre;
|
4105 |
}
|
4106 |
|
4107 |
+
var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g;
|
4108 |
function buildToken(builder, text, style, startStyle, endStyle) {
|
4109 |
if (!text) return;
|
4110 |
if (!tokenSpecialChars.test(text)) {
|
4144 |
}
|
4145 |
|
4146 |
function buildTokenMeasure(builder, text, style, startStyle, endStyle) {
|
4147 |
+
var wrapping = builder.cm.options.lineWrapping;
|
4148 |
for (var i = 0; i < text.length; ++i) {
|
4149 |
+
var ch = text.charAt(i), start = i == 0;
|
4150 |
+
if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
|
4151 |
+
ch = text.slice(i, i + 2);
|
4152 |
+
++i;
|
4153 |
+
} else if (i && wrapping &&
|
4154 |
+
spanAffectsWrapping.test(text.slice(i - 1, i + 1))) {
|
4155 |
builder.pre.appendChild(elt("wbr"));
|
4156 |
+
}
|
4157 |
+
var span = builder.measure[builder.pos] =
|
4158 |
+
buildToken(builder, ch, style,
|
4159 |
+
start && startStyle, i == text.length - 1 && endStyle);
|
4160 |
+
// In IE single-space nodes wrap differently than spaces
|
4161 |
+
// embedded in larger text nodes, except when set to
|
4162 |
+
// white-space: normal (issue #1268).
|
4163 |
+
if (ie && wrapping && ch == " " && i && !/\s/.test(text.charAt(i - 1)) &&
|
4164 |
+
i < text.length - 1 && !/\s/.test(text.charAt(i + 1)))
|
4165 |
+
span.style.whiteSpace = "normal";
|
4166 |
+
builder.pos += ch.length;
|
4167 |
}
|
4168 |
if (text.length) builder.addedOne = true;
|
4169 |
}
|
4170 |
|
4171 |
+
function buildTokenSplitSpaces(inner) {
|
4172 |
+
function split(old) {
|
4173 |
+
var out = " ";
|
4174 |
+
for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0";
|
4175 |
+
out += " ";
|
4176 |
+
return out;
|
4177 |
+
}
|
4178 |
+
return function(builder, text, style, startStyle, endStyle) {
|
4179 |
+
return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle);
|
4180 |
+
};
|
4181 |
+
}
|
4182 |
+
|
4183 |
function buildCollapsedSpan(builder, size, widget) {
|
4184 |
if (widget) {
|
4185 |
if (!builder.display) widget = widget.cloneNode(true);
|
4194 |
|
4195 |
// Outputs a number of spans to make up a line, taking highlighting
|
4196 |
// and marked text into account.
|
4197 |
+
function insertLineContent(line, builder, styles) {
|
4198 |
+
var spans = line.markedSpans;
|
4199 |
if (!spans) {
|
4200 |
+
for (var i = 1; i < styles.length; i+=2)
|
4201 |
+
builder.addToken(builder, styles[i], styleToClass(styles[i+1]));
|
4202 |
return;
|
4203 |
}
|
4204 |
|
4205 |
var allText = line.text, len = allText.length;
|
4206 |
+
var pos = 0, i = 1, text = "", style;
|
4207 |
var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed;
|
4208 |
for (;;) {
|
4209 |
if (nextChange == pos) { // Update current marker set
|
4240 |
var end = pos + text.length;
|
4241 |
if (!collapsed) {
|
4242 |
var tokenText = end > upto ? text.slice(0, upto - pos) : text;
|
4243 |
+
builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
|
4244 |
spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "");
|
4245 |
}
|
4246 |
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
|
4247 |
pos = end;
|
4248 |
spanStartStyle = "";
|
4249 |
}
|
4250 |
+
text = styles[i++]; style = styleToClass(styles[i++]);
|
4251 |
}
|
4252 |
}
|
4253 |
}
|
4254 |
|
4255 |
// DOCUMENT DATA STRUCTURE
|
4256 |
|
4257 |
+
function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) {
|
4258 |
+
function spansFor(n) {return markedSpans ? markedSpans[n] : null;}
|
4259 |
+
function update(line, text, spans) {
|
4260 |
+
updateLine(line, text, spans, estimateHeight);
|
4261 |
+
signalLater(line, "change", line, change);
|
4262 |
+
}
|
4263 |
+
|
4264 |
+
var from = change.from, to = change.to, text = change.text;
|
4265 |
+
var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
|
4266 |
+
var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
|
4267 |
+
|
4268 |
+
// First adjust the line structure
|
4269 |
+
if (from.ch == 0 && to.ch == 0 && lastText == "") {
|
4270 |
+
// This is a whole-line replace. Treated specially to make
|
4271 |
+
// sure line objects move the way they are supposed to.
|
4272 |
+
for (var i = 0, e = text.length - 1, added = []; i < e; ++i)
|
4273 |
+
added.push(makeLine(text[i], spansFor(i), estimateHeight));
|
4274 |
+
update(lastLine, lastLine.text, lastSpans);
|
4275 |
+
if (nlines) doc.remove(from.line, nlines);
|
4276 |
+
if (added.length) doc.insert(from.line, added);
|
4277 |
+
} else if (firstLine == lastLine) {
|
4278 |
+
if (text.length == 1) {
|
4279 |
+
update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
|
4280 |
+
} else {
|
4281 |
+
for (var added = [], i = 1, e = text.length - 1; i < e; ++i)
|
4282 |
+
added.push(makeLine(text[i], spansFor(i), estimateHeight));
|
4283 |
+
added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
|
4284 |
+
update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
|
4285 |
+
doc.insert(from.line + 1, added);
|
4286 |
+
}
|
4287 |
+
} else if (text.length == 1) {
|
4288 |
+
update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
|
4289 |
+
doc.remove(from.line + 1, nlines);
|
4290 |
+
} else {
|
4291 |
+
update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
|
4292 |
+
update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
|
4293 |
+
for (var i = 1, e = text.length - 1, added = []; i < e; ++i)
|
4294 |
+
added.push(makeLine(text[i], spansFor(i), estimateHeight));
|
4295 |
+
if (nlines > 1) doc.remove(from.line + 1, nlines - 1);
|
4296 |
+
doc.insert(from.line + 1, added);
|
4297 |
+
}
|
4298 |
+
|
4299 |
+
signalLater(doc, "change", doc, change);
|
4300 |
+
setSelection(doc, selAfter.anchor, selAfter.head, null, true);
|
4301 |
+
}
|
4302 |
+
|
4303 |
function LeafChunk(lines) {
|
4304 |
this.lines = lines;
|
4305 |
this.parent = null;
|
4312 |
|
4313 |
LeafChunk.prototype = {
|
4314 |
chunkSize: function() { return this.lines.length; },
|
4315 |
+
removeInner: function(at, n) {
|
4316 |
for (var i = at, e = at + n; i < e; ++i) {
|
4317 |
var line = this.lines[i];
|
4318 |
this.height -= line.height;
|
4319 |
cleanUpLine(line);
|
4320 |
+
signalLater(line, "delete");
|
4321 |
}
|
4322 |
this.lines.splice(at, n);
|
4323 |
},
|
4324 |
collapse: function(lines) {
|
4325 |
lines.splice.apply(lines, [lines.length, 0].concat(this.lines));
|
4326 |
},
|
4327 |
+
insertInner: function(at, lines, height) {
|
4328 |
this.height += height;
|
4329 |
this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
|
4330 |
for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
|
4350 |
|
4351 |
BranchChunk.prototype = {
|
4352 |
chunkSize: function() { return this.size; },
|
4353 |
+
removeInner: function(at, n) {
|
4354 |
this.size -= n;
|
4355 |
for (var i = 0; i < this.children.length; ++i) {
|
4356 |
var child = this.children[i], sz = child.chunkSize();
|
4357 |
if (at < sz) {
|
4358 |
var rm = Math.min(n, sz - at), oldHeight = child.height;
|
4359 |
+
child.removeInner(at, rm);
|
4360 |
this.height -= oldHeight - child.height;
|
4361 |
if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
|
4362 |
if ((n -= rm) == 0) break;
|
4373 |
collapse: function(lines) {
|
4374 |
for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines);
|
4375 |
},
|
4376 |
+
insertInner: function(at, lines, height) {
|
|
|
|
|
|
|
|
|
|
|
4377 |
this.size += lines.length;
|
4378 |
this.height += height;
|
4379 |
for (var i = 0, e = this.children.length; i < e; ++i) {
|
4380 |
var child = this.children[i], sz = child.chunkSize();
|
4381 |
if (at <= sz) {
|
4382 |
+
child.insertInner(at, lines, height);
|
4383 |
if (child.lines && child.lines.length > 50) {
|
4384 |
while (child.lines.length > 50) {
|
4385 |
var spilled = child.lines.splice(child.lines.length - 25, 25);
|
4416 |
} while (me.children.length > 10);
|
4417 |
me.parent.maybeSpill();
|
4418 |
},
|
|
|
4419 |
iterN: function(at, n, op) {
|
4420 |
for (var i = 0, e = this.children.length; i < e; ++i) {
|
4421 |
var child = this.children[i], sz = child.chunkSize();
|
4429 |
}
|
4430 |
};
|
4431 |
|
4432 |
+
var nextDocId = 0;
|
4433 |
+
var Doc = CodeMirror.Doc = function(text, mode, firstLine) {
|
4434 |
+
if (!(this instanceof Doc)) return new Doc(text, mode, firstLine);
|
4435 |
+
if (firstLine == null) firstLine = 0;
|
4436 |
+
|
4437 |
+
BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]);
|
4438 |
+
this.first = firstLine;
|
4439 |
+
this.scrollTop = this.scrollLeft = 0;
|
4440 |
+
this.cantEdit = false;
|
4441 |
+
this.history = makeHistory();
|
4442 |
+
this.frontier = firstLine;
|
4443 |
+
var start = Pos(firstLine, 0);
|
4444 |
+
this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null};
|
4445 |
+
this.id = ++nextDocId;
|
4446 |
+
this.modeOption = mode;
|
4447 |
+
|
4448 |
+
if (typeof text == "string") text = splitLines(text);
|
4449 |
+
updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start});
|
4450 |
+
};
|
4451 |
+
|
4452 |
+
Doc.prototype = createObj(BranchChunk.prototype, {
|
4453 |
+
iter: function(from, to, op) {
|
4454 |
+
if (op) this.iterN(from - this.first, to - from, op);
|
4455 |
+
else this.iterN(this.first, this.first + this.size, from);
|
4456 |
+
},
|
4457 |
+
|
4458 |
+
insert: function(at, lines) {
|
4459 |
+
var height = 0;
|
4460 |
+
for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height;
|
4461 |
+
this.insertInner(at - this.first, lines, height);
|
4462 |
+
},
|
4463 |
+
remove: function(at, n) { this.removeInner(at - this.first, n); },
|
4464 |
+
|
4465 |
+
getValue: function(lineSep) {
|
4466 |
+
var lines = getLines(this, this.first, this.first + this.size);
|
4467 |
+
if (lineSep === false) return lines;
|
4468 |
+
return lines.join(lineSep || "\n");
|
4469 |
+
},
|
4470 |
+
setValue: function(code) {
|
4471 |
+
var top = Pos(this.first, 0), last = this.first + this.size - 1;
|
4472 |
+
makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
|
4473 |
+
text: splitLines(code), origin: "setValue"},
|
4474 |
+
{head: top, anchor: top}, true);
|
4475 |
+
},
|
4476 |
+
replaceRange: function(code, from, to, origin) {
|
4477 |
+
from = clipPos(this, from);
|
4478 |
+
to = to ? clipPos(this, to) : from;
|
4479 |
+
replaceRange(this, code, from, to, origin);
|
4480 |
+
},
|
4481 |
+
getRange: function(from, to, lineSep) {
|
4482 |
+
var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
|
4483 |
+
if (lineSep === false) return lines;
|
4484 |
+
return lines.join(lineSep || "\n");
|
4485 |
+
},
|
4486 |
+
|
4487 |
+
getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;},
|
4488 |
+
setLine: function(line, text) {
|
4489 |
+
if (isLine(this, line))
|
4490 |
+
replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line)));
|
4491 |
+
},
|
4492 |
+
removeLine: function(line) {
|
4493 |
+
if (isLine(this, line))
|
4494 |
+
replaceRange(this, "", Pos(line, 0), clipPos(this, Pos(line + 1, 0)));
|
4495 |
+
},
|
4496 |
+
|
4497 |
+
getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);},
|
4498 |
+
getLineNumber: function(line) {return lineNo(line);},
|
4499 |
+
|
4500 |
+
lineCount: function() {return this.size;},
|
4501 |
+
firstLine: function() {return this.first;},
|
4502 |
+
lastLine: function() {return this.first + this.size - 1;},
|
4503 |
+
|
4504 |
+
clipPos: function(pos) {return clipPos(this, pos);},
|
4505 |
+
|
4506 |
+
getCursor: function(start) {
|
4507 |
+
var sel = this.sel, pos;
|
4508 |
+
if (start == null || start == "head") pos = sel.head;
|
4509 |
+
else if (start == "anchor") pos = sel.anchor;
|
4510 |
+
else if (start == "end" || start === false) pos = sel.to;
|
4511 |
+
else pos = sel.from;
|
4512 |
+
return copyPos(pos);
|
4513 |
+
},
|
4514 |
+
somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);},
|
4515 |
+
|
4516 |
+
setCursor: docOperation(function(line, ch, extend) {
|
4517 |
+
var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line);
|
4518 |
+
if (extend) extendSelection(this, pos);
|
4519 |
+
else setSelection(this, pos, pos);
|
4520 |
+
}),
|
4521 |
+
setSelection: docOperation(function(anchor, head) {
|
4522 |
+
setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor));
|
4523 |
+
}),
|
4524 |
+
extendSelection: docOperation(function(from, to) {
|
4525 |
+
extendSelection(this, clipPos(this, from), to && clipPos(this, to));
|
4526 |
+
}),
|
4527 |
+
|
4528 |
+
getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);},
|
4529 |
+
replaceSelection: function(code, collapse, origin) {
|
4530 |
+
makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around");
|
4531 |
+
},
|
4532 |
+
undo: docOperation(function() {makeChangeFromHistory(this, "undo");}),
|
4533 |
+
redo: docOperation(function() {makeChangeFromHistory(this, "redo");}),
|
4534 |
+
|
4535 |
+
setExtending: function(val) {this.sel.extend = val;},
|
4536 |
+
|
4537 |
+
historySize: function() {
|
4538 |
+
var hist = this.history;
|
4539 |
+
return {undo: hist.done.length, redo: hist.undone.length};
|
4540 |
+
},
|
4541 |
+
clearHistory: function() {this.history = makeHistory();},
|
4542 |
+
|
4543 |
+
markClean: function() {
|
4544 |
+
this.history.dirtyCounter = 0;
|
4545 |
+
this.history.lastOp = this.history.lastOrigin = null;
|
4546 |
+
},
|
4547 |
+
isClean: function () {return this.history.dirtyCounter == 0;},
|
4548 |
+
|
4549 |
+
getHistory: function() {
|
4550 |
+
return {done: copyHistoryArray(this.history.done),
|
4551 |
+
undone: copyHistoryArray(this.history.undone)};
|
4552 |
+
},
|
4553 |
+
setHistory: function(histData) {
|
4554 |
+
var hist = this.history = makeHistory();
|
4555 |
+
hist.done = histData.done.slice(0);
|
4556 |
+
hist.undone = histData.undone.slice(0);
|
4557 |
+
},
|
4558 |
+
|
4559 |
+
markText: function(from, to, options) {
|
4560 |
+
return markText(this, clipPos(this, from), clipPos(this, to), options, "range");
|
4561 |
+
},
|
4562 |
+
setBookmark: function(pos, options) {
|
4563 |
+
var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
|
4564 |
+
insertLeft: options && options.insertLeft};
|
4565 |
+
pos = clipPos(this, pos);
|
4566 |
+
return markText(this, pos, pos, realOpts, "bookmark");
|
4567 |
+
},
|
4568 |
+
findMarksAt: function(pos) {
|
4569 |
+
pos = clipPos(this, pos);
|
4570 |
+
var markers = [], spans = getLine(this, pos.line).markedSpans;
|
4571 |
+
if (spans) for (var i = 0; i < spans.length; ++i) {
|
4572 |
+
var span = spans[i];
|
4573 |
+
if ((span.from == null || span.from <= pos.ch) &&
|
4574 |
+
(span.to == null || span.to >= pos.ch))
|
4575 |
+
markers.push(span.marker.parent || span.marker);
|
4576 |
+
}
|
4577 |
+
return markers;
|
4578 |
+
},
|
4579 |
+
getAllMarks: function() {
|
4580 |
+
var markers = [];
|
4581 |
+
this.iter(function(line) {
|
4582 |
+
var sps = line.markedSpans;
|
4583 |
+
if (sps) for (var i = 0; i < sps.length; ++i)
|
4584 |
+
if (sps[i].from != null) markers.push(sps[i].marker);
|
4585 |
+
});
|
4586 |
+
return markers;
|
4587 |
+
},
|
4588 |
+
|
4589 |
+
posFromIndex: function(off) {
|
4590 |
+
var ch, lineNo = this.first;
|
4591 |
+
this.iter(function(line) {
|
4592 |
+
var sz = line.text.length + 1;
|
4593 |
+
if (sz > off) { ch = off; return true; }
|
4594 |
+
off -= sz;
|
4595 |
+
++lineNo;
|
4596 |
+
});
|
4597 |
+
return clipPos(this, Pos(lineNo, ch));
|
4598 |
+
},
|
4599 |
+
indexFromPos: function (coords) {
|
4600 |
+
coords = clipPos(this, coords);
|
4601 |
+
var index = coords.ch;
|
4602 |
+
if (coords.line < this.first || coords.ch < 0) return 0;
|
4603 |
+
this.iter(this.first, coords.line, function (line) {
|
4604 |
+
index += line.text.length + 1;
|
4605 |
+
});
|
4606 |
+
return index;
|
4607 |
+
},
|
4608 |
+
|
4609 |
+
copy: function(copyHistory) {
|
4610 |
+
var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first);
|
4611 |
+
doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
|
4612 |
+
doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor,
|
4613 |
+
shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn};
|
4614 |
+
if (copyHistory) {
|
4615 |
+
doc.history.undoDepth = this.history.undoDepth;
|
4616 |
+
doc.setHistory(this.getHistory());
|
4617 |
+
}
|
4618 |
+
return doc;
|
4619 |
+
},
|
4620 |
+
|
4621 |
+
linkedDoc: function(options) {
|
4622 |
+
if (!options) options = {};
|
4623 |
+
var from = this.first, to = this.first + this.size;
|
4624 |
+
if (options.from != null && options.from > from) from = options.from;
|
4625 |
+
if (options.to != null && options.to < to) to = options.to;
|
4626 |
+
var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from);
|
4627 |
+
if (options.sharedHist) copy.history = this.history;
|
4628 |
+
(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
|
4629 |
+
copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
|
4630 |
+
return copy;
|
4631 |
+
},
|
4632 |
+
unlinkDoc: function(other) {
|
4633 |
+
if (other instanceof CodeMirror) other = other.doc;
|
4634 |
+
if (this.linked) for (var i = 0; i < this.linked.length; ++i) {
|
4635 |
+
var link = this.linked[i];
|
4636 |
+
if (link.doc != other) continue;
|
4637 |
+
this.linked.splice(i, 1);
|
4638 |
+
other.unlinkDoc(this);
|
4639 |
+
break;
|
4640 |
+
}
|
4641 |
+
// If the histories were shared, split them again
|
4642 |
+
if (other.history == this.history) {
|
4643 |
+
var splitIds = [other.id];
|
4644 |
+
linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true);
|
4645 |
+
other.history = makeHistory();
|
4646 |
+
other.history.done = copyHistoryArray(this.history.done, splitIds);
|
4647 |
+
other.history.undone = copyHistoryArray(this.history.undone, splitIds);
|
4648 |
+
}
|
4649 |
+
},
|
4650 |
+
iterLinkedDocs: function(f) {linkedDocs(this, f);},
|
4651 |
+
|
4652 |
+
getMode: function() {return this.mode;},
|
4653 |
+
getEditor: function() {return this.cm;}
|
4654 |
+
});
|
4655 |
+
|
4656 |
+
Doc.prototype.eachLine = Doc.prototype.iter;
|
4657 |
+
|
4658 |
+
// The Doc methods that should be available on CodeMirror instances
|
4659 |
+
var dontDelegate = "iter insert remove copy getEditor".split(" ");
|
4660 |
+
for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
|
4661 |
+
CodeMirror.prototype[prop] = (function(method) {
|
4662 |
+
return function() {return method.apply(this.doc, arguments);};
|
4663 |
+
})(Doc.prototype[prop]);
|
4664 |
+
|
4665 |
+
function linkedDocs(doc, f, sharedHistOnly) {
|
4666 |
+
function propagate(doc, skip, sharedHist) {
|
4667 |
+
if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) {
|
4668 |
+
var rel = doc.linked[i];
|
4669 |
+
if (rel.doc == skip) continue;
|
4670 |
+
var shared = sharedHist && rel.sharedHist;
|
4671 |
+
if (sharedHistOnly && !shared) continue;
|
4672 |
+
f(rel.doc, shared);
|
4673 |
+
propagate(rel.doc, doc, shared);
|
4674 |
+
}
|
4675 |
+
}
|
4676 |
+
propagate(doc, null, true);
|
4677 |
+
}
|
4678 |
+
|
4679 |
+
function attachDoc(cm, doc) {
|
4680 |
+
if (doc.cm) throw new Error("This document is already in use.");
|
4681 |
+
cm.doc = doc;
|
4682 |
+
doc.cm = cm;
|
4683 |
+
estimateLineHeights(cm);
|
4684 |
+
loadMode(cm);
|
4685 |
+
if (!cm.options.lineWrapping) computeMaxLength(cm);
|
4686 |
+
cm.options.mode = doc.modeOption;
|
4687 |
+
regChange(cm);
|
4688 |
+
}
|
4689 |
+
|
4690 |
// LINE UTILITIES
|
4691 |
|
4692 |
function getLine(chunk, n) {
|
4693 |
+
n -= chunk.first;
|
4694 |
while (!chunk.lines) {
|
4695 |
for (var i = 0;; ++i) {
|
4696 |
var child = chunk.children[i], sz = child.chunkSize();
|
4701 |
return chunk.lines[n];
|
4702 |
}
|
4703 |
|
4704 |
+
function getBetween(doc, start, end) {
|
4705 |
+
var out = [], n = start.line;
|
4706 |
+
doc.iter(start.line, end.line + 1, function(line) {
|
4707 |
+
var text = line.text;
|
4708 |
+
if (n == end.line) text = text.slice(0, end.ch);
|
4709 |
+
if (n == start.line) text = text.slice(start.ch);
|
4710 |
+
out.push(text);
|
4711 |
+
++n;
|
4712 |
+
});
|
4713 |
+
return out;
|
4714 |
+
}
|
4715 |
+
function getLines(doc, from, to) {
|
4716 |
+
var out = [];
|
4717 |
+
doc.iter(from, to, function(line) { out.push(line.text); });
|
4718 |
+
return out;
|
4719 |
+
}
|
4720 |
+
|
4721 |
function updateLineHeight(line, height) {
|
4722 |
var diff = height - line.height;
|
4723 |
for (var n = line; n; n = n.parent) n.height += diff;
|
4732 |
no += chunk.children[i].chunkSize();
|
4733 |
}
|
4734 |
}
|
4735 |
+
return no + cur.first;
|
4736 |
}
|
4737 |
|
4738 |
function lineAtHeight(chunk, h) {
|
4739 |
+
var n = chunk.first;
|
4740 |
outer: do {
|
4741 |
for (var i = 0, e = chunk.children.length; i < e; ++i) {
|
4742 |
var child = chunk.children[i], ch = child.height;
|
4755 |
}
|
4756 |
|
4757 |
function heightAtLine(cm, lineObj) {
|
4758 |
+
lineObj = visualLine(cm.doc, lineObj);
|
4759 |
|
4760 |
var h = 0, chunk = lineObj.parent;
|
4761 |
for (var i = 0; i < chunk.lines.length; ++i) {
|
4786 |
// Arrays of history events. Doing something adds an event to
|
4787 |
// done and clears undo. Undoing moves events from done to
|
4788 |
// undone, redoing moves them in the other direction.
|
4789 |
+
done: [], undone: [], undoDepth: Infinity,
|
4790 |
// Used to track when changes can be merged into a single undo
|
4791 |
// event
|
4792 |
lastTime: 0, lastOp: null, lastOrigin: null,
|
4795 |
};
|
4796 |
}
|
4797 |
|
4798 |
+
function attachLocalSpans(doc, change, from, to) {
|
4799 |
+
var existing = change["spans_" + doc.id], n = 0;
|
4800 |
+
doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) {
|
4801 |
+
if (line.markedSpans)
|
4802 |
+
(existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans;
|
4803 |
+
++n;
|
4804 |
+
});
|
4805 |
+
}
|
4806 |
+
|
4807 |
+
function historyChangeFromChange(doc, change) {
|
4808 |
+
var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
|
4809 |
+
attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
|
4810 |
+
linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true);
|
4811 |
+
return histChange;
|
4812 |
+
}
|
4813 |
+
|
4814 |
+
function addToHistory(doc, change, selAfter, opId) {
|
4815 |
+
var hist = doc.history;
|
4816 |
+
hist.undone.length = 0;
|
4817 |
+
var time = +new Date, cur = lst(hist.done);
|
4818 |
+
|
4819 |
if (cur &&
|
4820 |
+
(hist.lastOp == opId ||
|
4821 |
+
hist.lastOrigin == change.origin && change.origin &&
|
4822 |
+
((change.origin.charAt(0) == "+" && hist.lastTime > time - 600) || change.origin.charAt(0) == "*"))) {
|
4823 |
// Merge this change into the last event
|
4824 |
+
var last = lst(cur.changes);
|
4825 |
+
if (posEq(change.from, change.to) && posEq(change.from, last.to)) {
|
4826 |
+
// Optimized case for simple insertion -- don't want to add
|
4827 |
+
// new changesets for every character typed
|
4828 |
+
last.to = changeEnd(change);
|
4829 |
} else {
|
4830 |
+
// Add new sub-event
|
4831 |
+
cur.changes.push(historyChangeFromChange(doc, change));
|
|
|
|
|
|
|
|
|
|
|
4832 |
}
|
4833 |
+
cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head;
|
4834 |
} else {
|
4835 |
// Can not be merged, start a new event.
|
4836 |
+
cur = {changes: [historyChangeFromChange(doc, change)],
|
4837 |
+
anchorBefore: doc.sel.anchor, headBefore: doc.sel.head,
|
4838 |
+
anchorAfter: selAfter.anchor, headAfter: selAfter.head};
|
4839 |
+
hist.done.push(cur);
|
4840 |
+
while (hist.done.length > hist.undoDepth)
|
4841 |
+
hist.done.shift();
|
4842 |
+
if (hist.dirtyCounter < 0)
|
4843 |
+
// The user has made a change after undoing past the last clean state.
|
4844 |
+
// We can never get back to a clean state now until markClean() is called.
|
4845 |
+
hist.dirtyCounter = NaN;
|
4846 |
else
|
4847 |
+
hist.dirtyCounter++;
|
4848 |
+
}
|
4849 |
+
hist.lastTime = time;
|
4850 |
+
hist.lastOp = opId;
|
4851 |
+
hist.lastOrigin = change.origin;
|
4852 |
+
}
|
4853 |
+
|
4854 |
+
function removeClearedSpans(spans) {
|
4855 |
+
if (!spans) return null;
|
4856 |
+
for (var i = 0, out; i < spans.length; ++i) {
|
4857 |
+
if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); }
|
4858 |
+
else if (out) out.push(spans[i]);
|
4859 |
+
}
|
4860 |
+
return !out ? spans : out.length ? out : null;
|
4861 |
+
}
|
4862 |
+
|
4863 |
+
function getOldSpans(doc, change) {
|
4864 |
+
var found = change["spans_" + doc.id];
|
4865 |
+
if (!found) return null;
|
4866 |
+
for (var i = 0, nw = []; i < change.text.length; ++i)
|
4867 |
+
nw.push(removeClearedSpans(found[i]));
|
4868 |
+
return nw;
|
4869 |
+
}
|
4870 |
+
|
4871 |
+
// Used both to provide a JSON-safe object in .getHistory, and, when
|
4872 |
+
// detaching a document, to split the history in two
|
4873 |
+
function copyHistoryArray(events, newGroup) {
|
4874 |
+
for (var i = 0, copy = []; i < events.length; ++i) {
|
4875 |
+
var event = events[i], changes = event.changes, newChanges = [];
|
4876 |
+
copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore,
|
4877 |
+
anchorAfter: event.anchorAfter, headAfter: event.headAfter});
|
4878 |
+
for (var j = 0; j < changes.length; ++j) {
|
4879 |
+
var change = changes[j], m;
|
4880 |
+
newChanges.push({from: change.from, to: change.to, text: change.text});
|
4881 |
+
if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) {
|
4882 |
+
if (indexOf(newGroup, Number(m[1])) > -1) {
|
4883 |
+
lst(newChanges)[prop] = change[prop];
|
4884 |
+
delete change[prop];
|
4885 |
+
}
|
4886 |
+
}
|
4887 |
+
}
|
4888 |
+
}
|
4889 |
+
return copy;
|
4890 |
+
}
|
4891 |
+
|
4892 |
+
// Rebasing/resetting history to deal with externally-sourced changes
|
4893 |
+
|
4894 |
+
function rebaseHistSel(pos, from, to, diff) {
|
4895 |
+
if (to < pos.line) {
|
4896 |
+
pos.line += diff;
|
4897 |
+
} else if (from < pos.line) {
|
4898 |
+
pos.line = from;
|
4899 |
+
pos.ch = 0;
|
4900 |
+
}
|
4901 |
+
}
|
4902 |
+
|
4903 |
+
// Tries to rebase an array of history events given a change in the
|
4904 |
+
// document. If the change touches the same lines as the event, the
|
4905 |
+
// event, and everything 'behind' it, is discarded. If the change is
|
4906 |
+
// before the event, the event's positions are updated. Uses a
|
4907 |
+
// copy-on-write scheme for the positions, to avoid having to
|
4908 |
+
// reallocate them all on every rebase, but also avoid problems with
|
4909 |
+
// shared position objects being unsafely updated.
|
4910 |
+
function rebaseHistArray(array, from, to, diff) {
|
4911 |
+
for (var i = 0; i < array.length; ++i) {
|
4912 |
+
var sub = array[i], ok = true;
|
4913 |
+
for (var j = 0; j < sub.changes.length; ++j) {
|
4914 |
+
var cur = sub.changes[j];
|
4915 |
+
if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); }
|
4916 |
+
if (to < cur.from.line) {
|
4917 |
+
cur.from.line += diff;
|
4918 |
+
cur.to.line += diff;
|
4919 |
+
} else if (from <= cur.to.line) {
|
4920 |
+
ok = false;
|
4921 |
+
break;
|
4922 |
+
}
|
4923 |
+
}
|
4924 |
+
if (!sub.copied) {
|
4925 |
+
sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore);
|
4926 |
+
sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter);
|
4927 |
+
sub.copied = true;
|
4928 |
+
}
|
4929 |
+
if (!ok) {
|
4930 |
+
array.splice(0, i + 1);
|
4931 |
+
i = 0;
|
4932 |
+
} else {
|
4933 |
+
rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore);
|
4934 |
+
rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter);
|
4935 |
+
}
|
4936 |
}
|
4937 |
+
}
|
4938 |
+
|
4939 |
+
function rebaseHist(hist, change) {
|
4940 |
+
var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
|
4941 |
+
rebaseHistArray(hist.done, from, to, diff);
|
4942 |
+
rebaseHistArray(hist.undone, from, to, diff);
|
4943 |
}
|
4944 |
|
4945 |
// EVENT OPERATORS
|
4976 |
return b;
|
4977 |
}
|
4978 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4979 |
// EVENT HANDLING
|
4980 |
|
4981 |
function on(emitter, type, f) {
|
5010 |
for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
|
5011 |
}
|
5012 |
|
5013 |
+
var delayedCallbacks, delayedCallbackDepth = 0;
|
5014 |
+
function signalLater(emitter, type /*, values...*/) {
|
5015 |
var arr = emitter._handlers && emitter._handlers[type];
|
5016 |
if (!arr) return;
|
5017 |
+
var args = Array.prototype.slice.call(arguments, 2);
|
5018 |
+
if (!delayedCallbacks) {
|
5019 |
+
++delayedCallbackDepth;
|
5020 |
+
delayedCallbacks = [];
|
5021 |
+
setTimeout(fireDelayed, 0);
|
5022 |
+
}
|
5023 |
function bnd(f) {return function(){f.apply(null, args);};};
|
5024 |
for (var i = 0; i < arr.length; ++i)
|
5025 |
+
delayedCallbacks.push(bnd(arr[i]));
|
5026 |
+
}
|
5027 |
+
|
5028 |
+
function fireDelayed() {
|
5029 |
+
--delayedCallbackDepth;
|
5030 |
+
var delayed = delayedCallbacks;
|
5031 |
+
delayedCallbacks = null;
|
5032 |
+
for (var i = 0; i < delayed.length; ++i) delayed[i]();
|
5033 |
}
|
5034 |
|
5035 |
function hasHandler(emitter, type) {
|
5053 |
|
5054 |
// Counts the column offset in a string, taking tabs into account.
|
5055 |
// Used mostly to find indentation.
|
5056 |
+
function countColumn(string, end, tabSize, startIndex, startValue) {
|
5057 |
if (end == null) {
|
5058 |
end = string.search(/[^\s\u00a0]/);
|
5059 |
if (end == -1) end = string.length;
|
5060 |
}
|
5061 |
+
for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) {
|
5062 |
if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
|
5063 |
else ++n;
|
5064 |
}
|
5089 |
return -1;
|
5090 |
}
|
5091 |
|
5092 |
+
function createObj(base, props) {
|
5093 |
+
function Obj() {}
|
5094 |
+
Obj.prototype = base;
|
5095 |
+
var inst = new Obj();
|
5096 |
+
if (props) copyObj(props, inst);
|
5097 |
+
return inst;
|
5098 |
+
}
|
5099 |
+
|
5100 |
+
function copyObj(obj, target) {
|
5101 |
+
if (!target) target = {};
|
5102 |
+
for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
|
5103 |
+
return target;
|
5104 |
+
}
|
5105 |
+
|
5106 |
function emptyArray(size) {
|
5107 |
for (var a = [], i = 0; i < size; ++i) a.push(undefined);
|
5108 |
return a;
|
5120 |
}
|
5121 |
|
5122 |
function isEmpty(obj) {
|
5123 |
+
for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false;
|
5124 |
+
return true;
|
|
|
5125 |
}
|
5126 |
|
5127 |
+
var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/;
|
5128 |
|
5129 |
// DOM UTILITIES
|
5130 |
|
5138 |
}
|
5139 |
|
5140 |
function removeChildren(e) {
|
5141 |
+
for (var count = e.childNodes.length; count > 0; --count)
|
5142 |
+
e.removeChild(e.firstChild);
|
5143 |
return e;
|
5144 |
}
|
5145 |
|
5154 |
} else e.textContent = str;
|
5155 |
}
|
5156 |
|
5157 |
+
function getRect(node) {
|
5158 |
+
return node.getBoundingClientRect();
|
5159 |
+
}
|
5160 |
+
CodeMirror.replaceGetRect = function(f) { getRect = f; };
|
5161 |
+
|
5162 |
// FEATURE DETECTION
|
5163 |
|
5164 |
// Detect drag-and-drop
|
5179 |
// various browsers.
|
5180 |
var spanAffectsWrapping = /^$/; // Won't match any two-character string
|
5181 |
if (gecko) spanAffectsWrapping = /$'/;
|
5182 |
+
else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
|
5183 |
+
else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.]|\?[\w~`@#$%\^&*(_=+{[|><]/;
|
5184 |
|
5185 |
var knownScrollbarWidth;
|
5186 |
function scrollbarWidth(measure) {
|
5267 |
if (!order) return f(from, to, "ltr");
|
5268 |
for (var i = 0; i < order.length; ++i) {
|
5269 |
var part = order[i];
|
5270 |
+
if (part.from < to && part.to > from || from == to && part.to == from)
|
5271 |
f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr");
|
5272 |
}
|
5273 |
}
|
5283 |
}
|
5284 |
|
5285 |
function lineStart(cm, lineN) {
|
5286 |
+
var line = getLine(cm.doc, lineN);
|
5287 |
+
var visual = visualLine(cm.doc, line);
|
5288 |
if (visual != line) lineN = lineNo(visual);
|
5289 |
var order = getOrder(visual);
|
5290 |
var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual);
|
5291 |
+
return Pos(lineN, ch);
|
5292 |
}
|
5293 |
+
function lineEnd(cm, lineN) {
|
5294 |
var merged, line;
|
5295 |
+
while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN)))
|
5296 |
+
lineN = merged.find().to.line;
|
5297 |
var order = getOrder(line);
|
5298 |
var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line);
|
5299 |
+
return Pos(lineN, ch);
|
5300 |
}
|
5301 |
|
5302 |
// This is somewhat involved. It is needed in order to move
|
5385 |
|
5386 |
var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
|
5387 |
var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
|
5388 |
+
// Browsers seem to always treat the boundaries of block elements as being L.
|
5389 |
+
var outerType = "L";
|
5390 |
|
5391 |
+
return function(str) {
|
5392 |
if (!bidiRE.test(str)) return false;
|
5393 |
+
var len = str.length, types = [];
|
5394 |
+
for (var i = 0, type; i < len; ++i)
|
5395 |
types.push(type = charType(str.charCodeAt(i)));
|
|
|
|
|
|
|
|
|
|
|
|
|
5396 |
|
5397 |
// W1. Examine each non-spacing mark (NSM) in the level run, and
|
5398 |
// change the type of the NSM to the type of the previous
|
5399 |
// character. If the NSM is at the start of the level run, it will
|
5400 |
// get the type of sor.
|
5401 |
+
for (var i = 0, prev = outerType; i < len; ++i) {
|
5402 |
var type = types[i];
|
5403 |
if (type == "m") types[i] = prev;
|
5404 |
else prev = type;
|
5409 |
// AL is found, change the type of the European number to Arabic
|
5410 |
// number.
|
5411 |
// W3. Change all ALs to R.
|
5412 |
+
for (var i = 0, cur = outerType; i < len; ++i) {
|
5413 |
var type = types[i];
|
5414 |
if (type == "1" && cur == "r") types[i] = "n";
|
5415 |
else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; }
|
5444 |
// W7. Search backwards from each instance of a European number
|
5445 |
// until the first strong type (R, L, or sor) is found. If an L is
|
5446 |
// found, then change the type of the European number to L.
|
5447 |
+
for (var i = 0, cur = outerType; i < len; ++i) {
|
5448 |
var type = types[i];
|
5449 |
if (cur == "L" && type == "1") types[i] = "L";
|
5450 |
else if (isStrong.test(type)) cur = type;
|
5459 |
for (var i = 0; i < len; ++i) {
|
5460 |
if (isNeutral.test(types[i])) {
|
5461 |
for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {}
|
5462 |
+
var before = (i ? types[i-1] : outerType) == "L";
|
5463 |
+
var after = (end < len - 1 ? types[end] : outerType) == "L";
|
5464 |
var replace = before || after ? "L" : "R";
|
5465 |
for (var j = i; j < end; ++j) types[j] = replace;
|
5466 |
i = end - 1;
|
5510 |
|
5511 |
// THE END
|
5512 |
|
5513 |
+
CodeMirror.version = "3.11";
|
5514 |
|
5515 |
return CodeMirror;
|
5516 |
})();
|
assets/{mode → codemirror/mode}/clike.js
RENAMED
@@ -1,6 +1,7 @@
|
|
1 |
CodeMirror.defineMode("clike", function(config, parserConfig) {
|
2 |
var indentUnit = config.indentUnit,
|
3 |
statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
|
|
|
4 |
keywords = parserConfig.keywords || {},
|
5 |
builtin = parserConfig.builtin || {},
|
6 |
blockKeywords = parserConfig.blockKeywords || {},
|
@@ -149,6 +150,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
|
|
149 |
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
|
150 |
var closing = firstChar == ctx.type;
|
151 |
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
|
|
|
152 |
else if (ctx.align) return ctx.column + (closing ? 0 : 1);
|
153 |
else return ctx.indented + (closing ? 0 : indentUnit);
|
154 |
},
|
1 |
CodeMirror.defineMode("clike", function(config, parserConfig) {
|
2 |
var indentUnit = config.indentUnit,
|
3 |
statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
|
4 |
+
dontAlignCalls = parserConfig.dontAlignCalls,
|
5 |
keywords = parserConfig.keywords || {},
|
6 |
builtin = parserConfig.builtin || {},
|
7 |
blockKeywords = parserConfig.blockKeywords || {},
|
150 |
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
|
151 |
var closing = firstChar == ctx.type;
|
152 |
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
|
153 |
+
else if (dontAlignCalls && ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
|
154 |
else if (ctx.align) return ctx.column + (closing ? 0 : 1);
|
155 |
else return ctx.indented + (closing ? 0 : indentUnit);
|
156 |
},
|
assets/{mode → codemirror/mode}/php.js
RENAMED
@@ -121,7 +121,7 @@
|
|
121 |
|
122 |
innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
|
123 |
};
|
124 |
-
}, "htmlmixed");
|
125 |
|
126 |
CodeMirror.defineMIME("application/x-httpd-php", "php");
|
127 |
CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
|
121 |
|
122 |
innerMode: function(state) { return {state: state.curState, mode: state.curMode}; }
|
123 |
};
|
124 |
+
}, "htmlmixed", "clike");
|
125 |
|
126 |
CodeMirror.defineMIME("application/x-httpd-php", "php");
|
127 |
CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true});
|
assets/icon.css
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
#icon-snippets.icon32 {
|
2 |
-
background: url('../images/icon32.png') no-repeat scroll transparent;
|
3 |
-
}
|
|
|
|
|
|
assets/menu-icon.mp6.css
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Use our awesome new SVG icon on
|
3 |
+
* MP6 admin pages
|
4 |
+
*
|
5 |
+
* @package Code Snippets
|
6 |
+
* @subpackage Assets
|
7 |
+
*/
|
8 |
+
|
9 |
+
#toplevel_page_snippets .wp-menu-image {
|
10 |
+
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cgo8c3ZnIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjQ0cHQiIHdpZHRoPSI0OHB0IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA0OCA0NCI+CgoJPHBhdGggY2xhc3M9InNuaXBwZXRzLWljb24tc3ZnIiBkPSJNMzIuNzIsMS44MWMzLjE3LTIuMjcsOS43MS0xLjE2LDEwLjMyLDMuMTMtMi44LDMuNzItNi40OSw2LjY1LTkuNjQsMTAuMDYsNC44MiwwLjUzLDEwLjE2LTEuMjIsMTQuNiwxLjE2djEuNTVjLTAuMzcsMy4xMS0yLjM1LDYuMTQtNS40OSw2Ljk3LTYuNzksMS4yOS0xNC4wNC0xLjAyLTIwLjYyLDEuMjUsMi42NiwxLjY3LDUuNTYsMy43NCw2LjA3LDcuMSwxLjI0LDUuMTItMi45MSwxMC40My04LjA4LDEwLjgtNS4yNywwLjUtMTAuMzctNC40NS05LjY2LTkuNzksMC4zNi00LjY2LDQuNzMtNy4yMiw3LjQ2LTEwLjQ5LDEuMTUtMC45OCwxLjAyLTMuMTYtMC43My0zLjM3LTMuNzgtMC41Ny03Ljc1LDAuNDMtMTEuNDItMC44OS0zLjA2LTEuMi00Ljk5LTQuMTktNS41My03LjM1di0yLjE1YzAuODMtNS44Nyw3Ljc4LTkuNzQsMTMuMDgtNi43OSw0LjU5LDIuMTcsNS43Nyw3LjgzLDQuMjEsMTIuMzIsMC41OC0wLjIxLDEuNzUtMC42MSwyLjM0LTAuODIsNC41Ny00LDguMzgtOC44NCwxMy4wOS0xMi42OXogTTcuMzMsNy40YzIuNDgtMS4yNyw1Ljc5LDAuODEsNS42OSwzLjYxLDAuMTMsMy4wNi0zLjg4LDUuMTctNi4yOCwzLjIzLTIuNTgtMS40OS0yLjE1LTUuNzcsMC41OS02Ljg0eiBNMTcuMzYsMzEuMzhjMi44MS0xLjc4LDYuNzQsMS40NSw1LjY2LDQuNTYtMC42OCwyLjgzLTQuNjgsMy45LTYuNjUsMS43NC0xLjkzLTEuNzctMS40LTUuMjEsMC45OS02LjN6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiM4ODg4ODgiLz4KCjwvc3ZnPg==) !important;
|
11 |
+
background-position: center center !important;
|
12 |
+
background-size: 20px 20px !important;
|
13 |
+
background-repeat: no-repeat;
|
14 |
+
}
|
15 |
+
|
16 |
+
#toplevel_page_snippets.wp-has-current-submenu .wp-menu-image,
|
17 |
+
#toplevel_page_snippets.wp-has-current-submenu:hover .wp-menu-image {
|
18 |
+
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cgo8c3ZnIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjQ0cHQiIHdpZHRoPSI0OHB0IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA0OCA0NCI+CgoJPHBhdGggY2xhc3M9InNuaXBwZXRzLWljb24tc3ZnIiBkPSJNMzIuNzIsMS44MWMzLjE3LTIuMjcsOS43MS0xLjE2LDEwLjMyLDMuMTMtMi44LDMuNzItNi40OSw2LjY1LTkuNjQsMTAuMDYsNC44MiwwLjUzLDEwLjE2LTEuMjIsMTQuNiwxLjE2djEuNTVjLTAuMzcsMy4xMS0yLjM1LDYuMTQtNS40OSw2Ljk3LTYuNzksMS4yOS0xNC4wNC0xLjAyLTIwLjYyLDEuMjUsMi42NiwxLjY3LDUuNTYsMy43NCw2LjA3LDcuMSwxLjI0LDUuMTItMi45MSwxMC40My04LjA4LDEwLjgtNS4yNywwLjUtMTAuMzctNC40NS05LjY2LTkuNzksMC4zNi00LjY2LDQuNzMtNy4yMiw3LjQ2LTEwLjQ5LDEuMTUtMC45OCwxLjAyLTMuMTYtMC43My0zLjM3LTMuNzgtMC41Ny03Ljc1LDAuNDMtMTEuNDItMC44OS0zLjA2LTEuMi00Ljk5LTQuMTktNS41My03LjM1di0yLjE1YzAuODMtNS44Nyw3Ljc4LTkuNzQsMTMuMDgtNi43OSw0LjU5LDIuMTcsNS43Nyw3LjgzLDQuMjEsMTIuMzIsMC41OC0wLjIxLDEuNzUtMC42MSwyLjM0LTAuODIsNC41Ny00LDguMzgtOC44NCwxMy4wOS0xMi42OXogTTcuMzMsNy40YzIuNDgtMS4yNyw1Ljc5LDAuODEsNS42OSwzLjYxLDAuMTMsMy4wNi0zLjg4LDUuMTctNi4yOCwzLjIzLTIuNTgtMS40OS0yLjE1LTUuNzcsMC41OS02Ljg0eiBNMTcuMzYsMzEuMzhjMi44MS0xLjc4LDYuNzQsMS40NSw1LjY2LDQuNTYtMC42OCwyLjgzLTQuNjgsMy45LTYuNjUsMS43NC0xLjkzLTEuNzctMS40LTUuMjEsMC45OS02LjN6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiNmZmYiLz4KCjwvc3ZnPg==) !important;
|
19 |
+
}
|
20 |
+
|
21 |
+
#toplevel_page_snippets:hover .wp-menu-image {
|
22 |
+
background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+Cgo8c3ZnIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjQ0cHQiIHdpZHRoPSI0OHB0IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCA0OCA0NCI+CgoJPHBhdGggY2xhc3M9InNuaXBwZXRzLWljb24tc3ZnIiBkPSJNMzIuNzIsMS44MWMzLjE3LTIuMjcsOS43MS0xLjE2LDEwLjMyLDMuMTMtMi44LDMuNzItNi40OSw2LjY1LTkuNjQsMTAuMDYsNC44MiwwLjUzLDEwLjE2LTEuMjIsMTQuNiwxLjE2djEuNTVjLTAuMzcsMy4xMS0yLjM1LDYuMTQtNS40OSw2Ljk3LTYuNzksMS4yOS0xNC4wNC0xLjAyLTIwLjYyLDEuMjUsMi42NiwxLjY3LDUuNTYsMy43NCw2LjA3LDcuMSwxLjI0LDUuMTItMi45MSwxMC40My04LjA4LDEwLjgtNS4yNywwLjUtMTAuMzctNC40NS05LjY2LTkuNzksMC4zNi00LjY2LDQuNzMtNy4yMiw3LjQ2LTEwLjQ5LDEuMTUtMC45OCwxLjAyLTMuMTYtMC43My0zLjM3LTMuNzgtMC41Ny03Ljc1LDAuNDMtMTEuNDItMC44OS0zLjA2LTEuMi00Ljk5LTQuMTktNS41My03LjM1di0yLjE1YzAuODMtNS44Nyw3Ljc4LTkuNzQsMTMuMDgtNi43OSw0LjU5LDIuMTcsNS43Nyw3LjgzLDQuMjEsMTIuMzIsMC41OC0wLjIxLDEuNzUtMC42MSwyLjM0LTAuODIsNC41Ny00LDguMzgtOC44NCwxMy4wOS0xMi42OXogTTcuMzMsNy40YzIuNDgtMS4yNyw1Ljc5LDAuODEsNS42OSwzLjYxLDAuMTMsMy4wNi0zLjg4LDUuMTctNi4yOCwzLjIzLTIuNTgtMS40OS0yLjE1LTUuNzcsMC41OS02Ljg0eiBNMTcuMzYsMzEuMzhjMi44MS0xLjc4LDYuNzQsMS40NSw1LjY2LDQuNTYtMC42OCwyLjgzLTQuNjgsMy45LTYuNjUsMS43NC0xLjkzLTEuNzctMS40LTUuMjEsMC45OS02LjN6IiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9IiMyZWEyY2MiLz4KCjwvc3ZnPg==) !important;
|
23 |
+
}
|
assets/mode/css.js
DELETED
@@ -1,465 +0,0 @@
|
|
1 |
-
CodeMirror.defineMode("css", function(config) {
|
2 |
-
var indentUnit = config.indentUnit, type;
|
3 |
-
|
4 |
-
var atMediaTypes = keySet([
|
5 |
-
"all", "aural", "braille", "handheld", "print", "projection", "screen",
|
6 |
-
"tty", "tv", "embossed"
|
7 |
-
]);
|
8 |
-
|
9 |
-
var atMediaFeatures = keySet([
|
10 |
-
"width", "min-width", "max-width", "height", "min-height", "max-height",
|
11 |
-
"device-width", "min-device-width", "max-device-width", "device-height",
|
12 |
-
"min-device-height", "max-device-height", "aspect-ratio",
|
13 |
-
"min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
|
14 |
-
"min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
|
15 |
-
"max-color", "color-index", "min-color-index", "max-color-index",
|
16 |
-
"monochrome", "min-monochrome", "max-monochrome", "resolution",
|
17 |
-
"min-resolution", "max-resolution", "scan", "grid"
|
18 |
-
]);
|
19 |
-
|
20 |
-
var propertyKeywords = keySet([
|
21 |
-
"align-content", "align-items", "align-self", "alignment-adjust",
|
22 |
-
"alignment-baseline", "anchor-point", "animation", "animation-delay",
|
23 |
-
"animation-direction", "animation-duration", "animation-iteration-count",
|
24 |
-
"animation-name", "animation-play-state", "animation-timing-function",
|
25 |
-
"appearance", "azimuth", "backface-visibility", "background",
|
26 |
-
"background-attachment", "background-clip", "background-color",
|
27 |
-
"background-image", "background-origin", "background-position",
|
28 |
-
"background-repeat", "background-size", "baseline-shift", "binding",
|
29 |
-
"bleed", "bookmark-label", "bookmark-level", "bookmark-state",
|
30 |
-
"bookmark-target", "border", "border-bottom", "border-bottom-color",
|
31 |
-
"border-bottom-left-radius", "border-bottom-right-radius",
|
32 |
-
"border-bottom-style", "border-bottom-width", "border-collapse",
|
33 |
-
"border-color", "border-image", "border-image-outset",
|
34 |
-
"border-image-repeat", "border-image-slice", "border-image-source",
|
35 |
-
"border-image-width", "border-left", "border-left-color",
|
36 |
-
"border-left-style", "border-left-width", "border-radius", "border-right",
|
37 |
-
"border-right-color", "border-right-style", "border-right-width",
|
38 |
-
"border-spacing", "border-style", "border-top", "border-top-color",
|
39 |
-
"border-top-left-radius", "border-top-right-radius", "border-top-style",
|
40 |
-
"border-top-width", "border-width", "bottom", "box-decoration-break",
|
41 |
-
"box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
|
42 |
-
"caption-side", "clear", "clip", "color", "color-profile", "column-count",
|
43 |
-
"column-fill", "column-gap", "column-rule", "column-rule-color",
|
44 |
-
"column-rule-style", "column-rule-width", "column-span", "column-width",
|
45 |
-
"columns", "content", "counter-increment", "counter-reset", "crop", "cue",
|
46 |
-
"cue-after", "cue-before", "cursor", "direction", "display",
|
47 |
-
"dominant-baseline", "drop-initial-after-adjust",
|
48 |
-
"drop-initial-after-align", "drop-initial-before-adjust",
|
49 |
-
"drop-initial-before-align", "drop-initial-size", "drop-initial-value",
|
50 |
-
"elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
|
51 |
-
"flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
|
52 |
-
"float", "float-offset", "font", "font-feature-settings", "font-family",
|
53 |
-
"font-kerning", "font-language-override", "font-size", "font-size-adjust",
|
54 |
-
"font-stretch", "font-style", "font-synthesis", "font-variant",
|
55 |
-
"font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
|
56 |
-
"font-variant-ligatures", "font-variant-numeric", "font-variant-position",
|
57 |
-
"font-weight", "grid-cell", "grid-column", "grid-column-align",
|
58 |
-
"grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow",
|
59 |
-
"grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span",
|
60 |
-
"grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens",
|
61 |
-
"icon", "image-orientation", "image-rendering", "image-resolution",
|
62 |
-
"inline-box-align", "justify-content", "left", "letter-spacing",
|
63 |
-
"line-break", "line-height", "line-stacking", "line-stacking-ruby",
|
64 |
-
"line-stacking-shift", "line-stacking-strategy", "list-style",
|
65 |
-
"list-style-image", "list-style-position", "list-style-type", "margin",
|
66 |
-
"margin-bottom", "margin-left", "margin-right", "margin-top",
|
67 |
-
"marker-offset", "marks", "marquee-direction", "marquee-loop",
|
68 |
-
"marquee-play-count", "marquee-speed", "marquee-style", "max-height",
|
69 |
-
"max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
|
70 |
-
"nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline",
|
71 |
-
"outline-color", "outline-offset", "outline-style", "outline-width",
|
72 |
-
"overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
|
73 |
-
"padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
|
74 |
-
"page", "page-break-after", "page-break-before", "page-break-inside",
|
75 |
-
"page-policy", "pause", "pause-after", "pause-before", "perspective",
|
76 |
-
"perspective-origin", "pitch", "pitch-range", "play-during", "position",
|
77 |
-
"presentation-level", "punctuation-trim", "quotes", "rendering-intent",
|
78 |
-
"resize", "rest", "rest-after", "rest-before", "richness", "right",
|
79 |
-
"rotation", "rotation-point", "ruby-align", "ruby-overhang",
|
80 |
-
"ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header",
|
81 |
-
"speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
|
82 |
-
"tab-size", "table-layout", "target", "target-name", "target-new",
|
83 |
-
"target-position", "text-align", "text-align-last", "text-decoration",
|
84 |
-
"text-decoration-color", "text-decoration-line", "text-decoration-skip",
|
85 |
-
"text-decoration-style", "text-emphasis", "text-emphasis-color",
|
86 |
-
"text-emphasis-position", "text-emphasis-style", "text-height",
|
87 |
-
"text-indent", "text-justify", "text-outline", "text-shadow",
|
88 |
-
"text-space-collapse", "text-transform", "text-underline-position",
|
89 |
-
"text-wrap", "top", "transform", "transform-origin", "transform-style",
|
90 |
-
"transition", "transition-delay", "transition-duration",
|
91 |
-
"transition-property", "transition-timing-function", "unicode-bidi",
|
92 |
-
"vertical-align", "visibility", "voice-balance", "voice-duration",
|
93 |
-
"voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
|
94 |
-
"voice-volume", "volume", "white-space", "widows", "width", "word-break",
|
95 |
-
"word-spacing", "word-wrap", "z-index"
|
96 |
-
]);
|
97 |
-
|
98 |
-
var colorKeywords = keySet([
|
99 |
-
"black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia",
|
100 |
-
"green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua"
|
101 |
-
]);
|
102 |
-
|
103 |
-
var valueKeywords = keySet([
|
104 |
-
"above", "absolute", "activeborder", "activecaption", "afar",
|
105 |
-
"after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
|
106 |
-
"always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
|
107 |
-
"arabic-indic", "armenian", "asterisks", "auto", "avoid", "background",
|
108 |
-
"backwards", "baseline", "below", "bidi-override", "binary", "bengali",
|
109 |
-
"blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
|
110 |
-
"both", "bottom", "break-all", "break-word", "button", "button-bevel",
|
111 |
-
"buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
|
112 |
-
"capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
|
113 |
-
"cell", "center", "checkbox", "circle", "cjk-earthly-branch",
|
114 |
-
"cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
|
115 |
-
"col-resize", "collapse", "compact", "condensed", "contain", "content",
|
116 |
-
"content-box", "context-menu", "continuous", "copy", "cover", "crop",
|
117 |
-
"cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
|
118 |
-
"decimal-leading-zero", "default", "default-button", "destination-atop",
|
119 |
-
"destination-in", "destination-out", "destination-over", "devanagari",
|
120 |
-
"disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
|
121 |
-
"double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
|
122 |
-
"element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
|
123 |
-
"ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
|
124 |
-
"ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
|
125 |
-
"ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
|
126 |
-
"ethiopic-halehame-gez", "ethiopic-halehame-om-et",
|
127 |
-
"ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
|
128 |
-
"ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
|
129 |
-
"ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
|
130 |
-
"extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
|
131 |
-
"forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
|
132 |
-
"gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
|
133 |
-
"help", "hidden", "hide", "higher", "highlight", "highlighttext",
|
134 |
-
"hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
|
135 |
-
"inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
|
136 |
-
"infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
|
137 |
-
"inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
|
138 |
-
"italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer",
|
139 |
-
"landscape", "lao", "large", "larger", "left", "level", "lighter",
|
140 |
-
"line-through", "linear", "lines", "list-item", "listbox", "listitem",
|
141 |
-
"local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
|
142 |
-
"lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
|
143 |
-
"lower-roman", "lowercase", "ltr", "malayalam", "match",
|
144 |
-
"media-controls-background", "media-current-time-display",
|
145 |
-
"media-fullscreen-button", "media-mute-button", "media-play-button",
|
146 |
-
"media-return-to-realtime-button", "media-rewind-button",
|
147 |
-
"media-seek-back-button", "media-seek-forward-button", "media-slider",
|
148 |
-
"media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
|
149 |
-
"media-volume-slider-container", "media-volume-sliderthumb", "medium",
|
150 |
-
"menu", "menulist", "menulist-button", "menulist-text",
|
151 |
-
"menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
|
152 |
-
"mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
|
153 |
-
"narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
|
154 |
-
"no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
|
155 |
-
"ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
|
156 |
-
"optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
|
157 |
-
"outside", "overlay", "overline", "padding", "padding-box", "painted",
|
158 |
-
"paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait",
|
159 |
-
"pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
|
160 |
-
"radio", "read-only", "read-write", "read-write-plaintext-only", "relative",
|
161 |
-
"repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
|
162 |
-
"ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
|
163 |
-
"s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
|
164 |
-
"searchfield-cancel-button", "searchfield-decoration",
|
165 |
-
"searchfield-results-button", "searchfield-results-decoration",
|
166 |
-
"semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
|
167 |
-
"single", "skip-white-space", "slide", "slider-horizontal",
|
168 |
-
"slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
|
169 |
-
"small", "small-caps", "small-caption", "smaller", "solid", "somali",
|
170 |
-
"source-atop", "source-in", "source-out", "source-over", "space", "square",
|
171 |
-
"square-button", "start", "static", "status-bar", "stretch", "stroke",
|
172 |
-
"sub", "subpixel-antialiased", "super", "sw-resize", "table",
|
173 |
-
"table-caption", "table-cell", "table-column", "table-column-group",
|
174 |
-
"table-footer-group", "table-header-group", "table-row", "table-row-group",
|
175 |
-
"telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
|
176 |
-
"thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
|
177 |
-
"threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
|
178 |
-
"tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
|
179 |
-
"transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
|
180 |
-
"upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
|
181 |
-
"upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
|
182 |
-
"vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
|
183 |
-
"visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider",
|
184 |
-
"window", "windowframe", "windowtext", "x-large", "x-small", "xor",
|
185 |
-
"xx-large", "xx-small", "yellow"
|
186 |
-
]);
|
187 |
-
|
188 |
-
function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) keys[array[i]] = true; return keys; }
|
189 |
-
function ret(style, tp) {type = tp; return style;}
|
190 |
-
|
191 |
-
function tokenBase(stream, state) {
|
192 |
-
var ch = stream.next();
|
193 |
-
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());}
|
194 |
-
else if (ch == "/" && stream.eat("*")) {
|
195 |
-
state.tokenize = tokenCComment;
|
196 |
-
return tokenCComment(stream, state);
|
197 |
-
}
|
198 |
-
else if (ch == "<" && stream.eat("!")) {
|
199 |
-
state.tokenize = tokenSGMLComment;
|
200 |
-
return tokenSGMLComment(stream, state);
|
201 |
-
}
|
202 |
-
else if (ch == "=") ret(null, "compare");
|
203 |
-
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
|
204 |
-
else if (ch == "\"" || ch == "'") {
|
205 |
-
state.tokenize = tokenString(ch);
|
206 |
-
return state.tokenize(stream, state);
|
207 |
-
}
|
208 |
-
else if (ch == "#") {
|
209 |
-
stream.eatWhile(/[\w\\\-]/);
|
210 |
-
return ret("atom", "hash");
|
211 |
-
}
|
212 |
-
else if (ch == "!") {
|
213 |
-
stream.match(/^\s*\w*/);
|
214 |
-
return ret("keyword", "important");
|
215 |
-
}
|
216 |
-
else if (/\d/.test(ch)) {
|
217 |
-
stream.eatWhile(/[\w.%]/);
|
218 |
-
return ret("number", "unit");
|
219 |
-
}
|
220 |
-
else if (ch === "-") {
|
221 |
-
if (/\d/.test(stream.peek())) {
|
222 |
-
stream.eatWhile(/[\w.%]/);
|
223 |
-
return ret("number", "unit");
|
224 |
-
} else if (stream.match(/^[^-]+-/)) {
|
225 |
-
return ret("meta", type);
|
226 |
-
}
|
227 |
-
}
|
228 |
-
else if (/[,+>*\/]/.test(ch)) {
|
229 |
-
return ret(null, "select-op");
|
230 |
-
}
|
231 |
-
else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
|
232 |
-
return ret("qualifier", type);
|
233 |
-
}
|
234 |
-
else if (ch == ":") {
|
235 |
-
return ret("operator", ch);
|
236 |
-
}
|
237 |
-
else if (/[;{}\[\]\(\)]/.test(ch)) {
|
238 |
-
return ret(null, ch);
|
239 |
-
}
|
240 |
-
else if (ch == "u" && stream.match("rl(")) {
|
241 |
-
stream.backUp(1);
|
242 |
-
state.tokenize = tokenParenthesized;
|
243 |
-
return ret("property", "variable");
|
244 |
-
}
|
245 |
-
else {
|
246 |
-
stream.eatWhile(/[\w\\\-]/);
|
247 |
-
return ret("property", "variable");
|
248 |
-
}
|
249 |
-
}
|
250 |
-
|
251 |
-
function tokenCComment(stream, state) {
|
252 |
-
var maybeEnd = false, ch;
|
253 |
-
while ((ch = stream.next()) != null) {
|
254 |
-
if (maybeEnd && ch == "/") {
|
255 |
-
state.tokenize = tokenBase;
|
256 |
-
break;
|
257 |
-
}
|
258 |
-
maybeEnd = (ch == "*");
|
259 |
-
}
|
260 |
-
return ret("comment", "comment");
|
261 |
-
}
|
262 |
-
|
263 |
-
function tokenSGMLComment(stream, state) {
|
264 |
-
var dashes = 0, ch;
|
265 |
-
while ((ch = stream.next()) != null) {
|
266 |
-
if (dashes >= 2 && ch == ">") {
|
267 |
-
state.tokenize = tokenBase;
|
268 |
-
break;
|
269 |
-
}
|
270 |
-
dashes = (ch == "-") ? dashes + 1 : 0;
|
271 |
-
}
|
272 |
-
return ret("comment", "comment");
|
273 |
-
}
|
274 |
-
|
275 |
-
function tokenString(quote, nonInclusive) {
|
276 |
-
return function(stream, state) {
|
277 |
-
var escaped = false, ch;
|
278 |
-
while ((ch = stream.next()) != null) {
|
279 |
-
if (ch == quote && !escaped)
|
280 |
-
break;
|
281 |
-
escaped = !escaped && ch == "\\";
|
282 |
-
}
|
283 |
-
if (!escaped) {
|
284 |
-
if (nonInclusive) stream.backUp(1);
|
285 |
-
state.tokenize = tokenBase;
|
286 |
-
}
|
287 |
-
return ret("string", "string");
|
288 |
-
};
|
289 |
-
}
|
290 |
-
|
291 |
-
function tokenParenthesized(stream, state) {
|
292 |
-
stream.next(); // Must be '('
|
293 |
-
if (!stream.match(/\s*[\"\']/, false))
|
294 |
-
state.tokenize = tokenString(")", true);
|
295 |
-
else
|
296 |
-
state.tokenize = tokenBase;
|
297 |
-
return ret(null, "(");
|
298 |
-
}
|
299 |
-
|
300 |
-
return {
|
301 |
-
startState: function(base) {
|
302 |
-
return {tokenize: tokenBase,
|
303 |
-
baseIndent: base || 0,
|
304 |
-
stack: []};
|
305 |
-
},
|
306 |
-
|
307 |
-
token: function(stream, state) {
|
308 |
-
|
309 |
-
// Use these terms when applicable (see http://www.xanthir.com/blog/b4E50)
|
310 |
-
//
|
311 |
-
// rule** or **ruleset:
|
312 |
-
// A selector + braces combo, or an at-rule.
|
313 |
-
//
|
314 |
-
// declaration block:
|
315 |
-
// A sequence of declarations.
|
316 |
-
//
|
317 |
-
// declaration:
|
318 |
-
// A property + colon + value combo.
|
319 |
-
//
|
320 |
-
// property value:
|
321 |
-
// The entire value of a property.
|
322 |
-
//
|
323 |
-
// component value:
|
324 |
-
// A single piece of a property value. Like the 5px in
|
325 |
-
// text-shadow: 0 0 5px blue;. Can also refer to things that are
|
326 |
-
// multiple terms, like the 1-4 terms that make up the background-size
|
327 |
-
// portion of the background shorthand.
|
328 |
-
//
|
329 |
-
// term:
|
330 |
-
// The basic unit of author-facing CSS, like a single number (5),
|
331 |
-
// dimension (5px), string ("foo"), or function. Officially defined
|
332 |
-
// by the CSS 2.1 grammar (look for the 'term' production)
|
333 |
-
//
|
334 |
-
//
|
335 |
-
// simple selector:
|
336 |
-
// A single atomic selector, like a type selector, an attr selector, a
|
337 |
-
// class selector, etc.
|
338 |
-
//
|
339 |
-
// compound selector:
|
340 |
-
// One or more simple selectors without a combinator. div.example is
|
341 |
-
// compound, div > .example is not.
|
342 |
-
//
|
343 |
-
// complex selector:
|
344 |
-
// One or more compound selectors chained with combinators.
|
345 |
-
//
|
346 |
-
// combinator:
|
347 |
-
// The parts of selectors that express relationships. There are four
|
348 |
-
// currently - the space (descendant combinator), the greater-than
|
349 |
-
// bracket (child combinator), the plus sign (next sibling combinator),
|
350 |
-
// and the tilda (following sibling combinator).
|
351 |
-
//
|
352 |
-
// sequence of selectors:
|
353 |
-
// One or more of the named type of selector chained with commas.
|
354 |
-
|
355 |
-
if (state.tokenize == tokenBase && stream.eatSpace()) return null;
|
356 |
-
var style = state.tokenize(stream, state);
|
357 |
-
|
358 |
-
// Changing style returned based on context
|
359 |
-
var context = state.stack[state.stack.length-1];
|
360 |
-
if (style == "property") {
|
361 |
-
if (context == "propertyValue"){
|
362 |
-
if (valueKeywords[stream.current()]) {
|
363 |
-
style = "string-2";
|
364 |
-
} else if (colorKeywords[stream.current()]) {
|
365 |
-
style = "keyword";
|
366 |
-
} else {
|
367 |
-
style = "variable-2";
|
368 |
-
}
|
369 |
-
} else if (context == "rule") {
|
370 |
-
if (!propertyKeywords[stream.current()]) {
|
371 |
-
style += " error";
|
372 |
-
}
|
373 |
-
} else if (!context || context == "@media{") {
|
374 |
-
style = "tag";
|
375 |
-
} else if (context == "@media") {
|
376 |
-
if (atMediaTypes[stream.current()]) {
|
377 |
-
style = "attribute"; // Known attribute
|
378 |
-
} else if (/^(only|not)$/i.test(stream.current())) {
|
379 |
-
style = "keyword";
|
380 |
-
} else if (stream.current().toLowerCase() == "and") {
|
381 |
-
style = "error"; // "and" is only allowed in @mediaType
|
382 |
-
} else if (atMediaFeatures[stream.current()]) {
|
383 |
-
style = "error"; // Known property, should be in @mediaType(
|
384 |
-
} else {
|
385 |
-
// Unknown, expecting keyword or attribute, assuming attribute
|
386 |
-
style = "attribute error";
|
387 |
-
}
|
388 |
-
} else if (context == "@mediaType") {
|
389 |
-
if (atMediaTypes[stream.current()]) {
|
390 |
-
style = "attribute";
|
391 |
-
} else if (stream.current().toLowerCase() == "and") {
|
392 |
-
style = "operator";
|
393 |
-
} else if (/^(only|not)$/i.test(stream.current())) {
|
394 |
-
style = "error"; // Only allowed in @media
|
395 |
-
} else if (atMediaFeatures[stream.current()]) {
|
396 |
-
style = "error"; // Known property, should be in parentheses
|
397 |
-
} else {
|
398 |
-
// Unknown attribute or property, but expecting property (preceded
|
399 |
-
// by "and"). Should be in parentheses
|
400 |
-
style = "error";
|
401 |
-
}
|
402 |
-
} else if (context == "@mediaType(") {
|
403 |
-
if (propertyKeywords[stream.current()]) {
|
404 |
-
// do nothing, remains "property"
|
405 |
-
} else if (atMediaTypes[stream.current()]) {
|
406 |
-
style = "error"; // Known property, should be in parentheses
|
407 |
-
} else if (stream.current().toLowerCase() == "and") {
|
408 |
-
style = "operator";
|
409 |
-
} else if (/^(only|not)$/i.test(stream.current())) {
|
410 |
-
style = "error"; // Only allowed in @media
|
411 |
-
} else {
|
412 |
-
style += " error";
|
413 |
-
}
|
414 |
-
} else {
|
415 |
-
style = "error";
|
416 |
-
}
|
417 |
-
} else if (style == "atom") {
|
418 |
-
if(!context || context == "@media{") {
|
419 |
-
style = "builtin";
|
420 |
-
} else if (context == "propertyValue") {
|
421 |
-
if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
|
422 |
-
style += " error";
|
423 |
-
}
|
424 |
-
} else {
|
425 |
-
style = "error";
|
426 |
-
}
|
427 |
-
} else if (context == "@media" && type == "{") {
|
428 |
-
style = "error";
|
429 |
-
}
|
430 |
-
|
431 |
-
// Push/pop context stack
|
432 |
-
if (type == "{") {
|
433 |
-
if (context == "@media" || context == "@mediaType") {
|
434 |
-
state.stack.pop();
|
435 |
-
state.stack[state.stack.length-1] = "@media{";
|
436 |
-
}
|
437 |
-
else state.stack.push("rule");
|
438 |
-
}
|
439 |
-
else if (type == "}") {
|
440 |
-
state.stack.pop();
|
441 |
-
if (context == "propertyValue") state.stack.pop();
|
442 |
-
}
|
443 |
-
else if (type == "@media") state.stack.push("@media");
|
444 |
-
else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
|
445 |
-
state.stack.push("@mediaType");
|
446 |
-
else if (context == "@mediaType" && stream.current() == ",") state.stack.pop();
|
447 |
-
else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType(");
|
448 |
-
else if (context == "@mediaType(" && type == ")") state.stack.pop();
|
449 |
-
else if (context == "rule" && type == ":") state.stack.push("propertyValue");
|
450 |
-
else if (context == "propertyValue" && type == ";") state.stack.pop();
|
451 |
-
return style;
|
452 |
-
},
|
453 |
-
|
454 |
-
indent: function(state, textAfter) {
|
455 |
-
var n = state.stack.length;
|
456 |
-
if (/^\}/.test(textAfter))
|
457 |
-
n -= state.stack[state.stack.length-1] == "propertyValue" ? 2 : 1;
|
458 |
-
return state.baseIndent + n * indentUnit;
|
459 |
-
},
|
460 |
-
|
461 |
-
electricChars: "}"
|
462 |
-
};
|
463 |
-
});
|
464 |
-
|
465 |
-
CodeMirror.defineMIME("text/css", "css");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/mode/htmlmixed.js
DELETED
@@ -1,84 +0,0 @@
|
|
1 |
-
CodeMirror.defineMode("htmlmixed", function(config) {
|
2 |
-
var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
|
3 |
-
var jsMode = CodeMirror.getMode(config, "javascript");
|
4 |
-
var cssMode = CodeMirror.getMode(config, "css");
|
5 |
-
|
6 |
-
function html(stream, state) {
|
7 |
-
var style = htmlMode.token(stream, state.htmlState);
|
8 |
-
if (/(?:^|\s)tag(?:\s|$)/.test(style) && stream.current() == ">" && state.htmlState.context) {
|
9 |
-
if (/^script$/i.test(state.htmlState.context.tagName)) {
|
10 |
-
state.token = javascript;
|
11 |
-
state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
|
12 |
-
}
|
13 |
-
else if (/^style$/i.test(state.htmlState.context.tagName)) {
|
14 |
-
state.token = css;
|
15 |
-
state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
|
16 |
-
}
|
17 |
-
}
|
18 |
-
return style;
|
19 |
-
}
|
20 |
-
function maybeBackup(stream, pat, style) {
|
21 |
-
var cur = stream.current();
|
22 |
-
var close = cur.search(pat), m;
|
23 |
-
if (close > -1) stream.backUp(cur.length - close);
|
24 |
-
else if (m = cur.match(/<\/?$/)) {
|
25 |
-
stream.backUp(cur.length);
|
26 |
-
if (!stream.match(pat, false)) stream.match(cur[0]);
|
27 |
-
}
|
28 |
-
return style;
|
29 |
-
}
|
30 |
-
function javascript(stream, state) {
|
31 |
-
if (stream.match(/^<\/\s*script\s*>/i, false)) {
|
32 |
-
state.token = html;
|
33 |
-
state.localState = null;
|
34 |
-
return html(stream, state);
|
35 |
-
}
|
36 |
-
return maybeBackup(stream, /<\/\s*script\s*>/,
|
37 |
-
jsMode.token(stream, state.localState));
|
38 |
-
}
|
39 |
-
function css(stream, state) {
|
40 |
-
if (stream.match(/^<\/\s*style\s*>/i, false)) {
|
41 |
-
state.token = html;
|
42 |
-
state.localState = null;
|
43 |
-
return html(stream, state);
|
44 |
-
}
|
45 |
-
return maybeBackup(stream, /<\/\s*style\s*>/,
|
46 |
-
cssMode.token(stream, state.localState));
|
47 |
-
}
|
48 |
-
|
49 |
-
return {
|
50 |
-
startState: function() {
|
51 |
-
var state = htmlMode.startState();
|
52 |
-
return {token: html, localState: null, mode: "html", htmlState: state};
|
53 |
-
},
|
54 |
-
|
55 |
-
copyState: function(state) {
|
56 |
-
if (state.localState)
|
57 |
-
var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
|
58 |
-
return {token: state.token, localState: local, mode: state.mode,
|
59 |
-
htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
|
60 |
-
},
|
61 |
-
|
62 |
-
token: function(stream, state) {
|
63 |
-
return state.token(stream, state);
|
64 |
-
},
|
65 |
-
|
66 |
-
indent: function(state, textAfter) {
|
67 |
-
if (state.token == html || /^\s*<\//.test(textAfter))
|
68 |
-
return htmlMode.indent(state.htmlState, textAfter);
|
69 |
-
else if (state.token == javascript)
|
70 |
-
return jsMode.indent(state.localState, textAfter);
|
71 |
-
else
|
72 |
-
return cssMode.indent(state.localState, textAfter);
|
73 |
-
},
|
74 |
-
|
75 |
-
electricChars: "/{}:",
|
76 |
-
|
77 |
-
innerMode: function(state) {
|
78 |
-
var mode = state.token == html ? htmlMode : state.token == javascript ? jsMode : cssMode;
|
79 |
-
return {state: state.localState || state.htmlState, mode: mode};
|
80 |
-
}
|
81 |
-
};
|
82 |
-
}, "xml", "javascript", "css");
|
83 |
-
|
84 |
-
CodeMirror.defineMIME("text/html", "htmlmixed");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/mode/javascript.js
DELETED
@@ -1,411 +0,0 @@
|
|
1 |
-
// TODO actually recognize syntax of TypeScript constructs
|
2 |
-
|
3 |
-
CodeMirror.defineMode("javascript", function(config, parserConfig) {
|
4 |
-
var indentUnit = config.indentUnit;
|
5 |
-
var jsonMode = parserConfig.json;
|
6 |
-
var isTS = parserConfig.typescript;
|
7 |
-
|
8 |
-
// Tokenizer
|
9 |
-
|
10 |
-
var keywords = function(){
|
11 |
-
function kw(type) {return {type: type, style: "keyword"};}
|
12 |
-
var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
|
13 |
-
var operator = kw("operator"), atom = {type: "atom", style: "atom"};
|
14 |
-
|
15 |
-
var jsKeywords = {
|
16 |
-
"if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
|
17 |
-
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
|
18 |
-
"var": kw("var"), "const": kw("var"), "let": kw("var"),
|
19 |
-
"function": kw("function"), "catch": kw("catch"),
|
20 |
-
"for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
|
21 |
-
"in": operator, "typeof": operator, "instanceof": operator,
|
22 |
-
"true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
|
23 |
-
};
|
24 |
-
|
25 |
-
// Extend the 'normal' keywords with the TypeScript language extensions
|
26 |
-
if (isTS) {
|
27 |
-
var type = {type: "variable", style: "variable-3"};
|
28 |
-
var tsKeywords = {
|
29 |
-
// object-like things
|
30 |
-
"interface": kw("interface"),
|
31 |
-
"class": kw("class"),
|
32 |
-
"extends": kw("extends"),
|
33 |
-
"constructor": kw("constructor"),
|
34 |
-
|
35 |
-
// scope modifiers
|
36 |
-
"public": kw("public"),
|
37 |
-
"private": kw("private"),
|
38 |
-
"protected": kw("protected"),
|
39 |
-
"static": kw("static"),
|
40 |
-
|
41 |
-
"super": kw("super"),
|
42 |
-
|
43 |
-
// types
|
44 |
-
"string": type, "number": type, "bool": type, "any": type
|
45 |
-
};
|
46 |
-
|
47 |
-
for (var attr in tsKeywords) {
|
48 |
-
jsKeywords[attr] = tsKeywords[attr];
|
49 |
-
}
|
50 |
-
}
|
51 |
-
|
52 |
-
return jsKeywords;
|
53 |
-
}();
|
54 |
-
|
55 |
-
var isOperatorChar = /[+\-*&%=<>!?|]/;
|
56 |
-
|
57 |
-
function chain(stream, state, f) {
|
58 |
-
state.tokenize = f;
|
59 |
-
return f(stream, state);
|
60 |
-
}
|
61 |
-
|
62 |
-
function nextUntilUnescaped(stream, end) {
|
63 |
-
var escaped = false, next;
|
64 |
-
while ((next = stream.next()) != null) {
|
65 |
-
if (next == end && !escaped)
|
66 |
-
return false;
|
67 |
-
escaped = !escaped && next == "\\";
|
68 |
-
}
|
69 |
-
return escaped;
|
70 |
-
}
|
71 |
-
|
72 |
-
// Used as scratch variables to communicate multiple values without
|
73 |
-
// consing up tons of objects.
|
74 |
-
var type, content;
|
75 |
-
function ret(tp, style, cont) {
|
76 |
-
type = tp; content = cont;
|
77 |
-
return style;
|
78 |
-
}
|
79 |
-
|
80 |
-
function jsTokenBase(stream, state) {
|
81 |
-
var ch = stream.next();
|
82 |
-
if (ch == '"' || ch == "'")
|
83 |
-
return chain(stream, state, jsTokenString(ch));
|
84 |
-
else if (/[\[\]{}\(\),;\:\.]/.test(ch))
|
85 |
-
return ret(ch);
|
86 |
-
else if (ch == "0" && stream.eat(/x/i)) {
|
87 |
-
stream.eatWhile(/[\da-f]/i);
|
88 |
-
return ret("number", "number");
|
89 |
-
}
|
90 |
-
else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
|
91 |
-
stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
|
92 |
-
return ret("number", "number");
|
93 |
-
}
|
94 |
-
else if (ch == "/") {
|
95 |
-
if (stream.eat("*")) {
|
96 |
-
return chain(stream, state, jsTokenComment);
|
97 |
-
}
|
98 |
-
else if (stream.eat("/")) {
|
99 |
-
stream.skipToEnd();
|
100 |
-
return ret("comment", "comment");
|
101 |
-
}
|
102 |
-
else if (state.lastType == "operator" || state.lastType == "keyword c" ||
|
103 |
-
/^[\[{}\(,;:]$/.test(state.lastType)) {
|
104 |
-
nextUntilUnescaped(stream, "/");
|
105 |
-
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
|
106 |
-
return ret("regexp", "string-2");
|
107 |
-
}
|
108 |
-
else {
|
109 |
-
stream.eatWhile(isOperatorChar);
|
110 |
-
return ret("operator", null, stream.current());
|
111 |
-
}
|
112 |
-
}
|
113 |
-
else if (ch == "#") {
|
114 |
-
stream.skipToEnd();
|
115 |
-
return ret("error", "error");
|
116 |
-
}
|
117 |
-
else if (isOperatorChar.test(ch)) {
|
118 |
-
stream.eatWhile(isOperatorChar);
|
119 |
-
return ret("operator", null, stream.current());
|
120 |
-
}
|
121 |
-
else {
|
122 |
-
stream.eatWhile(/[\w\$_]/);
|
123 |
-
var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
|
124 |
-
return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
|
125 |
-
ret("variable", "variable", word);
|
126 |
-
}
|
127 |
-
}
|
128 |
-
|
129 |
-
function jsTokenString(quote) {
|
130 |
-
return function(stream, state) {
|
131 |
-
if (!nextUntilUnescaped(stream, quote))
|
132 |
-
state.tokenize = jsTokenBase;
|
133 |
-
return ret("string", "string");
|
134 |
-
};
|
135 |
-
}
|
136 |
-
|
137 |
-
function jsTokenComment(stream, state) {
|
138 |
-
var maybeEnd = false, ch;
|
139 |
-
while (ch = stream.next()) {
|
140 |
-
if (ch == "/" && maybeEnd) {
|
141 |
-
state.tokenize = jsTokenBase;
|
142 |
-
break;
|
143 |
-
}
|
144 |
-
maybeEnd = (ch == "*");
|
145 |
-
}
|
146 |
-
return ret("comment", "comment");
|
147 |
-
}
|
148 |
-
|
149 |
-
// Parser
|
150 |
-
|
151 |
-
var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
|
152 |
-
|
153 |
-
function JSLexical(indented, column, type, align, prev, info) {
|
154 |
-
this.indented = indented;
|
155 |
-
this.column = column;
|
156 |
-
this.type = type;
|
157 |
-
this.prev = prev;
|
158 |
-
this.info = info;
|
159 |
-
if (align != null) this.align = align;
|
160 |
-
}
|
161 |
-
|
162 |
-
function inScope(state, varname) {
|
163 |
-
for (var v = state.localVars; v; v = v.next)
|
164 |
-
if (v.name == varname) return true;
|
165 |
-
}
|
166 |
-
|
167 |
-
function parseJS(state, style, type, content, stream) {
|
168 |
-
var cc = state.cc;
|
169 |
-
// Communicate our context to the combinators.
|
170 |
-
// (Less wasteful than consing up a hundred closures on every call.)
|
171 |
-
cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
|
172 |
-
|
173 |
-
if (!state.lexical.hasOwnProperty("align"))
|
174 |
-
state.lexical.align = true;
|
175 |
-
|
176 |
-
while(true) {
|
177 |
-
var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
|
178 |
-
if (combinator(type, content)) {
|
179 |
-
while(cc.length && cc[cc.length - 1].lex)
|
180 |
-
cc.pop()();
|
181 |
-
if (cx.marked) return cx.marked;
|
182 |
-
if (type == "variable" && inScope(state, content)) return "variable-2";
|
183 |
-
return style;
|
184 |
-
}
|
185 |
-
}
|
186 |
-
}
|
187 |
-
|
188 |
-
// Combinator utils
|
189 |
-
|
190 |
-
var cx = {state: null, column: null, marked: null, cc: null};
|
191 |
-
function pass() {
|
192 |
-
for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
|
193 |
-
}
|
194 |
-
function cont() {
|
195 |
-
pass.apply(null, arguments);
|
196 |
-
return true;
|
197 |
-
}
|
198 |
-
function register(varname) {
|
199 |
-
var state = cx.state;
|
200 |
-
if (state.context) {
|
201 |
-
cx.marked = "def";
|
202 |
-
for (var v = state.localVars; v; v = v.next)
|
203 |
-
if (v.name == varname) return;
|
204 |
-
state.localVars = {name: varname, next: state.localVars};
|
205 |
-
}
|
206 |
-
}
|
207 |
-
|
208 |
-
// Combinators
|
209 |
-
|
210 |
-
var defaultVars = {name: "this", next: {name: "arguments"}};
|
211 |
-
function pushcontext() {
|
212 |
-
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
|
213 |
-
cx.state.localVars = defaultVars;
|
214 |
-
}
|
215 |
-
function popcontext() {
|
216 |
-
cx.state.localVars = cx.state.context.vars;
|
217 |
-
cx.state.context = cx.state.context.prev;
|
218 |
-
}
|
219 |
-
function pushlex(type, info) {
|
220 |
-
var result = function() {
|
221 |
-
var state = cx.state;
|
222 |
-
state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
|
223 |
-
};
|
224 |
-
result.lex = true;
|
225 |
-
return result;
|
226 |
-
}
|
227 |
-
function poplex() {
|
228 |
-
var state = cx.state;
|
229 |
-
if (state.lexical.prev) {
|
230 |
-
if (state.lexical.type == ")")
|
231 |
-
state.indented = state.lexical.indented;
|
232 |
-
state.lexical = state.lexical.prev;
|
233 |
-
}
|
234 |
-
}
|
235 |
-
poplex.lex = true;
|
236 |
-
|
237 |
-
function expect(wanted) {
|
238 |
-
return function expecting(type) {
|
239 |
-
if (type == wanted) return cont();
|
240 |
-
else if (wanted == ";") return pass();
|
241 |
-
else return cont(arguments.callee);
|
242 |
-
};
|
243 |
-
}
|
244 |
-
|
245 |
-
function statement(type) {
|
246 |
-
if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
|
247 |
-
if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
|
248 |
-
if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
|
249 |
-
if (type == "{") return cont(pushlex("}"), block, poplex);
|
250 |
-
if (type == ";") return cont();
|
251 |
-
if (type == "function") return cont(functiondef);
|
252 |
-
if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
|
253 |
-
poplex, statement, poplex);
|
254 |
-
if (type == "variable") return cont(pushlex("stat"), maybelabel);
|
255 |
-
if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
|
256 |
-
block, poplex, poplex);
|
257 |
-
if (type == "case") return cont(expression, expect(":"));
|
258 |
-
if (type == "default") return cont(expect(":"));
|
259 |
-
if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
|
260 |
-
statement, poplex, popcontext);
|
261 |
-
return pass(pushlex("stat"), expression, expect(";"), poplex);
|
262 |
-
}
|
263 |
-
function expression(type) {
|
264 |
-
if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
|
265 |
-
if (type == "function") return cont(functiondef);
|
266 |
-
if (type == "keyword c") return cont(maybeexpression);
|
267 |
-
if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
|
268 |
-
if (type == "operator") return cont(expression);
|
269 |
-
if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
|
270 |
-
if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
|
271 |
-
return cont();
|
272 |
-
}
|
273 |
-
function maybeexpression(type) {
|
274 |
-
if (type.match(/[;\}\)\],]/)) return pass();
|
275 |
-
return pass(expression);
|
276 |
-
}
|
277 |
-
|
278 |
-
function maybeoperator(type, value) {
|
279 |
-
if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
|
280 |
-
if (type == "operator" && value == "?") return cont(expression, expect(":"), expression);
|
281 |
-
if (type == ";") return;
|
282 |
-
if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
|
283 |
-
if (type == ".") return cont(property, maybeoperator);
|
284 |
-
if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
|
285 |
-
}
|
286 |
-
function maybelabel(type) {
|
287 |
-
if (type == ":") return cont(poplex, statement);
|
288 |
-
return pass(maybeoperator, expect(";"), poplex);
|
289 |
-
}
|
290 |
-
function property(type) {
|
291 |
-
if (type == "variable") {cx.marked = "property"; return cont();}
|
292 |
-
}
|
293 |
-
function objprop(type) {
|
294 |
-
if (type == "variable") cx.marked = "property";
|
295 |
-
if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
|
296 |
-
}
|
297 |
-
function commasep(what, end) {
|
298 |
-
function proceed(type) {
|
299 |
-
if (type == ",") return cont(what, proceed);
|
300 |
-
if (type == end) return cont();
|
301 |
-
return cont(expect(end));
|
302 |
-
}
|
303 |
-
return function commaSeparated(type) {
|
304 |
-
if (type == end) return cont();
|
305 |
-
else return pass(what, proceed);
|
306 |
-
};
|
307 |
-
}
|
308 |
-
function block(type) {
|
309 |
-
if (type == "}") return cont();
|
310 |
-
return pass(statement, block);
|
311 |
-
}
|
312 |
-
function maybetype(type) {
|
313 |
-
if (type == ":") return cont(typedef);
|
314 |
-
return pass();
|
315 |
-
}
|
316 |
-
function typedef(type) {
|
317 |
-
if (type == "variable"){cx.marked = "variable-3"; return cont();}
|
318 |
-
return pass();
|
319 |
-
}
|
320 |
-
function vardef1(type, value) {
|
321 |
-
if (type == "variable") {
|
322 |
-
register(value);
|
323 |
-
return isTS ? cont(maybetype, vardef2) : cont(vardef2);
|
324 |
-
}
|
325 |
-
return pass();
|
326 |
-
}
|
327 |
-
function vardef2(type, value) {
|
328 |
-
if (value == "=") return cont(expression, vardef2);
|
329 |
-
if (type == ",") return cont(vardef1);
|
330 |
-
}
|
331 |
-
function forspec1(type) {
|
332 |
-
if (type == "var") return cont(vardef1, expect(";"), forspec2);
|
333 |
-
if (type == ";") return cont(forspec2);
|
334 |
-
if (type == "variable") return cont(formaybein);
|
335 |
-
return cont(forspec2);
|
336 |
-
}
|
337 |
-
function formaybein(_type, value) {
|
338 |
-
if (value == "in") return cont(expression);
|
339 |
-
return cont(maybeoperator, forspec2);
|
340 |
-
}
|
341 |
-
function forspec2(type, value) {
|
342 |
-
if (type == ";") return cont(forspec3);
|
343 |
-
if (value == "in") return cont(expression);
|
344 |
-
return cont(expression, expect(";"), forspec3);
|
345 |
-
}
|
346 |
-
function forspec3(type) {
|
347 |
-
if (type != ")") cont(expression);
|
348 |
-
}
|
349 |
-
function functiondef(type, value) {
|
350 |
-
if (type == "variable") {register(value); return cont(functiondef);}
|
351 |
-
if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
|
352 |
-
}
|
353 |
-
function funarg(type, value) {
|
354 |
-
if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();}
|
355 |
-
}
|
356 |
-
|
357 |
-
// Interface
|
358 |
-
|
359 |
-
return {
|
360 |
-
startState: function(basecolumn) {
|
361 |
-
return {
|
362 |
-
tokenize: jsTokenBase,
|
363 |
-
lastType: null,
|
364 |
-
cc: [],
|
365 |
-
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
|
366 |
-
localVars: parserConfig.localVars,
|
367 |
-
context: parserConfig.localVars && {vars: parserConfig.localVars},
|
368 |
-
indented: 0
|
369 |
-
};
|
370 |
-
},
|
371 |
-
|
372 |
-
token: function(stream, state) {
|
373 |
-
if (stream.sol()) {
|
374 |
-
if (!state.lexical.hasOwnProperty("align"))
|
375 |
-
state.lexical.align = false;
|
376 |
-
state.indented = stream.indentation();
|
377 |
-
}
|
378 |
-
if (stream.eatSpace()) return null;
|
379 |
-
var style = state.tokenize(stream, state);
|
380 |
-
if (type == "comment") return style;
|
381 |
-
state.lastType = type;
|
382 |
-
return parseJS(state, style, type, content, stream);
|
383 |
-
},
|
384 |
-
|
385 |
-
indent: function(state, textAfter) {
|
386 |
-
if (state.tokenize == jsTokenComment) return CodeMirror.Pass;
|
387 |
-
if (state.tokenize != jsTokenBase) return 0;
|
388 |
-
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
|
389 |
-
if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
|
390 |
-
var type = lexical.type, closing = firstChar == type;
|
391 |
-
if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
|
392 |
-
else if (type == "form" && firstChar == "{") return lexical.indented;
|
393 |
-
else if (type == "form") return lexical.indented + indentUnit;
|
394 |
-
else if (type == "stat")
|
395 |
-
return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
|
396 |
-
else if (lexical.info == "switch" && !closing)
|
397 |
-
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
|
398 |
-
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
|
399 |
-
else return lexical.indented + (closing ? 0 : indentUnit);
|
400 |
-
},
|
401 |
-
|
402 |
-
electricChars: ":{}",
|
403 |
-
|
404 |
-
jsonMode: jsonMode
|
405 |
-
};
|
406 |
-
});
|
407 |
-
|
408 |
-
CodeMirror.defineMIME("text/javascript", "javascript");
|
409 |
-
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
|
410 |
-
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
|
411 |
-
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/mode/xml.js
DELETED
@@ -1,324 +0,0 @@
|
|
1 |
-
CodeMirror.defineMode("xml", function(config, parserConfig) {
|
2 |
-
var indentUnit = config.indentUnit;
|
3 |
-
var Kludges = parserConfig.htmlMode ? {
|
4 |
-
autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
|
5 |
-
'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
|
6 |
-
'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
|
7 |
-
'track': true, 'wbr': true},
|
8 |
-
implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
|
9 |
-
'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
|
10 |
-
'th': true, 'tr': true},
|
11 |
-
contextGrabbers: {
|
12 |
-
'dd': {'dd': true, 'dt': true},
|
13 |
-
'dt': {'dd': true, 'dt': true},
|
14 |
-
'li': {'li': true},
|
15 |
-
'option': {'option': true, 'optgroup': true},
|
16 |
-
'optgroup': {'optgroup': true},
|
17 |
-
'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
|
18 |
-
'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
|
19 |
-
'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
|
20 |
-
'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
|
21 |
-
'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
|
22 |
-
'rp': {'rp': true, 'rt': true},
|
23 |
-
'rt': {'rp': true, 'rt': true},
|
24 |
-
'tbody': {'tbody': true, 'tfoot': true},
|
25 |
-
'td': {'td': true, 'th': true},
|
26 |
-
'tfoot': {'tbody': true},
|
27 |
-
'th': {'td': true, 'th': true},
|
28 |
-
'thead': {'tbody': true, 'tfoot': true},
|
29 |
-
'tr': {'tr': true}
|
30 |
-
},
|
31 |
-
doNotIndent: {"pre": true},
|
32 |
-
allowUnquoted: true,
|
33 |
-
allowMissing: true
|
34 |
-
} : {
|
35 |
-
autoSelfClosers: {},
|
36 |
-
implicitlyClosed: {},
|
37 |
-
contextGrabbers: {},
|
38 |
-
doNotIndent: {},
|
39 |
-
allowUnquoted: false,
|
40 |
-
allowMissing: false
|
41 |
-
};
|
42 |
-
var alignCDATA = parserConfig.alignCDATA;
|
43 |
-
|
44 |
-
// Return variables for tokenizers
|
45 |
-
var tagName, type;
|
46 |
-
|
47 |
-
function inText(stream, state) {
|
48 |
-
function chain(parser) {
|
49 |
-
state.tokenize = parser;
|
50 |
-
return parser(stream, state);
|
51 |
-
}
|
52 |
-
|
53 |
-
var ch = stream.next();
|
54 |
-
if (ch == "<") {
|
55 |
-
if (stream.eat("!")) {
|
56 |
-
if (stream.eat("[")) {
|
57 |
-
if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
|
58 |
-
else return null;
|
59 |
-
}
|
60 |
-
else if (stream.match("--")) return chain(inBlock("comment", "-->"));
|
61 |
-
else if (stream.match("DOCTYPE", true, true)) {
|
62 |
-
stream.eatWhile(/[\w\._\-]/);
|
63 |
-
return chain(doctype(1));
|
64 |
-
}
|
65 |
-
else return null;
|
66 |
-
}
|
67 |
-
else if (stream.eat("?")) {
|
68 |
-
stream.eatWhile(/[\w\._\-]/);
|
69 |
-
state.tokenize = inBlock("meta", "?>");
|
70 |
-
return "meta";
|
71 |
-
}
|
72 |
-
else {
|
73 |
-
var isClose = stream.eat("/");
|
74 |
-
tagName = "";
|
75 |
-
var c;
|
76 |
-
while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
|
77 |
-
if (!tagName) return "error";
|
78 |
-
type = isClose ? "closeTag" : "openTag";
|
79 |
-
state.tokenize = inTag;
|
80 |
-
return "tag";
|
81 |
-
}
|
82 |
-
}
|
83 |
-
else if (ch == "&") {
|
84 |
-
var ok;
|
85 |
-
if (stream.eat("#")) {
|
86 |
-
if (stream.eat("x")) {
|
87 |
-
ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
|
88 |
-
} else {
|
89 |
-
ok = stream.eatWhile(/[\d]/) && stream.eat(";");
|
90 |
-
}
|
91 |
-
} else {
|
92 |
-
ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
|
93 |
-
}
|
94 |
-
return ok ? "atom" : "error";
|
95 |
-
}
|
96 |
-
else {
|
97 |
-
stream.eatWhile(/[^&<]/);
|
98 |
-
return null;
|
99 |
-
}
|
100 |
-
}
|
101 |
-
|
102 |
-
function inTag(stream, state) {
|
103 |
-
var ch = stream.next();
|
104 |
-
if (ch == ">" || (ch == "/" && stream.eat(">"))) {
|
105 |
-
state.tokenize = inText;
|
106 |
-
type = ch == ">" ? "endTag" : "selfcloseTag";
|
107 |
-
return "tag";
|
108 |
-
}
|
109 |
-
else if (ch == "=") {
|
110 |
-
type = "equals";
|
111 |
-
return null;
|
112 |
-
}
|
113 |
-
else if (/[\'\"]/.test(ch)) {
|
114 |
-
state.tokenize = inAttribute(ch);
|
115 |
-
return state.tokenize(stream, state);
|
116 |
-
}
|
117 |
-
else {
|
118 |
-
stream.eatWhile(/[^\s\u00a0=<>\"\']/);
|
119 |
-
return "word";
|
120 |
-
}
|
121 |
-
}
|
122 |
-
|
123 |
-
function inAttribute(quote) {
|
124 |
-
return function(stream, state) {
|
125 |
-
while (!stream.eol()) {
|
126 |
-
if (stream.next() == quote) {
|
127 |
-
state.tokenize = inTag;
|
128 |
-
break;
|
129 |
-
}
|
130 |
-
}
|
131 |
-
return "string";
|
132 |
-
};
|
133 |
-
}
|
134 |
-
|
135 |
-
function inBlock(style, terminator) {
|
136 |
-
return function(stream, state) {
|
137 |
-
while (!stream.eol()) {
|
138 |
-
if (stream.match(terminator)) {
|
139 |
-
state.tokenize = inText;
|
140 |
-
break;
|
141 |
-
}
|
142 |
-
stream.next();
|
143 |
-
}
|
144 |
-
return style;
|
145 |
-
};
|
146 |
-
}
|
147 |
-
function doctype(depth) {
|
148 |
-
return function(stream, state) {
|
149 |
-
var ch;
|
150 |
-
while ((ch = stream.next()) != null) {
|
151 |
-
if (ch == "<") {
|
152 |
-
state.tokenize = doctype(depth + 1);
|
153 |
-
return state.tokenize(stream, state);
|
154 |
-
} else if (ch == ">") {
|
155 |
-
if (depth == 1) {
|
156 |
-
state.tokenize = inText;
|
157 |
-
break;
|
158 |
-
} else {
|
159 |
-
state.tokenize = doctype(depth - 1);
|
160 |
-
return state.tokenize(stream, state);
|
161 |
-
}
|
162 |
-
}
|
163 |
-
}
|
164 |
-
return "meta";
|
165 |
-
};
|
166 |
-
}
|
167 |
-
|
168 |
-
var curState, setStyle;
|
169 |
-
function pass() {
|
170 |
-
for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
|
171 |
-
}
|
172 |
-
function cont() {
|
173 |
-
pass.apply(null, arguments);
|
174 |
-
return true;
|
175 |
-
}
|
176 |
-
|
177 |
-
function pushContext(tagName, startOfLine) {
|
178 |
-
var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
|
179 |
-
curState.context = {
|
180 |
-
prev: curState.context,
|
181 |
-
tagName: tagName,
|
182 |
-
indent: curState.indented,
|
183 |
-
startOfLine: startOfLine,
|
184 |
-
noIndent: noIndent
|
185 |
-
};
|
186 |
-
}
|
187 |
-
function popContext() {
|
188 |
-
if (curState.context) curState.context = curState.context.prev;
|
189 |
-
}
|
190 |
-
|
191 |
-
function element(type) {
|
192 |
-
if (type == "openTag") {
|
193 |
-
curState.tagName = tagName;
|
194 |
-
return cont(attributes, endtag(curState.startOfLine));
|
195 |
-
} else if (type == "closeTag") {
|
196 |
-
var err = false;
|
197 |
-
if (curState.context) {
|
198 |
-
if (curState.context.tagName != tagName) {
|
199 |
-
if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
|
200 |
-
popContext();
|
201 |
-
}
|
202 |
-
err = !curState.context || curState.context.tagName != tagName;
|
203 |
-
}
|
204 |
-
} else {
|
205 |
-
err = true;
|
206 |
-
}
|
207 |
-
if (err) setStyle = "error";
|
208 |
-
return cont(endclosetag(err));
|
209 |
-
}
|
210 |
-
return cont();
|
211 |
-
}
|
212 |
-
function endtag(startOfLine) {
|
213 |
-
return function(type) {
|
214 |
-
var tagName = curState.tagName;
|
215 |
-
curState.tagName = null;
|
216 |
-
if (type == "selfcloseTag" ||
|
217 |
-
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) {
|
218 |
-
maybePopContext(tagName.toLowerCase());
|
219 |
-
return cont();
|
220 |
-
}
|
221 |
-
if (type == "endTag") {
|
222 |
-
maybePopContext(tagName.toLowerCase());
|
223 |
-
pushContext(tagName, startOfLine);
|
224 |
-
return cont();
|
225 |
-
}
|
226 |
-
return cont();
|
227 |
-
};
|
228 |
-
}
|
229 |
-
function endclosetag(err) {
|
230 |
-
return function(type) {
|
231 |
-
if (err) setStyle = "error";
|
232 |
-
if (type == "endTag") { popContext(); return cont(); }
|
233 |
-
setStyle = "error";
|
234 |
-
return cont(arguments.callee);
|
235 |
-
};
|
236 |
-
}
|
237 |
-
function maybePopContext(nextTagName) {
|
238 |
-
var parentTagName;
|
239 |
-
while (true) {
|
240 |
-
if (!curState.context) {
|
241 |
-
return;
|
242 |
-
}
|
243 |
-
parentTagName = curState.context.tagName.toLowerCase();
|
244 |
-
if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) ||
|
245 |
-
!Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
|
246 |
-
return;
|
247 |
-
}
|
248 |
-
popContext();
|
249 |
-
}
|
250 |
-
}
|
251 |
-
|
252 |
-
function attributes(type) {
|
253 |
-
if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
|
254 |
-
if (type == "endTag" || type == "selfcloseTag") return pass();
|
255 |
-
setStyle = "error";
|
256 |
-
return cont(attributes);
|
257 |
-
}
|
258 |
-
function attribute(type) {
|
259 |
-
if (type == "equals") return cont(attvalue, attributes);
|
260 |
-
if (!Kludges.allowMissing) setStyle = "error";
|
261 |
-
else if (type == "word") setStyle = "attribute";
|
262 |
-
return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
|
263 |
-
}
|
264 |
-
function attvalue(type) {
|
265 |
-
if (type == "string") return cont(attvaluemaybe);
|
266 |
-
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
|
267 |
-
setStyle = "error";
|
268 |
-
return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
|
269 |
-
}
|
270 |
-
function attvaluemaybe(type) {
|
271 |
-
if (type == "string") return cont(attvaluemaybe);
|
272 |
-
else return pass();
|
273 |
-
}
|
274 |
-
|
275 |
-
return {
|
276 |
-
startState: function() {
|
277 |
-
return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
|
278 |
-
},
|
279 |
-
|
280 |
-
token: function(stream, state) {
|
281 |
-
if (stream.sol()) {
|
282 |
-
state.startOfLine = true;
|
283 |
-
state.indented = stream.indentation();
|
284 |
-
}
|
285 |
-
if (stream.eatSpace()) return null;
|
286 |
-
|
287 |
-
setStyle = type = tagName = null;
|
288 |
-
var style = state.tokenize(stream, state);
|
289 |
-
state.type = type;
|
290 |
-
if ((style || type) && style != "comment") {
|
291 |
-
curState = state;
|
292 |
-
while (true) {
|
293 |
-
var comb = state.cc.pop() || element;
|
294 |
-
if (comb(type || style)) break;
|
295 |
-
}
|
296 |
-
}
|
297 |
-
state.startOfLine = false;
|
298 |
-
return setStyle || style;
|
299 |
-
},
|
300 |
-
|
301 |
-
indent: function(state, textAfter, fullLine) {
|
302 |
-
var context = state.context;
|
303 |
-
if ((state.tokenize != inTag && state.tokenize != inText) ||
|
304 |
-
context && context.noIndent)
|
305 |
-
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
|
306 |
-
if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
|
307 |
-
if (context && /^<\//.test(textAfter))
|
308 |
-
context = context.prev;
|
309 |
-
while (context && !context.startOfLine)
|
310 |
-
context = context.prev;
|
311 |
-
if (context) return context.indent + indentUnit;
|
312 |
-
else return 0;
|
313 |
-
},
|
314 |
-
|
315 |
-
electricChars: "/",
|
316 |
-
|
317 |
-
configuration: parserConfig.htmlMode ? "html" : "xml"
|
318 |
-
};
|
319 |
-
});
|
320 |
-
|
321 |
-
CodeMirror.defineMIME("text/xml", "xml");
|
322 |
-
CodeMirror.defineMIME("application/xml", "xml");
|
323 |
-
if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
|
324 |
-
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/screen-icon.css
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Add the snippet screen icon to admin pages
|
3 |
+
*
|
4 |
+
* @package Code Snippets
|
5 |
+
* @subpackage Assets
|
6 |
+
*/
|
7 |
+
|
8 |
+
#icon-snippets.icon32 {
|
9 |
+
background: url('../images/screen-icon.png') no-repeat scroll transparent;
|
10 |
+
}
|
assets/table.css
CHANGED
@@ -1,3 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
.snippets .inactive a {
|
2 |
color: #557799;
|
3 |
}
|
1 |
+
/**
|
2 |
+
* Custom styling for the snippets table
|
3 |
+
*
|
4 |
+
* @package Code Snippets
|
5 |
+
* @subpackage Assets
|
6 |
+
*/
|
7 |
+
|
8 |
.snippets .inactive a {
|
9 |
color: #557799;
|
10 |
}
|
assets/table.mp6.css
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Custom styling for the snippets table
|
3 |
+
* and the MP6 interface
|
4 |
+
*
|
5 |
+
* @package Code Snippets
|
6 |
+
* @subpackage Assets
|
7 |
+
*/
|
8 |
+
|
9 |
+
.snippets .row-actions-visible {
|
10 |
+
color: #ddd;
|
11 |
+
}
|
12 |
+
|
13 |
+
.snippets tfoot th {
|
14 |
+
border-top: none !important;
|
15 |
+
}
|
16 |
+
|
17 |
+
.snippets a.delete:hover,
|
18 |
+
#all-snippets-table .snippets a.delete:hover,
|
19 |
+
#search-snippets-table .snippets a.delete:hover {
|
20 |
+
color: #f00;
|
21 |
+
border-bottom: 1px solid #f00;
|
22 |
+
}
|
23 |
+
|
24 |
+
.snippets .column-description p {
|
25 |
+
color: #333;
|
26 |
+
}
|
27 |
+
|
28 |
+
.snippets .inactive .column-name strong {
|
29 |
+
color: #333;
|
30 |
+
font-weight: 400;
|
31 |
+
}
|
32 |
+
|
33 |
+
.snippets,
|
34 |
+
.snippets th,
|
35 |
+
.snippets td {
|
36 |
+
color: #000;
|
37 |
+
}
|
38 |
+
|
39 |
+
.snippets .inactive a {
|
40 |
+
color: #579;
|
41 |
+
}
|
42 |
+
|
43 |
+
.snippets tr {
|
44 |
+
background: #fff;
|
45 |
+
}
|
46 |
+
|
47 |
+
.snippets .inactive td,
|
48 |
+
.snippets .inactive th,
|
49 |
+
.snippets .active td,
|
50 |
+
.snippets .active th {
|
51 |
+
border: none;
|
52 |
+
padding: 10px 9px;
|
53 |
+
-webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);
|
54 |
+
box-shadow: inset 0 -1px 0 rgba(0,0,0,0.1);;
|
55 |
+
}
|
56 |
+
|
57 |
+
.snippets tbody th.check-column {
|
58 |
+
padding: 13px 0 0 3px;
|
59 |
+
}
|
60 |
+
|
61 |
+
.snippets .active td,
|
62 |
+
.snippets .active th {
|
63 |
+
background-color: rgba(120,200,230,0.06);
|
64 |
+
}
|
65 |
+
|
66 |
+
.snippets tr.active + tr.inactive th,
|
67 |
+
.snippets tr.active + tr.inactive td {
|
68 |
+
border-top: 1px solid rgba(0,0,0,0.03);
|
69 |
+
-webkit-box-shadow: inset 0px 1px 0 rgba(0,0,0,0.02), inset 0 -1px 0 #e1e1e1;
|
70 |
+
box-shadow: inset 0px 1px 0 rgba(0,0,0,0.02), inset 0 -1px 0 #e1e1e1;
|
71 |
+
}
|
72 |
+
|
73 |
+
.snippets thead th.check-column,
|
74 |
+
.snippets tfoot th.check-column,
|
75 |
+
.snippets .inactive th.check-column {
|
76 |
+
padding-left: 5px;
|
77 |
+
}
|
78 |
+
|
79 |
+
.snippets .active th.check-column {
|
80 |
+
border-left: 2px solid #2ea2cc;
|
81 |
+
}
|
82 |
+
|
83 |
+
#wpbody-content .snippets .column-name {
|
84 |
+
white-space: nowrap; /* prevents wrapping of snippet title */
|
85 |
+
}
|
assets/util/dialog.css
DELETED
@@ -1,32 +0,0 @@
|
|
1 |
-
.CodeMirror-dialog {
|
2 |
-
position: absolute;
|
3 |
-
left: 0; right: 0;
|
4 |
-
background: white;
|
5 |
-
z-index: 15;
|
6 |
-
padding: .1em .8em;
|
7 |
-
overflow: hidden;
|
8 |
-
color: #333;
|
9 |
-
}
|
10 |
-
|
11 |
-
.CodeMirror-dialog-top {
|
12 |
-
border-bottom: 1px solid #eee;
|
13 |
-
top: 0;
|
14 |
-
}
|
15 |
-
|
16 |
-
.CodeMirror-dialog-bottom {
|
17 |
-
border-top: 1px solid #eee;
|
18 |
-
bottom: 0;
|
19 |
-
}
|
20 |
-
|
21 |
-
.CodeMirror-dialog input {
|
22 |
-
border: none;
|
23 |
-
outline: none;
|
24 |
-
background: transparent;
|
25 |
-
width: 20em;
|
26 |
-
color: inherit;
|
27 |
-
font-family: monospace;
|
28 |
-
}
|
29 |
-
|
30 |
-
.CodeMirror-dialog button {
|
31 |
-
font-size: 70%;
|
32 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/util/dialog.js
DELETED
@@ -1,75 +0,0 @@
|
|
1 |
-
// Open simple dialogs on top of an editor. Relies on dialog.css.
|
2 |
-
|
3 |
-
(function() {
|
4 |
-
function dialogDiv(cm, template, bottom) {
|
5 |
-
var wrap = cm.getWrapperElement();
|
6 |
-
var dialog;
|
7 |
-
dialog = wrap.appendChild(document.createElement("div"));
|
8 |
-
if (bottom) {
|
9 |
-
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
|
10 |
-
} else {
|
11 |
-
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
|
12 |
-
}
|
13 |
-
dialog.innerHTML = template;
|
14 |
-
return dialog;
|
15 |
-
}
|
16 |
-
|
17 |
-
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
|
18 |
-
var dialog = dialogDiv(this, template, options && options.bottom);
|
19 |
-
var closed = false, me = this;
|
20 |
-
function close() {
|
21 |
-
if (closed) return;
|
22 |
-
closed = true;
|
23 |
-
dialog.parentNode.removeChild(dialog);
|
24 |
-
}
|
25 |
-
var inp = dialog.getElementsByTagName("input")[0], button;
|
26 |
-
if (inp) {
|
27 |
-
CodeMirror.on(inp, "keydown", function(e) {
|
28 |
-
if (e.keyCode == 13 || e.keyCode == 27) {
|
29 |
-
CodeMirror.e_stop(e);
|
30 |
-
close();
|
31 |
-
me.focus();
|
32 |
-
if (e.keyCode == 13) callback(inp.value);
|
33 |
-
}
|
34 |
-
});
|
35 |
-
inp.focus();
|
36 |
-
CodeMirror.on(inp, "blur", close);
|
37 |
-
} else if (button = dialog.getElementsByTagName("button")[0]) {
|
38 |
-
CodeMirror.on(button, "click", function() {
|
39 |
-
close();
|
40 |
-
me.focus();
|
41 |
-
});
|
42 |
-
button.focus();
|
43 |
-
CodeMirror.on(button, "blur", close);
|
44 |
-
}
|
45 |
-
return close;
|
46 |
-
});
|
47 |
-
|
48 |
-
CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
|
49 |
-
var dialog = dialogDiv(this, template, options && options.bottom);
|
50 |
-
var buttons = dialog.getElementsByTagName("button");
|
51 |
-
var closed = false, me = this, blurring = 1;
|
52 |
-
function close() {
|
53 |
-
if (closed) return;
|
54 |
-
closed = true;
|
55 |
-
dialog.parentNode.removeChild(dialog);
|
56 |
-
me.focus();
|
57 |
-
}
|
58 |
-
buttons[0].focus();
|
59 |
-
for (var i = 0; i < buttons.length; ++i) {
|
60 |
-
var b = buttons[i];
|
61 |
-
(function(callback) {
|
62 |
-
CodeMirror.on(b, "click", function(e) {
|
63 |
-
CodeMirror.e_preventDefault(e);
|
64 |
-
close();
|
65 |
-
if (callback) callback(me);
|
66 |
-
});
|
67 |
-
})(callbacks[i]);
|
68 |
-
CodeMirror.on(b, "blur", function() {
|
69 |
-
--blurring;
|
70 |
-
setTimeout(function() { if (blurring <= 0) close(); }, 200);
|
71 |
-
});
|
72 |
-
CodeMirror.on(b, "focus", function() { ++blurring; });
|
73 |
-
}
|
74 |
-
});
|
75 |
-
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code-snippets.php
CHANGED
@@ -15,9 +15,8 @@
|
|
15 |
* Description: An easy, clean and simple way to add code snippets to your site. No need to edit to your theme's functions.php file again!
|
16 |
* Author: Shea Bunge
|
17 |
* Author URI: http://bungeshea.com
|
18 |
-
* Version: 1.
|
19 |
-
* License:
|
20 |
-
* Network: true
|
21 |
* Text Domain: code-snippets
|
22 |
* Domain Path: /languages/
|
23 |
*
|
@@ -58,66 +57,53 @@ if ( ! class_exists( 'Code_Snippets' ) ) :
|
|
58 |
final class Code_Snippets {
|
59 |
|
60 |
/**
|
61 |
-
* The
|
62 |
-
* This will later be
|
63 |
-
*
|
64 |
-
* DO NOT EDIT THIS VARIABLE!
|
65 |
-
* Instead, use the 'code_snippets_table' filter
|
66 |
*
|
67 |
-
*
|
68 |
-
*
|
69 |
*
|
70 |
* @since Code Snippets 1.0
|
71 |
* @access public
|
72 |
*/
|
73 |
-
|
74 |
|
75 |
/**
|
76 |
-
* The
|
77 |
-
* This will later be prepended with the WordPress base table prefix
|
78 |
-
*
|
79 |
-
* DO NOT EDIT THIS VARIABLE!
|
80 |
-
* Instead, use the 'code_snippets_multisite_table' filter
|
81 |
*
|
82 |
-
*
|
83 |
-
*
|
84 |
-
*
|
85 |
-
* @since Code Snippets 1.4
|
86 |
-
* @access private
|
87 |
*/
|
88 |
-
|
89 |
|
90 |
/**
|
91 |
-
* The
|
92 |
-
*
|
93 |
-
*
|
94 |
-
* This should be set to the 'Plugin Version' value,
|
95 |
-
* as defined above in the plugin header
|
96 |
*
|
97 |
* @since Code Snippets 1.0
|
98 |
* @access public
|
99 |
*/
|
100 |
-
public $
|
101 |
|
102 |
/**
|
103 |
-
*
|
104 |
*
|
105 |
* @since Code Snippets 1.0
|
106 |
* @access public
|
107 |
*/
|
108 |
-
public $
|
109 |
|
110 |
/**
|
111 |
-
*
|
112 |
-
* Used primarily for enqueueing scripts and styles
|
113 |
*
|
114 |
-
* @since Code Snippets 1.
|
115 |
* @access public
|
116 |
*/
|
117 |
-
public $
|
118 |
|
119 |
/**
|
120 |
* The constructor function for our class
|
|
|
121 |
*
|
122 |
* @since Code Snippets 1.0
|
123 |
* @access private
|
@@ -125,9 +111,47 @@ final class Code_Snippets {
|
|
125 |
* @return void
|
126 |
*/
|
127 |
function __construct() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
$this->setup_vars(); // initialise the variables
|
129 |
$this->setup_hooks(); // register the action and filter hooks
|
130 |
$this->upgrade(); // check if we need to change some stuff
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
}
|
132 |
|
133 |
/**
|
@@ -142,9 +166,6 @@ final class Code_Snippets {
|
|
142 |
global $wpdb;
|
143 |
$this->file = __FILE__;
|
144 |
|
145 |
-
$this->table = apply_filters( 'code_snippets_table', $wpdb->prefix . $this->table );
|
146 |
-
$this->ms_table = apply_filters( 'code_snippets_multisite_table', $wpdb->base_prefix . $this->ms_table );
|
147 |
-
|
148 |
$this->basename = plugin_basename( $this->file );
|
149 |
$this->plugin_dir = plugin_dir_path( $this->file );
|
150 |
$this->plugin_url = plugin_dir_url ( $this->file );
|
@@ -166,26 +187,50 @@ final class Code_Snippets {
|
|
166 |
*/
|
167 |
function setup_hooks() {
|
168 |
|
169 |
-
/* execute the snippets once the plugins are loaded */
|
170 |
-
add_action( 'plugins_loaded', array( $this, 'run_snippets' ) );
|
171 |
-
|
172 |
/* add the administration menus */
|
173 |
-
add_action( 'admin_menu', array( $this, 'add_admin_menus' ) );
|
174 |
-
add_action( 'network_admin_menu', array( $this, '
|
175 |
|
176 |
/* register the importer */
|
177 |
-
add_action( 'admin_init', array( $this, 'load_importer' )
|
178 |
add_action( 'network_admin_menu', array( $this, 'add_import_admin_menu' ) );
|
179 |
|
180 |
-
/* load the translations */
|
181 |
-
add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
|
182 |
-
|
183 |
/* add helpful links to the Plugins menu */
|
184 |
add_filter( 'plugin_action_links_' . $this->basename, array( $this, 'settings_link' ) );
|
185 |
add_filter( 'plugin_row_meta', array( $this, 'plugin_meta' ), 10, 2 );
|
186 |
|
187 |
/* Add a custom icon to Snippets menu pages */
|
188 |
-
add_action( '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
}
|
190 |
|
191 |
/**
|
@@ -236,6 +281,8 @@ final class Code_Snippets {
|
|
236 |
*/
|
237 |
function get_table_name( $scope = '', $check_screen = true ) {
|
238 |
|
|
|
|
|
239 |
$this->create_tables(); // create the snippet tables if they do not exist
|
240 |
|
241 |
if ( ! is_multisite() ) {
|
@@ -246,17 +293,17 @@ final class Code_Snippets {
|
|
246 |
$screen = get_current_screen();
|
247 |
$network = $screen->is_network;
|
248 |
}
|
249 |
-
elseif (
|
250 |
$network = true;
|
251 |
}
|
252 |
-
elseif (
|
253 |
$network = false;
|
254 |
}
|
255 |
else {
|
256 |
$network = false;
|
257 |
}
|
258 |
|
259 |
-
$table = ( $network ? $
|
260 |
|
261 |
return $table;
|
262 |
}
|
@@ -268,17 +315,20 @@ final class Code_Snippets {
|
|
268 |
* @access public
|
269 |
*
|
270 |
* @uses $this->create_table() To create a single snippet table
|
|
|
271 |
*
|
272 |
* @return void
|
273 |
*/
|
274 |
public function create_tables() {
|
275 |
|
276 |
-
|
|
|
|
|
|
|
277 |
|
278 |
-
if ( is_multisite() )
|
279 |
-
$this->create_table( $
|
280 |
|
281 |
-
add_site_option( 'code_snippets_version', $this->version );
|
282 |
}
|
283 |
|
284 |
/**
|
@@ -288,25 +338,38 @@ final class Code_Snippets {
|
|
288 |
* @since Code Snippets 1.6
|
289 |
* @access public
|
290 |
*
|
|
|
|
|
291 |
* @param string $table_name The name of the table to create
|
292 |
* @return void
|
293 |
*/
|
294 |
function create_table( $table_name ) {
|
|
|
295 |
|
296 |
global $wpdb;
|
297 |
|
298 |
-
if ( $wpdb->
|
299 |
-
$
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
active TINYINT(1) NOT NULL DEFAULT 0,
|
305 |
-
UNIQUE KEY id (id)
|
306 |
-
);";
|
307 |
-
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
308 |
-
dbDelta( $sql );
|
309 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
310 |
}
|
311 |
|
312 |
/**
|
@@ -315,14 +378,10 @@ final class Code_Snippets {
|
|
315 |
* @since Code Snippets 1.2
|
316 |
* @access private
|
317 |
*
|
318 |
-
* @return
|
319 |
*/
|
320 |
function upgrade() {
|
321 |
-
|
322 |
-
/* add backwards-compatibly for the CS_SAFE_MODE constant */
|
323 |
-
if ( defined( 'CS_SAFE_MODE' ) && ! defined( 'CODE_SNIPPETS_SAFE_MODE' ) ) {
|
324 |
-
define( 'CODE_SNIPPETS_SAFE_MODE', CS_SAFE_MODE );
|
325 |
-
}
|
326 |
|
327 |
/* get the current plugin version from the database */
|
328 |
if ( get_option( 'cs_db_version' ) ) {
|
@@ -334,70 +393,44 @@ final class Code_Snippets {
|
|
334 |
$this->current_version = get_site_option( 'code_snippets_version', $this->version );
|
335 |
}
|
336 |
|
337 |
-
|
338 |
-
if ( $this->current_version < $this->version ) return false;
|
339 |
-
|
340 |
-
if ( ! get_site_option( 'code_snippets_version' ) ) {
|
341 |
|
342 |
/* This is the first time the plugin has run */
|
343 |
|
344 |
-
$this->add_caps(); // register the capabilities
|
345 |
|
346 |
if ( is_multisite() ) {
|
347 |
-
$this->add_caps( 'multisite' ); // register the multisite capabilities
|
348 |
}
|
349 |
}
|
350 |
|
351 |
-
/*
|
352 |
-
if (
|
353 |
-
add_site_option( 'recently_activated_snippets', get_option( 'recently_network_activated_snippets', array() ) );
|
354 |
-
delete_option( 'recently_network_activated_snippets' );
|
355 |
-
}
|
356 |
-
|
357 |
-
/* preform version specific upgrades */
|
358 |
-
|
359 |
-
if ( $this->current_version < 1.5 ) {
|
360 |
-
global $wpdb;
|
361 |
-
|
362 |
-
/* Let's alter the name column to accept up to 64 characters */
|
363 |
-
$wpdb->query( "ALTER TABLE $this->table CHANGE COLUMN name name VARCHAR(64) NOT NULL" );
|
364 |
|
365 |
-
|
366 |
-
|
367 |
-
|
|
|
368 |
}
|
369 |
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
if ( $this->current_version < 1.2 ) {
|
375 |
-
/* The 'Complete Uninstall' option was removed in version 1.2 */
|
376 |
-
delete_option( 'cs_complete_uninstall' );
|
377 |
-
}
|
378 |
|
379 |
-
if ( $this->current_version < $this->version ) {
|
380 |
/* Update the current version */
|
381 |
update_site_option( 'code_snippets_version', $this->version );
|
382 |
}
|
383 |
-
|
384 |
-
return true;
|
385 |
}
|
386 |
|
387 |
/**
|
388 |
-
*
|
389 |
-
* Place it in this plugin's "languages" folder and name it "code-snippets-[value in wp-config].mo"
|
390 |
-
*
|
391 |
-
* If you wish to contribute a language file to be included in the Code Snippets package,
|
392 |
-
* please see the plugin's website at http://code-snippets.bungeshea.com
|
393 |
*
|
394 |
* @since Code Snippets 1.5
|
395 |
* @access private
|
396 |
-
*
|
397 |
-
* @return void
|
398 |
*/
|
399 |
-
function
|
400 |
-
|
401 |
}
|
402 |
|
403 |
/**
|
@@ -413,7 +446,7 @@ final class Code_Snippets {
|
|
413 |
*/
|
414 |
public function add_caps( $scope = '' ) {
|
415 |
|
416 |
-
$network = (
|
417 |
|
418 |
if ( $network && is_multisite() )
|
419 |
$this->setup_ms_roles( 'add' );
|
@@ -434,7 +467,7 @@ final class Code_Snippets {
|
|
434 |
*/
|
435 |
public function remove_caps( $scope = '' ) {
|
436 |
|
437 |
-
$network = (
|
438 |
|
439 |
if ( $network && is_multisite() )
|
440 |
$this->setup_ms_roles( 'remove' );
|
@@ -453,10 +486,10 @@ final class Code_Snippets {
|
|
453 |
*/
|
454 |
function setup_roles( $action = 'install' ) {
|
455 |
|
456 |
-
if (
|
457 |
$install = true;
|
458 |
|
459 |
-
elseif (
|
460 |
$install = false;
|
461 |
|
462 |
else
|
@@ -492,10 +525,10 @@ final class Code_Snippets {
|
|
492 |
|
493 |
if ( ! is_multisite() ) return;
|
494 |
|
495 |
-
if (
|
496 |
$install = true;
|
497 |
|
498 |
-
elseif (
|
499 |
$install = false;
|
500 |
|
501 |
else
|
@@ -527,94 +560,65 @@ final class Code_Snippets {
|
|
527 |
*
|
528 |
* @uses add_menu_page() To register a top-level menu
|
529 |
* @uses add_submenu_page() To register a submenu page
|
530 |
-
* @uses apply_filters() To retrieve the
|
531 |
* @uses plugins_url() To retrieve the URL to a resource
|
532 |
* @return void
|
533 |
*/
|
534 |
function add_admin_menus() {
|
535 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
536 |
$this->admin_manage = add_menu_page(
|
537 |
__('Snippets', 'code-snippets'),
|
538 |
__('Snippets', 'code-snippets'),
|
539 |
-
'manage_snippets',
|
540 |
-
|
541 |
array( $this, 'display_admin_manage' ),
|
542 |
-
|
543 |
-
67
|
544 |
);
|
545 |
|
546 |
add_submenu_page(
|
547 |
-
|
548 |
__('Snippets', 'code-snippets'),
|
549 |
-
__('Manage
|
550 |
-
'manage_snippets',
|
551 |
-
|
552 |
array( $this, 'display_admin_manage')
|
553 |
);
|
554 |
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
add_action( "admin_print_styles-$this->admin_single", array( $this, 'load_editor_styles' ) );
|
568 |
-
add_action( "admin_print_scripts-$this->admin_single", array( $this, 'load_editor_scripts' ) );
|
569 |
-
}
|
570 |
-
|
571 |
-
/**
|
572 |
-
* Add the network dashboard admin menu and subpages
|
573 |
-
*
|
574 |
-
* @since Code Snippets 1.4
|
575 |
-
* @access private
|
576 |
-
*
|
577 |
-
* @uses add_menu_page() To register a top-level menu
|
578 |
-
* @uses add_submenu_page() To register a submenu page
|
579 |
-
* @uses apply_filters() To retrieve the corrent menu slug
|
580 |
-
* @uses plugins_url() To retrieve the URL to a resource
|
581 |
-
* @return void
|
582 |
-
*/
|
583 |
-
function add_network_admin_menus() {
|
584 |
-
|
585 |
-
$this->admin_manage = add_menu_page(
|
586 |
-
__('Snippets', 'code-snippets'),
|
587 |
-
__('Snippets', 'code-snippets'),
|
588 |
-
'manage_network_snippets',
|
589 |
-
apply_filters( 'code_snippets_manage_url', 'snippets' ),
|
590 |
-
array( $this, 'display_admin_manage' ),
|
591 |
-
plugins_url( 'images/icon16.png', $this->file ),
|
592 |
-
21
|
593 |
-
);
|
594 |
|
595 |
-
|
596 |
-
apply_filters( 'code_snippets_manage_url', 'snippets' ),
|
597 |
-
__('Snippets', 'code-snippets'),
|
598 |
-
__('Manage Snippets', 'code-snippets'),
|
599 |
-
'manage_network_snippets',
|
600 |
-
apply_filters( 'code_snippets_manage_url', 'snippets' ),
|
601 |
-
array( $this, 'display_admin_manage' )
|
602 |
-
);
|
603 |
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
|
|
612 |
|
613 |
add_action( "load-$this->admin_manage", array( $this, 'load_admin_manage' ) );
|
614 |
add_action( "load-$this->admin_single", array( $this, 'load_admin_single' ) );
|
615 |
-
|
616 |
-
add_action( "admin_print_styles-$this->admin_single", array( $this, 'load_editor_styles' ) );
|
617 |
-
add_action( "admin_print_scripts-$this->admin_single", array( $this, 'load_editor_scripts' ) );
|
618 |
}
|
619 |
|
620 |
/**
|
@@ -626,14 +630,14 @@ final class Code_Snippets {
|
|
626 |
* @access private
|
627 |
*
|
628 |
* @uses add_submenu_page() To register the menu page
|
629 |
-
* @uses apply_filters() To retrieve the
|
630 |
* @uses add_action() To enqueue scripts and styles
|
631 |
* @return void
|
632 |
*/
|
633 |
function add_import_admin_menu() {
|
634 |
|
635 |
$this->admin_import = add_submenu_page(
|
636 |
-
|
637 |
__('Import Snippets', 'code-snippets'),
|
638 |
__('Import', 'code-snippets'),
|
639 |
'import_snippets',
|
@@ -642,7 +646,6 @@ final class Code_Snippets {
|
|
642 |
);
|
643 |
|
644 |
$this->admin_import_url = self_admin_url( 'admin.php?page=import-code-snippets' );
|
645 |
-
add_action( "admin_print_styles-$this->admin_import", array( $this, 'load_stylesheet' ) );
|
646 |
add_action( "load-$this->admin_import", array( $this, 'load_admin_import' ) );
|
647 |
}
|
648 |
|
@@ -658,122 +661,190 @@ final class Code_Snippets {
|
|
658 |
*/
|
659 |
function load_admin_icon_style() {
|
660 |
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
667 |
}
|
668 |
|
669 |
/**
|
670 |
-
*
|
671 |
*
|
672 |
-
* @since Code Snippets 1.
|
673 |
-
* @access
|
674 |
-
*
|
675 |
-
* @uses wp_register_script()
|
676 |
-
* @uses wp_enqueue_style() To add the scripts to the queue
|
677 |
*
|
678 |
-
* @
|
|
|
679 |
*/
|
680 |
-
function
|
681 |
|
682 |
-
|
683 |
-
$version = '3.0';
|
684 |
|
685 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
686 |
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
|
|
|
|
693 |
|
694 |
-
|
|
|
|
|
|
|
695 |
|
696 |
-
|
|
|
|
|
|
|
697 |
|
698 |
-
|
|
|
|
|
|
|
699 |
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
706 |
}
|
707 |
|
708 |
-
|
|
|
709 |
|
710 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
711 |
|
712 |
-
|
|
|
713 |
|
714 |
-
|
715 |
-
|
716 |
-
plugins_url( "assets/util/$util.js", $this->file ),
|
717 |
-
array( 'codemirror' ),
|
718 |
-
$version
|
719 |
-
);
|
720 |
}
|
721 |
|
722 |
-
|
723 |
-
|
724 |
-
wp_enqueue_script( array(
|
725 |
-
'codemirror-util-matchbrackets',
|
726 |
-
'codemirror-mode-htmlmixed',
|
727 |
-
'codemirror-mode-xml',
|
728 |
-
'codemirror-mode-js',
|
729 |
-
'codemirror-mode-css',
|
730 |
-
'codemirror-mode-clike',
|
731 |
-
'codemirror-mode-php',
|
732 |
-
'codemirror-util-search',
|
733 |
-
) );
|
734 |
}
|
735 |
|
736 |
/**
|
737 |
-
*
|
738 |
-
*
|
739 |
-
* @since Code Snippets 1.4
|
740 |
-
* @access private
|
741 |
*
|
742 |
-
* @
|
743 |
-
* @
|
744 |
*
|
745 |
-
* @
|
|
|
746 |
*/
|
747 |
-
function
|
748 |
|
749 |
-
|
750 |
-
$version = '3.0';
|
751 |
|
752 |
-
/*
|
|
|
|
|
|
|
|
|
753 |
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
);
|
760 |
|
761 |
-
|
|
|
762 |
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
|
|
|
|
|
|
|
|
|
|
769 |
|
770 |
-
|
771 |
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
) );
|
776 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
777 |
}
|
778 |
|
779 |
/**
|
@@ -803,7 +874,11 @@ final class Code_Snippets {
|
|
803 |
array( '%d' ),
|
804 |
array( '%d' )
|
805 |
);
|
|
|
|
|
806 |
}
|
|
|
|
|
807 |
}
|
808 |
|
809 |
/**
|
@@ -835,18 +910,22 @@ final class Code_Snippets {
|
|
835 |
array( '%d' )
|
836 |
);
|
837 |
$recently_active = array( $id => time() ) + (array) $recently_active;
|
|
|
|
|
838 |
}
|
839 |
|
840 |
-
if ( $table
|
841 |
update_site_option(
|
842 |
'recently_activated_snippets',
|
843 |
$recently_active + (array) get_site_option( 'recently_activated_snippets' )
|
844 |
);
|
845 |
-
|
846 |
update_option(
|
847 |
'recently_activated_snippets',
|
848 |
$recently_active + (array) get_option( 'recently_activated_snippets' )
|
849 |
);
|
|
|
|
|
850 |
}
|
851 |
|
852 |
/**
|
@@ -862,9 +941,10 @@ final class Code_Snippets {
|
|
862 |
global $wpdb;
|
863 |
|
864 |
$table = $this->get_table_name( $scope );
|
865 |
-
$id = intval( $id );
|
866 |
|
867 |
-
$wpdb->query( "DELETE FROM $table WHERE id='
|
|
|
|
|
868 |
}
|
869 |
|
870 |
/**
|
@@ -876,34 +956,43 @@ final class Code_Snippets {
|
|
876 |
* @uses $wpdb To update/add the snippet to the database
|
877 |
* @uses $this->get_table_name() To dynamically retrieve the name of the snippet table
|
878 |
*
|
879 |
-
* @param
|
880 |
* @return int|bool The ID of the snippet on success, false on failure
|
881 |
*/
|
882 |
public function save_snippet( $snippet, $scope = '' ) {
|
883 |
global $wpdb;
|
884 |
|
885 |
-
$
|
886 |
-
$description = mysql_real_escape_string( htmlspecialchars( $snippet['description'] ) );
|
887 |
-
$code = mysql_real_escape_string( htmlspecialchars( $snippet['code'] ) );
|
888 |
|
889 |
-
if ( empty( $name ) or empty( $code ) )
|
890 |
return false;
|
891 |
|
892 |
$table = $this->get_table_name( $scope );
|
893 |
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
-
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
902 |
} else {
|
903 |
-
|
904 |
-
|
905 |
-
|
906 |
-
|
|
|
|
|
907 |
return $wpdb->insert_id;
|
908 |
}
|
909 |
}
|
@@ -914,7 +1003,7 @@ final class Code_Snippets {
|
|
914 |
* @since Code Snippets 1.5
|
915 |
* @access public
|
916 |
*
|
917 |
-
|
918 |
*
|
919 |
* @param file $file The path to the XML file to import
|
920 |
* @param string $scope Import into network-wide table or site-wide table?
|
@@ -927,13 +1016,14 @@ final class Code_Snippets {
|
|
927 |
|
928 |
$xml = simplexml_load_file( $file );
|
929 |
|
930 |
-
foreach ( $xml->children() as $
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
'code' => $child->code,
|
935 |
-
), $scope );
|
936 |
}
|
|
|
|
|
|
|
937 |
return $xml->count();
|
938 |
}
|
939 |
|
@@ -1000,9 +1090,47 @@ final class Code_Snippets {
|
|
1000 |
$result = eval( $code );
|
1001 |
$output = ob_get_contents();
|
1002 |
ob_end_clean();
|
|
|
1003 |
return $result;
|
1004 |
}
|
1005 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1006 |
/**
|
1007 |
* Replaces the text 'Add New Snippet' with 'Edit Snippet'
|
1008 |
*
|
@@ -1041,25 +1169,31 @@ final class Code_Snippets {
|
|
1041 |
|
1042 |
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'action', 'id' ) );
|
1043 |
|
1044 |
-
|
|
|
|
|
1045 |
$this->activate( $id );
|
1046 |
-
wp_redirect( add_query_arg( 'activate', true ) );
|
1047 |
}
|
1048 |
-
elseif ( 'deactivate'
|
1049 |
$this->deactivate( $id );
|
1050 |
-
wp_redirect( add_query_arg( 'deactivate', true ) );
|
1051 |
}
|
1052 |
-
elseif ( 'delete'
|
1053 |
$this->delete_snippet( $id );
|
1054 |
-
wp_redirect( add_query_arg( 'delete', true ) );
|
1055 |
}
|
1056 |
-
elseif ( 'export'
|
1057 |
$this->export( $id );
|
1058 |
}
|
1059 |
-
elseif ( 'export-php'
|
1060 |
$this->export_php( $id );
|
1061 |
}
|
1062 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1063 |
endif;
|
1064 |
|
1065 |
include $this->plugin_dir . 'includes/help/manage.php'; // Load the help tabs
|
@@ -1069,9 +1203,8 @@ final class Code_Snippets {
|
|
1069 |
*/
|
1070 |
require_once $this->plugin_dir . 'includes/class-list-table.php';
|
1071 |
|
1072 |
-
|
1073 |
-
$
|
1074 |
-
$code_snippets_list_table->prepare_items();
|
1075 |
}
|
1076 |
|
1077 |
/**
|
@@ -1087,26 +1220,24 @@ final class Code_Snippets {
|
|
1087 |
*/
|
1088 |
function load_admin_single() {
|
1089 |
|
|
|
|
|
|
|
|
|
|
|
|
|
1090 |
$this->create_tables(); // create the snippet tables if they do not exist
|
1091 |
|
1092 |
-
if ( isset( $_REQUEST['save_snippet'] ) ) {
|
1093 |
|
1094 |
-
|
1095 |
-
|
1096 |
-
|
1097 |
-
'description' => $_REQUEST['snippet_description'],
|
1098 |
-
'code' => $_REQUEST['snippet_code'],
|
1099 |
-
'id' => $_REQUEST['snippet_id'],
|
1100 |
-
) );
|
1101 |
-
} else {
|
1102 |
-
$result = $this->save_snippet( array(
|
1103 |
-
'name' => $_REQUEST['snippet_name'],
|
1104 |
-
'description' => $_REQUEST['snippet_description'],
|
1105 |
-
'code' => $_REQUEST['snippet_code'],
|
1106 |
-
) );
|
1107 |
-
}
|
1108 |
|
1109 |
-
|
|
|
|
|
|
|
1110 |
|
1111 |
if ( ! $result || $result < 1 ) {
|
1112 |
wp_redirect( add_query_arg( 'invalid', true ) );
|
@@ -1130,6 +1261,102 @@ final class Code_Snippets {
|
|
1130 |
|
1131 |
include $this->plugin_dir . 'includes/help/single.php'; // Load the help tabs
|
1132 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1133 |
}
|
1134 |
|
1135 |
/**
|
@@ -1192,6 +1419,37 @@ final class Code_Snippets {
|
|
1192 |
require_once $this->plugin_dir . 'includes/admin/import.php';
|
1193 |
}
|
1194 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1195 |
/**
|
1196 |
* Adds a link pointing to the Manage Snippets page
|
1197 |
*
|
@@ -1218,7 +1476,7 @@ final class Code_Snippets {
|
|
1218 |
*/
|
1219 |
function plugin_meta( $links, $file ) {
|
1220 |
|
1221 |
-
if ( $file
|
1222 |
|
1223 |
$format = '<a href="%1$s" title="%2$s">%3$s</a>';
|
1224 |
|
@@ -1235,7 +1493,7 @@ final class Code_Snippets {
|
|
1235 |
),
|
1236 |
sprintf( $format,
|
1237 |
'http://code-snippets.bungeshea.com/donate/',
|
1238 |
-
__(
|
1239 |
__('Donate', 'code-snippets')
|
1240 |
)
|
1241 |
) );
|
@@ -1256,17 +1514,28 @@ final class Code_Snippets {
|
|
1256 |
*/
|
1257 |
function run_snippets() {
|
1258 |
|
1259 |
-
if ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE )
|
1260 |
-
return;
|
1261 |
|
1262 |
global $wpdb;
|
1263 |
|
1264 |
-
|
1265 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1266 |
|
1267 |
-
if ( is_multisite() ) {
|
|
|
|
|
1268 |
$active_snippets = array_merge(
|
1269 |
-
$wpdb->
|
1270 |
$active_snippets
|
1271 |
);
|
1272 |
}
|
@@ -1274,7 +1543,7 @@ final class Code_Snippets {
|
|
1274 |
if ( count( $active_snippets ) ) {
|
1275 |
foreach( $active_snippets as $snippet ) {
|
1276 |
// execute the php code
|
1277 |
-
$this->execute_snippet( htmlspecialchars_decode( stripslashes( $snippet
|
1278 |
}
|
1279 |
}
|
1280 |
}
|
@@ -1293,8 +1562,6 @@ $code_snippets = new Code_Snippets;
|
|
1293 |
global $cs;
|
1294 |
$cs = &$code_snippets;
|
1295 |
|
1296 |
-
endif; // class exists check
|
1297 |
-
|
1298 |
register_uninstall_hook( $code_snippets->file, 'code_snippets_uninstall' );
|
1299 |
|
1300 |
/**
|
@@ -1315,26 +1582,27 @@ register_uninstall_hook( $code_snippets->file, 'code_snippets_uninstall' );
|
|
1315 |
function code_snippets_uninstall() {
|
1316 |
global $wpdb, $code_snippets;
|
1317 |
if ( is_multisite() ) {
|
1318 |
-
$
|
1319 |
-
if ( $
|
1320 |
-
foreach( $
|
1321 |
-
switch_to_blog( $
|
1322 |
-
$wpdb->query(
|
1323 |
delete_option( 'cs_db_version' );
|
1324 |
delete_option( 'recently_activated_snippets' );
|
1325 |
$code_snippets->remove_caps();
|
1326 |
}
|
1327 |
restore_current_blog();
|
1328 |
}
|
1329 |
-
$wpdb->query(
|
1330 |
delete_site_option( 'recently_activated_snippets' );
|
1331 |
$code_snippets->remove_caps( 'multisite' );
|
1332 |
} else {
|
1333 |
-
$wpdb->query(
|
1334 |
delete_option( 'recently_activated_snippets' );
|
1335 |
delete_option( 'cs_db_version' );
|
1336 |
$code_snippets->remove_caps();
|
1337 |
}
|
1338 |
-
|
1339 |
delete_site_option( 'code_snippets_version' );
|
1340 |
-
}
|
|
|
|
15 |
* Description: An easy, clean and simple way to add code snippets to your site. No need to edit to your theme's functions.php file again!
|
16 |
* Author: Shea Bunge
|
17 |
* Author URI: http://bungeshea.com
|
18 |
+
* Version: 1.7
|
19 |
+
* License: MIT
|
|
|
20 |
* Text Domain: code-snippets
|
21 |
* Domain Path: /languages/
|
22 |
*
|
57 |
final class Code_Snippets {
|
58 |
|
59 |
/**
|
60 |
+
* The version number for this release of the plugin.
|
61 |
+
* This will later be used for upgrades and enqueueing files
|
|
|
|
|
|
|
62 |
*
|
63 |
+
* This should be set to the 'Plugin Version' value,
|
64 |
+
* as defined above in the plugin header
|
65 |
*
|
66 |
* @since Code Snippets 1.0
|
67 |
* @access public
|
68 |
*/
|
69 |
+
public $version = 1.7;
|
70 |
|
71 |
/**
|
72 |
+
* The full URLs to the admin pages
|
|
|
|
|
|
|
|
|
73 |
*
|
74 |
+
* @since Code Snippets 1.0
|
75 |
+
* @access public
|
|
|
|
|
|
|
76 |
*/
|
77 |
+
public $admin_manage_url, $admin_single_url, $admin_import_url;
|
78 |
|
79 |
/**
|
80 |
+
* The hooks for the admin pages
|
81 |
+
* Used primarily for enqueueing scripts and styles
|
|
|
|
|
|
|
82 |
*
|
83 |
* @since Code Snippets 1.0
|
84 |
* @access public
|
85 |
*/
|
86 |
+
public $admin_manage, $admin_single, $admin_import;
|
87 |
|
88 |
/**
|
89 |
+
* Variables to hold plugin paths
|
90 |
*
|
91 |
* @since Code Snippets 1.0
|
92 |
* @access public
|
93 |
*/
|
94 |
+
public $file, $basename, $plugin_dir, $plugin_url;
|
95 |
|
96 |
/**
|
97 |
+
* Stores an instance of the list table class
|
|
|
98 |
*
|
99 |
+
* @since Code Snippets 1.5
|
100 |
* @access public
|
101 |
*/
|
102 |
+
public $list_table;
|
103 |
|
104 |
/**
|
105 |
* The constructor function for our class
|
106 |
+
* Hooks our initialize function to the plugins_loaded action
|
107 |
*
|
108 |
* @since Code Snippets 1.0
|
109 |
* @access private
|
111 |
* @return void
|
112 |
*/
|
113 |
function __construct() {
|
114 |
+
add_action( 'plugins_loaded', array( $this, 'init' ) );
|
115 |
+
|
116 |
+
/* execute the snippets once the plugins are loaded */
|
117 |
+
add_action( 'plugins_loaded', array( $this, 'run_snippets' ), 1 );
|
118 |
+
|
119 |
+
/* add backwards-compatibly for the CS_SAFE_MODE constant */
|
120 |
+
if ( defined( 'CS_SAFE_MODE' ) && ! defined( 'CODE_SNIPPETS_SAFE_MODE' ) ) {
|
121 |
+
define( 'CODE_SNIPPETS_SAFE_MODE', CS_SAFE_MODE );
|
122 |
+
}
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Call our loader functions
|
127 |
+
*
|
128 |
+
* @since Code Snippets 1.7
|
129 |
+
* @access private
|
130 |
+
*
|
131 |
+
* @return void
|
132 |
+
*/
|
133 |
+
function init() {
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Call the functions required to load Code Snippets
|
137 |
+
*/
|
138 |
$this->setup_vars(); // initialise the variables
|
139 |
$this->setup_hooks(); // register the action and filter hooks
|
140 |
$this->upgrade(); // check if we need to change some stuff
|
141 |
+
|
142 |
+
/*
|
143 |
+
* Load up the localization file if we're using WordPress in a different language
|
144 |
+
* Place it in this plugin's "languages" folder and name it "code-snippets-[value in wp-config].mo"
|
145 |
+
*
|
146 |
+
* If you wish to contribute a language file to be included in the Code Snippets package,
|
147 |
+
* please see create an issue on GitHub: https://github.com/bungeshea/code-snippets/issues
|
148 |
+
*/
|
149 |
+
load_plugin_textdomain( 'code-snippets', false, dirname( $this->basename ) . '/languages/' );
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Let extention plugins know that it's okay to load
|
153 |
+
*/
|
154 |
+
do_action( 'code_snippets_init' );
|
155 |
}
|
156 |
|
157 |
/**
|
166 |
global $wpdb;
|
167 |
$this->file = __FILE__;
|
168 |
|
|
|
|
|
|
|
169 |
$this->basename = plugin_basename( $this->file );
|
170 |
$this->plugin_dir = plugin_dir_path( $this->file );
|
171 |
$this->plugin_url = plugin_dir_url ( $this->file );
|
187 |
*/
|
188 |
function setup_hooks() {
|
189 |
|
|
|
|
|
|
|
190 |
/* add the administration menus */
|
191 |
+
add_action( 'admin_menu', array( $this, 'add_admin_menus' ), 5 );
|
192 |
+
add_action( 'network_admin_menu', array( $this, 'add_admin_menus' ), 5 );
|
193 |
|
194 |
/* register the importer */
|
195 |
+
add_action( 'admin_init', array( $this, 'load_importer' ) );
|
196 |
add_action( 'network_admin_menu', array( $this, 'add_import_admin_menu' ) );
|
197 |
|
|
|
|
|
|
|
198 |
/* add helpful links to the Plugins menu */
|
199 |
add_filter( 'plugin_action_links_' . $this->basename, array( $this, 'settings_link' ) );
|
200 |
add_filter( 'plugin_row_meta', array( $this, 'plugin_meta' ), 10, 2 );
|
201 |
|
202 |
/* Add a custom icon to Snippets menu pages */
|
203 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'load_admin_icon_style' ) );
|
204 |
+
|
205 |
+
/* Register the table name with WordPress */
|
206 |
+
add_action( 'init', array( $this, 'set_table_vars' ), 1 );
|
207 |
+
add_action( 'switch_blog', array( $this, 'set_table_vars' ), 1 );
|
208 |
+
|
209 |
+
/* Add the description editor to the Snippets > Add New page */
|
210 |
+
add_action( 'code_snippets_admin_single', array( $this, 'description_editor_box' ), 5 );
|
211 |
+
|
212 |
+
/* Handle saving the user's screen option preferences */
|
213 |
+
add_filter( 'set-screen-option', array( $this, 'set_screen_option' ), 10, 3 );
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Initialize the variables holding the table names
|
218 |
+
*
|
219 |
+
* @since Code Snippets 1.7
|
220 |
+
* @access public
|
221 |
+
*
|
222 |
+
* @uses $wpdb
|
223 |
+
*
|
224 |
+
* @return void
|
225 |
+
*/
|
226 |
+
public function set_table_vars() {
|
227 |
+
global $wpdb;
|
228 |
+
|
229 |
+
$wpdb->snippets = apply_filters( 'code_snippets_table', $wpdb->prefix . 'snippets' );
|
230 |
+
$wpdb->ms_snippets = apply_filters( 'code_snippets_multisite_table', $wpdb->base_prefix . 'ms_snippets' );
|
231 |
+
|
232 |
+
$this->table = &$wpdb->snippets;
|
233 |
+
$this->ms_table = &$wpdb->ms_snippets;
|
234 |
}
|
235 |
|
236 |
/**
|
281 |
*/
|
282 |
function get_table_name( $scope = '', $check_screen = true ) {
|
283 |
|
284 |
+
global $wpdb;
|
285 |
+
|
286 |
$this->create_tables(); // create the snippet tables if they do not exist
|
287 |
|
288 |
if ( ! is_multisite() ) {
|
293 |
$screen = get_current_screen();
|
294 |
$network = $screen->is_network;
|
295 |
}
|
296 |
+
elseif ( 'multisite' === $scope || 'network' === $scope ) {
|
297 |
$network = true;
|
298 |
}
|
299 |
+
elseif ( 'site' === $scope || 'single' === $scope ) {
|
300 |
$network = false;
|
301 |
}
|
302 |
else {
|
303 |
$network = false;
|
304 |
}
|
305 |
|
306 |
+
$table = ( $network ? $wpdb->ms_snippets : $wpdb->snippets );
|
307 |
|
308 |
return $table;
|
309 |
}
|
315 |
* @access public
|
316 |
*
|
317 |
* @uses $this->create_table() To create a single snippet table
|
318 |
+
* @uses $wpdb->get_var() To test of the table exists
|
319 |
*
|
320 |
* @return void
|
321 |
*/
|
322 |
public function create_tables() {
|
323 |
|
324 |
+
global $wpdb;
|
325 |
+
|
326 |
+
if ( $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->snippets'" ) !== $wpdb->snippets )
|
327 |
+
$this->create_table( $wpdb->snippets );
|
328 |
|
329 |
+
if ( is_multisite() && $wpdb->get_var( "SHOW TABLES LIKE '$wpdb->ms_snippets'" ) !== $wpdb->ms_snippets )
|
330 |
+
$this->create_table( $wpdb->ms_snippets );
|
331 |
|
|
|
332 |
}
|
333 |
|
334 |
/**
|
338 |
* @since Code Snippets 1.6
|
339 |
* @access public
|
340 |
*
|
341 |
+
* @uses dbDelta() To add the table to the database
|
342 |
+
*
|
343 |
* @param string $table_name The name of the table to create
|
344 |
* @return void
|
345 |
*/
|
346 |
function create_table( $table_name ) {
|
347 |
+
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
348 |
|
349 |
global $wpdb;
|
350 |
|
351 |
+
if ( ! empty( $wpdb->charset ) ) {
|
352 |
+
$charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
|
353 |
+
}
|
354 |
+
|
355 |
+
if ( ! empty( $wpdb->collate ) ) {
|
356 |
+
$charset_collate .= " COLLATE $wpdb->collate";
|
|
|
|
|
|
|
|
|
|
|
357 |
}
|
358 |
+
|
359 |
+
$sql = "CREATE TABLE $table_name (
|
360 |
+
id bigint(20) auto_increment,
|
361 |
+
name tinytext not null,
|
362 |
+
description text,
|
363 |
+
code longtext not null,
|
364 |
+
active tinyint(1) not null default 0,
|
365 |
+
PRIMARY KEY (id),
|
366 |
+
KEY id (id)
|
367 |
+
|
368 |
+
) {$charset_collate};";
|
369 |
+
|
370 |
+
dbDelta( apply_filters( 'code_snippets_table_sql', $sql ) );
|
371 |
+
|
372 |
+
do_action( 'code_snippets_create_table', $table_name );
|
373 |
}
|
374 |
|
375 |
/**
|
378 |
* @since Code Snippets 1.2
|
379 |
* @access private
|
380 |
*
|
381 |
+
* @return void
|
382 |
*/
|
383 |
function upgrade() {
|
384 |
+
global $wpdb;
|
|
|
|
|
|
|
|
|
385 |
|
386 |
/* get the current plugin version from the database */
|
387 |
if ( get_option( 'cs_db_version' ) ) {
|
393 |
$this->current_version = get_site_option( 'code_snippets_version', $this->version );
|
394 |
}
|
395 |
|
396 |
+
if ( ! get_site_option( 'code_snippets_version' ) || $this->current_version < 1.5 ) {
|
|
|
|
|
|
|
397 |
|
398 |
/* This is the first time the plugin has run */
|
399 |
|
400 |
+
$this->add_caps(); // register the capabilities once only
|
401 |
|
402 |
if ( is_multisite() ) {
|
403 |
+
$this->add_caps( 'multisite' ); // register the multisite capabilities once only
|
404 |
}
|
405 |
}
|
406 |
|
407 |
+
/* skip this if we're on the latest version */
|
408 |
+
if ( get_site_option( 'code_snippets_version' ) < $this->version ) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
409 |
|
410 |
+
/* migrate the recently_network_activated_snippets to the site options */
|
411 |
+
if ( is_multisite() && get_option( 'recently_network_activated_snippets' ) ) {
|
412 |
+
add_site_option( 'recently_activated_snippets', get_option( 'recently_network_activated_snippets', array() ) );
|
413 |
+
delete_option( 'recently_network_activated_snippets' );
|
414 |
}
|
415 |
|
416 |
+
if ( $this->current_version < 1.2 ) {
|
417 |
+
/* The 'Complete Uninstall' option was removed in version 1.2 */
|
418 |
+
delete_option( 'cs_complete_uninstall' );
|
419 |
+
}
|
|
|
|
|
|
|
|
|
420 |
|
|
|
421 |
/* Update the current version */
|
422 |
update_site_option( 'code_snippets_version', $this->version );
|
423 |
}
|
|
|
|
|
424 |
}
|
425 |
|
426 |
/**
|
427 |
+
* Handles saving the user's screen option preference
|
|
|
|
|
|
|
|
|
428 |
*
|
429 |
* @since Code Snippets 1.5
|
430 |
* @access private
|
|
|
|
|
431 |
*/
|
432 |
+
function set_screen_option( $status, $option, $value ) {
|
433 |
+
if ( 'snippets_per_page' === $option ) return $value;
|
434 |
}
|
435 |
|
436 |
/**
|
446 |
*/
|
447 |
public function add_caps( $scope = '' ) {
|
448 |
|
449 |
+
$network = ( 'multisite' === $scope || 'network' === $scope ? true : false );
|
450 |
|
451 |
if ( $network && is_multisite() )
|
452 |
$this->setup_ms_roles( 'add' );
|
467 |
*/
|
468 |
public function remove_caps( $scope = '' ) {
|
469 |
|
470 |
+
$network = ( 'multisite' === $scope || 'network' === $scope ? true : false );
|
471 |
|
472 |
if ( $network && is_multisite() )
|
473 |
$this->setup_ms_roles( 'remove' );
|
486 |
*/
|
487 |
function setup_roles( $action = 'install' ) {
|
488 |
|
489 |
+
if ( 'install' === $action || 'add' === $action )
|
490 |
$install = true;
|
491 |
|
492 |
+
elseif ( 'uninstall' === $action || 'remove' === $action )
|
493 |
$install = false;
|
494 |
|
495 |
else
|
525 |
|
526 |
if ( ! is_multisite() ) return;
|
527 |
|
528 |
+
if ( 'install' === $action || 'add' === $action )
|
529 |
$install = true;
|
530 |
|
531 |
+
elseif ( 'uninstall' === $action || 'remove' === $action )
|
532 |
$install = false;
|
533 |
|
534 |
else
|
560 |
*
|
561 |
* @uses add_menu_page() To register a top-level menu
|
562 |
* @uses add_submenu_page() To register a submenu page
|
563 |
+
* @uses apply_filters() To retrieve the current menu slug
|
564 |
* @uses plugins_url() To retrieve the URL to a resource
|
565 |
* @return void
|
566 |
*/
|
567 |
function add_admin_menus() {
|
568 |
|
569 |
+
/* Use a different screen icon for the MP6 interface */
|
570 |
+
if ( get_user_option( 'admin_color' ) !== 'mp6' )
|
571 |
+
$menu_icon = apply_filters( 'code_snippets_menu_icon', plugins_url( 'images/menu-icon.png', $this->file ) );
|
572 |
+
else
|
573 |
+
$menu_icon = 'div';
|
574 |
+
|
575 |
+
/* Add the top-level menu and relevant subpage */
|
576 |
$this->admin_manage = add_menu_page(
|
577 |
__('Snippets', 'code-snippets'),
|
578 |
__('Snippets', 'code-snippets'),
|
579 |
+
is_network_admin() ? 'manage_network_snippets' : 'manage_snippets',
|
580 |
+
$this->admin_manage_slug,
|
581 |
array( $this, 'display_admin_manage' ),
|
582 |
+
$menu_icon,
|
583 |
+
is_network_admin() ? 21 : 67
|
584 |
);
|
585 |
|
586 |
add_submenu_page(
|
587 |
+
$this->admin_manage_slug,
|
588 |
__('Snippets', 'code-snippets'),
|
589 |
+
__('Manage', 'code-snippets'),
|
590 |
+
is_network_admin() ? 'manage_network_snippets' : 'manage_snippets',
|
591 |
+
$this->admin_manage_slug,
|
592 |
array( $this, 'display_admin_manage')
|
593 |
);
|
594 |
|
595 |
+
/* Add the Edit/Add New Snippet page */
|
596 |
+
if ( isset( $_REQUEST['page'], $_REQUEST['edit'] ) &&
|
597 |
+
$this->admin_single_slug === $_REQUEST['page'] ) {
|
598 |
+
|
599 |
+
$this->admin_single = add_submenu_page(
|
600 |
+
$this->admin_manage_slug,
|
601 |
+
__('Edit Snippet', 'code-snippets'),
|
602 |
+
__('Edit', 'code-snippets'),
|
603 |
+
is_network_admin() ? 'install_network_snippets' : 'install_snippets',
|
604 |
+
$this->admin_single_slug,
|
605 |
+
array( $this, 'display_admin_single' )
|
606 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
607 |
|
608 |
+
} else {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
609 |
|
610 |
+
$this->admin_single = add_submenu_page(
|
611 |
+
$this->admin_manage_slug,
|
612 |
+
__('Add New Snippet', 'code-snippets'),
|
613 |
+
__('Add New', 'code-snippets'),
|
614 |
+
is_network_admin() ? 'install_network_snippets' : 'install_snippets',
|
615 |
+
$this->admin_single_slug,
|
616 |
+
array( $this, 'display_admin_single' )
|
617 |
+
);
|
618 |
+
}
|
619 |
|
620 |
add_action( "load-$this->admin_manage", array( $this, 'load_admin_manage' ) );
|
621 |
add_action( "load-$this->admin_single", array( $this, 'load_admin_single' ) );
|
|
|
|
|
|
|
622 |
}
|
623 |
|
624 |
/**
|
630 |
* @access private
|
631 |
*
|
632 |
* @uses add_submenu_page() To register the menu page
|
633 |
+
* @uses apply_filters() To retrieve the current menu slug
|
634 |
* @uses add_action() To enqueue scripts and styles
|
635 |
* @return void
|
636 |
*/
|
637 |
function add_import_admin_menu() {
|
638 |
|
639 |
$this->admin_import = add_submenu_page(
|
640 |
+
$this->admin_manage_slug,
|
641 |
__('Import Snippets', 'code-snippets'),
|
642 |
__('Import', 'code-snippets'),
|
643 |
'import_snippets',
|
646 |
);
|
647 |
|
648 |
$this->admin_import_url = self_admin_url( 'admin.php?page=import-code-snippets' );
|
|
|
649 |
add_action( "load-$this->admin_import", array( $this, 'load_admin_import' ) );
|
650 |
}
|
651 |
|
661 |
*/
|
662 |
function load_admin_icon_style() {
|
663 |
|
664 |
+
if ( 'mp6' === get_user_option( 'admin_color' ) ) {
|
665 |
+
|
666 |
+
wp_enqueue_style(
|
667 |
+
'icon-snippets',
|
668 |
+
plugins_url( 'assets/menu-icon.mp6.css', $this->file ),
|
669 |
+
false,
|
670 |
+
$this->version
|
671 |
+
);
|
672 |
+
|
673 |
+
} else {
|
674 |
+
|
675 |
+
wp_enqueue_style(
|
676 |
+
'icon-snippets',
|
677 |
+
plugins_url( 'assets/screen-icon.css', $this->file ),
|
678 |
+
false,
|
679 |
+
$this->version
|
680 |
+
);
|
681 |
+
}
|
682 |
}
|
683 |
|
684 |
/**
|
685 |
+
* Converts an array of snippet data into a snippet object
|
686 |
*
|
687 |
+
* @since Code Snippets 1.7
|
688 |
+
* @access public
|
|
|
|
|
|
|
689 |
*
|
690 |
+
* @param mixed $data The snippet data to convert
|
691 |
+
* @return object The resulting snippet object
|
692 |
*/
|
693 |
+
public function build_snippet_object( $data = null ) {
|
694 |
|
695 |
+
$snippet = new stdClass;
|
|
|
696 |
|
697 |
+
// define an empty snippet object (or one with default values )
|
698 |
+
$snippet->id = 0;
|
699 |
+
$snippet->name = '';
|
700 |
+
$snippet->description = '';
|
701 |
+
$snippet->code = '';
|
702 |
+
$snippet->active = 0;
|
703 |
+
$snippet = apply_filters( 'code_snippets_build_default_snippet', $snippet );
|
704 |
|
705 |
+
if ( ! isset( $data ) ) {
|
706 |
+
return $snippet;
|
707 |
+
}
|
708 |
+
elseif ( is_object( $data ) ) {
|
709 |
+
/* If we already have a snippet object, merge it with the default */
|
710 |
+
return (object) array_merge( (array) $snippet, (array) $data );
|
711 |
+
}
|
712 |
+
elseif ( is_array( $data ) ) {
|
713 |
|
714 |
+
if ( isset( $data['id'] ) )
|
715 |
+
$snippet->id = $data['id'];
|
716 |
+
elseif ( isset( $data['snippet_id'] ) )
|
717 |
+
$snippet->id = $data['snippet_id'];
|
718 |
|
719 |
+
if ( isset( $data['name'] ) )
|
720 |
+
$snippet->name = $data['name'];
|
721 |
+
elseif ( isset( $data['snippet_name'] ) )
|
722 |
+
$snippet->name = $data['snippet_name'];
|
723 |
|
724 |
+
if ( isset( $data['description'] ) )
|
725 |
+
$snippet->description = $data['description'];
|
726 |
+
elseif ( isset( $data['snippet_description'] ) )
|
727 |
+
$snippet->description = $data['snippet_description'];
|
728 |
|
729 |
+
if ( isset( $data['code'] ) )
|
730 |
+
$snippet->code = $data['code'];
|
731 |
+
elseif ( isset( $data['snippet_code'] ) )
|
732 |
+
$snippet->code = $data['snippet_code'];
|
733 |
+
|
734 |
+
|
735 |
+
if ( isset( $data['active' ] ) )
|
736 |
+
$snippet->active = $data['active'];
|
737 |
+
elseif ( isset( $data['snippet_active'] ) )
|
738 |
+
$snippet->active = $data['snippet_active'];
|
739 |
+
|
740 |
+
return apply_filters( 'code_snippets_build_snippet_object', $snippet, $data );
|
741 |
}
|
742 |
|
743 |
+
return $snippet;
|
744 |
+
}
|
745 |
|
746 |
+
/**
|
747 |
+
* Retrieve a list of snippets from the database
|
748 |
+
*
|
749 |
+
* @since Code Snippets 1.7
|
750 |
+
* @access public
|
751 |
+
*
|
752 |
+
* @uses $wpdb To query the database for snippets
|
753 |
+
* @uses $this->get_table_name() To dynamically retrieve the snippet table name
|
754 |
+
*
|
755 |
+
* @param string $scope Retrieve multisite-wide or site-wide snippets?
|
756 |
+
* @return array An array of snippet objects
|
757 |
+
*/
|
758 |
+
public function get_snippets( $scope = '' ) {
|
759 |
+
global $wpdb;
|
760 |
|
761 |
+
$table = $this->get_table_name( $scope );
|
762 |
+
$snippets = $wpdb->get_results( "SELECT * FROM $table", ARRAY_A );
|
763 |
|
764 |
+
foreach( $snippets as $index => $snippet ) {
|
765 |
+
$snippets[ $index ] = $this->unescape_snippet_data( $snippet );
|
|
|
|
|
|
|
|
|
766 |
}
|
767 |
|
768 |
+
return apply_filters( 'code_snippets_get_snippets', $snippets, $scope );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
769 |
}
|
770 |
|
771 |
/**
|
772 |
+
* Escape snippet data for inserting into the database
|
|
|
|
|
|
|
773 |
*
|
774 |
+
* @since Code Snippets 1.7
|
775 |
+
* @access public
|
776 |
*
|
777 |
+
* @param mixed $snippet An object or array containing the data to escape
|
778 |
+
* @return object The resulting snippet object, with data escaped
|
779 |
*/
|
780 |
+
public function escape_snippet_data( $snippet ) {
|
781 |
|
782 |
+
$snippet = $this->build_snippet_object( $snippet );
|
|
|
783 |
|
784 |
+
/* remove the <?php and ?> tags from the snippet */
|
785 |
+
$snippet->code = trim ( $snippet->code );
|
786 |
+
$snippet->code = ltrim( $snippet->code, '<?php' );
|
787 |
+
$snippet->code = ltrim( $snippet->code, '<?' );
|
788 |
+
$snippet->code = rtrim( $snippet->code, '?>' );
|
789 |
|
790 |
+
/* escape the data */
|
791 |
+
$snippet->name = mysql_real_escape_string( htmlspecialchars( $snippet->name ) );
|
792 |
+
$snippet->description = mysql_real_escape_string( htmlspecialchars( $snippet->description ) );
|
793 |
+
$snippet->code = mysql_real_escape_string( htmlspecialchars( $snippet->code ) );
|
794 |
+
$snippet->id = intval( $snippet->id );
|
|
|
795 |
|
796 |
+
return apply_filters( 'code_snippets_escape_snippet_data', $snippet );
|
797 |
+
}
|
798 |
|
799 |
+
/**
|
800 |
+
* Unescape snippet data after retrieving from the database
|
801 |
+
* ready for use
|
802 |
+
*
|
803 |
+
* @since Code Snippets 1.7
|
804 |
+
* @access public
|
805 |
+
*
|
806 |
+
* @param mixed $snippet An object or array containing the data to unescape
|
807 |
+
* @return object The resulting snippet object, with data unescaped
|
808 |
+
*/
|
809 |
+
public function unescape_snippet_data( $snippet ) {
|
810 |
|
811 |
+
$snippet = $this->build_snippet_object( $snippet );
|
812 |
|
813 |
+
$snippet->name = htmlspecialchars_decode( stripslashes( $snippet->name ) );
|
814 |
+
$snippet->code = htmlspecialchars_decode( stripslashes( $snippet->code ) );
|
815 |
+
$snippet->description = htmlspecialchars_decode( stripslashes( $snippet->description ) );
|
|
|
816 |
|
817 |
+
return apply_filters( 'code_snippets_unescape_snippet_data', $snippet );
|
818 |
+
}
|
819 |
+
|
820 |
+
/**
|
821 |
+
* Retrieve a single snippets from the database
|
822 |
+
* Will return empty snippet object if no snippet
|
823 |
+
* ID is specified
|
824 |
+
*
|
825 |
+
* @since Code Snippets 1.7
|
826 |
+
* @access public
|
827 |
+
*
|
828 |
+
* @uses $wpdb To query the database for snippets
|
829 |
+
* @uses $this->get_table_name() To dynamically retrieve the snippet table name
|
830 |
+
*
|
831 |
+
* @param string $scope Retrieve a multisite-wide or site-wide snippet?
|
832 |
+
* @return object A single snippet object
|
833 |
+
*/
|
834 |
+
public function get_snippet( $id = 0, $scope = '' ) {
|
835 |
+
global $wpdb;
|
836 |
+
$table = $this->get_table_name( $scope );
|
837 |
+
|
838 |
+
if ( ! empty( $id ) && 0 !== intval( $id ) ) {
|
839 |
+
/* Retrieve the snippet from the database */
|
840 |
+
$snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $id ) );
|
841 |
+
/* Unescape the snippet data, ready for use */
|
842 |
+
$snippet = $this->unescape_snippet_data( $snippet );
|
843 |
+
} else {
|
844 |
+
// get an empty snippet object
|
845 |
+
$snippet = $this->build_snippet_object();
|
846 |
+
}
|
847 |
+
return apply_filters( 'code_snippets_get_snippet', $snippet, $id, $scope );
|
848 |
}
|
849 |
|
850 |
/**
|
874 |
array( '%d' ),
|
875 |
array( '%d' )
|
876 |
);
|
877 |
+
|
878 |
+
do_action( 'code_snippets_activate_snippet', $id, $scope );
|
879 |
}
|
880 |
+
|
881 |
+
do_action( 'code_snippets_activate', $ids, $scope );
|
882 |
}
|
883 |
|
884 |
/**
|
910 |
array( '%d' )
|
911 |
);
|
912 |
$recently_active = array( $id => time() ) + (array) $recently_active;
|
913 |
+
|
914 |
+
do_action( 'code_snippets_deactivate_snippet', $id, $scope );
|
915 |
}
|
916 |
|
917 |
+
if ( $table === $wpdb->ms_snippets )
|
918 |
update_site_option(
|
919 |
'recently_activated_snippets',
|
920 |
$recently_active + (array) get_site_option( 'recently_activated_snippets' )
|
921 |
);
|
922 |
+
elseif ( $table === $wpdb->snippets )
|
923 |
update_option(
|
924 |
'recently_activated_snippets',
|
925 |
$recently_active + (array) get_option( 'recently_activated_snippets' )
|
926 |
);
|
927 |
+
|
928 |
+
do_action( 'code_snippets_deactivate', $ids, $scope );
|
929 |
}
|
930 |
|
931 |
/**
|
941 |
global $wpdb;
|
942 |
|
943 |
$table = $this->get_table_name( $scope );
|
|
|
944 |
|
945 |
+
$wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE id='%d' LIMIT 1", intval( $id ) ) );
|
946 |
+
|
947 |
+
do_action( 'code_snippets_delete_snippet', $id, $scope );
|
948 |
}
|
949 |
|
950 |
/**
|
956 |
* @uses $wpdb To update/add the snippet to the database
|
957 |
* @uses $this->get_table_name() To dynamically retrieve the name of the snippet table
|
958 |
*
|
959 |
+
* @param object $snippet The snippet to add/update to the database
|
960 |
* @return int|bool The ID of the snippet on success, false on failure
|
961 |
*/
|
962 |
public function save_snippet( $snippet, $scope = '' ) {
|
963 |
global $wpdb;
|
964 |
|
965 |
+
$snippet = $this->escape_snippet_data( $snippet );
|
|
|
|
|
966 |
|
967 |
+
if ( empty( $snippet->name ) or empty( $snippet->code ) )
|
968 |
return false;
|
969 |
|
970 |
$table = $this->get_table_name( $scope );
|
971 |
|
972 |
+
$fields = '';
|
973 |
+
foreach ( get_object_vars( $snippet ) as $field => $value ) {
|
974 |
+
if ( 'id' !== $field ) {
|
975 |
+
$fields .= "{$field}='{$value}',";
|
976 |
+
}
|
977 |
+
}
|
978 |
+
$fields = rtrim( $fields, ',' );
|
979 |
+
|
980 |
+
if ( isset( $snippet->id ) && 0 !== $snippet->id ) {
|
981 |
+
|
982 |
+
if ( $fields ) {
|
983 |
+
$wpdb->query( $wpdb->prepare( "UPDATE $table SET $fields WHERE id='%d' LIMIT 1", $snippet->id ) );
|
984 |
+
}
|
985 |
+
|
986 |
+
do_action( 'code_snippets_update_snippet', $snippet, $table );
|
987 |
+
return $snippet->id;
|
988 |
+
|
989 |
} else {
|
990 |
+
|
991 |
+
if ( $fields ) {
|
992 |
+
$wpdb->query( "INSERT INTO $table SET $fields" );
|
993 |
+
}
|
994 |
+
|
995 |
+
do_action( 'code_snippets_create_snippet', $snippet, $table );
|
996 |
return $wpdb->insert_id;
|
997 |
}
|
998 |
}
|
1003 |
* @since Code Snippets 1.5
|
1004 |
* @access public
|
1005 |
*
|
1006 |
+
* @uses $this->save_snippet() To add the snippets to the database
|
1007 |
*
|
1008 |
* @param file $file The path to the XML file to import
|
1009 |
* @param string $scope Import into network-wide table or site-wide table?
|
1016 |
|
1017 |
$xml = simplexml_load_file( $file );
|
1018 |
|
1019 |
+
foreach ( $xml->children() as $snippet ) {
|
1020 |
+
/* force manual build of object to strip out unsupported fields
|
1021 |
+
by converting snippet object into array */
|
1022 |
+
$this->save_snippet( get_object_vars( $snippet ), $scope );
|
|
|
|
|
1023 |
}
|
1024 |
+
|
1025 |
+
do_action( 'code_snippets_import', $xml, $scope );
|
1026 |
+
|
1027 |
return $xml->count();
|
1028 |
}
|
1029 |
|
1090 |
$result = eval( $code );
|
1091 |
$output = ob_get_contents();
|
1092 |
ob_end_clean();
|
1093 |
+
do_action( 'code_snippets_execute_snippet', $code );
|
1094 |
return $result;
|
1095 |
}
|
1096 |
|
1097 |
+
/**
|
1098 |
+
* Returns the (de)activate link for a snippet
|
1099 |
+
*
|
1100 |
+
* @since Code Snippets 1.7
|
1101 |
+
* @access public
|
1102 |
+
*
|
1103 |
+
* @param mixed $snippet A snippet object
|
1104 |
+
* @return The HTML code for the (de)activation link
|
1105 |
+
*/
|
1106 |
+
public function get_activate_link( $snippet ) {
|
1107 |
+
|
1108 |
+
$snippet = $this->build_snippet_object( $snippet );
|
1109 |
+
$screen = get_current_screen();
|
1110 |
+
|
1111 |
+
if ( $snippet->active ) {
|
1112 |
+
$activate_link = sprintf(
|
1113 |
+
'<a href="%1$s">%2$s</a>',
|
1114 |
+
add_query_arg( array(
|
1115 |
+
'action' => 'deactivate',
|
1116 |
+
'id' => $snippet->id
|
1117 |
+
), $this->admin_manage_url ),
|
1118 |
+
$screen->is_network ? __('Network Deactivate', 'code-snippets') : __('Deactivate', 'code-snippets')
|
1119 |
+
);
|
1120 |
+
} else {
|
1121 |
+
$activate_link = sprintf(
|
1122 |
+
'<a href="%1$s">%2$s</a>',
|
1123 |
+
add_query_arg( array(
|
1124 |
+
'action' => 'activate',
|
1125 |
+
'id' => $snippet->id
|
1126 |
+
), $this->admin_manage_url ),
|
1127 |
+
$screen->is_network ? __('Network Activate', 'code-snippets') : __('Activate', 'code-snippets')
|
1128 |
+
);
|
1129 |
+
}
|
1130 |
+
|
1131 |
+
return apply_filters( 'code_snippets_activate_link', $activate_link, $snippet );
|
1132 |
+
}
|
1133 |
+
|
1134 |
/**
|
1135 |
* Replaces the text 'Add New Snippet' with 'Edit Snippet'
|
1136 |
*
|
1169 |
|
1170 |
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'action', 'id' ) );
|
1171 |
|
1172 |
+
$action = sanitize_key( $_GET['action'] );
|
1173 |
+
|
1174 |
+
if ( 'activate' === $action ) {
|
1175 |
$this->activate( $id );
|
|
|
1176 |
}
|
1177 |
+
elseif ( 'deactivate' === $action ) {
|
1178 |
$this->deactivate( $id );
|
|
|
1179 |
}
|
1180 |
+
elseif ( 'delete' === $action ) {
|
1181 |
$this->delete_snippet( $id );
|
|
|
1182 |
}
|
1183 |
+
elseif ( 'export' === $action ) {
|
1184 |
$this->export( $id );
|
1185 |
}
|
1186 |
+
elseif ( 'export-php' === $action ) {
|
1187 |
$this->export_php( $id );
|
1188 |
}
|
1189 |
|
1190 |
+
if ( 'export' !== $action || 'export-php' !== $action ) {
|
1191 |
+
wp_redirect( apply_filters(
|
1192 |
+
"code_snippets_{$action}_redirect",
|
1193 |
+
add_query_arg( $action, true )
|
1194 |
+
) );
|
1195 |
+
}
|
1196 |
+
|
1197 |
endif;
|
1198 |
|
1199 |
include $this->plugin_dir . 'includes/help/manage.php'; // Load the help tabs
|
1203 |
*/
|
1204 |
require_once $this->plugin_dir . 'includes/class-list-table.php';
|
1205 |
|
1206 |
+
$this->list_table = new Code_Snippets_List_Table();
|
1207 |
+
$this->list_table->prepare_items();
|
|
|
1208 |
}
|
1209 |
|
1210 |
/**
|
1220 |
*/
|
1221 |
function load_admin_single() {
|
1222 |
|
1223 |
+
$screen = get_current_screen();
|
1224 |
+
$can_edit = current_user_can( $screen->is_network ? 'edit_network_snippets' : 'edit_snippets' );
|
1225 |
+
|
1226 |
+
if ( isset( $_REQUEST['edit'] ) && ! $can_edit )
|
1227 |
+
wp_die( __("Sorry, you're not allowed to edit snippets", 'code-snippets') );
|
1228 |
+
|
1229 |
$this->create_tables(); // create the snippet tables if they do not exist
|
1230 |
|
1231 |
+
if ( isset( $_REQUEST['save_snippet'] ) || isset( $_REQUEST['save_snippet_activate'] ) ) {
|
1232 |
|
1233 |
+
$result = $this->save_snippet( $_POST );
|
1234 |
+
|
1235 |
+
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'added', 'updated', 'activated', 'invalid' ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1236 |
|
1237 |
+
if ( isset( $_REQUEST['save_snippet_activate'] ) && $result ) {
|
1238 |
+
$this->activate( $result );
|
1239 |
+
$_SERVER['REQUEST_URI'] = add_query_arg( 'activated', true );
|
1240 |
+
}
|
1241 |
|
1242 |
if ( ! $result || $result < 1 ) {
|
1243 |
wp_redirect( add_query_arg( 'invalid', true ) );
|
1261 |
|
1262 |
include $this->plugin_dir . 'includes/help/single.php'; // Load the help tabs
|
1263 |
|
1264 |
+
add_filter( 'admin_enqueue_scripts', array( $this, 'admin_single_enqueue_scripts' ) );
|
1265 |
+
}
|
1266 |
+
|
1267 |
+
/**
|
1268 |
+
* Registers and loads the code editor's scripts
|
1269 |
+
*
|
1270 |
+
* @since Code Snippets 1.7
|
1271 |
+
* @access private
|
1272 |
+
*
|
1273 |
+
* @uses wp_register_script()
|
1274 |
+
* @uses wp_register_style()
|
1275 |
+
* @uses wp_enqueue_script() To add the scripts to the queue
|
1276 |
+
* @uses wp_enqueue_style() To add the stylesheets to the queue
|
1277 |
+
*
|
1278 |
+
* @return void
|
1279 |
+
*/
|
1280 |
+
function admin_single_enqueue_scripts( $hook ) {
|
1281 |
+
|
1282 |
+
if ( $hook !== $this->admin_single )
|
1283 |
+
return;
|
1284 |
+
|
1285 |
+
/* CodeMirror package version */
|
1286 |
+
$codemirror_version = '3.11';
|
1287 |
+
|
1288 |
+
/* CodeMirror base framework */
|
1289 |
+
|
1290 |
+
wp_register_script(
|
1291 |
+
'codemirror',
|
1292 |
+
plugins_url( 'assets/codemirror/lib/codemirror.js', $this->file ),
|
1293 |
+
false,
|
1294 |
+
$codemirror_version
|
1295 |
+
);
|
1296 |
+
|
1297 |
+
wp_register_style(
|
1298 |
+
'codemirror',
|
1299 |
+
plugins_url( 'assets/codemirror/lib/codemirror.css', $this->file ),
|
1300 |
+
false,
|
1301 |
+
$codemirror_version
|
1302 |
+
);
|
1303 |
+
|
1304 |
+
/* CodeMirror modes */
|
1305 |
+
|
1306 |
+
$modes = array( 'php', 'clike' );
|
1307 |
+
|
1308 |
+
foreach ( $modes as $mode ) {
|
1309 |
+
|
1310 |
+
wp_register_script(
|
1311 |
+
"codemirror-mode-$mode",
|
1312 |
+
plugins_url( "assets/codemirror/mode/$mode.js", $this->file ),
|
1313 |
+
array( 'codemirror' ),
|
1314 |
+
$codemirror_version
|
1315 |
+
);
|
1316 |
+
}
|
1317 |
+
|
1318 |
+
/* CodeMirror addons */
|
1319 |
+
|
1320 |
+
$addons = array( 'dialog', 'searchcursor', 'search', 'matchbrackets' );
|
1321 |
+
|
1322 |
+
foreach ( $addons as $addon ) {
|
1323 |
+
|
1324 |
+
wp_register_script(
|
1325 |
+
"codemirror-addon-$addon",
|
1326 |
+
plugins_url( "assets/codemirror/addon/$addon.js", $this->file ),
|
1327 |
+
array( 'codemirror' ),
|
1328 |
+
$codemirror_version
|
1329 |
+
);
|
1330 |
+
}
|
1331 |
+
|
1332 |
+
wp_register_style(
|
1333 |
+
'codemirror-addon-dialog',
|
1334 |
+
plugins_url( 'assets/codemirror/addon/dialog.css', $this->file ),
|
1335 |
+
array( 'codemirror' ),
|
1336 |
+
$codemirror_version
|
1337 |
+
);
|
1338 |
+
|
1339 |
+
/* Enqueue the registered scripts */
|
1340 |
+
wp_enqueue_script( array(
|
1341 |
+
'codemirror-addon-matchbrackets',
|
1342 |
+
'codemirror-mode-clike',
|
1343 |
+
'codemirror-mode-php',
|
1344 |
+
'codemirror-addon-search',
|
1345 |
+
) );
|
1346 |
+
|
1347 |
+
/* Enqueue the registered stylesheets */
|
1348 |
+
wp_enqueue_style( array(
|
1349 |
+
'codemirror',
|
1350 |
+
'codemirror-addon-dialog',
|
1351 |
+
) );
|
1352 |
+
|
1353 |
+
/* Enqueue custom styling */
|
1354 |
+
wp_enqueue_style(
|
1355 |
+
'code-snippets-admin-single',
|
1356 |
+
plugins_url( 'assets/admin-single.css', $this->file ),
|
1357 |
+
false,
|
1358 |
+
$this->version
|
1359 |
+
);
|
1360 |
}
|
1361 |
|
1362 |
/**
|
1419 |
require_once $this->plugin_dir . 'includes/admin/import.php';
|
1420 |
}
|
1421 |
|
1422 |
+
/**
|
1423 |
+
* Add a description editor to the Add New/Edit Snippet page
|
1424 |
+
*
|
1425 |
+
* @since Code Snippets 1.7
|
1426 |
+
* @access private
|
1427 |
+
*
|
1428 |
+
*/
|
1429 |
+
function description_editor_box( $snippet ) {
|
1430 |
+
?>
|
1431 |
+
|
1432 |
+
<label for="snippet_description">
|
1433 |
+
<h3><div style="position: absolute;"><?php _e('Description', 'code-snippets');
|
1434 |
+
?> <span style="font-weight: normal;"><?php esc_html_e('(Optional)', 'code-snippets'); ?></span></div></h3>
|
1435 |
+
</label>
|
1436 |
+
|
1437 |
+
<?php
|
1438 |
+
|
1439 |
+
remove_editor_styles(); // stop custom theme styling interfering with the editor
|
1440 |
+
|
1441 |
+
wp_editor(
|
1442 |
+
$snippet->description,
|
1443 |
+
'description',
|
1444 |
+
apply_filters( 'code_snippets_description_editor_settings', array(
|
1445 |
+
'textarea_name' => 'snippet_description',
|
1446 |
+
'textarea_rows' => 10,
|
1447 |
+
'teeny' => true,
|
1448 |
+
'media_buttons' => false,
|
1449 |
+
) )
|
1450 |
+
);
|
1451 |
+
}
|
1452 |
+
|
1453 |
/**
|
1454 |
* Adds a link pointing to the Manage Snippets page
|
1455 |
*
|
1476 |
*/
|
1477 |
function plugin_meta( $links, $file ) {
|
1478 |
|
1479 |
+
if ( $file !== $this->basename ) return $links;
|
1480 |
|
1481 |
$format = '<a href="%1$s" title="%2$s">%3$s</a>';
|
1482 |
|
1493 |
),
|
1494 |
sprintf( $format,
|
1495 |
'http://code-snippets.bungeshea.com/donate/',
|
1496 |
+
__("Support this plugin's development", 'code-snippets'),
|
1497 |
__('Donate', 'code-snippets')
|
1498 |
)
|
1499 |
) );
|
1514 |
*/
|
1515 |
function run_snippets() {
|
1516 |
|
1517 |
+
if ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) return;
|
|
|
1518 |
|
1519 |
global $wpdb;
|
1520 |
|
1521 |
+
if ( ! isset( $wpdb->table ) )
|
1522 |
+
$this->set_table_vars();
|
1523 |
+
|
1524 |
+
$active_snippets = array();
|
1525 |
+
|
1526 |
+
// check that the table exists before continuing
|
1527 |
+
if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->snippets}';" ) ) {
|
1528 |
+
|
1529 |
+
// grab the active snippets from the database
|
1530 |
+
$active_snippets = $wpdb->get_col( "SELECT code FROM {$wpdb->snippets} WHERE active=1;" );
|
1531 |
+
|
1532 |
+
}
|
1533 |
|
1534 |
+
if ( is_multisite() && $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->ms_snippets}';" ) ) {
|
1535 |
+
|
1536 |
+
// grab the network active snippets from the database
|
1537 |
$active_snippets = array_merge(
|
1538 |
+
$wpdb->get_col( "SELECT code FROM {$wpdb->ms_snippets} WHERE active=1;" ),
|
1539 |
$active_snippets
|
1540 |
);
|
1541 |
}
|
1543 |
if ( count( $active_snippets ) ) {
|
1544 |
foreach( $active_snippets as $snippet ) {
|
1545 |
// execute the php code
|
1546 |
+
$this->execute_snippet( htmlspecialchars_decode( stripslashes( $snippet ) ) );
|
1547 |
}
|
1548 |
}
|
1549 |
}
|
1562 |
global $cs;
|
1563 |
$cs = &$code_snippets;
|
1564 |
|
|
|
|
|
1565 |
register_uninstall_hook( $code_snippets->file, 'code_snippets_uninstall' );
|
1566 |
|
1567 |
/**
|
1582 |
function code_snippets_uninstall() {
|
1583 |
global $wpdb, $code_snippets;
|
1584 |
if ( is_multisite() ) {
|
1585 |
+
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
|
1586 |
+
if ( $blog_ids ) {
|
1587 |
+
foreach ( $blog_ids as $blog_id ) {
|
1588 |
+
switch_to_blog( $blog_id );
|
1589 |
+
$wpdb->query( "DROP TABLE IF EXISTS $wpdb->snippets" );
|
1590 |
delete_option( 'cs_db_version' );
|
1591 |
delete_option( 'recently_activated_snippets' );
|
1592 |
$code_snippets->remove_caps();
|
1593 |
}
|
1594 |
restore_current_blog();
|
1595 |
}
|
1596 |
+
$wpdb->query( "DROP TABLE IF EXISTS $wpdb->ms_snippets" );
|
1597 |
delete_site_option( 'recently_activated_snippets' );
|
1598 |
$code_snippets->remove_caps( 'multisite' );
|
1599 |
} else {
|
1600 |
+
$wpdb->query( "DROP TABLE IF EXISTS $wpdb->snippets" );
|
1601 |
delete_option( 'recently_activated_snippets' );
|
1602 |
delete_option( 'cs_db_version' );
|
1603 |
$code_snippets->remove_caps();
|
1604 |
}
|
|
|
1605 |
delete_site_option( 'code_snippets_version' );
|
1606 |
+
}
|
1607 |
+
|
1608 |
+
endif; // class exists check
|
images/{icon16.png → menu-icon.png}
RENAMED
File without changes
|
images/{icon32.png → screen-icon.png}
RENAMED
File without changes
|
includes/admin/import.php
CHANGED
@@ -1,6 +1,17 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
|
5 |
echo '<div id="message" class="updated fade"><p>';
|
6 |
|
@@ -29,6 +40,7 @@ if ( isset( $_REQUEST['imported'] ) && intval( $_REQUEST['imported'] ) != 0 ) {
|
|
29 |
<p><?php printf( __('You will need to go to the <a href="%s">Manage Snippets</a> page to activate the imported snippets.', 'code-snippets'), $this->admin_manage_url ); ?></p>
|
30 |
|
31 |
<p><?php _e('Choose a Code Snippets (.xml) file to upload, then click Upload file and import.', 'code-snippets'); ?></p>
|
|
|
32 |
<form enctype="multipart/form-data" id="import-upload-form" method="post" action="" name="code_snippets_import">
|
33 |
<p>
|
34 |
<label for="upload"><?php _e('Choose a file from your computer:', 'code-snippets' ); ?></label> <?php _e('(Maximum size: 8MB)', 'code-snippets'); ?>
|
@@ -36,6 +48,9 @@ if ( isset( $_REQUEST['imported'] ) && intval( $_REQUEST['imported'] ) != 0 ) {
|
|
36 |
<input type="hidden" name="action" value="save" />
|
37 |
<input type="hidden" name="max_file_size" value="8388608" />
|
38 |
</p>
|
|
|
|
|
|
|
39 |
<p class="submit">
|
40 |
<input type="submit" name="submit" id="submit" class="button" value="<?php _e('Upload file and import', 'code-snippets'); ?>" />
|
41 |
</p>
|
1 |
<?php
|
2 |
|
3 |
+
/**
|
4 |
+
* HTML code for the Import Snippets page
|
5 |
+
*
|
6 |
+
* @package Code Snippets
|
7 |
+
* @subpackage Administration
|
8 |
+
*/
|
9 |
+
|
10 |
+
if ( ! class_exists( 'Code_Snippets' ) ) exit;
|
11 |
+
|
12 |
+
/* Display the admin notice */
|
13 |
+
|
14 |
+
if ( isset( $_REQUEST['imported'] ) && 0 !== intval( $_REQUEST['imported'] ) ) {
|
15 |
|
16 |
echo '<div id="message" class="updated fade"><p>';
|
17 |
|
40 |
<p><?php printf( __('You will need to go to the <a href="%s">Manage Snippets</a> page to activate the imported snippets.', 'code-snippets'), $this->admin_manage_url ); ?></p>
|
41 |
|
42 |
<p><?php _e('Choose a Code Snippets (.xml) file to upload, then click Upload file and import.', 'code-snippets'); ?></p>
|
43 |
+
|
44 |
<form enctype="multipart/form-data" id="import-upload-form" method="post" action="" name="code_snippets_import">
|
45 |
<p>
|
46 |
<label for="upload"><?php _e('Choose a file from your computer:', 'code-snippets' ); ?></label> <?php _e('(Maximum size: 8MB)', 'code-snippets'); ?>
|
48 |
<input type="hidden" name="action" value="save" />
|
49 |
<input type="hidden" name="max_file_size" value="8388608" />
|
50 |
</p>
|
51 |
+
|
52 |
+
<?php do_action( 'code_snippets_admin_import_form' ); ?>
|
53 |
+
|
54 |
<p class="submit">
|
55 |
<input type="submit" name="submit" id="submit" class="button" value="<?php _e('Upload file and import', 'code-snippets'); ?>" />
|
56 |
</p>
|
includes/admin/manage.php
CHANGED
@@ -1,9 +1,16 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
if ( ! class_exists( 'Code_Snippets' ) ) exit;
|
3 |
|
4 |
require_once $this->plugin_dir . 'includes/class-list-table.php';
|
5 |
|
6 |
-
global $code_snippets_list_table;
|
7 |
$screen = get_current_screen();
|
8 |
?>
|
9 |
<?php if ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) : ?>
|
@@ -26,21 +33,25 @@ $screen = get_current_screen();
|
|
26 |
|
27 |
<div class="wrap">
|
28 |
<?php screen_icon(); ?>
|
29 |
-
<h2><?php
|
30 |
<?php if ( current_user_can( $screen->is_network ? 'install_network_snippets' : 'install_snippets' ) ) { ?>
|
31 |
<a href="<?php echo $this->admin_single_url; ?>" class="add-new-h2"><?php echo esc_html_x('Add New', 'snippet', 'code-snippets'); ?></a>
|
32 |
<?php }
|
33 |
-
|
34 |
-
printf( '<span class="subtitle">' . __('Search results for “%s”', 'code-snippets') . '</span>', esc_html( $s ) ); ?></h2>
|
35 |
|
36 |
-
<?php $
|
37 |
|
38 |
<form method="get" action="">
|
39 |
-
|
40 |
-
|
|
|
|
|
41 |
</form>
|
42 |
<form method="post" action="">
|
43 |
-
|
44 |
-
<?php $
|
45 |
</form>
|
|
|
|
|
|
|
46 |
</div>
|
1 |
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* HTML code for the Manage Snippets page
|
5 |
+
*
|
6 |
+
* @package Code Snippets
|
7 |
+
* @subpackage Administration
|
8 |
+
*/
|
9 |
+
|
10 |
if ( ! class_exists( 'Code_Snippets' ) ) exit;
|
11 |
|
12 |
require_once $this->plugin_dir . 'includes/class-list-table.php';
|
13 |
|
|
|
14 |
$screen = get_current_screen();
|
15 |
?>
|
16 |
<?php if ( defined( 'CODE_SNIPPETS_SAFE_MODE' ) && CODE_SNIPPETS_SAFE_MODE ) : ?>
|
33 |
|
34 |
<div class="wrap">
|
35 |
<?php screen_icon(); ?>
|
36 |
+
<h2><?php esc_html_e('Snippets', 'code-snippets'); ?>
|
37 |
<?php if ( current_user_can( $screen->is_network ? 'install_network_snippets' : 'install_snippets' ) ) { ?>
|
38 |
<a href="<?php echo $this->admin_single_url; ?>" class="add-new-h2"><?php echo esc_html_x('Add New', 'snippet', 'code-snippets'); ?></a>
|
39 |
<?php }
|
40 |
+
$this->list_table->search_notice(); ?></h2>
|
|
|
41 |
|
42 |
+
<?php $this->list_table->views(); ?>
|
43 |
|
44 |
<form method="get" action="">
|
45 |
+
<?php
|
46 |
+
$this->list_table->required_form_fields( 'search_box' );
|
47 |
+
$this->list_table->search_box( __( 'Search Installed Snippets', 'code-snippets' ), 'search_id' );
|
48 |
+
?>
|
49 |
</form>
|
50 |
<form method="post" action="">
|
51 |
+
<?php $this->list_table->required_form_fields(); ?>
|
52 |
+
<?php $this->list_table->display(); ?>
|
53 |
</form>
|
54 |
+
|
55 |
+
<?php do_action( 'code_snippets_admin_manage' ); ?>
|
56 |
+
|
57 |
</div>
|
includes/admin/single.php
CHANGED
@@ -1,21 +1,29 @@
|
|
1 |
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
if ( ! class_exists( 'Code_Snippets' ) ) exit;
|
3 |
-
global $wpdb;
|
4 |
|
5 |
$table = $this->get_table_name();
|
6 |
$screen = get_current_screen();
|
7 |
-
$can_edit = current_user_can( $screen->is_network ? 'edit_network_snippets' : 'edit_snippets' );
|
8 |
$can_install = current_user_can( $screen->is_network ? 'install_network_snippets' : 'install_snippets' );
|
9 |
|
10 |
-
|
11 |
-
|
12 |
|
13 |
-
if ( isset( $_REQUEST['edit'] ) )
|
14 |
-
$edit_id = intval( $_REQUEST['edit'] );
|
15 |
?>
|
16 |
|
17 |
<?php if ( isset( $_REQUEST['invalid'] ) && $_REQUEST['invalid'] ) : ?>
|
18 |
<div id="message" class="error fade"><p><?php _e('Please provide a name for the snippet and its code.', 'code-snippets'); ?></p></div>
|
|
|
|
|
|
|
|
|
19 |
<?php elseif ( isset( $_REQUEST['updated'] ) && $_REQUEST['updated'] ) : ?>
|
20 |
<div id="message" class="updated fade"><p><?php _e('Snippet <strong>updated</strong>.', 'code-snippets'); ?></p></div>
|
21 |
<?php elseif ( isset( $_REQUEST['added'] ) && $_REQUEST['added'] ) : ?>
|
@@ -25,78 +33,60 @@ if ( isset( $_REQUEST['edit'] ) )
|
|
25 |
<div class="wrap">
|
26 |
<?php screen_icon(); ?>
|
27 |
<h2><?php
|
28 |
-
if (
|
29 |
esc_html_e('Edit Snippet', 'code-snippets');
|
30 |
|
31 |
if ( $can_install )
|
32 |
printf( ' <a href="%1$s" class="add-new-h2">%2$s</a>',
|
33 |
$this->admin_single_url,
|
34 |
-
|
35 |
);
|
36 |
} else {
|
37 |
-
|
38 |
}
|
39 |
?></h2>
|
40 |
|
41 |
<form method="post" action="" style="margin-top: 10px;">
|
42 |
-
<?php
|
43 |
-
if ( isset( $edit_id ) ) {
|
44 |
-
$snippet = $wpdb->get_row( "SELECT * FROM $table WHERE id = $edit_id" );
|
45 |
printf ( '<input type="hidden" name="snippet_id" value="%d" />', $snippet->id );
|
46 |
-
} else {
|
47 |
-
// define a empty object (or one with default values)
|
48 |
-
$snippet = new stdClass();
|
49 |
-
$snippet->name = '';
|
50 |
-
$snippet->description = '';
|
51 |
-
$snippet->code = '';
|
52 |
-
}
|
53 |
?>
|
54 |
<div id="titlediv">
|
55 |
<div id="titlewrap">
|
56 |
-
<label for="title" style="display: none;"><?php
|
57 |
-
<input id="title" type="text" autocomplete="off"
|
58 |
</div>
|
59 |
</div>
|
60 |
|
61 |
<label for="snippet_code">
|
62 |
-
<h3><?php
|
63 |
-
<span style="float: right; font-weight: normal;"><?php _e('Enter or paste the snippet code without the <code><?php</code> and <code>?></code> tags.', 'code-snippets'); ?></span></h3>
|
64 |
</label>
|
65 |
|
66 |
-
<textarea id="snippet_code" name="snippet_code" rows="20" spellcheck="false" style="font-family: monospace; width:100%;"><?php echo
|
67 |
|
68 |
-
|
69 |
-
<h3><?php esc_html_e('Description', 'code-snippets'); ?>
|
70 |
-
<span style="font-weight: normal;"><?php esc_html_e('(Optional)', 'code-snippets'); ?></span></h3>
|
71 |
-
</label>
|
72 |
-
|
73 |
-
<?php
|
74 |
-
wp_editor(
|
75 |
-
htmlspecialchars_decode( stripslashes( $snippet->description ) ),
|
76 |
-
'description',
|
77 |
-
array(
|
78 |
-
'textarea_name' => 'snippet_description',
|
79 |
-
'textarea_rows' => 10,
|
80 |
-
// 'media_buttons' => false,
|
81 |
-
)
|
82 |
-
);
|
83 |
-
?>
|
84 |
|
85 |
<p class="submit">
|
86 |
-
|
87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
</p>
|
|
|
89 |
</form>
|
90 |
</div>
|
91 |
<script type="text/javascript">
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
</script>
|
1 |
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* HTML code for the Add New/Edit Snippet page
|
5 |
+
*
|
6 |
+
* @package Code Snippets
|
7 |
+
* @subpackage Administration
|
8 |
+
*/
|
9 |
+
|
10 |
if ( ! class_exists( 'Code_Snippets' ) ) exit;
|
|
|
11 |
|
12 |
$table = $this->get_table_name();
|
13 |
$screen = get_current_screen();
|
|
|
14 |
$can_install = current_user_can( $screen->is_network ? 'install_network_snippets' : 'install_snippets' );
|
15 |
|
16 |
+
$edit_id = ( isset( $_REQUEST['edit'] ) ? intval( $_REQUEST['edit'] ) : 0 );
|
17 |
+
$snippet = $this->get_snippet( $edit_id );
|
18 |
|
|
|
|
|
19 |
?>
|
20 |
|
21 |
<?php if ( isset( $_REQUEST['invalid'] ) && $_REQUEST['invalid'] ) : ?>
|
22 |
<div id="message" class="error fade"><p><?php _e('Please provide a name for the snippet and its code.', 'code-snippets'); ?></p></div>
|
23 |
+
<?php elseif ( isset( $_REQUEST['activated'], $_REQUEST['updated'] ) && $_REQUEST['activated'] && $_REQUEST['updated'] ) : ?>
|
24 |
+
<div id="message" class="updated fade"><p><?php _e('Snippet <strong>updated</strong> and <strong>activated</strong>.', 'code-snippets'); ?></p></div>
|
25 |
+
<?php elseif ( isset( $_REQUEST['activated'], $_REQUEST['added'] ) && $_REQUEST['activated'] && $_REQUEST['added'] ) : ?>
|
26 |
+
<div id="message" class="updated fade"><p><?php _e('Snippet <strong>added</strong> and <strong>activated</strong>.', 'code-snippets'); ?></p></div>
|
27 |
<?php elseif ( isset( $_REQUEST['updated'] ) && $_REQUEST['updated'] ) : ?>
|
28 |
<div id="message" class="updated fade"><p><?php _e('Snippet <strong>updated</strong>.', 'code-snippets'); ?></p></div>
|
29 |
<?php elseif ( isset( $_REQUEST['added'] ) && $_REQUEST['added'] ) : ?>
|
33 |
<div class="wrap">
|
34 |
<?php screen_icon(); ?>
|
35 |
<h2><?php
|
36 |
+
if ( $edit_id ) {
|
37 |
esc_html_e('Edit Snippet', 'code-snippets');
|
38 |
|
39 |
if ( $can_install )
|
40 |
printf( ' <a href="%1$s" class="add-new-h2">%2$s</a>',
|
41 |
$this->admin_single_url,
|
42 |
+
esc_html_x('Add New', 'snippet', 'code-snippets')
|
43 |
);
|
44 |
} else {
|
45 |
+
esc_html_e('Add New Snippet', 'code-snippets');
|
46 |
}
|
47 |
?></h2>
|
48 |
|
49 |
<form method="post" action="" style="margin-top: 10px;">
|
50 |
+
<?php if ( intval( $snippet->id ) > 0 )
|
|
|
|
|
51 |
printf ( '<input type="hidden" name="snippet_id" value="%d" />', $snippet->id );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
?>
|
53 |
<div id="titlediv">
|
54 |
<div id="titlewrap">
|
55 |
+
<label for="title" style="display: none;"><?php _e('Name (short title)', 'code-snippets'); ?></label>
|
56 |
+
<input id="title" type="text" autocomplete="off" name="snippet_name" value="<?php echo esc_html( $snippet->name ); ?>" placeholder="<?php _e('Name (short title)', 'code-snippets'); ?>" required="required" />
|
57 |
</div>
|
58 |
</div>
|
59 |
|
60 |
<label for="snippet_code">
|
61 |
+
<h3><?php _e('Code', 'code-snippets'); ?></h3>
|
|
|
62 |
</label>
|
63 |
|
64 |
+
<textarea id="snippet_code" name="snippet_code" rows="20" spellcheck="false" style="font-family: monospace; width:100%;"><?php echo $snippet->code; ?></textarea>
|
65 |
|
66 |
+
<?php do_action( 'code_snippets_admin_single', $snippet ); ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
<p class="submit">
|
69 |
+
<?php
|
70 |
+
submit_button( null, 'primary', 'save_snippet', false );
|
71 |
+
|
72 |
+
if ( ! $snippet->active ) {
|
73 |
+
echo ' ';
|
74 |
+
submit_button( 'Save Changes & Activate', 'secondary', 'save_snippet_activate', false );
|
75 |
+
}
|
76 |
+
?>
|
77 |
</p>
|
78 |
+
|
79 |
</form>
|
80 |
</div>
|
81 |
<script type="text/javascript">
|
82 |
+
var editor = CodeMirror.fromTextArea(document.getElementById("snippet_code"), {
|
83 |
+
lineNumbers: true,
|
84 |
+
matchBrackets: true,
|
85 |
+
lineWrapping: true,
|
86 |
+
mode: "text/x-php",
|
87 |
+
indentUnit: 4,
|
88 |
+
indentWithTabs: true,
|
89 |
+
enterMode: "keep",
|
90 |
+
tabMode: "shift"
|
91 |
+
});
|
92 |
</script>
|
includes/class-list-table.php
CHANGED
@@ -13,13 +13,18 @@ if ( ! class_exists( 'WP_List_Table' ) ) {
|
|
13 |
*/
|
14 |
class Code_Snippets_List_Table extends WP_List_Table {
|
15 |
|
|
|
|
|
|
|
|
|
|
|
16 |
function __construct() {
|
17 |
global $status, $page, $code_snippets;
|
18 |
|
19 |
$screen = get_current_screen();
|
20 |
|
21 |
$status = 'all';
|
22 |
-
if ( isset( $_REQUEST['status'] ) && in_array( $_REQUEST['status'], array( 'active', 'inactive', 'recently_activated'
|
23 |
$status = $_REQUEST['status'];
|
24 |
|
25 |
if ( isset( $_REQUEST['s'] ) )
|
@@ -33,9 +38,10 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
33 |
'option' => 'snippets_per_page'
|
34 |
) );
|
35 |
|
36 |
-
add_filter( "get_user_option_manage{$screen->id}columnshidden", array( $this, 'get_default_hidden_columns' ) );
|
37 |
-
|
38 |
-
|
|
|
39 |
|
40 |
parent::__construct( array(
|
41 |
'singular' => 'snippet',
|
@@ -44,16 +50,6 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
44 |
) );
|
45 |
}
|
46 |
|
47 |
-
/**
|
48 |
-
* Handles saving the user's screen option preference
|
49 |
-
*
|
50 |
-
* @since Code Snippets 1.5
|
51 |
-
* @access private
|
52 |
-
*/
|
53 |
-
function set_screen_option( $status, $option, $value ) {
|
54 |
-
if ( 'snippets_per_page' === $option ) return $value;
|
55 |
-
}
|
56 |
-
|
57 |
/**
|
58 |
* Enqueue the table stylesheet
|
59 |
*
|
@@ -64,39 +60,69 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
64 |
*
|
65 |
* @return void
|
66 |
*/
|
67 |
-
function load_table_style() {
|
68 |
global $code_snippets;
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
}
|
76 |
|
77 |
-
function
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
switch( $column_name ) {
|
79 |
case 'id':
|
80 |
-
return
|
81 |
case 'description':
|
82 |
-
|
|
|
|
|
|
|
83 |
default:
|
84 |
-
return
|
85 |
}
|
86 |
}
|
87 |
|
88 |
-
function column_name( $
|
89 |
global $code_snippets;
|
90 |
$screen = get_current_screen();
|
91 |
$actions = array(); // Build row actions
|
92 |
|
93 |
-
if ( $
|
94 |
$actions['deactivate'] = sprintf(
|
95 |
'<a href="%1$s">%2$s</a>',
|
96 |
add_query_arg( array(
|
97 |
'page' => $_REQUEST['page'],
|
98 |
'action' => 'deactivate',
|
99 |
-
'id' => $
|
100 |
) ),
|
101 |
$screen->is_network ? __('Network Deactivate', 'code-snippets') : __('Deactivate', 'code-snippets')
|
102 |
);
|
@@ -106,7 +132,7 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
106 |
add_query_arg( array(
|
107 |
'page' => $_REQUEST['page'],
|
108 |
'action' => 'activate',
|
109 |
-
'id' => $
|
110 |
) ),
|
111 |
$screen->is_network ? __('Network Activate', 'code-snippets') : __('Activate', 'code-snippets')
|
112 |
);
|
@@ -115,14 +141,14 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
115 |
$actions['edit'] = sprintf(
|
116 |
'<a href="%s&edit=%s">Edit</a>',
|
117 |
$code_snippets->admin_single_url,
|
118 |
-
$
|
119 |
);
|
120 |
$actions['export'] = sprintf(
|
121 |
'<a href="%s">Export</a>',
|
122 |
add_query_arg( array(
|
123 |
'page' => $_REQUEST['page'],
|
124 |
'action' => 'export',
|
125 |
-
'id' => $
|
126 |
) )
|
127 |
);
|
128 |
$actions['delete'] = sprintf(
|
@@ -130,38 +156,47 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
130 |
add_query_arg( array(
|
131 |
'page' => $_REQUEST['page'],
|
132 |
'action' => 'delete',
|
133 |
-
'id' => $
|
134 |
) ),
|
135 |
-
esc_js(
|
136 |
-
|
|
|
|
|
|
|
137 |
);
|
138 |
|
139 |
// Return the name contents
|
140 |
-
return
|
|
|
|
|
|
|
|
|
141 |
}
|
142 |
|
143 |
-
function column_cb( $
|
144 |
-
return
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
}
|
150 |
|
151 |
function get_columns() {
|
152 |
-
|
153 |
'cb' => '<input type="checkbox" />',
|
154 |
'name' => __('Name', 'code-snippets'),
|
155 |
'id' => __('ID', 'code-snippets'),
|
156 |
'description' => __('Description', 'code-snippets'),
|
157 |
);
|
|
|
158 |
}
|
159 |
|
160 |
function get_sortable_columns() {
|
161 |
-
|
162 |
'id' => array( 'id', true ),
|
163 |
'name' => array( 'name', false ),
|
164 |
);
|
|
|
165 |
}
|
166 |
|
167 |
function get_default_hidden_columns( $result ) {
|
@@ -180,11 +215,12 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
180 |
'delete-selected' => __('Delete', 'code-snippets'),
|
181 |
'export-php-selected' => __('Export to PHP', 'code-snippets'),
|
182 |
);
|
183 |
-
return $actions;
|
184 |
}
|
185 |
|
186 |
function get_table_classes() {
|
187 |
-
|
|
|
188 |
}
|
189 |
|
190 |
function get_views() {
|
@@ -210,39 +246,69 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
210 |
break;
|
211 |
}
|
212 |
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
}
|
220 |
}
|
221 |
|
222 |
-
return $status_links;
|
223 |
}
|
224 |
|
225 |
function extra_tablenav( $which ) {
|
226 |
-
global $status;
|
227 |
|
228 |
-
|
229 |
-
return;
|
230 |
|
231 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
232 |
|
233 |
-
|
234 |
|
235 |
-
if ( 'recently_activated'
|
236 |
submit_button( __('Clear List', 'code-snippets'), 'secondary', 'clear-recent-list', false );
|
237 |
|
|
|
|
|
238 |
echo '</div>';
|
239 |
}
|
240 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
function current_action() {
|
242 |
if ( isset( $_POST['clear-recent-list'] ) )
|
243 |
-
|
244 |
-
|
245 |
-
|
|
|
246 |
}
|
247 |
|
248 |
/**
|
@@ -257,8 +323,9 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
257 |
*/
|
258 |
function process_bulk_actions() {
|
259 |
global $code_snippets;
|
260 |
-
|
261 |
-
|
|
|
262 |
|
263 |
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'activate', 'deactivate', 'delete', 'activate-multi', 'deactivate-multi', 'delete-multi' ) );
|
264 |
|
@@ -299,32 +366,43 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
299 |
}
|
300 |
}
|
301 |
|
|
|
|
|
|
|
302 |
function no_items() {
|
303 |
global $code_snippets;
|
304 |
printf( __('You do not appear to have any snippets available at this time. <a href="%s">Add New→</a>', 'code-snippets'), $code_snippets->admin_single_url );
|
305 |
}
|
306 |
|
|
|
|
|
|
|
|
|
|
|
307 |
function prepare_items() {
|
308 |
|
309 |
-
global $
|
310 |
|
311 |
wp_reset_vars( array( 'orderby', 'order', 's' ) );
|
312 |
|
313 |
$screen = get_current_screen();
|
314 |
$user = get_current_user_id();
|
315 |
-
$table = $code_snippets->get_table_name();
|
316 |
|
317 |
// first, lets process the bulk actions
|
318 |
$this->process_bulk_actions();
|
319 |
|
320 |
$snippets = array(
|
321 |
-
'all' =>
|
322 |
-
'search' => array(),
|
323 |
'active' => array(),
|
324 |
'inactive' => array(),
|
325 |
'recently_activated' => array(),
|
326 |
);
|
327 |
|
|
|
|
|
|
|
|
|
|
|
328 |
if ( $screen->is_network )
|
329 |
$recently_activated = get_site_option( 'recently_activated_snippets', array() );
|
330 |
else
|
@@ -342,32 +420,27 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
342 |
|
343 |
foreach ( (array) $snippets['all'] as $snippet ) {
|
344 |
// Filter into individual sections
|
345 |
-
if ( $snippet
|
346 |
$snippets['active'][] = $snippet;
|
347 |
} else {
|
348 |
-
if ( isset( $recently_activated[ $snippet
|
349 |
$snippets['recently_activated'][] = $snippet;
|
350 |
$snippets['inactive'][] = $snippet;
|
351 |
}
|
352 |
}
|
353 |
|
354 |
-
if ( $s ) {
|
355 |
-
$status = 'search';
|
356 |
-
$snippets['search'] = array_filter( $snippets['all'], array( &$this, '_search_callback' ) );
|
357 |
-
}
|
358 |
-
|
359 |
$totals = array();
|
360 |
foreach ( $snippets as $type => $list )
|
361 |
$totals[ $type ] = count( $list );
|
362 |
|
363 |
-
if ( empty( $snippets[ $status ] )
|
364 |
$status = 'all';
|
365 |
|
366 |
$data = $snippets[ $status ];
|
367 |
|
368 |
/**
|
369 |
* First, lets decide how many records per page to show
|
370 |
-
* by getting the user's setting in the Screen
|
371 |
* panel.
|
372 |
*/
|
373 |
$sort_by = $screen->get_option( 'per_page', 'option' );
|
@@ -395,13 +468,13 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
395 |
$order = ( ! empty( $_REQUEST['order'] ) ) ? $_REQUEST['order'] : 'asc';
|
396 |
|
397 |
// Determine sort order
|
398 |
-
if (
|
399 |
-
$result = $a
|
400 |
else
|
401 |
-
$result = strcmp( $a
|
402 |
|
403 |
// Send final sort direction to usort
|
404 |
-
return (
|
405 |
}
|
406 |
|
407 |
usort($data, 'usort_reorder');
|
@@ -432,7 +505,6 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
432 |
*/
|
433 |
$this->items = $data;
|
434 |
|
435 |
-
|
436 |
/**
|
437 |
* We also have to register our pagination options & calculations.
|
438 |
*/
|
@@ -443,27 +515,53 @@ class Code_Snippets_List_Table extends WP_List_Table {
|
|
443 |
) );
|
444 |
}
|
445 |
|
446 |
-
function _search_callback( $
|
447 |
static $term;
|
448 |
if ( is_null( $term ) )
|
449 |
$term = stripslashes( $_REQUEST['s'] );
|
450 |
|
451 |
-
foreach ( $
|
452 |
-
|
453 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
454 |
|
455 |
return false;
|
456 |
}
|
457 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
458 |
/**
|
459 |
* Generates content for a single row of the table
|
460 |
*/
|
461 |
-
function single_row( $
|
462 |
static $row_class = '';
|
463 |
-
$row_class = ( $
|
464 |
|
465 |
echo '<tr class="' . $row_class . '">';
|
466 |
-
echo $this->single_row_columns( $
|
467 |
echo '</tr>';
|
468 |
}
|
469 |
}
|
13 |
*/
|
14 |
class Code_Snippets_List_Table extends WP_List_Table {
|
15 |
|
16 |
+
/**
|
17 |
+
* The constructor function for our class
|
18 |
+
*
|
19 |
+
* Adds hooks, initializes variables, setups class
|
20 |
+
*/
|
21 |
function __construct() {
|
22 |
global $status, $page, $code_snippets;
|
23 |
|
24 |
$screen = get_current_screen();
|
25 |
|
26 |
$status = 'all';
|
27 |
+
if ( isset( $_REQUEST['status'] ) && in_array( $_REQUEST['status'], array( 'active', 'inactive', 'recently_activated' ) ) )
|
28 |
$status = $_REQUEST['status'];
|
29 |
|
30 |
if ( isset( $_REQUEST['s'] ) )
|
38 |
'option' => 'snippets_per_page'
|
39 |
) );
|
40 |
|
41 |
+
add_filter( "get_user_option_manage{$screen->id}columnshidden", array( $this, 'get_default_hidden_columns' ), 15 );
|
42 |
+
add_action( 'admin_enqueue_scripts', array( $this, 'load_table_style' ) );
|
43 |
+
|
44 |
+
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'activate', 'activate-multi', 'deactivate', 'deactivate-multi', 'delete', 'delete-multi' ) );
|
45 |
|
46 |
parent::__construct( array(
|
47 |
'singular' => 'snippet',
|
50 |
) );
|
51 |
}
|
52 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
/**
|
54 |
* Enqueue the table stylesheet
|
55 |
*
|
60 |
*
|
61 |
* @return void
|
62 |
*/
|
63 |
+
function load_table_style( $hook ) {
|
64 |
global $code_snippets;
|
65 |
+
|
66 |
+
if ( $hook !== $code_snippets->admin_manage )
|
67 |
+
return;
|
68 |
+
|
69 |
+
if ( 'mp6' === get_user_option( 'admin_color' ) ) {
|
70 |
+
|
71 |
+
wp_enqueue_style(
|
72 |
+
'snippets-table',
|
73 |
+
plugins_url( 'assets/table.mp6.css', $code_snippets->file ),
|
74 |
+
false,
|
75 |
+
$code_snippets->version
|
76 |
+
);
|
77 |
+
|
78 |
+
} else {
|
79 |
+
|
80 |
+
wp_enqueue_style(
|
81 |
+
'snippets-table',
|
82 |
+
plugins_url( 'assets/table.css', $code_snippets->file ),
|
83 |
+
false,
|
84 |
+
$code_snippets->version
|
85 |
+
);
|
86 |
+
}
|
87 |
}
|
88 |
|
89 |
+
function format_description( $desc ) {
|
90 |
+
$desc = wptexturize( $desc );
|
91 |
+
$desc = convert_smilies( $desc );
|
92 |
+
$desc = convert_chars( $desc );
|
93 |
+
$desc = wpautop( $desc );
|
94 |
+
$desc = shortcode_unautop( $desc );
|
95 |
+
$desc = capital_P_dangit( $desc );
|
96 |
+
return $desc;
|
97 |
+
}
|
98 |
+
|
99 |
+
function column_default( $snippet, $column_name ) {
|
100 |
+
|
101 |
switch( $column_name ) {
|
102 |
case 'id':
|
103 |
+
return $snippet->id;
|
104 |
case 'description':
|
105 |
+
if ( ! empty( $snippet->description ) )
|
106 |
+
return $this->format_description( $snippet->description );
|
107 |
+
else
|
108 |
+
return '—';
|
109 |
default:
|
110 |
+
return do_action( "code_snippets_list_table_column_{$column_name}", $snippet );
|
111 |
}
|
112 |
}
|
113 |
|
114 |
+
function column_name( $snippet ) {
|
115 |
global $code_snippets;
|
116 |
$screen = get_current_screen();
|
117 |
$actions = array(); // Build row actions
|
118 |
|
119 |
+
if ( $snippet->active ) {
|
120 |
$actions['deactivate'] = sprintf(
|
121 |
'<a href="%1$s">%2$s</a>',
|
122 |
add_query_arg( array(
|
123 |
'page' => $_REQUEST['page'],
|
124 |
'action' => 'deactivate',
|
125 |
+
'id' => $snippet->id
|
126 |
) ),
|
127 |
$screen->is_network ? __('Network Deactivate', 'code-snippets') : __('Deactivate', 'code-snippets')
|
128 |
);
|
132 |
add_query_arg( array(
|
133 |
'page' => $_REQUEST['page'],
|
134 |
'action' => 'activate',
|
135 |
+
'id' => $snippet->id
|
136 |
) ),
|
137 |
$screen->is_network ? __('Network Activate', 'code-snippets') : __('Activate', 'code-snippets')
|
138 |
);
|
141 |
$actions['edit'] = sprintf(
|
142 |
'<a href="%s&edit=%s">Edit</a>',
|
143 |
$code_snippets->admin_single_url,
|
144 |
+
$snippet->id
|
145 |
);
|
146 |
$actions['export'] = sprintf(
|
147 |
'<a href="%s">Export</a>',
|
148 |
add_query_arg( array(
|
149 |
'page' => $_REQUEST['page'],
|
150 |
'action' => 'export',
|
151 |
+
'id' => $snippet->id
|
152 |
) )
|
153 |
);
|
154 |
$actions['delete'] = sprintf(
|
156 |
add_query_arg( array(
|
157 |
'page' => $_REQUEST['page'],
|
158 |
'action' => 'delete',
|
159 |
+
'id' => $snippet->id
|
160 |
) ),
|
161 |
+
esc_js( sprintf(
|
162 |
+
'return confirm("%s");',
|
163 |
+
__("You are about to permanently delete the selected item.
|
164 |
+
'Cancel' to stop, 'OK' to delete.", 'code-snippets')
|
165 |
+
) )
|
166 |
);
|
167 |
|
168 |
// Return the name contents
|
169 |
+
return apply_filters(
|
170 |
+
'code_snippets_list_table_column_name',
|
171 |
+
'<strong>' . stripslashes( $snippet->name ) . '</strong>' . $this->row_actions( $actions, true ),
|
172 |
+
$snippet
|
173 |
+
);
|
174 |
}
|
175 |
|
176 |
+
function column_cb( $snippet ) {
|
177 |
+
return apply_filters(
|
178 |
+
'code_snippets_list_table_column_cb',
|
179 |
+
sprintf( '<input type="checkbox" name="ids[]" value="%s" />', $snippet->id ),
|
180 |
+
$snippet
|
181 |
+
);
|
182 |
}
|
183 |
|
184 |
function get_columns() {
|
185 |
+
$columns = array(
|
186 |
'cb' => '<input type="checkbox" />',
|
187 |
'name' => __('Name', 'code-snippets'),
|
188 |
'id' => __('ID', 'code-snippets'),
|
189 |
'description' => __('Description', 'code-snippets'),
|
190 |
);
|
191 |
+
return apply_filters( 'code_snippets_list_table_columns', $columns );
|
192 |
}
|
193 |
|
194 |
function get_sortable_columns() {
|
195 |
+
$sortable_columns = array(
|
196 |
'id' => array( 'id', true ),
|
197 |
'name' => array( 'name', false ),
|
198 |
);
|
199 |
+
return apply_filters( 'code_snippets_list_table_sortable_columns', $sortable_columns );
|
200 |
}
|
201 |
|
202 |
function get_default_hidden_columns( $result ) {
|
215 |
'delete-selected' => __('Delete', 'code-snippets'),
|
216 |
'export-php-selected' => __('Export to PHP', 'code-snippets'),
|
217 |
);
|
218 |
+
return apply_filters( 'code_snippets_bulk_actions', $actions );
|
219 |
}
|
220 |
|
221 |
function get_table_classes() {
|
222 |
+
$classes = array( 'widefat', $this->_args['plural'] );
|
223 |
+
return apply_filters( 'code_snippets_table_classes', $classes );
|
224 |
}
|
225 |
|
226 |
function get_views() {
|
246 |
break;
|
247 |
}
|
248 |
|
249 |
+
$status_links[$type] = sprintf( '<a href="%s"%s>%s</a>',
|
250 |
+
add_query_arg( 'status', $type ),
|
251 |
+
( $type === $status ) ? ' class="current"' : '',
|
252 |
+
sprintf( $text, number_format_i18n( $count ) )
|
253 |
+
);
|
254 |
+
|
|
|
255 |
}
|
256 |
|
257 |
+
return apply_filters( 'code_snippets_list_table_views', $status_links );
|
258 |
}
|
259 |
|
260 |
function extra_tablenav( $which ) {
|
261 |
+
global $status, $code_snippets;
|
262 |
|
263 |
+
$screen = get_current_screen();
|
|
|
264 |
|
265 |
+
if ( 'top' === $which && has_action( 'code_snippets_list_table_filter_controls' ) ) {
|
266 |
+
?>
|
267 |
+
<div class="alignleft actions">
|
268 |
+
<?php
|
269 |
+
do_action( 'code_snippets_list_table_filter_controls' );
|
270 |
+
submit_button( __('Filter', 'code-snippets'), 'button', false, false );
|
271 |
+
?>
|
272 |
+
</div>
|
273 |
+
<?php
|
274 |
+
}
|
275 |
|
276 |
+
echo '<div class="alignleft actions">';
|
277 |
|
278 |
+
if ( 'recently_activated' === $status )
|
279 |
submit_button( __('Clear List', 'code-snippets'), 'secondary', 'clear-recent-list', false );
|
280 |
|
281 |
+
do_action( 'code_snippets_list_table_actions', $which );
|
282 |
+
|
283 |
echo '</div>';
|
284 |
}
|
285 |
|
286 |
+
function required_form_fields( $context = 'main' ) {
|
287 |
+
|
288 |
+
$vars = apply_filters( 'code_snippets_list_table_required_form_fields', array( 'page', 's', 'status', 'paged' ), $context );
|
289 |
+
|
290 |
+
if ( 'search_box' === $context ) {
|
291 |
+
// remove the 's' var if we're doing this for the search box
|
292 |
+
$vars = array_diff( $vars, array( 's' ) );
|
293 |
+
}
|
294 |
+
|
295 |
+
foreach ( $vars as $var ) {
|
296 |
+
if ( ! empty( $_REQUEST[ $var ] ) ) {
|
297 |
+
printf ( '<input type="hidden" name="%s" value="%s" />', $var, $_REQUEST[ $var ] );
|
298 |
+
print "\n";
|
299 |
+
}
|
300 |
+
}
|
301 |
+
|
302 |
+
do_action( 'code_snippets_list_table_print_required_form_fields', $context );
|
303 |
+
}
|
304 |
+
|
305 |
+
|
306 |
function current_action() {
|
307 |
if ( isset( $_POST['clear-recent-list'] ) )
|
308 |
+
$action = 'clear-recent-list';
|
309 |
+
else
|
310 |
+
$action = parent::current_action();
|
311 |
+
return apply_filters( 'code_snippets_list_table_current_action', $action );
|
312 |
}
|
313 |
|
314 |
/**
|
323 |
*/
|
324 |
function process_bulk_actions() {
|
325 |
global $code_snippets;
|
326 |
+
|
327 |
+
if ( ! isset( $_POST['ids'] ) ) return;
|
328 |
+
$ids = $_POST['ids'];
|
329 |
|
330 |
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'activate', 'deactivate', 'delete', 'activate-multi', 'deactivate-multi', 'delete-multi' ) );
|
331 |
|
366 |
}
|
367 |
}
|
368 |
|
369 |
+
/**
|
370 |
+
* Message to display if no snippets are found
|
371 |
+
*/
|
372 |
function no_items() {
|
373 |
global $code_snippets;
|
374 |
printf( __('You do not appear to have any snippets available at this time. <a href="%s">Add New→</a>', 'code-snippets'), $code_snippets->admin_single_url );
|
375 |
}
|
376 |
|
377 |
+
/**
|
378 |
+
* Prepares the items to later display in the table
|
379 |
+
*
|
380 |
+
* Should run before any headers are sent
|
381 |
+
*/
|
382 |
function prepare_items() {
|
383 |
|
384 |
+
global $code_snippets, $status, $snippets, $totals, $page, $orderby, $order, $s;
|
385 |
|
386 |
wp_reset_vars( array( 'orderby', 'order', 's' ) );
|
387 |
|
388 |
$screen = get_current_screen();
|
389 |
$user = get_current_user_id();
|
|
|
390 |
|
391 |
// first, lets process the bulk actions
|
392 |
$this->process_bulk_actions();
|
393 |
|
394 |
$snippets = array(
|
395 |
+
'all' => apply_filters( 'code_snippets_list_table_get_snippets', $code_snippets->get_snippets() ),
|
|
|
396 |
'active' => array(),
|
397 |
'inactive' => array(),
|
398 |
'recently_activated' => array(),
|
399 |
);
|
400 |
|
401 |
+
// filter snippets based on search query
|
402 |
+
if ( $s ) {
|
403 |
+
$snippets['all'] = array_filter( $snippets[ 'all' ], array( &$this, '_search_callback' ) );
|
404 |
+
}
|
405 |
+
|
406 |
if ( $screen->is_network )
|
407 |
$recently_activated = get_site_option( 'recently_activated_snippets', array() );
|
408 |
else
|
420 |
|
421 |
foreach ( (array) $snippets['all'] as $snippet ) {
|
422 |
// Filter into individual sections
|
423 |
+
if ( $snippet->active ) {
|
424 |
$snippets['active'][] = $snippet;
|
425 |
} else {
|
426 |
+
if ( isset( $recently_activated[ $snippet->id ] ) ) // Was the snippet recently activated?
|
427 |
$snippets['recently_activated'][] = $snippet;
|
428 |
$snippets['inactive'][] = $snippet;
|
429 |
}
|
430 |
}
|
431 |
|
|
|
|
|
|
|
|
|
|
|
432 |
$totals = array();
|
433 |
foreach ( $snippets as $type => $list )
|
434 |
$totals[ $type ] = count( $list );
|
435 |
|
436 |
+
if ( empty( $snippets[ $status ] ) )
|
437 |
$status = 'all';
|
438 |
|
439 |
$data = $snippets[ $status ];
|
440 |
|
441 |
/**
|
442 |
* First, lets decide how many records per page to show
|
443 |
+
* by getting the user's setting in the Screen Options
|
444 |
* panel.
|
445 |
*/
|
446 |
$sort_by = $screen->get_option( 'per_page', 'option' );
|
468 |
$order = ( ! empty( $_REQUEST['order'] ) ) ? $_REQUEST['order'] : 'asc';
|
469 |
|
470 |
// Determine sort order
|
471 |
+
if ( 'id' === $orderby )
|
472 |
+
$result = $a->$orderby - $b->$orderby; // get the result for numerical data
|
473 |
else
|
474 |
+
$result = strcmp( $a->$orderby, $b->$orderby ); // get the result for string data
|
475 |
|
476 |
// Send final sort direction to usort
|
477 |
+
return ( 'asc' === $order ) ? $result : -$result;
|
478 |
}
|
479 |
|
480 |
usort($data, 'usort_reorder');
|
505 |
*/
|
506 |
$this->items = $data;
|
507 |
|
|
|
508 |
/**
|
509 |
* We also have to register our pagination options & calculations.
|
510 |
*/
|
515 |
) );
|
516 |
}
|
517 |
|
518 |
+
function _search_callback( $snippet ) {
|
519 |
static $term;
|
520 |
if ( is_null( $term ) )
|
521 |
$term = stripslashes( $_REQUEST['s'] );
|
522 |
|
523 |
+
foreach ( $snippet as $value ) {
|
524 |
+
|
525 |
+
if ( is_string( $value ) ) {
|
526 |
+
if ( false !== stripos( $value, $term ) )
|
527 |
+
return true;
|
528 |
+
}
|
529 |
+
elseif ( is_array( $value ) ) {
|
530 |
+
if ( false !== in_array( $term, $value ) )
|
531 |
+
return true;
|
532 |
+
}
|
533 |
+
}
|
534 |
|
535 |
return false;
|
536 |
}
|
537 |
|
538 |
+
function search_notice() {
|
539 |
+
if ( ! empty( $_REQUEST['s'] ) || apply_filters( 'code_snippets_list_table_search_notice', '' ) ) {
|
540 |
+
|
541 |
+
echo '<span class="subtitle">' . __('Search results', 'code-snippets');
|
542 |
+
|
543 |
+
if ( ! empty ( $_REQUEST['s'] ) )
|
544 |
+
echo sprintf ( __(' for “%s”'), esc_html( $_REQUEST['s'] ) );
|
545 |
+
|
546 |
+
echo apply_filters( 'code_snippets_list_table_search_notice', '' );
|
547 |
+
echo '</span>';
|
548 |
+
|
549 |
+
printf (
|
550 |
+
' <a class="button" href="%s">' . __('Clear Filters', 'code-snippets') . '</a>',
|
551 |
+
remove_query_arg( apply_filters( 'code_snippets_list_table_required_form_fields', array( 's' ), 'clear_filters' ) )
|
552 |
+
);
|
553 |
+
}
|
554 |
+
}
|
555 |
+
|
556 |
/**
|
557 |
* Generates content for a single row of the table
|
558 |
*/
|
559 |
+
function single_row( $snippet ) {
|
560 |
static $row_class = '';
|
561 |
+
$row_class = ( $snippet->active ? 'active' : 'inactive' );
|
562 |
|
563 |
echo '<tr class="' . $row_class . '">';
|
564 |
+
echo $this->single_row_columns( $snippet );
|
565 |
echo '</tr>';
|
566 |
}
|
567 |
}
|
includes/export.php
CHANGED
@@ -1,97 +1,146 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* This file handles the export functions
|
5 |
-
*
|
6 |
-
* It's better to call the $code_snippets->export()
|
7 |
-
* and $code_snippets->export_php() methods then
|
8 |
-
* directly use those in this file
|
9 |
-
*
|
10 |
-
* @package
|
11 |
-
* @subpackage Export
|
12 |
-
*/
|
13 |
-
|
14 |
-
if ( ! function_exists( 'code_snippets_export' ) ) :
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Exports seleted snippets to a XML or PHP file.
|
18 |
-
*
|
19 |
-
* @package Code Snippets
|
20 |
-
* @since Code Snippets 1.3
|
21 |
-
*
|
22 |
-
* @param array $ids The IDs of the snippets to export
|
23 |
-
* @param string $format The format of the export file
|
24 |
-
* @return void
|
25 |
-
*/
|
26 |
-
function code_snippets_export( $ids, $format = 'xml' ) {
|
27 |
-
|
28 |
-
global $wpdb, $code_snippets;
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
$sitename =
|
38 |
-
}
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
echo "
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
endif; // function exists check
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* This file handles the export functions
|
5 |
+
*
|
6 |
+
* It's better to call the $code_snippets->export()
|
7 |
+
* and $code_snippets->export_php() methods then
|
8 |
+
* directly use those in this file
|
9 |
+
*
|
10 |
+
* @package Code Snippets
|
11 |
+
* @subpackage Export
|
12 |
+
*/
|
13 |
+
|
14 |
+
if ( ! function_exists( 'code_snippets_export' ) ) :
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Exports seleted snippets to a XML or PHP file.
|
18 |
+
*
|
19 |
+
* @package Code Snippets
|
20 |
+
* @since Code Snippets 1.3
|
21 |
+
*
|
22 |
+
* @param array $ids The IDs of the snippets to export
|
23 |
+
* @param string $format The format of the export file
|
24 |
+
* @return void
|
25 |
+
*/
|
26 |
+
function code_snippets_export( $ids, $format = 'xml' ) {
|
27 |
+
|
28 |
+
global $wpdb, $code_snippets;
|
29 |
+
$ids = (array) $ids;
|
30 |
+
|
31 |
+
if ( 1 === count( $ids ) ) {
|
32 |
+
// If there is only snippet to export, use its name instead of the site name
|
33 |
+
$entry = $code_snippets->get_snippet( $ids );
|
34 |
+
$sitename = strtolower( $entry->name );
|
35 |
+
} else {
|
36 |
+
// Otherwise, use the site name as set in Settings > General
|
37 |
+
$sitename = strtolower( get_bloginfo( 'name' ) );
|
38 |
+
}
|
39 |
+
|
40 |
+
$filename = sanitize_file_name( apply_filters( 'code_snippets_export_filename', "{$sitename}.code-snippets.{$format}", $format, $sitename ) );
|
41 |
+
|
42 |
+
/* Apply the file headers */
|
43 |
+
|
44 |
+
header( 'Content-Disposition: attachment; filename=' . $filename );
|
45 |
+
|
46 |
+
if ( $format === 'xml' ) {
|
47 |
+
header( 'Content-Type: text/xml; charset=' . get_bloginfo('charset') );
|
48 |
+
|
49 |
+
echo '<?xml version="1.0" encoding="' . get_bloginfo('charset') . "\" ?>\n";
|
50 |
+
|
51 |
+
?>
|
52 |
+
<!-- This is a code snippets export file generated by the Code Snippets WordPress plugin. -->
|
53 |
+
<!-- http://wordpress.org/extend/plugins/code-snippets -->
|
54 |
+
|
55 |
+
<!-- To import these snippets a WordPress site follow these steps: -->
|
56 |
+
<!-- 1. Log in to that site as an administrator. -->
|
57 |
+
<!-- 2. Install the Code Snippets plugin using the directions provided at the above link. -->
|
58 |
+
<!-- 3. Go to 'Tools: Import' in the WordPress admin panel. -->
|
59 |
+
<!-- 4. Click on the "Code Snippets" importer in the list -->
|
60 |
+
<!-- 5. Upload this file using the form provided on that page. -->
|
61 |
+
<!-- 6. Code Snippets will then import all of the snippets and associated information -->
|
62 |
+
<!-- contained in this file into your site. -->
|
63 |
+
<!-- 7. You will then have to visit the 'Snippets: Manage' admin menu and activate desired snippets -->
|
64 |
+
|
65 |
+
<?php
|
66 |
+
|
67 |
+
// run the generator line through the standard WordPress filter
|
68 |
+
$gen = '<!-- generator="Code Snippets/' . $code_snippets->version . '" created="' . date('Y-m-d H:i') . '" -->';
|
69 |
+
$type = 'code_snippets_export';
|
70 |
+
echo apply_filters( "get_the_generator_$type", $gen, $type );
|
71 |
+
|
72 |
+
// start the XML section
|
73 |
+
echo "\n<snippets>";
|
74 |
+
|
75 |
+
} elseif ( 'php' === $format ) {
|
76 |
+
|
77 |
+
echo "<?php\n";
|
78 |
+
|
79 |
+
}
|
80 |
+
|
81 |
+
do_action( 'code_snippets_export_file_header', $format, $ids, $filename );
|
82 |
+
|
83 |
+
/* Loop through the snippets */
|
84 |
+
|
85 |
+
$table = $code_snippets->get_table_name();
|
86 |
+
$exclude = apply_filters( 'code_snippets_exclude_from_export', array( 'id', 'active' ) );
|
87 |
+
|
88 |
+
foreach( $ids as $id ) {
|
89 |
+
|
90 |
+
// grab the snippet from the database
|
91 |
+
$snippet = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $table WHERE id = %d", $id ), ARRAY_A );
|
92 |
+
|
93 |
+
// remove slashes
|
94 |
+
$snippet = stripslashes_deep( $snippet );
|
95 |
+
|
96 |
+
if ( 'xml' === $format ) {
|
97 |
+
|
98 |
+
echo "\n\t" . '<snippet>';
|
99 |
+
|
100 |
+
foreach ( $snippet as $field => $value ) {
|
101 |
+
|
102 |
+
// don't export certain fields
|
103 |
+
if ( in_array( $field, $exclude ) ) continue;
|
104 |
+
|
105 |
+
// output the field and value as indented XML
|
106 |
+
if ( $value = apply_filters( "code_snippets_export_$field", $value ) )
|
107 |
+
echo "\n\t\t<$field>$value</$field>";
|
108 |
+
}
|
109 |
+
echo "\n\t" . '</snippet>';
|
110 |
+
}
|
111 |
+
elseif ( 'php' === $format ) {
|
112 |
+
|
113 |
+
echo "\n/**\n * {$snippet['name']}\n";
|
114 |
+
|
115 |
+
if ( ! empty( $snippet['description'] ) ) {
|
116 |
+
|
117 |
+
// convert description to PHP Doc
|
118 |
+
$desc = strip_tags( str_replace( "\n", "\n * ", $snippet['description'] ) );
|
119 |
+
|
120 |
+
echo " *\n * $desc\n";
|
121 |
+
}
|
122 |
+
|
123 |
+
echo " */\n{$snippet['code']}\n";
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
do_action( 'code_snippets_export_file_snippet', $format, $id, $filename );
|
128 |
+
|
129 |
+
/* Finish off the file */
|
130 |
+
|
131 |
+
if ( 'xml' === $format ) {
|
132 |
+
|
133 |
+
echo "\n</snippets>";
|
134 |
+
|
135 |
+
} elseif ( 'php' === $format ) {
|
136 |
+
|
137 |
+
echo '?>';
|
138 |
+
|
139 |
+
}
|
140 |
+
|
141 |
+
do_action( 'code_snippets_export_file_footer', $format, $ids, $filename );
|
142 |
+
|
143 |
+
exit;
|
144 |
+
}
|
145 |
+
|
146 |
endif; // function exists check
|
includes/help/import.php
CHANGED
@@ -11,7 +11,7 @@ $screen->add_help_tab( array(
|
|
11 |
'id' => 'import',
|
12 |
'title' => __('Importing', 'code-snippets'),
|
13 |
'content' =>
|
14 |
-
'<p>' . __('You can load your snippets from a
|
15 |
sprintf( __('Snippets will be added to the database along with your existing snippets. Regardless of whether the snippets were active on the previous site, imported snippets are always inactive until activated using the <a href="%s">Manage Snippets</a> page.</p>', 'code-snippets'), $this->admin_manage_url ) . '</p>'
|
16 |
) );
|
17 |
|
@@ -27,4 +27,4 @@ $screen->set_help_sidebar(
|
|
27 |
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
|
28 |
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
29 |
'<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
|
30 |
-
);
|
11 |
'id' => 'import',
|
12 |
'title' => __('Importing', 'code-snippets'),
|
13 |
'content' =>
|
14 |
+
'<p>' . __('You can load your snippets from a code snippets (.xml) export file using this page.', 'code-snippets') .
|
15 |
sprintf( __('Snippets will be added to the database along with your existing snippets. Regardless of whether the snippets were active on the previous site, imported snippets are always inactive until activated using the <a href="%s">Manage Snippets</a> page.</p>', 'code-snippets'), $this->admin_manage_url ) . '</p>'
|
16 |
) );
|
17 |
|
27 |
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
|
28 |
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
29 |
'<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
|
30 |
+
);
|
includes/help/single.php
CHANGED
@@ -24,7 +24,6 @@ $screen->add_help_tab( array(
|
|
24 |
'title' => __('Adding Snippets', 'code-snippets'),
|
25 |
'content' =>
|
26 |
'<p>' . __('You need to fill out the name and code fields for your snippet to be added. While the description field will add more information about how your snippet works, what is does and where you found it, it is completely optional.', 'code-snippets') . '</p>' .
|
27 |
-
'<p>' . __('Make sure that you don\'t add the <code><?php</code>, <code><?</code> or <code>?></code> the beginning and end of the code. You can however use these tags in the code to stop and start PHP sections', 'code-snippets') . '</p>' .
|
28 |
'<p>' . __('Please be sure to check that your snippet is valid PHP code and will not produce errors before adding it through this page. While doing so will not become active straight away, it will help to minimise the chance of a faulty snippet becoming active on your site.', 'code-snippets') . '</p>'
|
29 |
) );
|
30 |
|
@@ -33,4 +32,4 @@ $screen->set_help_sidebar(
|
|
33 |
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
|
34 |
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
35 |
'<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
|
36 |
-
);
|
24 |
'title' => __('Adding Snippets', 'code-snippets'),
|
25 |
'content' =>
|
26 |
'<p>' . __('You need to fill out the name and code fields for your snippet to be added. While the description field will add more information about how your snippet works, what is does and where you found it, it is completely optional.', 'code-snippets') . '</p>' .
|
|
|
27 |
'<p>' . __('Please be sure to check that your snippet is valid PHP code and will not produce errors before adding it through this page. While doing so will not become active straight away, it will help to minimise the chance of a faulty snippet becoming active on your site.', 'code-snippets') . '</p>'
|
28 |
) );
|
29 |
|
32 |
'<p>' . __('<a href="http://wordpress.org/extend/plugins/code-snippets" target="_blank">WordPress Extend</a>', 'code-snippets') . '</p>' .
|
33 |
'<p>' . __('<a href="http://wordpress.org/support/plugin/code-snippets" target="_blank">Support Forums</a>', 'code-snippets') . '</p>' .
|
34 |
'<p>' . __('<a href="http://code-snippets.bungeshea.com/" target="_blank">Project Website</a>', 'code-snippets') . '</p>'
|
35 |
+
);
|
languages/code-snippets.pot
CHANGED
@@ -1,268 +1,291 @@
|
|
1 |
-
# Copyright (C)
|
2 |
# This file is distributed under the same license as the Code Snippets package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
-
"Project-Id-Version: Code Snippets 1.
|
6 |
-
"Report-Msgid-Bugs-To: http://wordpress.org/
|
7 |
-
"POT-Creation-Date:
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=UTF-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
11 |
-
"PO-Revision-Date:
|
12 |
-
"Last-Translator:
|
|
|
13 |
|
14 |
-
|
|
|
|
|
15 |
msgid "Code Snippets"
|
16 |
msgstr ""
|
17 |
|
18 |
-
#: code-snippets.php:
|
19 |
msgid "Import snippets from a <strong>Code Snippets</strong> export file"
|
20 |
msgstr ""
|
21 |
|
22 |
-
#: code-snippets.php:
|
23 |
-
#:
|
24 |
-
#: includes/admin/manage.php:29
|
25 |
msgid "Snippets"
|
26 |
msgstr ""
|
27 |
|
28 |
-
#: code-snippets.php:
|
29 |
-
msgid "Manage
|
30 |
msgstr ""
|
31 |
|
32 |
-
#: code-snippets.php:
|
33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
msgid "Add New Snippet"
|
35 |
msgstr ""
|
36 |
|
37 |
-
#: code-snippets.php:
|
38 |
msgid "Add New"
|
39 |
msgstr ""
|
40 |
|
41 |
-
#: code-snippets.php:
|
42 |
msgid "Import Snippets"
|
43 |
msgstr ""
|
44 |
|
45 |
-
#: code-snippets.php:
|
46 |
msgid "Import"
|
47 |
msgstr ""
|
48 |
|
49 |
-
#: code-snippets.php:
|
50 |
-
|
|
|
51 |
msgstr ""
|
52 |
|
53 |
-
#: code-snippets.php:
|
54 |
-
|
|
|
55 |
msgstr ""
|
56 |
|
57 |
-
#: code-snippets.php:
|
58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
msgstr ""
|
60 |
|
61 |
-
#: code-snippets.php:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
msgid "Visit the WordPress.org plugin page"
|
63 |
msgstr ""
|
64 |
|
65 |
-
#: code-snippets.php:
|
66 |
msgid "About"
|
67 |
msgstr ""
|
68 |
|
69 |
-
#: code-snippets.php:
|
70 |
msgid "Visit the support forums"
|
71 |
msgstr ""
|
72 |
|
73 |
-
#: code-snippets.php:
|
74 |
msgid "Support"
|
75 |
msgstr ""
|
76 |
|
77 |
-
#: code-snippets.php:
|
78 |
msgid "Support this plugin's development"
|
79 |
msgstr ""
|
80 |
|
81 |
-
#: code-snippets.php:
|
82 |
msgid "Donate"
|
83 |
msgstr ""
|
84 |
|
85 |
-
#: includes/admin/import.php:
|
86 |
msgid "Imported <strong>%d</strong> snippet."
|
87 |
msgid_plural "Imported <strong>%d</strong> snippets."
|
88 |
msgstr[0] ""
|
89 |
msgstr[1] ""
|
90 |
|
91 |
-
#: includes/admin/import.php:
|
92 |
-
msgid "
|
|
|
|
|
93 |
msgstr ""
|
94 |
|
95 |
-
#: includes/admin/import.php:
|
96 |
-
msgid "
|
|
|
|
|
97 |
msgstr ""
|
98 |
|
99 |
-
#: includes/admin/import.php:
|
100 |
-
msgid "
|
|
|
|
|
101 |
msgstr ""
|
102 |
|
103 |
-
#: includes/admin/import.php:
|
104 |
msgid "Choose a file from your computer:"
|
105 |
msgstr ""
|
106 |
|
107 |
-
#: includes/admin/import.php:
|
108 |
msgid "(Maximum size: 8MB)"
|
109 |
msgstr ""
|
110 |
|
111 |
-
#: includes/admin/import.php:
|
112 |
msgid "Upload file and import"
|
113 |
msgstr ""
|
114 |
|
115 |
-
#: includes/admin/manage.php:
|
116 |
msgid "Snippet <strong>activated</strong>."
|
117 |
msgstr ""
|
118 |
|
119 |
-
#: includes/admin/manage.php:
|
120 |
msgid "Selected snippets <strong>activated</strong>."
|
121 |
msgstr ""
|
122 |
|
123 |
-
#: includes/admin/manage.php:
|
124 |
msgid "Snippet <strong>deactivated</strong>."
|
125 |
msgstr ""
|
126 |
|
127 |
-
#: includes/admin/manage.php:
|
128 |
msgid "Selected snippets <strong>deactivated</strong>."
|
129 |
msgstr ""
|
130 |
|
131 |
-
#: includes/admin/manage.php:
|
132 |
msgid "Snippet <strong>deleted</strong>."
|
133 |
msgstr ""
|
134 |
|
135 |
-
#: includes/admin/manage.php:
|
136 |
msgid "Selected snippets <strong>deleted</strong>."
|
137 |
msgstr ""
|
138 |
|
139 |
-
#: includes/admin/manage.php:
|
140 |
msgctxt "snippet"
|
141 |
msgid "Add New"
|
142 |
msgstr ""
|
143 |
|
144 |
-
#: includes/admin/manage.php:
|
145 |
-
msgid "Search results for “%s”"
|
146 |
-
msgstr ""
|
147 |
-
|
148 |
-
#: includes/admin/manage.php:40
|
149 |
msgid "Search Installed Snippets"
|
150 |
msgstr ""
|
151 |
|
152 |
-
#: includes/admin/single.php:
|
153 |
-
msgid "
|
154 |
msgstr ""
|
155 |
|
156 |
-
#: includes/admin/single.php:
|
157 |
-
msgid "
|
158 |
msgstr ""
|
159 |
|
160 |
-
#: includes/admin/single.php:
|
161 |
msgid "Snippet <strong>updated</strong>."
|
162 |
msgstr ""
|
163 |
|
164 |
-
#: includes/admin/single.php:
|
165 |
msgid "Snippet <strong>added</strong>."
|
166 |
msgstr ""
|
167 |
|
168 |
-
#: includes/admin/single.php:
|
169 |
msgid "Name (short title)"
|
170 |
msgstr ""
|
171 |
|
172 |
-
#: includes/admin/single.php:
|
173 |
msgid "Code"
|
174 |
msgstr ""
|
175 |
|
176 |
-
#: includes/
|
177 |
-
msgid "Enter or paste the snippet code without the <code><?php</code> and <code>?></code> tags."
|
178 |
-
msgstr ""
|
179 |
-
|
180 |
-
#: includes/admin/single.php:69 includes/class-list-table.php:156
|
181 |
-
msgid "Description"
|
182 |
-
msgstr ""
|
183 |
-
|
184 |
-
#: includes/admin/single.php:70
|
185 |
-
msgid "(Optional)"
|
186 |
-
msgstr ""
|
187 |
-
|
188 |
-
#: includes/admin/single.php:86
|
189 |
-
msgid "Save"
|
190 |
-
msgstr ""
|
191 |
-
|
192 |
-
#: includes/admin/single.php:87
|
193 |
-
msgid "Cancel"
|
194 |
-
msgstr ""
|
195 |
-
|
196 |
-
#: includes/class-list-table.php:31
|
197 |
msgid "Snippets per page"
|
198 |
msgstr ""
|
199 |
|
200 |
-
#: includes/class-list-table.php:
|
201 |
-
msgid "
|
202 |
-
|
203 |
-
|
204 |
-
#: includes/class-list-table.php:101 includes/class-list-table.php:178
|
205 |
-
msgid "Deactivate"
|
206 |
-
msgstr ""
|
207 |
-
|
208 |
-
#: includes/class-list-table.php:111 includes/class-list-table.php:177
|
209 |
-
msgid "Network Activate"
|
210 |
-
msgstr ""
|
211 |
-
|
212 |
-
#: includes/class-list-table.php:111 includes/class-list-table.php:177
|
213 |
-
msgid "Activate"
|
214 |
msgstr ""
|
215 |
|
216 |
-
#: includes/class-list-table.php:
|
217 |
msgid "Name"
|
218 |
msgstr ""
|
219 |
|
220 |
-
#: includes/class-list-table.php:
|
221 |
msgid "ID"
|
222 |
msgstr ""
|
223 |
|
224 |
-
#: includes/class-list-table.php:
|
225 |
msgid "Export"
|
226 |
msgstr ""
|
227 |
|
228 |
-
#: includes/class-list-table.php:
|
229 |
msgid "Delete"
|
230 |
msgstr ""
|
231 |
|
232 |
-
#: includes/class-list-table.php:
|
233 |
msgid "Export to PHP"
|
234 |
msgstr ""
|
235 |
|
236 |
-
#: includes/class-list-table.php:
|
237 |
msgid "All <span class=\"count\">(%s)</span>"
|
238 |
msgid_plural "All <span class=\"count\">(%s)</span>"
|
239 |
msgstr[0] ""
|
240 |
msgstr[1] ""
|
241 |
|
242 |
-
#: includes/class-list-table.php:
|
243 |
msgid "Active <span class=\"count\">(%s)</span>"
|
244 |
msgid_plural "Active <span class=\"count\">(%s)</span>"
|
245 |
msgstr[0] ""
|
246 |
msgstr[1] ""
|
247 |
|
248 |
-
#: includes/class-list-table.php:
|
249 |
msgid "Recently Active <span class=\"count\">(%s)</span>"
|
250 |
msgid_plural "Recently Active <span class=\"count\">(%s)</span>"
|
251 |
msgstr[0] ""
|
252 |
msgstr[1] ""
|
253 |
|
254 |
-
#: includes/class-list-table.php:
|
255 |
msgid "Inactive <span class=\"count\">(%s)</span>"
|
256 |
msgid_plural "Inactive <span class=\"count\">(%s)</span>"
|
257 |
msgstr[0] ""
|
258 |
msgstr[1] ""
|
259 |
|
260 |
-
#: includes/class-list-table.php:
|
|
|
|
|
|
|
|
|
261 |
msgid "Clear List"
|
262 |
msgstr ""
|
263 |
|
264 |
-
#: includes/class-list-table.php:
|
265 |
-
msgid "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
266 |
msgstr ""
|
267 |
|
268 |
#: includes/help/import.php:5 includes/help/manage.php:5
|
@@ -271,7 +294,12 @@ msgid "Overview"
|
|
271 |
msgstr ""
|
272 |
|
273 |
#: includes/help/import.php:7
|
274 |
-
msgid "
|
|
|
|
|
|
|
|
|
|
|
275 |
msgstr ""
|
276 |
|
277 |
#: includes/help/import.php:12
|
@@ -279,11 +307,17 @@ msgid "Importing"
|
|
279 |
msgstr ""
|
280 |
|
281 |
#: includes/help/import.php:14
|
282 |
-
msgid "
|
|
|
|
|
283 |
msgstr ""
|
284 |
|
285 |
#: includes/help/import.php:15
|
286 |
-
msgid "
|
|
|
|
|
|
|
|
|
287 |
msgstr ""
|
288 |
|
289 |
#: includes/help/import.php:20
|
@@ -291,30 +325,43 @@ msgid "Exporting"
|
|
291 |
msgstr ""
|
292 |
|
293 |
#: includes/help/import.php:22
|
294 |
-
msgid "
|
|
|
|
|
295 |
msgstr ""
|
296 |
|
297 |
#: includes/help/import.php:26 includes/help/manage.php:27
|
298 |
-
#: includes/help/single.php:
|
299 |
msgid "For more information:"
|
300 |
msgstr ""
|
301 |
|
302 |
-
#: includes/help/import.php:27 includes/help/single.php:
|
303 |
-
msgid "
|
|
|
|
|
304 |
msgstr ""
|
305 |
|
306 |
#: includes/help/import.php:28 includes/help/manage.php:29
|
307 |
-
#: includes/help/single.php:
|
308 |
-
msgid "
|
|
|
|
|
309 |
msgstr ""
|
310 |
|
311 |
#: includes/help/import.php:29 includes/help/manage.php:30
|
312 |
-
#: includes/help/single.php:
|
313 |
-
msgid "
|
|
|
|
|
314 |
msgstr ""
|
315 |
|
316 |
#: includes/help/manage.php:7
|
317 |
-
msgid "
|
|
|
|
|
|
|
|
|
|
|
318 |
msgstr ""
|
319 |
|
320 |
#: includes/help/manage.php:12
|
@@ -322,11 +369,20 @@ msgid "Safe Mode"
|
|
322 |
msgstr ""
|
323 |
|
324 |
#: includes/help/manage.php:14
|
325 |
-
msgid "
|
|
|
|
|
|
|
326 |
msgstr ""
|
327 |
|
328 |
#: includes/help/manage.php:15
|
329 |
-
msgid "
|
|
|
|
|
|
|
|
|
|
|
|
|
330 |
msgstr ""
|
331 |
|
332 |
#: includes/help/manage.php:20
|
@@ -334,19 +390,33 @@ msgid "Uninstall"
|
|
334 |
msgstr ""
|
335 |
|
336 |
#: includes/help/manage.php:22
|
337 |
-
msgid "
|
|
|
|
|
|
|
|
|
|
|
338 |
msgstr ""
|
339 |
|
340 |
#: includes/help/manage.php:23
|
341 |
-
msgid "
|
|
|
|
|
|
|
342 |
msgstr ""
|
343 |
|
344 |
#: includes/help/manage.php:28
|
345 |
-
msgid "
|
|
|
|
|
346 |
msgstr ""
|
347 |
|
348 |
#: includes/help/single.php:7
|
349 |
-
msgid "
|
|
|
|
|
|
|
|
|
350 |
msgstr ""
|
351 |
|
352 |
#: includes/help/single.php:11
|
@@ -355,17 +425,24 @@ msgstr ""
|
|
355 |
|
356 |
#: includes/help/single.php:13
|
357 |
msgid ""
|
358 |
-
"Here are some links to websites which host a large number of snippets that
|
359 |
-
"\
|
360 |
-
"\t\t
|
361 |
-
"\t\t\t<li><a href=\"http://
|
362 |
-
"
|
363 |
-
"\t\t\t<li><a href=\"http://
|
|
|
|
|
|
|
|
|
364 |
"\t\t</ul>"
|
365 |
msgstr ""
|
366 |
|
367 |
#: includes/help/single.php:20
|
368 |
-
msgid "
|
|
|
|
|
|
|
369 |
msgstr ""
|
370 |
|
371 |
#: includes/help/single.php:24
|
@@ -373,18 +450,18 @@ msgid "Adding Snippets"
|
|
373 |
msgstr ""
|
374 |
|
375 |
#: includes/help/single.php:26
|
376 |
-
msgid "
|
|
|
|
|
|
|
377 |
msgstr ""
|
378 |
|
379 |
#: includes/help/single.php:27
|
380 |
-
msgid "
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
msgstr ""
|
386 |
-
#. Plugin Name of the plugin/theme
|
387 |
-
msgid "Code Snippets"
|
388 |
msgstr ""
|
389 |
|
390 |
#. Plugin URI of the plugin/theme
|
@@ -392,7 +469,9 @@ msgid "http://code-snippets.bungeshea.com"
|
|
392 |
msgstr ""
|
393 |
|
394 |
#. Description of the plugin/theme
|
395 |
-
msgid "
|
|
|
|
|
396 |
msgstr ""
|
397 |
|
398 |
#. Author of the plugin/theme
|
1 |
+
# Copyright (C) 2013 Code Snippets
|
2 |
# This file is distributed under the same license as the Code Snippets package.
|
3 |
msgid ""
|
4 |
msgstr ""
|
5 |
+
"Project-Id-Version: Code Snippets 1.7\n"
|
6 |
+
"Report-Msgid-Bugs-To: http://wordpress.org/tag/code-snippets\n"
|
7 |
+
"POT-Creation-Date: 2013-03-26 07:01:58+00:00\n"
|
8 |
"MIME-Version: 1.0\n"
|
9 |
"Content-Type: text/plain; charset=UTF-8\n"
|
10 |
"Content-Transfer-Encoding: 8bit\n"
|
11 |
+
"PO-Revision-Date: 2013-MO-DA HO:MI+ZONE\n"
|
12 |
+
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
13 |
+
"Language-Team: LANGUAGE <LL@li.org>\n"
|
14 |
|
15 |
+
#. #-#-#-#-# plugin.pot (Code Snippets 1.7) #-#-#-#-#
|
16 |
+
#. Plugin Name of the plugin/theme
|
17 |
+
#: code-snippets.php:245
|
18 |
msgid "Code Snippets"
|
19 |
msgstr ""
|
20 |
|
21 |
+
#: code-snippets.php:246
|
22 |
msgid "Import snippets from a <strong>Code Snippets</strong> export file"
|
23 |
msgstr ""
|
24 |
|
25 |
+
#: code-snippets.php:581 code-snippets.php:582 code-snippets.php:592
|
26 |
+
#: includes/admin/manage.php:36
|
|
|
27 |
msgid "Snippets"
|
28 |
msgstr ""
|
29 |
|
30 |
+
#: code-snippets.php:593 code-snippets.php:1467
|
31 |
+
msgid "Manage"
|
32 |
msgstr ""
|
33 |
|
34 |
+
#: code-snippets.php:605 code-snippets.php:1150 includes/admin/single.php:37
|
35 |
+
msgid "Edit Snippet"
|
36 |
+
msgstr ""
|
37 |
+
|
38 |
+
#: code-snippets.php:606
|
39 |
+
msgid "Edit"
|
40 |
+
msgstr ""
|
41 |
+
|
42 |
+
#: code-snippets.php:616 code-snippets.php:1149 includes/admin/single.php:45
|
43 |
msgid "Add New Snippet"
|
44 |
msgstr ""
|
45 |
|
46 |
+
#: code-snippets.php:617
|
47 |
msgid "Add New"
|
48 |
msgstr ""
|
49 |
|
50 |
+
#: code-snippets.php:645 includes/admin/import.php:34
|
51 |
msgid "Import Snippets"
|
52 |
msgstr ""
|
53 |
|
54 |
+
#: code-snippets.php:646
|
55 |
msgid "Import"
|
56 |
msgstr ""
|
57 |
|
58 |
+
#: code-snippets.php:1122 includes/class-list-table.php:127
|
59 |
+
#: includes/class-list-table.php:213
|
60 |
+
msgid "Network Deactivate"
|
61 |
msgstr ""
|
62 |
|
63 |
+
#: code-snippets.php:1122 includes/class-list-table.php:127
|
64 |
+
#: includes/class-list-table.php:213
|
65 |
+
msgid "Deactivate"
|
66 |
msgstr ""
|
67 |
|
68 |
+
#: code-snippets.php:1131 includes/class-list-table.php:137
|
69 |
+
#: includes/class-list-table.php:212
|
70 |
+
msgid "Network Activate"
|
71 |
+
msgstr ""
|
72 |
+
|
73 |
+
#: code-snippets.php:1131 includes/class-list-table.php:137
|
74 |
+
#: includes/class-list-table.php:212
|
75 |
+
msgid "Activate"
|
76 |
msgstr ""
|
77 |
|
78 |
+
#: code-snippets.php:1231
|
79 |
+
msgid "Sorry, you're not allowed to edit snippets"
|
80 |
+
msgstr ""
|
81 |
+
|
82 |
+
#: code-snippets.php:1434 includes/class-list-table.php:189
|
83 |
+
msgid "Description"
|
84 |
+
msgstr ""
|
85 |
+
|
86 |
+
#: code-snippets.php:1435
|
87 |
+
msgid "(Optional)"
|
88 |
+
msgstr ""
|
89 |
+
|
90 |
+
#: code-snippets.php:1466
|
91 |
+
msgid "Manage your existing snippets"
|
92 |
+
msgstr ""
|
93 |
+
|
94 |
+
#: code-snippets.php:1487
|
95 |
msgid "Visit the WordPress.org plugin page"
|
96 |
msgstr ""
|
97 |
|
98 |
+
#: code-snippets.php:1488
|
99 |
msgid "About"
|
100 |
msgstr ""
|
101 |
|
102 |
+
#: code-snippets.php:1492
|
103 |
msgid "Visit the support forums"
|
104 |
msgstr ""
|
105 |
|
106 |
+
#: code-snippets.php:1493
|
107 |
msgid "Support"
|
108 |
msgstr ""
|
109 |
|
110 |
+
#: code-snippets.php:1497
|
111 |
msgid "Support this plugin's development"
|
112 |
msgstr ""
|
113 |
|
114 |
+
#: code-snippets.php:1498
|
115 |
msgid "Donate"
|
116 |
msgstr ""
|
117 |
|
118 |
+
#: includes/admin/import.php:19
|
119 |
msgid "Imported <strong>%d</strong> snippet."
|
120 |
msgid_plural "Imported <strong>%d</strong> snippets."
|
121 |
msgstr[0] ""
|
122 |
msgstr[1] ""
|
123 |
|
124 |
+
#: includes/admin/import.php:38
|
125 |
+
msgid ""
|
126 |
+
"Howdy! Upload your Code Snippets export file and we’ll import the "
|
127 |
+
"snippets to this site."
|
128 |
msgstr ""
|
129 |
|
130 |
+
#: includes/admin/import.php:40
|
131 |
+
msgid ""
|
132 |
+
"You will need to go to the <a href=\"%s\">Manage Snippets</a> page to "
|
133 |
+
"activate the imported snippets."
|
134 |
msgstr ""
|
135 |
|
136 |
+
#: includes/admin/import.php:42
|
137 |
+
msgid ""
|
138 |
+
"Choose a Code Snippets (.xml) file to upload, then click Upload file and "
|
139 |
+
"import."
|
140 |
msgstr ""
|
141 |
|
142 |
+
#: includes/admin/import.php:46
|
143 |
msgid "Choose a file from your computer:"
|
144 |
msgstr ""
|
145 |
|
146 |
+
#: includes/admin/import.php:46
|
147 |
msgid "(Maximum size: 8MB)"
|
148 |
msgstr ""
|
149 |
|
150 |
+
#: includes/admin/import.php:55
|
151 |
msgid "Upload file and import"
|
152 |
msgstr ""
|
153 |
|
154 |
+
#: includes/admin/manage.php:21
|
155 |
msgid "Snippet <strong>activated</strong>."
|
156 |
msgstr ""
|
157 |
|
158 |
+
#: includes/admin/manage.php:23
|
159 |
msgid "Selected snippets <strong>activated</strong>."
|
160 |
msgstr ""
|
161 |
|
162 |
+
#: includes/admin/manage.php:25
|
163 |
msgid "Snippet <strong>deactivated</strong>."
|
164 |
msgstr ""
|
165 |
|
166 |
+
#: includes/admin/manage.php:27
|
167 |
msgid "Selected snippets <strong>deactivated</strong>."
|
168 |
msgstr ""
|
169 |
|
170 |
+
#: includes/admin/manage.php:29
|
171 |
msgid "Snippet <strong>deleted</strong>."
|
172 |
msgstr ""
|
173 |
|
174 |
+
#: includes/admin/manage.php:31
|
175 |
msgid "Selected snippets <strong>deleted</strong>."
|
176 |
msgstr ""
|
177 |
|
178 |
+
#: includes/admin/manage.php:38 includes/admin/single.php:42
|
179 |
msgctxt "snippet"
|
180 |
msgid "Add New"
|
181 |
msgstr ""
|
182 |
|
183 |
+
#: includes/admin/manage.php:47
|
|
|
|
|
|
|
|
|
184 |
msgid "Search Installed Snippets"
|
185 |
msgstr ""
|
186 |
|
187 |
+
#: includes/admin/single.php:22
|
188 |
+
msgid "Please provide a name for the snippet and its code."
|
189 |
msgstr ""
|
190 |
|
191 |
+
#: includes/admin/single.php:24 includes/admin/single.php:26
|
192 |
+
msgid "Snippet <strong>updated</strong> and <strong>activated</strong>."
|
193 |
msgstr ""
|
194 |
|
195 |
+
#: includes/admin/single.php:28
|
196 |
msgid "Snippet <strong>updated</strong>."
|
197 |
msgstr ""
|
198 |
|
199 |
+
#: includes/admin/single.php:30
|
200 |
msgid "Snippet <strong>added</strong>."
|
201 |
msgstr ""
|
202 |
|
203 |
+
#: includes/admin/single.php:55 includes/admin/single.php:56
|
204 |
msgid "Name (short title)"
|
205 |
msgstr ""
|
206 |
|
207 |
+
#: includes/admin/single.php:61
|
208 |
msgid "Code"
|
209 |
msgstr ""
|
210 |
|
211 |
+
#: includes/class-list-table.php:36
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
msgid "Snippets per page"
|
213 |
msgstr ""
|
214 |
|
215 |
+
#: includes/class-list-table.php:163
|
216 |
+
msgid ""
|
217 |
+
"You are about to permanently delete the selected item.\r\n"
|
218 |
+
"\t\t\t\t'Cancel' to stop, 'OK' to delete."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
219 |
msgstr ""
|
220 |
|
221 |
+
#: includes/class-list-table.php:187
|
222 |
msgid "Name"
|
223 |
msgstr ""
|
224 |
|
225 |
+
#: includes/class-list-table.php:188
|
226 |
msgid "ID"
|
227 |
msgstr ""
|
228 |
|
229 |
+
#: includes/class-list-table.php:214
|
230 |
msgid "Export"
|
231 |
msgstr ""
|
232 |
|
233 |
+
#: includes/class-list-table.php:215
|
234 |
msgid "Delete"
|
235 |
msgstr ""
|
236 |
|
237 |
+
#: includes/class-list-table.php:216
|
238 |
msgid "Export to PHP"
|
239 |
msgstr ""
|
240 |
|
241 |
+
#: includes/class-list-table.php:236
|
242 |
msgid "All <span class=\"count\">(%s)</span>"
|
243 |
msgid_plural "All <span class=\"count\">(%s)</span>"
|
244 |
msgstr[0] ""
|
245 |
msgstr[1] ""
|
246 |
|
247 |
+
#: includes/class-list-table.php:239
|
248 |
msgid "Active <span class=\"count\">(%s)</span>"
|
249 |
msgid_plural "Active <span class=\"count\">(%s)</span>"
|
250 |
msgstr[0] ""
|
251 |
msgstr[1] ""
|
252 |
|
253 |
+
#: includes/class-list-table.php:242
|
254 |
msgid "Recently Active <span class=\"count\">(%s)</span>"
|
255 |
msgid_plural "Recently Active <span class=\"count\">(%s)</span>"
|
256 |
msgstr[0] ""
|
257 |
msgstr[1] ""
|
258 |
|
259 |
+
#: includes/class-list-table.php:245
|
260 |
msgid "Inactive <span class=\"count\">(%s)</span>"
|
261 |
msgid_plural "Inactive <span class=\"count\">(%s)</span>"
|
262 |
msgstr[0] ""
|
263 |
msgstr[1] ""
|
264 |
|
265 |
+
#: includes/class-list-table.php:270
|
266 |
+
msgid "Filter"
|
267 |
+
msgstr ""
|
268 |
+
|
269 |
+
#: includes/class-list-table.php:279
|
270 |
msgid "Clear List"
|
271 |
msgstr ""
|
272 |
|
273 |
+
#: includes/class-list-table.php:374
|
274 |
+
msgid ""
|
275 |
+
"You do not appear to have any snippets available at this time. <a href=\"%s"
|
276 |
+
"\">Add New→</a>"
|
277 |
+
msgstr ""
|
278 |
+
|
279 |
+
#: includes/class-list-table.php:541
|
280 |
+
msgid "Search results"
|
281 |
+
msgstr ""
|
282 |
+
|
283 |
+
#: includes/class-list-table.php:544
|
284 |
+
msgid " for “%s”"
|
285 |
+
msgstr ""
|
286 |
+
|
287 |
+
#: includes/class-list-table.php:550
|
288 |
+
msgid "Clear Filters"
|
289 |
msgstr ""
|
290 |
|
291 |
#: includes/help/import.php:5 includes/help/manage.php:5
|
294 |
msgstr ""
|
295 |
|
296 |
#: includes/help/import.php:7
|
297 |
+
msgid ""
|
298 |
+
"Snippets are similar to plugins - they both extend and expand the "
|
299 |
+
"functionality of WordPress. Snippets are more light-weight, just a few lines "
|
300 |
+
"of code, and do not put as much load on your server. Here you can load "
|
301 |
+
"snippets from a Code Snippets (.xml) import file into the database with your "
|
302 |
+
"existing snippets."
|
303 |
msgstr ""
|
304 |
|
305 |
#: includes/help/import.php:12
|
307 |
msgstr ""
|
308 |
|
309 |
#: includes/help/import.php:14
|
310 |
+
msgid ""
|
311 |
+
"You can load your snippets from a code snippets (.xml) export file using "
|
312 |
+
"this page."
|
313 |
msgstr ""
|
314 |
|
315 |
#: includes/help/import.php:15
|
316 |
+
msgid ""
|
317 |
+
"Snippets will be added to the database along with your existing snippets. "
|
318 |
+
"Regardless of whether the snippets were active on the previous site, "
|
319 |
+
"imported snippets are always inactive until activated using the <a href=\"%s"
|
320 |
+
"\">Manage Snippets</a> page.</p>"
|
321 |
msgstr ""
|
322 |
|
323 |
#: includes/help/import.php:20
|
325 |
msgstr ""
|
326 |
|
327 |
#: includes/help/import.php:22
|
328 |
+
msgid ""
|
329 |
+
"You can save your snippets to a Code Snippets (.xml) export file using the "
|
330 |
+
"<a href=\"%s\">Manage Snippets</a> page."
|
331 |
msgstr ""
|
332 |
|
333 |
#: includes/help/import.php:26 includes/help/manage.php:27
|
334 |
+
#: includes/help/single.php:31
|
335 |
msgid "For more information:"
|
336 |
msgstr ""
|
337 |
|
338 |
+
#: includes/help/import.php:27 includes/help/single.php:32
|
339 |
+
msgid ""
|
340 |
+
"<a href=\"http://wordpress.org/extend/plugins/code-snippets\" target=\"_blank"
|
341 |
+
"\">WordPress Extend</a>"
|
342 |
msgstr ""
|
343 |
|
344 |
#: includes/help/import.php:28 includes/help/manage.php:29
|
345 |
+
#: includes/help/single.php:33
|
346 |
+
msgid ""
|
347 |
+
"<a href=\"http://wordpress.org/support/plugin/code-snippets\" target=\"_blank"
|
348 |
+
"\">Support Forums</a>"
|
349 |
msgstr ""
|
350 |
|
351 |
#: includes/help/import.php:29 includes/help/manage.php:30
|
352 |
+
#: includes/help/single.php:34
|
353 |
+
msgid ""
|
354 |
+
"<a href=\"http://code-snippets.bungeshea.com/\" target=\"_blank\">Project "
|
355 |
+
"Website</a>"
|
356 |
msgstr ""
|
357 |
|
358 |
#: includes/help/manage.php:7
|
359 |
+
msgid ""
|
360 |
+
"Snippets are similar to plugins - they both extend and expand the "
|
361 |
+
"functionality of WordPress. Snippets are more light-weight, just a few lines "
|
362 |
+
"of code, and do not put as much load on your server. Here you can manage "
|
363 |
+
"your existing snippets and preform tasks on them such as activating, "
|
364 |
+
"deactivating, deleting and exporting."
|
365 |
msgstr ""
|
366 |
|
367 |
#: includes/help/manage.php:12
|
369 |
msgstr ""
|
370 |
|
371 |
#: includes/help/manage.php:14
|
372 |
+
msgid ""
|
373 |
+
"Be sure to check your snippets for errors before you activate them, as a "
|
374 |
+
"faulty snippet could bring your whole blog down. If your site starts doing "
|
375 |
+
"strange things, deactivate all your snippets and activate them one at a time."
|
376 |
msgstr ""
|
377 |
|
378 |
#: includes/help/manage.php:15
|
379 |
+
msgid ""
|
380 |
+
"If something goes wrong with a snippet and you can't use WordPress, you can "
|
381 |
+
"cause all snippets to stop executing by adding <code>define"
|
382 |
+
"('CODE_SNIPPETS_SAFE_MODE', true);</code> to your <code>wp-config.php</code> "
|
383 |
+
"file. After you have deactivated the offending snippet, you can turn off "
|
384 |
+
"safe mode by removing this line or replacing <strong>true</strong> with "
|
385 |
+
"<strong>false</strong>."
|
386 |
msgstr ""
|
387 |
|
388 |
#: includes/help/manage.php:20
|
390 |
msgstr ""
|
391 |
|
392 |
#: includes/help/manage.php:22
|
393 |
+
msgid ""
|
394 |
+
"When you delete Code Snippets through the Plugins menu in WordPress it will "
|
395 |
+
"clear up the <code>%1$s</code> table and a few other bits of data stored in "
|
396 |
+
"the database. If you want to keep this data (ie: you are only temporally "
|
397 |
+
"uninstalling Code Snippets) then remove the <code>%2$s</code> folder using "
|
398 |
+
"FTP."
|
399 |
msgstr ""
|
400 |
|
401 |
#: includes/help/manage.php:23
|
402 |
+
msgid ""
|
403 |
+
"Even if you're sure that you don't want to use Code Snippets ever again on "
|
404 |
+
"this WordPress installation, you may want to use the export feature to back "
|
405 |
+
"up your snippets."
|
406 |
msgstr ""
|
407 |
|
408 |
#: includes/help/manage.php:28
|
409 |
+
msgid ""
|
410 |
+
"<a href=\"http://wordpress.org/extend/plugins/code-snippets\" target=\"_blank"
|
411 |
+
"\">WordPress Extend</a></p>"
|
412 |
msgstr ""
|
413 |
|
414 |
#: includes/help/single.php:7
|
415 |
+
msgid ""
|
416 |
+
"Snippets are similar to plugins - they both extend and expand the "
|
417 |
+
"functionality of WordPress. Snippets are more light-weight, just a few lines "
|
418 |
+
"of code, and do not put as much load on your server. Here you can add a new "
|
419 |
+
"snippet, or edit an existing one."
|
420 |
msgstr ""
|
421 |
|
422 |
#: includes/help/single.php:11
|
425 |
|
426 |
#: includes/help/single.php:13
|
427 |
msgid ""
|
428 |
+
"Here are some links to websites which host a large number of snippets that "
|
429 |
+
"you can add to your site.\r\n"
|
430 |
+
"\t\t<ul>\r\n"
|
431 |
+
"\t\t\t<li><a href=\"http://wp-snippets.com\" title=\"WordPress Snippets\">WP-"
|
432 |
+
"Snippets</a></li>\r\n"
|
433 |
+
"\t\t\t<li><a href=\"http://wpsnipp.com\" title=\"WP Snipp\">WP Snipp</a></li>"
|
434 |
+
"\r\n"
|
435 |
+
"\t\t\t<li><a href=\"http://www.catswhocode.com/blog/snippets\" title=\"Cats "
|
436 |
+
"Who Code Snippet Library\">Cats Who Code</a></li>\r\n"
|
437 |
+
"\t\t\t<li><a href=\"http://www.wpfunction.me\">WP Function Me</a></li>\r\n"
|
438 |
"\t\t</ul>"
|
439 |
msgstr ""
|
440 |
|
441 |
#: includes/help/single.php:20
|
442 |
+
msgid ""
|
443 |
+
"More places to find snippets, as well as a selection of example snippets, "
|
444 |
+
"can be found in the <a href=\"http://code-snippets.bungeshea.com/docs/"
|
445 |
+
"finding-snippets/\">plugin documentation</a>"
|
446 |
msgstr ""
|
447 |
|
448 |
#: includes/help/single.php:24
|
450 |
msgstr ""
|
451 |
|
452 |
#: includes/help/single.php:26
|
453 |
+
msgid ""
|
454 |
+
"You need to fill out the name and code fields for your snippet to be added. "
|
455 |
+
"While the description field will add more information about how your snippet "
|
456 |
+
"works, what is does and where you found it, it is completely optional."
|
457 |
msgstr ""
|
458 |
|
459 |
#: includes/help/single.php:27
|
460 |
+
msgid ""
|
461 |
+
"Please be sure to check that your snippet is valid PHP code and will not "
|
462 |
+
"produce errors before adding it through this page. While doing so will not "
|
463 |
+
"become active straight away, it will help to minimise the chance of a faulty "
|
464 |
+
"snippet becoming active on your site."
|
|
|
|
|
|
|
465 |
msgstr ""
|
466 |
|
467 |
#. Plugin URI of the plugin/theme
|
469 |
msgstr ""
|
470 |
|
471 |
#. Description of the plugin/theme
|
472 |
+
msgid ""
|
473 |
+
"An easy, clean and simple way to add code snippets to your site. No need to "
|
474 |
+
"edit to your theme's functions.php file again!"
|
475 |
msgstr ""
|
476 |
|
477 |
#. Author of the plugin/theme
|
license.txt
CHANGED
@@ -1,674 +1,7 @@
|
|
1 |
-
|
2 |
-
Version 3, 29 June 2007
|
3 |
|
4 |
-
|
5 |
-
Everyone is permitted to copy and distribute verbatim copies
|
6 |
-
of this license document, but changing it is not allowed.
|
7 |
|
8 |
-
|
9 |
|
10 |
-
|
11 |
-
software and other kinds of works.
|
12 |
-
|
13 |
-
The licenses for most software and other practical works are designed
|
14 |
-
to take away your freedom to share and change the works. By contrast,
|
15 |
-
the GNU General Public License is intended to guarantee your freedom to
|
16 |
-
share and change all versions of a program--to make sure it remains free
|
17 |
-
software for all its users. We, the Free Software Foundation, use the
|
18 |
-
GNU General Public License for most of our software; it applies also to
|
19 |
-
any other work released this way by its authors. You can apply it to
|
20 |
-
your programs, too.
|
21 |
-
|
22 |
-
When we speak of free software, we are referring to freedom, not
|
23 |
-
price. Our General Public Licenses are designed to make sure that you
|
24 |
-
have the freedom to distribute copies of free software (and charge for
|
25 |
-
them if you wish), that you receive source code or can get it if you
|
26 |
-
want it, that you can change the software or use pieces of it in new
|
27 |
-
free programs, and that you know you can do these things.
|
28 |
-
|
29 |
-
To protect your rights, we need to prevent others from denying you
|
30 |
-
these rights or asking you to surrender the rights. Therefore, you have
|
31 |
-
certain responsibilities if you distribute copies of the software, or if
|
32 |
-
you modify it: responsibilities to respect the freedom of others.
|
33 |
-
|
34 |
-
For example, if you distribute copies of such a program, whether
|
35 |
-
gratis or for a fee, you must pass on to the recipients the same
|
36 |
-
freedoms that you received. You must make sure that they, too, receive
|
37 |
-
or can get the source code. And you must show them these terms so they
|
38 |
-
know their rights.
|
39 |
-
|
40 |
-
Developers that use the GNU GPL protect your rights with two steps:
|
41 |
-
(1) assert copyright on the software, and (2) offer you this License
|
42 |
-
giving you legal permission to copy, distribute and/or modify it.
|
43 |
-
|
44 |
-
For the developers' and authors' protection, the GPL clearly explains
|
45 |
-
that there is no warranty for this free software. For both users' and
|
46 |
-
authors' sake, the GPL requires that modified versions be marked as
|
47 |
-
changed, so that their problems will not be attributed erroneously to
|
48 |
-
authors of previous versions.
|
49 |
-
|
50 |
-
Some devices are designed to deny users access to install or run
|
51 |
-
modified versions of the software inside them, although the manufacturer
|
52 |
-
can do so. This is fundamentally incompatible with the aim of
|
53 |
-
protecting users' freedom to change the software. The systematic
|
54 |
-
pattern of such abuse occurs in the area of products for individuals to
|
55 |
-
use, which is precisely where it is most unacceptable. Therefore, we
|
56 |
-
have designed this version of the GPL to prohibit the practice for those
|
57 |
-
products. If such problems arise substantially in other domains, we
|
58 |
-
stand ready to extend this provision to those domains in future versions
|
59 |
-
of the GPL, as needed to protect the freedom of users.
|
60 |
-
|
61 |
-
Finally, every program is threatened constantly by software patents.
|
62 |
-
States should not allow patents to restrict development and use of
|
63 |
-
software on general-purpose computers, but in those that do, we wish to
|
64 |
-
avoid the special danger that patents applied to a free program could
|
65 |
-
make it effectively proprietary. To prevent this, the GPL assures that
|
66 |
-
patents cannot be used to render the program non-free.
|
67 |
-
|
68 |
-
The precise terms and conditions for copying, distribution and
|
69 |
-
modification follow.
|
70 |
-
|
71 |
-
TERMS AND CONDITIONS
|
72 |
-
|
73 |
-
0. Definitions.
|
74 |
-
|
75 |
-
"This License" refers to version 3 of the GNU General Public License.
|
76 |
-
|
77 |
-
"Copyright" also means copyright-like laws that apply to other kinds of
|
78 |
-
works, such as semiconductor masks.
|
79 |
-
|
80 |
-
"The Program" refers to any copyrightable work licensed under this
|
81 |
-
License. Each licensee is addressed as "you". "Licensees" and
|
82 |
-
"recipients" may be individuals or organizations.
|
83 |
-
|
84 |
-
To "modify" a work means to copy from or adapt all or part of the work
|
85 |
-
in a fashion requiring copyright permission, other than the making of an
|
86 |
-
exact copy. The resulting work is called a "modified version" of the
|
87 |
-
earlier work or a work "based on" the earlier work.
|
88 |
-
|
89 |
-
A "covered work" means either the unmodified Program or a work based
|
90 |
-
on the Program.
|
91 |
-
|
92 |
-
To "propagate" a work means to do anything with it that, without
|
93 |
-
permission, would make you directly or secondarily liable for
|
94 |
-
infringement under applicable copyright law, except executing it on a
|
95 |
-
computer or modifying a private copy. Propagation includes copying,
|
96 |
-
distribution (with or without modification), making available to the
|
97 |
-
public, and in some countries other activities as well.
|
98 |
-
|
99 |
-
To "convey" a work means any kind of propagation that enables other
|
100 |
-
parties to make or receive copies. Mere interaction with a user through
|
101 |
-
a computer network, with no transfer of a copy, is not conveying.
|
102 |
-
|
103 |
-
An interactive user interface displays "Appropriate Legal Notices"
|
104 |
-
to the extent that it includes a convenient and prominently visible
|
105 |
-
feature that (1) displays an appropriate copyright notice, and (2)
|
106 |
-
tells the user that there is no warranty for the work (except to the
|
107 |
-
extent that warranties are provided), that licensees may convey the
|
108 |
-
work under this License, and how to view a copy of this License. If
|
109 |
-
the interface presents a list of user commands or options, such as a
|
110 |
-
menu, a prominent item in the list meets this criterion.
|
111 |
-
|
112 |
-
1. Source Code.
|
113 |
-
|
114 |
-
The "source code" for a work means the preferred form of the work
|
115 |
-
for making modifications to it. "Object code" means any non-source
|
116 |
-
form of a work.
|
117 |
-
|
118 |
-
A "Standard Interface" means an interface that either is an official
|
119 |
-
standard defined by a recognized standards body, or, in the case of
|
120 |
-
interfaces specified for a particular programming language, one that
|
121 |
-
is widely used among developers working in that language.
|
122 |
-
|
123 |
-
The "System Libraries" of an executable work include anything, other
|
124 |
-
than the work as a whole, that (a) is included in the normal form of
|
125 |
-
packaging a Major Component, but which is not part of that Major
|
126 |
-
Component, and (b) serves only to enable use of the work with that
|
127 |
-
Major Component, or to implement a Standard Interface for which an
|
128 |
-
implementation is available to the public in source code form. A
|
129 |
-
"Major Component", in this context, means a major essential component
|
130 |
-
(kernel, window system, and so on) of the specific operating system
|
131 |
-
(if any) on which the executable work runs, or a compiler used to
|
132 |
-
produce the work, or an object code interpreter used to run it.
|
133 |
-
|
134 |
-
The "Corresponding Source" for a work in object code form means all
|
135 |
-
the source code needed to generate, install, and (for an executable
|
136 |
-
work) run the object code and to modify the work, including scripts to
|
137 |
-
control those activities. However, it does not include the work's
|
138 |
-
System Libraries, or general-purpose tools or generally available free
|
139 |
-
programs which are used unmodified in performing those activities but
|
140 |
-
which are not part of the work. For example, Corresponding Source
|
141 |
-
includes interface definition files associated with source files for
|
142 |
-
the work, and the source code for shared libraries and dynamically
|
143 |
-
linked subprograms that the work is specifically designed to require,
|
144 |
-
such as by intimate data communication or control flow between those
|
145 |
-
subprograms and other parts of the work.
|
146 |
-
|
147 |
-
The Corresponding Source need not include anything that users
|
148 |
-
can regenerate automatically from other parts of the Corresponding
|
149 |
-
Source.
|
150 |
-
|
151 |
-
The Corresponding Source for a work in source code form is that
|
152 |
-
same work.
|
153 |
-
|
154 |
-
2. Basic Permissions.
|
155 |
-
|
156 |
-
All rights granted under this License are granted for the term of
|
157 |
-
copyright on the Program, and are irrevocable provided the stated
|
158 |
-
conditions are met. This License explicitly affirms your unlimited
|
159 |
-
permission to run the unmodified Program. The output from running a
|
160 |
-
covered work is covered by this License only if the output, given its
|
161 |
-
content, constitutes a covered work. This License acknowledges your
|
162 |
-
rights of fair use or other equivalent, as provided by copyright law.
|
163 |
-
|
164 |
-
You may make, run and propagate covered works that you do not
|
165 |
-
convey, without conditions so long as your license otherwise remains
|
166 |
-
in force. You may convey covered works to others for the sole purpose
|
167 |
-
of having them make modifications exclusively for you, or provide you
|
168 |
-
with facilities for running those works, provided that you comply with
|
169 |
-
the terms of this License in conveying all material for which you do
|
170 |
-
not control copyright. Those thus making or running the covered works
|
171 |
-
for you must do so exclusively on your behalf, under your direction
|
172 |
-
and control, on terms that prohibit them from making any copies of
|
173 |
-
your copyrighted material outside their relationship with you.
|
174 |
-
|
175 |
-
Conveying under any other circumstances is permitted solely under
|
176 |
-
the conditions stated below. Sublicensing is not allowed; section 10
|
177 |
-
makes it unnecessary.
|
178 |
-
|
179 |
-
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
180 |
-
|
181 |
-
No covered work shall be deemed part of an effective technological
|
182 |
-
measure under any applicable law fulfilling obligations under article
|
183 |
-
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
184 |
-
similar laws prohibiting or restricting circumvention of such
|
185 |
-
measures.
|
186 |
-
|
187 |
-
When you convey a covered work, you waive any legal power to forbid
|
188 |
-
circumvention of technological measures to the extent such circumvention
|
189 |
-
is effected by exercising rights under this License with respect to
|
190 |
-
the covered work, and you disclaim any intention to limit operation or
|
191 |
-
modification of the work as a means of enforcing, against the work's
|
192 |
-
users, your or third parties' legal rights to forbid circumvention of
|
193 |
-
technological measures.
|
194 |
-
|
195 |
-
4. Conveying Verbatim Copies.
|
196 |
-
|
197 |
-
You may convey verbatim copies of the Program's source code as you
|
198 |
-
receive it, in any medium, provided that you conspicuously and
|
199 |
-
appropriately publish on each copy an appropriate copyright notice;
|
200 |
-
keep intact all notices stating that this License and any
|
201 |
-
non-permissive terms added in accord with section 7 apply to the code;
|
202 |
-
keep intact all notices of the absence of any warranty; and give all
|
203 |
-
recipients a copy of this License along with the Program.
|
204 |
-
|
205 |
-
You may charge any price or no price for each copy that you convey,
|
206 |
-
and you may offer support or warranty protection for a fee.
|
207 |
-
|
208 |
-
5. Conveying Modified Source Versions.
|
209 |
-
|
210 |
-
You may convey a work based on the Program, or the modifications to
|
211 |
-
produce it from the Program, in the form of source code under the
|
212 |
-
terms of section 4, provided that you also meet all of these conditions:
|
213 |
-
|
214 |
-
a) The work must carry prominent notices stating that you modified
|
215 |
-
it, and giving a relevant date.
|
216 |
-
|
217 |
-
b) The work must carry prominent notices stating that it is
|
218 |
-
released under this License and any conditions added under section
|
219 |
-
7. This requirement modifies the requirement in section 4 to
|
220 |
-
"keep intact all notices".
|
221 |
-
|
222 |
-
c) You must license the entire work, as a whole, under this
|
223 |
-
License to anyone who comes into possession of a copy. This
|
224 |
-
License will therefore apply, along with any applicable section 7
|
225 |
-
additional terms, to the whole of the work, and all its parts,
|
226 |
-
regardless of how they are packaged. This License gives no
|
227 |
-
permission to license the work in any other way, but it does not
|
228 |
-
invalidate such permission if you have separately received it.
|
229 |
-
|
230 |
-
d) If the work has interactive user interfaces, each must display
|
231 |
-
Appropriate Legal Notices; however, if the Program has interactive
|
232 |
-
interfaces that do not display Appropriate Legal Notices, your
|
233 |
-
work need not make them do so.
|
234 |
-
|
235 |
-
A compilation of a covered work with other separate and independent
|
236 |
-
works, which are not by their nature extensions of the covered work,
|
237 |
-
and which are not combined with it such as to form a larger program,
|
238 |
-
in or on a volume of a storage or distribution medium, is called an
|
239 |
-
"aggregate" if the compilation and its resulting copyright are not
|
240 |
-
used to limit the access or legal rights of the compilation's users
|
241 |
-
beyond what the individual works permit. Inclusion of a covered work
|
242 |
-
in an aggregate does not cause this License to apply to the other
|
243 |
-
parts of the aggregate.
|
244 |
-
|
245 |
-
6. Conveying Non-Source Forms.
|
246 |
-
|
247 |
-
You may convey a covered work in object code form under the terms
|
248 |
-
of sections 4 and 5, provided that you also convey the
|
249 |
-
machine-readable Corresponding Source under the terms of this License,
|
250 |
-
in one of these ways:
|
251 |
-
|
252 |
-
a) Convey the object code in, or embodied in, a physical product
|
253 |
-
(including a physical distribution medium), accompanied by the
|
254 |
-
Corresponding Source fixed on a durable physical medium
|
255 |
-
customarily used for software interchange.
|
256 |
-
|
257 |
-
b) Convey the object code in, or embodied in, a physical product
|
258 |
-
(including a physical distribution medium), accompanied by a
|
259 |
-
written offer, valid for at least three years and valid for as
|
260 |
-
long as you offer spare parts or customer support for that product
|
261 |
-
model, to give anyone who possesses the object code either (1) a
|
262 |
-
copy of the Corresponding Source for all the software in the
|
263 |
-
product that is covered by this License, on a durable physical
|
264 |
-
medium customarily used for software interchange, for a price no
|
265 |
-
more than your reasonable cost of physically performing this
|
266 |
-
conveying of source, or (2) access to copy the
|
267 |
-
Corresponding Source from a network server at no charge.
|
268 |
-
|
269 |
-
c) Convey individual copies of the object code with a copy of the
|
270 |
-
written offer to provide the Corresponding Source. This
|
271 |
-
alternative is allowed only occasionally and noncommercially, and
|
272 |
-
only if you received the object code with such an offer, in accord
|
273 |
-
with subsection 6b.
|
274 |
-
|
275 |
-
d) Convey the object code by offering access from a designated
|
276 |
-
place (gratis or for a charge), and offer equivalent access to the
|
277 |
-
Corresponding Source in the same way through the same place at no
|
278 |
-
further charge. You need not require recipients to copy the
|
279 |
-
Corresponding Source along with the object code. If the place to
|
280 |
-
copy the object code is a network server, the Corresponding Source
|
281 |
-
may be on a different server (operated by you or a third party)
|
282 |
-
that supports equivalent copying facilities, provided you maintain
|
283 |
-
clear directions next to the object code saying where to find the
|
284 |
-
Corresponding Source. Regardless of what server hosts the
|
285 |
-
Corresponding Source, you remain obligated to ensure that it is
|
286 |
-
available for as long as needed to satisfy these requirements.
|
287 |
-
|
288 |
-
e) Convey the object code using peer-to-peer transmission, provided
|
289 |
-
you inform other peers where the object code and Corresponding
|
290 |
-
Source of the work are being offered to the general public at no
|
291 |
-
charge under subsection 6d.
|
292 |
-
|
293 |
-
A separable portion of the object code, whose source code is excluded
|
294 |
-
from the Corresponding Source as a System Library, need not be
|
295 |
-
included in conveying the object code work.
|
296 |
-
|
297 |
-
A "User Product" is either (1) a "consumer product", which means any
|
298 |
-
tangible personal property which is normally used for personal, family,
|
299 |
-
or household purposes, or (2) anything designed or sold for incorporation
|
300 |
-
into a dwelling. In determining whether a product is a consumer product,
|
301 |
-
doubtful cases shall be resolved in favor of coverage. For a particular
|
302 |
-
product received by a particular user, "normally used" refers to a
|
303 |
-
typical or common use of that class of product, regardless of the status
|
304 |
-
of the particular user or of the way in which the particular user
|
305 |
-
actually uses, or expects or is expected to use, the product. A product
|
306 |
-
is a consumer product regardless of whether the product has substantial
|
307 |
-
commercial, industrial or non-consumer uses, unless such uses represent
|
308 |
-
the only significant mode of use of the product.
|
309 |
-
|
310 |
-
"Installation Information" for a User Product means any methods,
|
311 |
-
procedures, authorization keys, or other information required to install
|
312 |
-
and execute modified versions of a covered work in that User Product from
|
313 |
-
a modified version of its Corresponding Source. The information must
|
314 |
-
suffice to ensure that the continued functioning of the modified object
|
315 |
-
code is in no case prevented or interfered with solely because
|
316 |
-
modification has been made.
|
317 |
-
|
318 |
-
If you convey an object code work under this section in, or with, or
|
319 |
-
specifically for use in, a User Product, and the conveying occurs as
|
320 |
-
part of a transaction in which the right of possession and use of the
|
321 |
-
User Product is transferred to the recipient in perpetuity or for a
|
322 |
-
fixed term (regardless of how the transaction is characterized), the
|
323 |
-
Corresponding Source conveyed under this section must be accompanied
|
324 |
-
by the Installation Information. But this requirement does not apply
|
325 |
-
if neither you nor any third party retains the ability to install
|
326 |
-
modified object code on the User Product (for example, the work has
|
327 |
-
been installed in ROM).
|
328 |
-
|
329 |
-
The requirement to provide Installation Information does not include a
|
330 |
-
requirement to continue to provide support service, warranty, or updates
|
331 |
-
for a work that has been modified or installed by the recipient, or for
|
332 |
-
the User Product in which it has been modified or installed. Access to a
|
333 |
-
network may be denied when the modification itself materially and
|
334 |
-
adversely affects the operation of the network or violates the rules and
|
335 |
-
protocols for communication across the network.
|
336 |
-
|
337 |
-
Corresponding Source conveyed, and Installation Information provided,
|
338 |
-
in accord with this section must be in a format that is publicly
|
339 |
-
documented (and with an implementation available to the public in
|
340 |
-
source code form), and must require no special password or key for
|
341 |
-
unpacking, reading or copying.
|
342 |
-
|
343 |
-
7. Additional Terms.
|
344 |
-
|
345 |
-
"Additional permissions" are terms that supplement the terms of this
|
346 |
-
License by making exceptions from one or more of its conditions.
|
347 |
-
Additional permissions that are applicable to the entire Program shall
|
348 |
-
be treated as though they were included in this License, to the extent
|
349 |
-
that they are valid under applicable law. If additional permissions
|
350 |
-
apply only to part of the Program, that part may be used separately
|
351 |
-
under those permissions, but the entire Program remains governed by
|
352 |
-
this License without regard to the additional permissions.
|
353 |
-
|
354 |
-
When you convey a copy of a covered work, you may at your option
|
355 |
-
remove any additional permissions from that copy, or from any part of
|
356 |
-
it. (Additional permissions may be written to require their own
|
357 |
-
removal in certain cases when you modify the work.) You may place
|
358 |
-
additional permissions on material, added by you to a covered work,
|
359 |
-
for which you have or can give appropriate copyright permission.
|
360 |
-
|
361 |
-
Notwithstanding any other provision of this License, for material you
|
362 |
-
add to a covered work, you may (if authorized by the copyright holders of
|
363 |
-
that material) supplement the terms of this License with terms:
|
364 |
-
|
365 |
-
a) Disclaiming warranty or limiting liability differently from the
|
366 |
-
terms of sections 15 and 16 of this License; or
|
367 |
-
|
368 |
-
b) Requiring preservation of specified reasonable legal notices or
|
369 |
-
author attributions in that material or in the Appropriate Legal
|
370 |
-
Notices displayed by works containing it; or
|
371 |
-
|
372 |
-
c) Prohibiting misrepresentation of the origin of that material, or
|
373 |
-
requiring that modified versions of such material be marked in
|
374 |
-
reasonable ways as different from the original version; or
|
375 |
-
|
376 |
-
d) Limiting the use for publicity purposes of names of licensors or
|
377 |
-
authors of the material; or
|
378 |
-
|
379 |
-
e) Declining to grant rights under trademark law for use of some
|
380 |
-
trade names, trademarks, or service marks; or
|
381 |
-
|
382 |
-
f) Requiring indemnification of licensors and authors of that
|
383 |
-
material by anyone who conveys the material (or modified versions of
|
384 |
-
it) with contractual assumptions of liability to the recipient, for
|
385 |
-
any liability that these contractual assumptions directly impose on
|
386 |
-
those licensors and authors.
|
387 |
-
|
388 |
-
All other non-permissive additional terms are considered "further
|
389 |
-
restrictions" within the meaning of section 10. If the Program as you
|
390 |
-
received it, or any part of it, contains a notice stating that it is
|
391 |
-
governed by this License along with a term that is a further
|
392 |
-
restriction, you may remove that term. If a license document contains
|
393 |
-
a further restriction but permits relicensing or conveying under this
|
394 |
-
License, you may add to a covered work material governed by the terms
|
395 |
-
of that license document, provided that the further restriction does
|
396 |
-
not survive such relicensing or conveying.
|
397 |
-
|
398 |
-
If you add terms to a covered work in accord with this section, you
|
399 |
-
must place, in the relevant source files, a statement of the
|
400 |
-
additional terms that apply to those files, or a notice indicating
|
401 |
-
where to find the applicable terms.
|
402 |
-
|
403 |
-
Additional terms, permissive or non-permissive, may be stated in the
|
404 |
-
form of a separately written license, or stated as exceptions;
|
405 |
-
the above requirements apply either way.
|
406 |
-
|
407 |
-
8. Termination.
|
408 |
-
|
409 |
-
You may not propagate or modify a covered work except as expressly
|
410 |
-
provided under this License. Any attempt otherwise to propagate or
|
411 |
-
modify it is void, and will automatically terminate your rights under
|
412 |
-
this License (including any patent licenses granted under the third
|
413 |
-
paragraph of section 11).
|
414 |
-
|
415 |
-
However, if you cease all violation of this License, then your
|
416 |
-
license from a particular copyright holder is reinstated (a)
|
417 |
-
provisionally, unless and until the copyright holder explicitly and
|
418 |
-
finally terminates your license, and (b) permanently, if the copyright
|
419 |
-
holder fails to notify you of the violation by some reasonable means
|
420 |
-
prior to 60 days after the cessation.
|
421 |
-
|
422 |
-
Moreover, your license from a particular copyright holder is
|
423 |
-
reinstated permanently if the copyright holder notifies you of the
|
424 |
-
violation by some reasonable means, this is the first time you have
|
425 |
-
received notice of violation of this License (for any work) from that
|
426 |
-
copyright holder, and you cure the violation prior to 30 days after
|
427 |
-
your receipt of the notice.
|
428 |
-
|
429 |
-
Termination of your rights under this section does not terminate the
|
430 |
-
licenses of parties who have received copies or rights from you under
|
431 |
-
this License. If your rights have been terminated and not permanently
|
432 |
-
reinstated, you do not qualify to receive new licenses for the same
|
433 |
-
material under section 10.
|
434 |
-
|
435 |
-
9. Acceptance Not Required for Having Copies.
|
436 |
-
|
437 |
-
You are not required to accept this License in order to receive or
|
438 |
-
run a copy of the Program. Ancillary propagation of a covered work
|
439 |
-
occurring solely as a consequence of using peer-to-peer transmission
|
440 |
-
to receive a copy likewise does not require acceptance. However,
|
441 |
-
nothing other than this License grants you permission to propagate or
|
442 |
-
modify any covered work. These actions infringe copyright if you do
|
443 |
-
not accept this License. Therefore, by modifying or propagating a
|
444 |
-
covered work, you indicate your acceptance of this License to do so.
|
445 |
-
|
446 |
-
10. Automatic Licensing of Downstream Recipients.
|
447 |
-
|
448 |
-
Each time you convey a covered work, the recipient automatically
|
449 |
-
receives a license from the original licensors, to run, modify and
|
450 |
-
propagate that work, subject to this License. You are not responsible
|
451 |
-
for enforcing compliance by third parties with this License.
|
452 |
-
|
453 |
-
An "entity transaction" is a transaction transferring control of an
|
454 |
-
organization, or substantially all assets of one, or subdividing an
|
455 |
-
organization, or merging organizations. If propagation of a covered
|
456 |
-
work results from an entity transaction, each party to that
|
457 |
-
transaction who receives a copy of the work also receives whatever
|
458 |
-
licenses to the work the party's predecessor in interest had or could
|
459 |
-
give under the previous paragraph, plus a right to possession of the
|
460 |
-
Corresponding Source of the work from the predecessor in interest, if
|
461 |
-
the predecessor has it or can get it with reasonable efforts.
|
462 |
-
|
463 |
-
You may not impose any further restrictions on the exercise of the
|
464 |
-
rights granted or affirmed under this License. For example, you may
|
465 |
-
not impose a license fee, royalty, or other charge for exercise of
|
466 |
-
rights granted under this License, and you may not initiate litigation
|
467 |
-
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
468 |
-
any patent claim is infringed by making, using, selling, offering for
|
469 |
-
sale, or importing the Program or any portion of it.
|
470 |
-
|
471 |
-
11. Patents.
|
472 |
-
|
473 |
-
A "contributor" is a copyright holder who authorizes use under this
|
474 |
-
License of the Program or a work on which the Program is based. The
|
475 |
-
work thus licensed is called the contributor's "contributor version".
|
476 |
-
|
477 |
-
A contributor's "essential patent claims" are all patent claims
|
478 |
-
owned or controlled by the contributor, whether already acquired or
|
479 |
-
hereafter acquired, that would be infringed by some manner, permitted
|
480 |
-
by this License, of making, using, or selling its contributor version,
|
481 |
-
but do not include claims that would be infringed only as a
|
482 |
-
consequence of further modification of the contributor version. For
|
483 |
-
purposes of this definition, "control" includes the right to grant
|
484 |
-
patent sublicenses in a manner consistent with the requirements of
|
485 |
-
this License.
|
486 |
-
|
487 |
-
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
488 |
-
patent license under the contributor's essential patent claims, to
|
489 |
-
make, use, sell, offer for sale, import and otherwise run, modify and
|
490 |
-
propagate the contents of its contributor version.
|
491 |
-
|
492 |
-
In the following three paragraphs, a "patent license" is any express
|
493 |
-
agreement or commitment, however denominated, not to enforce a patent
|
494 |
-
(such as an express permission to practice a patent or covenant not to
|
495 |
-
sue for patent infringement). To "grant" such a patent license to a
|
496 |
-
party means to make such an agreement or commitment not to enforce a
|
497 |
-
patent against the party.
|
498 |
-
|
499 |
-
If you convey a covered work, knowingly relying on a patent license,
|
500 |
-
and the Corresponding Source of the work is not available for anyone
|
501 |
-
to copy, free of charge and under the terms of this License, through a
|
502 |
-
publicly available network server or other readily accessible means,
|
503 |
-
then you must either (1) cause the Corresponding Source to be so
|
504 |
-
available, or (2) arrange to deprive yourself of the benefit of the
|
505 |
-
patent license for this particular work, or (3) arrange, in a manner
|
506 |
-
consistent with the requirements of this License, to extend the patent
|
507 |
-
license to downstream recipients. "Knowingly relying" means you have
|
508 |
-
actual knowledge that, but for the patent license, your conveying the
|
509 |
-
covered work in a country, or your recipient's use of the covered work
|
510 |
-
in a country, would infringe one or more identifiable patents in that
|
511 |
-
country that you have reason to believe are valid.
|
512 |
-
|
513 |
-
If, pursuant to or in connection with a single transaction or
|
514 |
-
arrangement, you convey, or propagate by procuring conveyance of, a
|
515 |
-
covered work, and grant a patent license to some of the parties
|
516 |
-
receiving the covered work authorizing them to use, propagate, modify
|
517 |
-
or convey a specific copy of the covered work, then the patent license
|
518 |
-
you grant is automatically extended to all recipients of the covered
|
519 |
-
work and works based on it.
|
520 |
-
|
521 |
-
A patent license is "discriminatory" if it does not include within
|
522 |
-
the scope of its coverage, prohibits the exercise of, or is
|
523 |
-
conditioned on the non-exercise of one or more of the rights that are
|
524 |
-
specifically granted under this License. You may not convey a covered
|
525 |
-
work if you are a party to an arrangement with a third party that is
|
526 |
-
in the business of distributing software, under which you make payment
|
527 |
-
to the third party based on the extent of your activity of conveying
|
528 |
-
the work, and under which the third party grants, to any of the
|
529 |
-
parties who would receive the covered work from you, a discriminatory
|
530 |
-
patent license (a) in connection with copies of the covered work
|
531 |
-
conveyed by you (or copies made from those copies), or (b) primarily
|
532 |
-
for and in connection with specific products or compilations that
|
533 |
-
contain the covered work, unless you entered into that arrangement,
|
534 |
-
or that patent license was granted, prior to 28 March 2007.
|
535 |
-
|
536 |
-
Nothing in this License shall be construed as excluding or limiting
|
537 |
-
any implied license or other defenses to infringement that may
|
538 |
-
otherwise be available to you under applicable patent law.
|
539 |
-
|
540 |
-
12. No Surrender of Others' Freedom.
|
541 |
-
|
542 |
-
If conditions are imposed on you (whether by court order, agreement or
|
543 |
-
otherwise) that contradict the conditions of this License, they do not
|
544 |
-
excuse you from the conditions of this License. If you cannot convey a
|
545 |
-
covered work so as to satisfy simultaneously your obligations under this
|
546 |
-
License and any other pertinent obligations, then as a consequence you may
|
547 |
-
not convey it at all. For example, if you agree to terms that obligate you
|
548 |
-
to collect a royalty for further conveying from those to whom you convey
|
549 |
-
the Program, the only way you could satisfy both those terms and this
|
550 |
-
License would be to refrain entirely from conveying the Program.
|
551 |
-
|
552 |
-
13. Use with the GNU Affero General Public License.
|
553 |
-
|
554 |
-
Notwithstanding any other provision of this License, you have
|
555 |
-
permission to link or combine any covered work with a work licensed
|
556 |
-
under version 3 of the GNU Affero General Public License into a single
|
557 |
-
combined work, and to convey the resulting work. The terms of this
|
558 |
-
License will continue to apply to the part which is the covered work,
|
559 |
-
but the special requirements of the GNU Affero General Public License,
|
560 |
-
section 13, concerning interaction through a network will apply to the
|
561 |
-
combination as such.
|
562 |
-
|
563 |
-
14. Revised Versions of this License.
|
564 |
-
|
565 |
-
The Free Software Foundation may publish revised and/or new versions of
|
566 |
-
the GNU General Public License from time to time. Such new versions will
|
567 |
-
be similar in spirit to the present version, but may differ in detail to
|
568 |
-
address new problems or concerns.
|
569 |
-
|
570 |
-
Each version is given a distinguishing version number. If the
|
571 |
-
Program specifies that a certain numbered version of the GNU General
|
572 |
-
Public License "or any later version" applies to it, you have the
|
573 |
-
option of following the terms and conditions either of that numbered
|
574 |
-
version or of any later version published by the Free Software
|
575 |
-
Foundation. If the Program does not specify a version number of the
|
576 |
-
GNU General Public License, you may choose any version ever published
|
577 |
-
by the Free Software Foundation.
|
578 |
-
|
579 |
-
If the Program specifies that a proxy can decide which future
|
580 |
-
versions of the GNU General Public License can be used, that proxy's
|
581 |
-
public statement of acceptance of a version permanently authorizes you
|
582 |
-
to choose that version for the Program.
|
583 |
-
|
584 |
-
Later license versions may give you additional or different
|
585 |
-
permissions. However, no additional obligations are imposed on any
|
586 |
-
author or copyright holder as a result of your choosing to follow a
|
587 |
-
later version.
|
588 |
-
|
589 |
-
15. Disclaimer of Warranty.
|
590 |
-
|
591 |
-
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
592 |
-
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
593 |
-
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
594 |
-
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
595 |
-
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
596 |
-
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
597 |
-
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
598 |
-
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
599 |
-
|
600 |
-
16. Limitation of Liability.
|
601 |
-
|
602 |
-
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
603 |
-
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
604 |
-
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
605 |
-
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
606 |
-
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
607 |
-
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
608 |
-
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
609 |
-
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
610 |
-
SUCH DAMAGES.
|
611 |
-
|
612 |
-
17. Interpretation of Sections 15 and 16.
|
613 |
-
|
614 |
-
If the disclaimer of warranty and limitation of liability provided
|
615 |
-
above cannot be given local legal effect according to their terms,
|
616 |
-
reviewing courts shall apply local law that most closely approximates
|
617 |
-
an absolute waiver of all civil liability in connection with the
|
618 |
-
Program, unless a warranty or assumption of liability accompanies a
|
619 |
-
copy of the Program in return for a fee.
|
620 |
-
|
621 |
-
END OF TERMS AND CONDITIONS
|
622 |
-
|
623 |
-
How to Apply These Terms to Your New Programs
|
624 |
-
|
625 |
-
If you develop a new program, and you want it to be of the greatest
|
626 |
-
possible use to the public, the best way to achieve this is to make it
|
627 |
-
free software which everyone can redistribute and change under these terms.
|
628 |
-
|
629 |
-
To do so, attach the following notices to the program. It is safest
|
630 |
-
to attach them to the start of each source file to most effectively
|
631 |
-
state the exclusion of warranty; and each file should have at least
|
632 |
-
the "copyright" line and a pointer to where the full notice is found.
|
633 |
-
|
634 |
-
<one line to give the program's name and a brief idea of what it does.>
|
635 |
-
Copyright (C) <year> <name of author>
|
636 |
-
|
637 |
-
This program is free software: you can redistribute it and/or modify
|
638 |
-
it under the terms of the GNU General Public License as published by
|
639 |
-
the Free Software Foundation, either version 3 of the License, or
|
640 |
-
(at your option) any later version.
|
641 |
-
|
642 |
-
This program is distributed in the hope that it will be useful,
|
643 |
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
644 |
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
645 |
-
GNU General Public License for more details.
|
646 |
-
|
647 |
-
You should have received a copy of the GNU General Public License
|
648 |
-
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
649 |
-
|
650 |
-
Also add information on how to contact you by electronic and paper mail.
|
651 |
-
|
652 |
-
If the program does terminal interaction, make it output a short
|
653 |
-
notice like this when it starts in an interactive mode:
|
654 |
-
|
655 |
-
<program> Copyright (C) <year> <name of author>
|
656 |
-
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
657 |
-
This is free software, and you are welcome to redistribute it
|
658 |
-
under certain conditions; type `show c' for details.
|
659 |
-
|
660 |
-
The hypothetical commands `show w' and `show c' should show the appropriate
|
661 |
-
parts of the General Public License. Of course, your program's commands
|
662 |
-
might be different; for a GUI interface, you would use an "about box".
|
663 |
-
|
664 |
-
You should also get your employer (if you work as a programmer) or school,
|
665 |
-
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
666 |
-
For more information on this, and how to apply and follow the GNU GPL, see
|
667 |
-
<http://www.gnu.org/licenses/>.
|
668 |
-
|
669 |
-
The GNU General Public License does not permit incorporating your program
|
670 |
-
into proprietary programs. If your program is a subroutine library, you
|
671 |
-
may consider it more useful to permit linking proprietary applications with
|
672 |
-
the library. If this is what you want to do, use the GNU Lesser General
|
673 |
-
Public License instead of this License. But first, please read
|
674 |
-
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
1 |
+
Copyright (c) 2013 Shea Bunge
|
|
|
2 |
|
3 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
|
|
|
4 |
|
5 |
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6 |
|
7 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
readme.txt
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
=== Code Snippets ===
|
2 |
Contributors: bungeshea
|
3 |
Donate link: http://code-snippets.bungeshea.com/donate/
|
4 |
-
Tags: snippets, code, php, network, multisite
|
5 |
Requires at least: 3.3
|
6 |
-
Tested up to: 3.5
|
7 |
-
Stable tag: 1.
|
8 |
-
License:
|
9 |
-
License URI:
|
10 |
|
11 |
An easy, clean and simple way to add code snippets to your site.
|
12 |
|
@@ -15,7 +15,7 @@ An easy, clean and simple way to add code snippets to your site.
|
|
15 |
**Code Snippets** is an easy, clean and simple way to add code snippets to your site. No need to edit to your theme's `functions.php` file again!
|
16 |
|
17 |
A snippet is a small chunk of PHP code that you can use to extend the functionality of a WordPress-powered website; essentially a mini-plugin with a *lot* less load on your site.
|
18 |
-
Most snippet-hosting sites tell you to add snippet code to your active theme's `functions.php` file, which can get rather long and messy after a while.
|
19 |
Code Snippets changes that by providing a GUI interface for adding snippets and **actually running them on your site** as if they were in your theme's `functions.php` file.
|
20 |
|
21 |
You can use a graphical interface, similar to the Plugins menu, to manage, activate, deactivate, edit and delete your snippets. Easily organise your snippets by adding a name and description using the visual editor. Code Snippets includes built-in syntax highlighting and other features to help you write your code. Snippets can be exported for transfer to another side, either in XML for later importing by the Code Snippets plugin, or in PHP for creating your own plugin or theme.
|
@@ -53,7 +53,7 @@ If you have any feedback, issues, or suggestions for improvements please leave a
|
|
53 |
Further documentation available on the [plugin website](http://code-snippets.bungeshea.com/docs/).
|
54 |
|
55 |
= Do I need to include the <?php, <? or ?> tags in my snippet? =
|
56 |
-
No, just copy all the content inside those tags.
|
57 |
|
58 |
= Is there a way to add a snippet but not run it right away? =
|
59 |
Yes. Just add it but do not activate it yet.
|
@@ -85,7 +85,10 @@ Yes! You can individually export a single snippet using the link below the snipp
|
|
85 |
Yes. Click the checkboxes next to the snippets you want to export, and then choose **Export to PHP** from the Bulk Actions menu and click Apply. The generated PHP file will contain the exported snippets' code, as well as their name and description in comments.
|
86 |
|
87 |
= Can I run network-wide snippets on a multisite installation? =
|
88 |
-
You can run snippets across an entire multisite network by **Network Activating** Code Snippets through the Network Dashboard.
|
|
|
|
|
|
|
89 |
|
90 |
= I need help with Code Snippets =
|
91 |
You can get help with Code Snippets either on the [WordPress Support Forums](http://wordpress.org/support/plugin/code-snippets/), on [GithHub](https://github.com/bungeshea/code-snippets/issues), or on [WordPress Answers](http://wordpress.stackexchange.com).
|
@@ -99,13 +102,25 @@ That's fantastic! Join me on [GitHub](https://github.com/bungeshea/code-snippets
|
|
99 |
== Screenshots ==
|
100 |
|
101 |
1. Managing existing snippets
|
102 |
-
2.
|
103 |
-
3.
|
104 |
-
4.
|
105 |
-
5.
|
106 |
|
107 |
== Changelog ==
|
108 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
109 |
= 1.6.1 =
|
110 |
* Fixed a bug with permissions not being applied on install ([#](http://wordpress.org/support/topic/permissions-problem-after-install))
|
111 |
* Fixed a bug in the uninstall method ([#](http://wordpress.org/support/topic/bug-in-delete-script))
|
@@ -178,6 +193,9 @@ Plugin updates will be posted on the [plugin's homepage](http://code-snippets.bu
|
|
178 |
|
179 |
== Upgrade Notice ==
|
180 |
|
|
|
|
|
|
|
181 |
= 1.6 =
|
182 |
Improvements and optimization with WordPress 3.5
|
183 |
|
@@ -194,8 +212,8 @@ Code Snippets has a new website: http://code-snippets.bungeshea.com/
|
|
194 |
Added import/export feature
|
195 |
|
196 |
= 1.2 =
|
197 |
-
Minor improvements |
|
198 |
-
Added code highlighting |
|
199 |
Plugin data will now be cleaned up when you delete the plugin.
|
200 |
|
201 |
= 1.1 =
|
1 |
=== Code Snippets ===
|
2 |
Contributors: bungeshea
|
3 |
Donate link: http://code-snippets.bungeshea.com/donate/
|
4 |
+
Tags: code-snippets, snippets, code, php, network, multisite
|
5 |
Requires at least: 3.3
|
6 |
+
Tested up to: 3.5.1
|
7 |
+
Stable tag: 1.7
|
8 |
+
License: MIT
|
9 |
+
License URI: license.txt
|
10 |
|
11 |
An easy, clean and simple way to add code snippets to your site.
|
12 |
|
15 |
**Code Snippets** is an easy, clean and simple way to add code snippets to your site. No need to edit to your theme's `functions.php` file again!
|
16 |
|
17 |
A snippet is a small chunk of PHP code that you can use to extend the functionality of a WordPress-powered website; essentially a mini-plugin with a *lot* less load on your site.
|
18 |
+
Most snippet-hosting sites tell you to add snippet code to your active theme's `functions.php` file, which can get rather long and messy after a while.
|
19 |
Code Snippets changes that by providing a GUI interface for adding snippets and **actually running them on your site** as if they were in your theme's `functions.php` file.
|
20 |
|
21 |
You can use a graphical interface, similar to the Plugins menu, to manage, activate, deactivate, edit and delete your snippets. Easily organise your snippets by adding a name and description using the visual editor. Code Snippets includes built-in syntax highlighting and other features to help you write your code. Snippets can be exported for transfer to another side, either in XML for later importing by the Code Snippets plugin, or in PHP for creating your own plugin or theme.
|
53 |
Further documentation available on the [plugin website](http://code-snippets.bungeshea.com/docs/).
|
54 |
|
55 |
= Do I need to include the <?php, <? or ?> tags in my snippet? =
|
56 |
+
No, just copy all the content inside those tags. If you accidentally forget (or just like being lazy), the tags will be stripped from the beginning and end of the snippet when you save it. You can, however, use those tags *inside* your snippets to start and end HTML sections.
|
57 |
|
58 |
= Is there a way to add a snippet but not run it right away? =
|
59 |
Yes. Just add it but do not activate it yet.
|
85 |
Yes. Click the checkboxes next to the snippets you want to export, and then choose **Export to PHP** from the Bulk Actions menu and click Apply. The generated PHP file will contain the exported snippets' code, as well as their name and description in comments.
|
86 |
|
87 |
= Can I run network-wide snippets on a multisite installation? =
|
88 |
+
You can run snippets across an entire multisite network by **Network Activating** Code Snippets through the Network Dashboard. You can also activate Code Snippets just on the main site, and then individually on other sites of your choice.
|
89 |
+
|
90 |
+
= Is there anyway to add categories to snippets? =
|
91 |
+
Users of Code Snippets version 1.7 and later can install the [Code Snippets Tags](http://wordpress.org/extend/plugins/code-snippets-tags) plugin for the ability to add tags to snippets, and then later filter the snippets by tag for easier organization.
|
92 |
|
93 |
= I need help with Code Snippets =
|
94 |
You can get help with Code Snippets either on the [WordPress Support Forums](http://wordpress.org/support/plugin/code-snippets/), on [GithHub](https://github.com/bungeshea/code-snippets/issues), or on [WordPress Answers](http://wordpress.stackexchange.com).
|
102 |
== Screenshots ==
|
103 |
|
104 |
1. Managing existing snippets
|
105 |
+
2. Adding a new snippet
|
106 |
+
3. Editing a snippet
|
107 |
+
4. Importing snippets from an XML file
|
108 |
+
5. Managing exiting snippets in the MP6 interface
|
109 |
|
110 |
== Changelog ==
|
111 |
|
112 |
+
= 1.7 =
|
113 |
+
* Improved plugin API
|
114 |
+
* Fixed a bug with saving snippets per page option ([#](http://wordpress.org/support/topic/plugin-code-snippets-snippets-per-page-does-not-work#post-3710991))
|
115 |
+
* Updated CodeMirror to version 3.11
|
116 |
+
* Allow plugin to be activated on individual sites on multisite ([#](http://wordpress.org/support/topic/dont-work-at-multisite))
|
117 |
+
* Slimmed down the description visual editor
|
118 |
+
* Added icon for the new MP6 admin UI ([#](http://wordpress.org/support/topic/icon-disappears-with-mp6))
|
119 |
+
* Strip PHP tags from the beginning and end of a snippet in case someone forgets
|
120 |
+
* Changed to [MIT license](http://opensource.org/licenses/mit-license.php)
|
121 |
+
* Removed HTML, CSS and JavaScript CodeMirror modes that were messing things up
|
122 |
+
* Made everything leaner, faster, and better
|
123 |
+
|
124 |
= 1.6.1 =
|
125 |
* Fixed a bug with permissions not being applied on install ([#](http://wordpress.org/support/topic/permissions-problem-after-install))
|
126 |
* Fixed a bug in the uninstall method ([#](http://wordpress.org/support/topic/bug-in-delete-script))
|
193 |
|
194 |
== Upgrade Notice ==
|
195 |
|
196 |
+
= 1.7 =
|
197 |
+
Many improvments and optimization. Download "Code Snippets Tags" plugin to add tags to snippets
|
198 |
+
|
199 |
= 1.6 =
|
200 |
Improvements and optimization with WordPress 3.5
|
201 |
|
212 |
Added import/export feature
|
213 |
|
214 |
= 1.2 =
|
215 |
+
Minor improvements |
|
216 |
+
Added code highlighting |
|
217 |
Plugin data will now be cleaned up when you delete the plugin.
|
218 |
|
219 |
= 1.1 =
|
screenshot-1.jpg
CHANGED
Binary file
|
screenshot-2.jpg
CHANGED
Binary file
|
screenshot-3.jpg
CHANGED
Binary file
|
screenshot-4.jpg
CHANGED
Binary file
|
screenshot-5.jpg
CHANGED
Binary file
|