keydown 0.7.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.travis.yml +3 -0
- data/LICENSE +1 -1
- data/README.md +16 -15
- data/VERSION +1 -1
- data/keydown.gemspec +9 -6
- data/lib/keydown.rb +9 -22
- data/lib/keydown/html_helpers.rb +12 -0
- data/lib/keydown/{lib/slide.rb → slide.rb} +16 -19
- data/lib/keydown/{lib/slidedeck.rb → slidedeck.rb} +20 -5
- data/lib/keydown/tasks.rb +5 -0
- data/lib/keydown/tasks/base.rb +15 -0
- data/lib/keydown/tasks/generate.rb +5 -1
- data/lib/keydown/tasks/slides.rb +1 -1
- data/lib/version.rb +1 -1
- data/spec/lib/html_helpers_spec.rb +26 -0
- data/spec/lib/slide_spec.rb +29 -17
- data/spec/lib/slidedeck_spec.rb +41 -13
- data/spec/spec_helper.rb +2 -6
- data/spec/tasks/generate_spec.rb +41 -20
- data/spec/tasks/slides_spec.rb +83 -44
- data/templates/deck.js/code.html.haml +1 -0
- data/templates/deck.js/index.html.haml +54 -0
- data/templates/deck.js/slide.html.haml +6 -0
- data/templates/generate/css/%presentation_name%.css +12 -1
- data/templates/generate/deck.js/core/deck.core.css +394 -0
- data/templates/generate/deck.js/core/deck.core.js +461 -0
- data/templates/generate/deck.js/core/deck.core.scss +432 -0
- data/templates/generate/deck.js/extensions/codemirror/CONTRIBUTORS.txt +2 -0
- data/templates/generate/deck.js/extensions/codemirror/MIT-LICENSE.txt +21 -0
- data/templates/generate/deck.js/extensions/codemirror/README.md +120 -0
- data/templates/generate/deck.js/extensions/codemirror/VERSION.txt +1 -0
- data/templates/generate/deck.js/extensions/codemirror/codemirror.js +21 -0
- data/templates/generate/deck.js/extensions/codemirror/deck.codemirror.css +89 -0
- data/templates/generate/deck.js/extensions/codemirror/deck.codemirror.js +213 -0
- data/templates/generate/deck.js/extensions/codemirror/deck.codemirror.scss +107 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/clike/clike.js +247 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/clike/index.html +102 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/clojure/clojure.js +207 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/clojure/index.html +85 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/coffeescript/LICENSE +22 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/coffeescript/coffeescript.js +325 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/coffeescript/index.html +722 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/css/css.js +124 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/css/index.html +56 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/diff/diff.css +3 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/diff/diff.js +13 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/diff/index.html +99 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/haskell/haskell.js +242 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/haskell/index.html +60 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/htmlmixed/htmlmixed.js +79 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/htmlmixed/index.html +52 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/javascript/index.html +78 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/javascript/javascript.js +348 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/lua/index.html +72 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/lua/lua.js +138 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/php/index.html +49 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/php/php.js +115 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/plsql/index.html +63 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/plsql/plsql.js +217 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/python/LICENSE.txt +21 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/python/index.html +123 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/python/python.js +321 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/r/LICENSE +24 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/r/index.html +74 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/r/r.js +141 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/rst/index.html +526 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/rst/rst.css +75 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/rst/rst.js +333 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/ruby/LICENSE +24 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/ruby/index.html +172 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/ruby/ruby.js +195 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/scheme/index.html +65 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/scheme/scheme.js +202 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/smalltalk/index.html +56 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/smalltalk/smalltalk.js +122 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/sparql/index.html +41 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/sparql/sparql.js +143 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/stex/index.html +96 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/stex/stex.js +167 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/velocity/index.html +103 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/velocity/velocity.js +146 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/xml/index.html +42 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/xml/xml.js +231 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/xmlpure/index.html +60 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/xmlpure/xmlpure.js +481 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/yaml/index.html +68 -0
- data/templates/generate/deck.js/extensions/codemirror/mode/yaml/yaml.js +95 -0
- data/templates/generate/deck.js/extensions/codemirror/themes/cobalt.css +17 -0
- data/templates/generate/deck.js/extensions/codemirror/themes/default.css +19 -0
- data/templates/generate/deck.js/extensions/codemirror/themes/elegant.css +9 -0
- data/templates/generate/deck.js/extensions/codemirror/themes/neat.css +8 -0
- data/templates/generate/deck.js/extensions/codemirror/themes/night.css +20 -0
- data/templates/generate/deck.js/extensions/goto/deck.goto.css +41 -0
- data/templates/generate/deck.js/extensions/goto/deck.goto.html +6 -0
- data/templates/generate/deck.js/extensions/goto/deck.goto.js +118 -0
- data/templates/generate/deck.js/extensions/goto/deck.goto.scss +46 -0
- data/templates/generate/deck.js/extensions/hash/deck.hash.css +13 -0
- data/templates/generate/deck.js/extensions/hash/deck.hash.html +2 -0
- data/templates/generate/deck.js/extensions/hash/deck.hash.js +125 -0
- data/templates/generate/deck.js/extensions/hash/deck.hash.scss +15 -0
- data/templates/generate/deck.js/extensions/menu/deck.menu.css +24 -0
- data/templates/generate/deck.js/extensions/menu/deck.menu.js +127 -0
- data/templates/generate/deck.js/extensions/menu/deck.menu.scss +29 -0
- data/templates/generate/deck.js/extensions/navigation/deck.navigation.css +43 -0
- data/templates/generate/deck.js/extensions/navigation/deck.navigation.html +3 -0
- data/templates/generate/deck.js/extensions/navigation/deck.navigation.js +83 -0
- data/templates/generate/deck.js/extensions/navigation/deck.navigation.scss +56 -0
- data/templates/generate/deck.js/extensions/scale/deck.scale.css +16 -0
- data/templates/generate/deck.js/extensions/scale/deck.scale.js +155 -0
- data/templates/generate/deck.js/extensions/scale/deck.scale.scss +17 -0
- data/templates/generate/deck.js/extensions/status/deck.status.css +14 -0
- data/templates/generate/deck.js/extensions/status/deck.status.html +6 -0
- data/templates/generate/deck.js/extensions/status/deck.status.js +42 -0
- data/templates/generate/deck.js/extensions/status/deck.status.scss +16 -0
- data/templates/generate/deck.js/support/jquery.1.6.4.min.js +4 -0
- data/templates/generate/deck.js/support/modernizr.custom.js +4 -0
- data/templates/generate/deck.js/themes/style/neon.css +114 -0
- data/templates/generate/deck.js/themes/style/neon.scss +139 -0
- data/templates/generate/deck.js/themes/style/swiss.css +75 -0
- data/templates/generate/deck.js/themes/style/swiss.scss +91 -0
- data/templates/generate/deck.js/themes/style/web-2.0.css +187 -0
- data/templates/generate/deck.js/themes/style/web-2.0.scss +214 -0
- data/templates/generate/deck.js/themes/transition/fade.css +44 -0
- data/templates/generate/deck.js/themes/transition/fade.scss +70 -0
- data/templates/generate/deck.js/themes/transition/horizontal-slide.css +79 -0
- data/templates/generate/deck.js/themes/transition/horizontal-slide.scss +94 -0
- data/templates/generate/deck.js/themes/transition/vertical-slide.css +97 -0
- data/templates/generate/deck.js/themes/transition/vertical-slide.scss +116 -0
- data/templates/keydown.css.erb +27 -23
- metadata +171 -32
- data/Gemfile.lock +0 -41
- data/templates/generate/css/rocks.css +0 -392
- data/templates/generate/css/syntax_highlighting.css +0 -135
- data/templates/generate/js/rocks.js +0 -419
- data/templates/rocks/index.rhtml +0 -132
- data/templates/rocks/slide.rhtml +0 -10
@@ -0,0 +1,231 @@
|
|
1
|
+
CodeMirror.defineMode("xml", function(config, parserConfig) {
|
2
|
+
var indentUnit = config.indentUnit;
|
3
|
+
var Kludges = parserConfig.htmlMode ? {
|
4
|
+
autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
|
5
|
+
"meta": true, "col": true, "frame": true, "base": true, "area": true},
|
6
|
+
doNotIndent: {"pre": true, "!cdata": true},
|
7
|
+
allowUnquoted: true
|
8
|
+
} : {autoSelfClosers: {}, doNotIndent: {"!cdata": true}, allowUnquoted: false};
|
9
|
+
var alignCDATA = parserConfig.alignCDATA;
|
10
|
+
|
11
|
+
// Return variables for tokenizers
|
12
|
+
var tagName, type;
|
13
|
+
|
14
|
+
function inText(stream, state) {
|
15
|
+
function chain(parser) {
|
16
|
+
state.tokenize = parser;
|
17
|
+
return parser(stream, state);
|
18
|
+
}
|
19
|
+
|
20
|
+
var ch = stream.next();
|
21
|
+
if (ch == "<") {
|
22
|
+
if (stream.eat("!")) {
|
23
|
+
if (stream.eat("[")) {
|
24
|
+
if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
|
25
|
+
else return null;
|
26
|
+
}
|
27
|
+
else if (stream.match("--")) return chain(inBlock("comment", "-->"));
|
28
|
+
else if (stream.match("DOCTYPE", true, true)) {
|
29
|
+
stream.eatWhile(/[\w\._\-]/);
|
30
|
+
return chain(inBlock("meta", ">"));
|
31
|
+
}
|
32
|
+
else return null;
|
33
|
+
}
|
34
|
+
else if (stream.eat("?")) {
|
35
|
+
stream.eatWhile(/[\w\._\-]/);
|
36
|
+
state.tokenize = inBlock("meta", "?>");
|
37
|
+
return "meta";
|
38
|
+
}
|
39
|
+
else {
|
40
|
+
type = stream.eat("/") ? "closeTag" : "openTag";
|
41
|
+
stream.eatSpace();
|
42
|
+
tagName = "";
|
43
|
+
var c;
|
44
|
+
while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
|
45
|
+
state.tokenize = inTag;
|
46
|
+
return "tag";
|
47
|
+
}
|
48
|
+
}
|
49
|
+
else if (ch == "&") {
|
50
|
+
stream.eatWhile(/[^;]/);
|
51
|
+
stream.eat(";");
|
52
|
+
return "atom";
|
53
|
+
}
|
54
|
+
else {
|
55
|
+
stream.eatWhile(/[^&<]/);
|
56
|
+
return null;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
function inTag(stream, state) {
|
61
|
+
var ch = stream.next();
|
62
|
+
if (ch == ">" || (ch == "/" && stream.eat(">"))) {
|
63
|
+
state.tokenize = inText;
|
64
|
+
type = ch == ">" ? "endTag" : "selfcloseTag";
|
65
|
+
return "tag";
|
66
|
+
}
|
67
|
+
else if (ch == "=") {
|
68
|
+
type = "equals";
|
69
|
+
return null;
|
70
|
+
}
|
71
|
+
else if (/[\'\"]/.test(ch)) {
|
72
|
+
state.tokenize = inAttribute(ch);
|
73
|
+
return state.tokenize(stream, state);
|
74
|
+
}
|
75
|
+
else {
|
76
|
+
stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
|
77
|
+
return "word";
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
function inAttribute(quote) {
|
82
|
+
return function(stream, state) {
|
83
|
+
while (!stream.eol()) {
|
84
|
+
if (stream.next() == quote) {
|
85
|
+
state.tokenize = inTag;
|
86
|
+
break;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
return "string";
|
90
|
+
};
|
91
|
+
}
|
92
|
+
|
93
|
+
function inBlock(style, terminator) {
|
94
|
+
return function(stream, state) {
|
95
|
+
while (!stream.eol()) {
|
96
|
+
if (stream.match(terminator)) {
|
97
|
+
state.tokenize = inText;
|
98
|
+
break;
|
99
|
+
}
|
100
|
+
stream.next();
|
101
|
+
}
|
102
|
+
return style;
|
103
|
+
};
|
104
|
+
}
|
105
|
+
|
106
|
+
var curState, setStyle;
|
107
|
+
function pass() {
|
108
|
+
for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
|
109
|
+
}
|
110
|
+
function cont() {
|
111
|
+
pass.apply(null, arguments);
|
112
|
+
return true;
|
113
|
+
}
|
114
|
+
|
115
|
+
function pushContext(tagName, startOfLine) {
|
116
|
+
var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
|
117
|
+
curState.context = {
|
118
|
+
prev: curState.context,
|
119
|
+
tagName: tagName,
|
120
|
+
indent: curState.indented,
|
121
|
+
startOfLine: startOfLine,
|
122
|
+
noIndent: noIndent
|
123
|
+
};
|
124
|
+
}
|
125
|
+
function popContext() {
|
126
|
+
if (curState.context) curState.context = curState.context.prev;
|
127
|
+
}
|
128
|
+
|
129
|
+
function element(type) {
|
130
|
+
if (type == "openTag") {curState.tagName = tagName; return cont(attributes, endtag(curState.startOfLine));}
|
131
|
+
else if (type == "closeTag") {
|
132
|
+
var err = false;
|
133
|
+
if (curState.context) {
|
134
|
+
err = curState.context.tagName != tagName;
|
135
|
+
} else {
|
136
|
+
err = true;
|
137
|
+
}
|
138
|
+
if (err) setStyle = "error";
|
139
|
+
return cont(endclosetag(err));
|
140
|
+
}
|
141
|
+
else if (type == "string") {
|
142
|
+
if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata");
|
143
|
+
if (curState.tokenize == inText) popContext();
|
144
|
+
return cont();
|
145
|
+
}
|
146
|
+
else return cont();
|
147
|
+
}
|
148
|
+
function endtag(startOfLine) {
|
149
|
+
return function(type) {
|
150
|
+
if (type == "selfcloseTag" ||
|
151
|
+
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
|
152
|
+
return cont();
|
153
|
+
if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
|
154
|
+
return cont();
|
155
|
+
};
|
156
|
+
}
|
157
|
+
function endclosetag(err) {
|
158
|
+
return function(type) {
|
159
|
+
if (err) setStyle = "error";
|
160
|
+
if (type == "endTag") { popContext(); return cont(); }
|
161
|
+
setStyle = "error";
|
162
|
+
return cont(arguments.callee);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
function attributes(type) {
|
167
|
+
if (type == "word") {setStyle = "attribute"; return cont(attributes);}
|
168
|
+
if (type == "equals") return cont(attvalue, attributes);
|
169
|
+
return pass();
|
170
|
+
}
|
171
|
+
function attvalue(type) {
|
172
|
+
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
|
173
|
+
if (type == "string") return cont(attvaluemaybe);
|
174
|
+
return pass();
|
175
|
+
}
|
176
|
+
function attvaluemaybe(type) {
|
177
|
+
if (type == "string") return cont(attvaluemaybe);
|
178
|
+
else return pass();
|
179
|
+
}
|
180
|
+
|
181
|
+
return {
|
182
|
+
startState: function() {
|
183
|
+
return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
|
184
|
+
},
|
185
|
+
|
186
|
+
token: function(stream, state) {
|
187
|
+
if (stream.sol()) {
|
188
|
+
state.startOfLine = true;
|
189
|
+
state.indented = stream.indentation();
|
190
|
+
}
|
191
|
+
if (stream.eatSpace()) return null;
|
192
|
+
|
193
|
+
setStyle = type = tagName = null;
|
194
|
+
var style = state.tokenize(stream, state);
|
195
|
+
if ((style || type) && style != "comment") {
|
196
|
+
curState = state;
|
197
|
+
while (true) {
|
198
|
+
var comb = state.cc.pop() || element;
|
199
|
+
if (comb(type || style)) break;
|
200
|
+
}
|
201
|
+
}
|
202
|
+
state.startOfLine = false;
|
203
|
+
return setStyle || style;
|
204
|
+
},
|
205
|
+
|
206
|
+
indent: function(state, textAfter) {
|
207
|
+
var context = state.context;
|
208
|
+
if (context && context.noIndent) return 0;
|
209
|
+
if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
|
210
|
+
if (context && /^<\//.test(textAfter))
|
211
|
+
context = context.prev;
|
212
|
+
while (context && !context.startOfLine)
|
213
|
+
context = context.prev;
|
214
|
+
if (context) return context.indent + indentUnit;
|
215
|
+
else return 0;
|
216
|
+
},
|
217
|
+
|
218
|
+
compareStates: function(a, b) {
|
219
|
+
if (a.indented != b.indented) return false;
|
220
|
+
for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
|
221
|
+
if (!ca || !cb) return ca == cb;
|
222
|
+
if (ca.tagName != cb.tagName) return false;
|
223
|
+
}
|
224
|
+
},
|
225
|
+
|
226
|
+
electricChars: "/"
|
227
|
+
};
|
228
|
+
});
|
229
|
+
|
230
|
+
CodeMirror.defineMIME("application/xml", "xml");
|
231
|
+
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
|
@@ -0,0 +1,60 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>CodeMirror 2: Pure XML mode</title>
|
5
|
+
<link rel="stylesheet" href="../../lib/codemirror.css">
|
6
|
+
<script src="../../lib/codemirror.js"></script>
|
7
|
+
<script src="xmlpure.js"></script>
|
8
|
+
<link rel="stylesheet" href="../../theme/default.css">
|
9
|
+
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
10
|
+
<link rel="stylesheet" href="../../css/docs.css">
|
11
|
+
</head>
|
12
|
+
<body>
|
13
|
+
<h1>CodeMirror 2: XML mode</h1>
|
14
|
+
<form><textarea id="code" name="code">
|
15
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
16
|
+
|
17
|
+
<!-- This is the pure XML mode,
|
18
|
+
and we're inside a comment! -->
|
19
|
+
|
20
|
+
<catalog>
|
21
|
+
<books>
|
22
|
+
<book id="bk01">
|
23
|
+
<title>Lord of Light</title>
|
24
|
+
<author>Roger Zelazny</author>
|
25
|
+
<year>1967</year>
|
26
|
+
<description><![CDATA[This is a great book, really!!]]></description>
|
27
|
+
</book>
|
28
|
+
</books>
|
29
|
+
</catalog>
|
30
|
+
</textarea></form>
|
31
|
+
<script>
|
32
|
+
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: {name: "xmlpure"}});
|
33
|
+
</script>
|
34
|
+
|
35
|
+
<p>This is my XML parser, based on the original:</p>
|
36
|
+
<ul>
|
37
|
+
<li>No html mode - this is pure xml</li>
|
38
|
+
<li>Illegal attributes and element names are errors</li>
|
39
|
+
<li>Attributes must have a value</li>
|
40
|
+
<li>XML declaration supported (e.g.: <b><?xml version="1.0" encoding="utf-8" standalone="no" ?></b>)</li>
|
41
|
+
<li>CDATA and comment blocks are not indented (except for their start-tag)</li>
|
42
|
+
<li>Better handling of errors per line with the state object - provides good infrastructure for extending it</li>
|
43
|
+
</ul>
|
44
|
+
|
45
|
+
<p>What's missing:</p>
|
46
|
+
<ul>
|
47
|
+
<li>Make sure only a single root element exists at the document level</li>
|
48
|
+
<li>Multi-line attributes should NOT indent</li>
|
49
|
+
<li>Start tags are not painted red when they have no matching end tags (is this really wrong?)</li>
|
50
|
+
</ul>
|
51
|
+
|
52
|
+
<p><strong>MIME types defined:</strong> <code>application/xml</code>, <code>text/xml</code>.</p>
|
53
|
+
|
54
|
+
<p><b>@author</b>: Dror BG (<i>deebug dot dev at gmail dot com</i>)<br/>
|
55
|
+
<p><b>@date</b>: August, 2011<br/>
|
56
|
+
<p><b>@github</b>: <a href='https://github.com/deebugger/CodeMirror2' target='blank'>https://github.com/deebugger/CodeMirror2</a></p>
|
57
|
+
|
58
|
+
<p><strong>MIME types defined:</strong> <code>application/xml</code>, <code>text/xml</code>.</p>
|
59
|
+
</body>
|
60
|
+
</html>
|
@@ -0,0 +1,481 @@
|
|
1
|
+
/**
|
2
|
+
* xmlpure.js
|
3
|
+
*
|
4
|
+
* Building upon and improving the CodeMirror 2 XML parser
|
5
|
+
* @author: Dror BG (deebug.dev@gmail.com)
|
6
|
+
* @date: August, 2011
|
7
|
+
*/
|
8
|
+
|
9
|
+
CodeMirror.defineMode("xmlpure", function(config, parserConfig) {
|
10
|
+
// constants
|
11
|
+
var STYLE_ERROR = "error";
|
12
|
+
var STYLE_INSTRUCTION = "comment";
|
13
|
+
var STYLE_COMMENT = "comment";
|
14
|
+
var STYLE_ELEMENT_NAME = "tag";
|
15
|
+
var STYLE_ATTRIBUTE = "attribute";
|
16
|
+
var STYLE_WORD = "string";
|
17
|
+
var STYLE_TEXT = "atom";
|
18
|
+
|
19
|
+
var TAG_INSTRUCTION = "!instruction";
|
20
|
+
var TAG_CDATA = "!cdata";
|
21
|
+
var TAG_COMMENT = "!comment";
|
22
|
+
var TAG_TEXT = "!text";
|
23
|
+
|
24
|
+
var doNotIndent = {
|
25
|
+
"!cdata": true,
|
26
|
+
"!comment": true,
|
27
|
+
"!text": true,
|
28
|
+
"!instruction": true
|
29
|
+
};
|
30
|
+
|
31
|
+
// options
|
32
|
+
var indentUnit = config.indentUnit;
|
33
|
+
|
34
|
+
///////////////////////////////////////////////////////////////////////////
|
35
|
+
// helper functions
|
36
|
+
|
37
|
+
// chain a parser to another parser
|
38
|
+
function chain(stream, state, parser) {
|
39
|
+
state.tokenize = parser;
|
40
|
+
return parser(stream, state);
|
41
|
+
}
|
42
|
+
|
43
|
+
// parse a block (comment, CDATA or text)
|
44
|
+
function inBlock(style, terminator, nextTokenize) {
|
45
|
+
return function(stream, state) {
|
46
|
+
while (!stream.eol()) {
|
47
|
+
if (stream.match(terminator)) {
|
48
|
+
popContext(state);
|
49
|
+
state.tokenize = nextTokenize;
|
50
|
+
break;
|
51
|
+
}
|
52
|
+
stream.next();
|
53
|
+
}
|
54
|
+
return style;
|
55
|
+
};
|
56
|
+
}
|
57
|
+
|
58
|
+
// go down a level in the document
|
59
|
+
// (hint: look at who calls this function to know what the contexts are)
|
60
|
+
function pushContext(state, tagName) {
|
61
|
+
var noIndent = doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.doIndent);
|
62
|
+
var newContext = {
|
63
|
+
tagName: tagName,
|
64
|
+
prev: state.context,
|
65
|
+
indent: state.context ? state.context.indent + indentUnit : 0,
|
66
|
+
lineNumber: state.lineNumber,
|
67
|
+
indented: state.indented,
|
68
|
+
noIndent: noIndent
|
69
|
+
};
|
70
|
+
state.context = newContext;
|
71
|
+
}
|
72
|
+
|
73
|
+
// go up a level in the document
|
74
|
+
function popContext(state) {
|
75
|
+
if (state.context) {
|
76
|
+
var oldContext = state.context;
|
77
|
+
state.context = oldContext.prev;
|
78
|
+
return oldContext;
|
79
|
+
}
|
80
|
+
|
81
|
+
// we shouldn't be here - it means we didn't have a context to pop
|
82
|
+
return null;
|
83
|
+
}
|
84
|
+
|
85
|
+
// return true if the current token is seperated from the tokens before it
|
86
|
+
// which means either this is the start of the line, or there is at least
|
87
|
+
// one space or tab character behind the token
|
88
|
+
// otherwise returns false
|
89
|
+
function isTokenSeparated(stream) {
|
90
|
+
return stream.sol() ||
|
91
|
+
stream.string.charAt(stream.start - 1) == " " ||
|
92
|
+
stream.string.charAt(stream.start - 1) == "\t";
|
93
|
+
}
|
94
|
+
|
95
|
+
///////////////////////////////////////////////////////////////////////////
|
96
|
+
// context: document
|
97
|
+
//
|
98
|
+
// an XML document can contain:
|
99
|
+
// - a single declaration (if defined, it must be the very first line)
|
100
|
+
// - exactly one root element
|
101
|
+
// @todo try to actually limit the number of root elements to 1
|
102
|
+
// - zero or more comments
|
103
|
+
function parseDocument(stream, state) {
|
104
|
+
if(stream.eat("<")) {
|
105
|
+
if(stream.eat("?")) {
|
106
|
+
// processing instruction
|
107
|
+
pushContext(state, TAG_INSTRUCTION);
|
108
|
+
state.tokenize = parseProcessingInstructionStartTag;
|
109
|
+
return STYLE_INSTRUCTION;
|
110
|
+
} else if(stream.match("!--")) {
|
111
|
+
// new context: comment
|
112
|
+
pushContext(state, TAG_COMMENT);
|
113
|
+
return chain(stream, state, inBlock(STYLE_COMMENT, "-->", parseDocument));
|
114
|
+
} else if(stream.eatSpace() || stream.eol() ) {
|
115
|
+
stream.skipToEnd();
|
116
|
+
return STYLE_ERROR;
|
117
|
+
} else {
|
118
|
+
// element
|
119
|
+
state.tokenize = parseElementTagName;
|
120
|
+
return STYLE_ELEMENT_NAME;
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
// error on line
|
125
|
+
stream.skipToEnd();
|
126
|
+
return STYLE_ERROR;
|
127
|
+
}
|
128
|
+
|
129
|
+
///////////////////////////////////////////////////////////////////////////
|
130
|
+
// context: XML element start-tag or end-tag
|
131
|
+
//
|
132
|
+
// - element start-tag can contain attributes
|
133
|
+
// - element start-tag may self-close (or start an element block if it doesn't)
|
134
|
+
// - element end-tag can contain only the tag name
|
135
|
+
function parseElementTagName(stream, state) {
|
136
|
+
// get the name of the tag
|
137
|
+
var startPos = stream.pos;
|
138
|
+
if(stream.match(/^[a-zA-Z_:][-a-zA-Z0-9_:.]*/)) {
|
139
|
+
// element start-tag
|
140
|
+
var tagName = stream.string.substring(startPos, stream.pos);
|
141
|
+
pushContext(state, tagName);
|
142
|
+
state.tokenize = parseElement;
|
143
|
+
return STYLE_ELEMENT_NAME;
|
144
|
+
} else if(stream.match(/^\/[a-zA-Z_:][-a-zA-Z0-9_:.]*( )*>/)) {
|
145
|
+
// element end-tag
|
146
|
+
var endTagName = stream.string.substring(startPos + 1, stream.pos - 1).trim();
|
147
|
+
var oldContext = popContext(state);
|
148
|
+
state.tokenize = state.context == null ? parseDocument : parseElementBlock;
|
149
|
+
if(oldContext == null || endTagName != oldContext.tagName) {
|
150
|
+
// the start and end tag names should match - error
|
151
|
+
return STYLE_ERROR;
|
152
|
+
}
|
153
|
+
return STYLE_ELEMENT_NAME;
|
154
|
+
} else {
|
155
|
+
// no tag name - error
|
156
|
+
state.tokenize = state.context == null ? parseDocument : parseElementBlock;
|
157
|
+
stream.eatWhile(/[^>]/);
|
158
|
+
stream.eat(">");
|
159
|
+
return STYLE_ERROR;
|
160
|
+
}
|
161
|
+
|
162
|
+
stream.skipToEnd();
|
163
|
+
return null;
|
164
|
+
}
|
165
|
+
|
166
|
+
function parseElement(stream, state) {
|
167
|
+
if(stream.match(/^\/>/)) {
|
168
|
+
// self-closing tag
|
169
|
+
popContext(state);
|
170
|
+
state.tokenize = state.context == null ? parseDocument : parseElementBlock;
|
171
|
+
return STYLE_ELEMENT_NAME;
|
172
|
+
} else if(stream.eat(/^>/)) {
|
173
|
+
state.tokenize = parseElementBlock;
|
174
|
+
return STYLE_ELEMENT_NAME;
|
175
|
+
} else if(isTokenSeparated(stream) && stream.match(/^[a-zA-Z_:][-a-zA-Z0-9_:.]*( )*=/)) {
|
176
|
+
// attribute
|
177
|
+
state.tokenize = parseAttribute;
|
178
|
+
return STYLE_ATTRIBUTE;
|
179
|
+
}
|
180
|
+
|
181
|
+
// no other options - this is an error
|
182
|
+
state.tokenize = state.context == null ? parseDocument : parseDocument;
|
183
|
+
stream.eatWhile(/[^>]/);
|
184
|
+
stream.eat(">");
|
185
|
+
return STYLE_ERROR;
|
186
|
+
}
|
187
|
+
|
188
|
+
///////////////////////////////////////////////////////////////////////////
|
189
|
+
// context: attribute
|
190
|
+
//
|
191
|
+
// attribute values may contain everything, except:
|
192
|
+
// - the ending quote (with ' or ") - this marks the end of the value
|
193
|
+
// - the character "<" - should never appear
|
194
|
+
// - ampersand ("&") - unless it starts a reference: a string that ends with a semi-colon (";")
|
195
|
+
// ---> note: this parser is lax in what may be put into a reference string,
|
196
|
+
// ---> consult http://www.w3.org/TR/REC-xml/#NT-Reference if you want to make it tighter
|
197
|
+
function parseAttribute(stream, state) {
|
198
|
+
var quote = stream.next();
|
199
|
+
if(quote != "\"" && quote != "'") {
|
200
|
+
// attribute must be quoted
|
201
|
+
stream.skipToEnd();
|
202
|
+
state.tokenize = parseElement;
|
203
|
+
return STYLE_ERROR;
|
204
|
+
}
|
205
|
+
|
206
|
+
state.tokParams.quote = quote;
|
207
|
+
state.tokenize = parseAttributeValue;
|
208
|
+
return STYLE_WORD;
|
209
|
+
}
|
210
|
+
|
211
|
+
// @todo: find out whether this attribute value spans multiple lines,
|
212
|
+
// and if so, push a context for it in order not to indent it
|
213
|
+
// (or something of the sort..)
|
214
|
+
function parseAttributeValue(stream, state) {
|
215
|
+
var ch = "";
|
216
|
+
while(!stream.eol()) {
|
217
|
+
ch = stream.next();
|
218
|
+
if(ch == state.tokParams.quote) {
|
219
|
+
// end quote found
|
220
|
+
state.tokenize = parseElement;
|
221
|
+
return STYLE_WORD;
|
222
|
+
} else if(ch == "<") {
|
223
|
+
// can't have less-than signs in an attribute value, ever
|
224
|
+
stream.skipToEnd()
|
225
|
+
state.tokenize = parseElement;
|
226
|
+
return STYLE_ERROR;
|
227
|
+
} else if(ch == "&") {
|
228
|
+
// reference - look for a semi-colon, or return error if none found
|
229
|
+
ch = stream.next();
|
230
|
+
|
231
|
+
// make sure that semi-colon isn't right after the ampersand
|
232
|
+
if(ch == ';') {
|
233
|
+
stream.skipToEnd()
|
234
|
+
state.tokenize = parseElement;
|
235
|
+
return STYLE_ERROR;
|
236
|
+
}
|
237
|
+
|
238
|
+
// make sure no less-than characters slipped in
|
239
|
+
while(!stream.eol() && ch != ";") {
|
240
|
+
if(ch == "<") {
|
241
|
+
// can't have less-than signs in an attribute value, ever
|
242
|
+
stream.skipToEnd()
|
243
|
+
state.tokenize = parseElement;
|
244
|
+
return STYLE_ERROR;
|
245
|
+
}
|
246
|
+
ch = stream.next();
|
247
|
+
}
|
248
|
+
if(stream.eol() && ch != ";") {
|
249
|
+
// no ampersand found - error
|
250
|
+
stream.skipToEnd();
|
251
|
+
state.tokenize = parseElement;
|
252
|
+
return STYLE_ERROR;
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
256
|
+
|
257
|
+
// attribute value continues to next line
|
258
|
+
return STYLE_WORD;
|
259
|
+
}
|
260
|
+
|
261
|
+
///////////////////////////////////////////////////////////////////////////
|
262
|
+
// context: element block
|
263
|
+
//
|
264
|
+
// a block can contain:
|
265
|
+
// - elements
|
266
|
+
// - text
|
267
|
+
// - CDATA sections
|
268
|
+
// - comments
|
269
|
+
function parseElementBlock(stream, state) {
|
270
|
+
if(stream.eat("<")) {
|
271
|
+
if(stream.match("?")) {
|
272
|
+
pushContext(state, TAG_INSTRUCTION);
|
273
|
+
state.tokenize = parseProcessingInstructionStartTag;
|
274
|
+
return STYLE_INSTRUCTION;
|
275
|
+
} else if(stream.match("!--")) {
|
276
|
+
// new context: comment
|
277
|
+
pushContext(state, TAG_COMMENT);
|
278
|
+
return chain(stream, state, inBlock(STYLE_COMMENT, "-->",
|
279
|
+
state.context == null ? parseDocument : parseElementBlock));
|
280
|
+
} else if(stream.match("![CDATA[")) {
|
281
|
+
// new context: CDATA section
|
282
|
+
pushContext(state, TAG_CDATA);
|
283
|
+
return chain(stream, state, inBlock(STYLE_TEXT, "]]>",
|
284
|
+
state.context == null ? parseDocument : parseElementBlock));
|
285
|
+
} else if(stream.eatSpace() || stream.eol() ) {
|
286
|
+
stream.skipToEnd();
|
287
|
+
return STYLE_ERROR;
|
288
|
+
} else {
|
289
|
+
// element
|
290
|
+
state.tokenize = parseElementTagName;
|
291
|
+
return STYLE_ELEMENT_NAME;
|
292
|
+
}
|
293
|
+
} else {
|
294
|
+
// new context: text
|
295
|
+
pushContext(state, TAG_TEXT);
|
296
|
+
state.tokenize = parseText;
|
297
|
+
return null;
|
298
|
+
}
|
299
|
+
|
300
|
+
state.tokenize = state.context == null ? parseDocument : parseElementBlock;
|
301
|
+
stream.skipToEnd();
|
302
|
+
return null;
|
303
|
+
}
|
304
|
+
|
305
|
+
function parseText(stream, state) {
|
306
|
+
stream.eatWhile(/[^<]/);
|
307
|
+
if(!stream.eol()) {
|
308
|
+
// we cannot possibly be in the document context,
|
309
|
+
// just inside an element block
|
310
|
+
popContext(state);
|
311
|
+
state.tokenize = parseElementBlock;
|
312
|
+
}
|
313
|
+
return STYLE_TEXT;
|
314
|
+
}
|
315
|
+
|
316
|
+
///////////////////////////////////////////////////////////////////////////
|
317
|
+
// context: XML processing instructions
|
318
|
+
//
|
319
|
+
// XML processing instructions (PIs) allow documents to contain instructions for applications.
|
320
|
+
// PI format: <?name data?>
|
321
|
+
// - 'name' can be anything other than 'xml' (case-insensitive)
|
322
|
+
// - 'data' can be anything which doesn't contain '?>'
|
323
|
+
// XML declaration is a special PI (see XML declaration context below)
|
324
|
+
function parseProcessingInstructionStartTag(stream, state) {
|
325
|
+
if(stream.match("xml", true, true)) {
|
326
|
+
// xml declaration
|
327
|
+
if(state.lineNumber > 1 || stream.pos > 5) {
|
328
|
+
state.tokenize = parseDocument;
|
329
|
+
stream.skipToEnd();
|
330
|
+
return STYLE_ERROR;
|
331
|
+
} else {
|
332
|
+
state.tokenize = parseDeclarationVersion;
|
333
|
+
return STYLE_INSTRUCTION;
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
// regular processing instruction
|
338
|
+
if(isTokenSeparated(stream) || stream.match("?>")) {
|
339
|
+
// we have a space after the start-tag, or nothing but the end-tag
|
340
|
+
// either way - error!
|
341
|
+
state.tokenize = parseDocument;
|
342
|
+
stream.skipToEnd();
|
343
|
+
return STYLE_ERROR;
|
344
|
+
}
|
345
|
+
|
346
|
+
state.tokenize = parseProcessingInstructionBody;
|
347
|
+
return STYLE_INSTRUCTION;
|
348
|
+
}
|
349
|
+
|
350
|
+
function parseProcessingInstructionBody(stream, state) {
|
351
|
+
stream.eatWhile(/[^?]/);
|
352
|
+
if(stream.eat("?")) {
|
353
|
+
if(stream.eat(">")) {
|
354
|
+
popContext(state);
|
355
|
+
state.tokenize = state.context == null ? parseDocument : parseElementBlock;
|
356
|
+
}
|
357
|
+
}
|
358
|
+
return STYLE_INSTRUCTION;
|
359
|
+
}
|
360
|
+
|
361
|
+
|
362
|
+
///////////////////////////////////////////////////////////////////////////
|
363
|
+
// context: XML declaration
|
364
|
+
//
|
365
|
+
// XML declaration is of the following format:
|
366
|
+
// <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
367
|
+
// - must start at the first character of the first line
|
368
|
+
// - may span multiple lines
|
369
|
+
// - must include 'version'
|
370
|
+
// - may include 'encoding' and 'standalone' (in that order after 'version')
|
371
|
+
// - attribute names must be lowercase
|
372
|
+
// - cannot contain anything else on the line
|
373
|
+
function parseDeclarationVersion(stream, state) {
|
374
|
+
state.tokenize = parseDeclarationEncoding;
|
375
|
+
|
376
|
+
if(isTokenSeparated(stream) && stream.match(/^version( )*=( )*"([a-zA-Z0-9_.:]|\-)+"/)) {
|
377
|
+
return STYLE_INSTRUCTION;
|
378
|
+
}
|
379
|
+
stream.skipToEnd();
|
380
|
+
return STYLE_ERROR;
|
381
|
+
}
|
382
|
+
|
383
|
+
function parseDeclarationEncoding(stream, state) {
|
384
|
+
state.tokenize = parseDeclarationStandalone;
|
385
|
+
|
386
|
+
if(isTokenSeparated(stream) && stream.match(/^encoding( )*=( )*"[A-Za-z]([A-Za-z0-9._]|\-)*"/)) {
|
387
|
+
return STYLE_INSTRUCTION;
|
388
|
+
}
|
389
|
+
return null;
|
390
|
+
}
|
391
|
+
|
392
|
+
function parseDeclarationStandalone(stream, state) {
|
393
|
+
state.tokenize = parseDeclarationEndTag;
|
394
|
+
|
395
|
+
if(isTokenSeparated(stream) && stream.match(/^standalone( )*=( )*"(yes|no)"/)) {
|
396
|
+
return STYLE_INSTRUCTION;
|
397
|
+
}
|
398
|
+
return null;
|
399
|
+
}
|
400
|
+
|
401
|
+
function parseDeclarationEndTag(stream, state) {
|
402
|
+
state.tokenize = parseDocument;
|
403
|
+
|
404
|
+
if(stream.match("?>") && stream.eol()) {
|
405
|
+
popContext(state);
|
406
|
+
return STYLE_INSTRUCTION;
|
407
|
+
}
|
408
|
+
stream.skipToEnd();
|
409
|
+
return STYLE_ERROR;
|
410
|
+
}
|
411
|
+
|
412
|
+
///////////////////////////////////////////////////////////////////////////
|
413
|
+
// returned object
|
414
|
+
return {
|
415
|
+
electricChars: "/",
|
416
|
+
|
417
|
+
startState: function() {
|
418
|
+
return {
|
419
|
+
tokenize: parseDocument,
|
420
|
+
tokParams: {},
|
421
|
+
lineNumber: 0,
|
422
|
+
lineError: false,
|
423
|
+
context: null,
|
424
|
+
indented: 0
|
425
|
+
};
|
426
|
+
},
|
427
|
+
|
428
|
+
token: function(stream, state) {
|
429
|
+
if(stream.sol()) {
|
430
|
+
// initialize a new line
|
431
|
+
state.lineNumber++;
|
432
|
+
state.lineError = false;
|
433
|
+
state.indented = stream.indentation();
|
434
|
+
}
|
435
|
+
|
436
|
+
// eat all (the spaces) you can
|
437
|
+
if(stream.eatSpace()) return null;
|
438
|
+
|
439
|
+
// run the current tokenize function, according to the state
|
440
|
+
var style = state.tokenize(stream, state);
|
441
|
+
|
442
|
+
// is there an error somewhere in the line?
|
443
|
+
state.lineError = (state.lineError || style == "error");
|
444
|
+
|
445
|
+
return style;
|
446
|
+
},
|
447
|
+
|
448
|
+
blankLine: function(state) {
|
449
|
+
// blank lines are lines too!
|
450
|
+
state.lineNumber++;
|
451
|
+
state.lineError = false;
|
452
|
+
},
|
453
|
+
|
454
|
+
indent: function(state, textAfter) {
|
455
|
+
if(state.context) {
|
456
|
+
if(state.context.noIndent == true) {
|
457
|
+
// do not indent - no return value at all
|
458
|
+
return;
|
459
|
+
}
|
460
|
+
if(textAfter.match(/^<\/.*/)) {
|
461
|
+
// eng-tag - indent back to last context
|
462
|
+
return state.context.indent;
|
463
|
+
}
|
464
|
+
// indent to last context + regular indent unit
|
465
|
+
return state.context.indent + indentUnit;
|
466
|
+
}
|
467
|
+
return 0;
|
468
|
+
},
|
469
|
+
|
470
|
+
compareStates: function(a, b) {
|
471
|
+
if (a.indented != b.indented) return false;
|
472
|
+
for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
|
473
|
+
if (!ca || !cb) return ca == cb;
|
474
|
+
if (ca.tagName != cb.tagName) return false;
|
475
|
+
}
|
476
|
+
}
|
477
|
+
};
|
478
|
+
});
|
479
|
+
|
480
|
+
CodeMirror.defineMIME("application/xml", "purexml");
|
481
|
+
CodeMirror.defineMIME("text/xml", "purexml");
|