liquid_cms 0.3.1.0 → 0.3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. data/CHANGELOG.rdoc +10 -0
  2. data/Gemfile +0 -3
  3. data/README.rdoc +16 -2
  4. data/TODO.rdoc +0 -2
  5. data/app/controllers/cms/assets_controller.rb +20 -6
  6. data/app/controllers/cms/components_controller.rb +11 -3
  7. data/app/controllers/cms/pages_controller.rb +15 -4
  8. data/app/helpers/cms/common_helper.rb +31 -32
  9. data/app/liquid/cms_paperclip_extension.rb +75 -3
  10. data/app/liquid/tags/{data_tag.rb → cms/data_tag.rb} +0 -0
  11. data/app/models/cms/asset.rb +1 -1
  12. data/app/models/cms/component.rb +3 -3
  13. data/app/views/cms/assets/_form.html.erb +1 -2
  14. data/app/views/cms/assets/update.js.rjs +1 -0
  15. data/app/views/cms/components/update.js.rjs +1 -0
  16. data/app/views/cms/pages/_form.html.erb +3 -3
  17. data/app/views/cms/pages/update.js.rjs +1 -0
  18. data/app/views/layouts/cms.html.erb +4 -3
  19. data/config/initializers/cms/simple_form.rb +70 -12
  20. data/config/initializers/cms/simple_form_updates.rb +9 -3
  21. data/config/locales/cms/en.yml +6 -0
  22. data/lib/generators/liquid_cms/install_generator.rb +5 -1
  23. data/lib/generators/liquid_cms/templates/migration_rev2.rb +13 -0
  24. data/lib/generators/liquid_cms/templates/public/cms/codemirror/LICENSE +16 -20
  25. data/lib/generators/liquid_cms/templates/public/cms/codemirror/lib/codemirror.css +68 -0
  26. data/lib/generators/liquid_cms/templates/public/cms/codemirror/lib/codemirror.js +2197 -0
  27. data/lib/generators/liquid_cms/templates/public/cms/codemirror/lib/overlay.js +51 -0
  28. data/lib/generators/liquid_cms/templates/public/cms/codemirror/lib/runmode.js +27 -0
  29. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/clike/clike.js +247 -0
  30. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/clike/index.html +102 -0
  31. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/clojure/clojure.js +207 -0
  32. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/clojure/index.html +85 -0
  33. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/coffeescript/LICENSE +22 -0
  34. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/coffeescript/coffeescript.js +325 -0
  35. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/coffeescript/index.html +722 -0
  36. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/css/css.js +124 -0
  37. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/css/index.html +56 -0
  38. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/diff/diff.css +3 -0
  39. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/diff/diff.js +13 -0
  40. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/diff/index.html +99 -0
  41. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/haskell/haskell.js +242 -0
  42. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/haskell/index.html +60 -0
  43. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/htmlmixed/htmlmixed.js +79 -0
  44. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/htmlmixed/index.html +52 -0
  45. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/javascript/index.html +78 -0
  46. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/javascript/javascript.js +352 -0
  47. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/jinja2/index.html +38 -0
  48. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/jinja2/jinja2.js +42 -0
  49. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/lua/index.html +72 -0
  50. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/lua/lua.js +140 -0
  51. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/markdown/index.html +340 -0
  52. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/markdown/markdown.css +10 -0
  53. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/markdown/markdown.js +230 -0
  54. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/ntriples/index.html +33 -0
  55. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/ntriples/ntriples.js +172 -0
  56. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/pascal/LICENSE +7 -0
  57. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/pascal/index.html +49 -0
  58. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/pascal/pascal.js +138 -0
  59. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/php/index.html +49 -0
  60. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/php/php.js +116 -0
  61. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/plsql/index.html +63 -0
  62. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/plsql/plsql.js +217 -0
  63. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/python/LICENSE.txt +21 -0
  64. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/python/index.html +123 -0
  65. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/python/python.js +320 -0
  66. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/r/LICENSE +24 -0
  67. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/r/index.html +74 -0
  68. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/r/r.js +141 -0
  69. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/rst/index.html +526 -0
  70. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/rst/rst.css +75 -0
  71. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/rst/rst.js +333 -0
  72. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/ruby/LICENSE +24 -0
  73. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/ruby/index.html +172 -0
  74. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/ruby/ruby.js +195 -0
  75. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/scheme/index.html +65 -0
  76. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/scheme/scheme.js +202 -0
  77. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/smalltalk/index.html +56 -0
  78. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/smalltalk/smalltalk.js +122 -0
  79. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/sparql/index.html +41 -0
  80. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/sparql/sparql.js +143 -0
  81. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/stex/index.html +96 -0
  82. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/stex/stex.js +167 -0
  83. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/velocity/index.html +103 -0
  84. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/velocity/velocity.js +146 -0
  85. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/xml/index.html +42 -0
  86. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/xml/xml.js +231 -0
  87. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/xmlpure/index.html +60 -0
  88. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/xmlpure/xmlpure.js +481 -0
  89. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/yaml/index.html +68 -0
  90. data/lib/generators/liquid_cms/templates/public/cms/codemirror/mode/yaml/yaml.js +95 -0
  91. data/lib/generators/liquid_cms/templates/public/cms/codemirror/theme/cobalt.css +17 -0
  92. data/lib/generators/liquid_cms/templates/public/cms/codemirror/theme/default.css +19 -0
  93. data/lib/generators/liquid_cms/templates/public/cms/codemirror/theme/eclipse.css +24 -0
  94. data/lib/generators/liquid_cms/templates/public/cms/codemirror/theme/elegant.css +9 -0
  95. data/lib/generators/liquid_cms/templates/public/cms/codemirror/theme/neat.css +8 -0
  96. data/lib/generators/liquid_cms/templates/public/cms/codemirror/theme/night.css +20 -0
  97. data/lib/generators/liquid_cms/templates/public/cms/javascripts/cms.js +1 -1
  98. data/lib/generators/liquid_cms/templates/public/cms/javascripts/codemirror_custom.js +96 -0
  99. data/lib/generators/liquid_cms/templates/public/cms/stylesheets/codemirror_changes.css +26 -0
  100. data/lib/generators/liquid_cms/templates/public/cms/stylesheets/liquid.css +7 -0
  101. data/lib/generators/liquid_cms/templates/public/cms/stylesheets/simple_form.css +0 -8
  102. data/lib/liquid_cms/version.rb +1 -1
  103. data/liquid_cms.gemspec +1 -1
  104. data/test/functional/assets_controller_test.rb +35 -20
  105. data/test/functional/components_controller_test.rb +15 -5
  106. data/test/functional/pages_controller_test.rb +19 -6
  107. data/test/rails_app/Gemfile +1 -2
  108. data/test/rails_app/config/locales/cms/en.yml +26 -4
  109. data/test/rails_app/db/migrate/20110511161859_create_liquid_cms_upgrade_rev2.rb +13 -0
  110. data/test/unit/asset_test.rb +1 -1
  111. data/test/unit/component_test.rb +1 -1
  112. metadata +89 -32
  113. data/generators/liquid_cms/templates/migration_rev1.rb +0 -38
  114. data/lib/generators/liquid_cms/templates/public/cms/codemirror/css/csscolors.css +0 -55
  115. data/lib/generators/liquid_cms/templates/public/cms/codemirror/css/docs.css +0 -158
  116. data/lib/generators/liquid_cms/templates/public/cms/codemirror/css/font.js +0 -15
  117. data/lib/generators/liquid_cms/templates/public/cms/codemirror/css/jscolors.css +0 -59
  118. data/lib/generators/liquid_cms/templates/public/cms/codemirror/css/sparqlcolors.css +0 -43
  119. data/lib/generators/liquid_cms/templates/public/cms/codemirror/css/xmlcolors.css +0 -55
  120. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/codemirror.js +0 -582
  121. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/editor.js +0 -1671
  122. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/highlight.js +0 -68
  123. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/mirrorframe.js +0 -81
  124. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/parsecss.js +0 -161
  125. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/parsedummy.js +0 -32
  126. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/parsehtmlmixed.js +0 -93
  127. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/parsejavascript.js +0 -359
  128. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/parsesparql.js +0 -162
  129. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/parsexml.js +0 -291
  130. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/select.js +0 -699
  131. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/stringstream.js +0 -159
  132. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/tokenize.js +0 -57
  133. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/tokenizejavascript.js +0 -174
  134. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/undo.js +0 -413
  135. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/unittests.js +0 -44
  136. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/util.js +0 -133
@@ -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
+ &lt;?xml version="1.0" encoding="UTF-8" standalone="no" ?&gt;
16
+
17
+ &lt;!-- This is the pure XML mode,
18
+ and we're inside a comment! --&gt;
19
+
20
+ &lt;catalog&gt;
21
+ &lt;books&gt;
22
+ &lt;book id="bk01"&gt;
23
+ &lt;title&gt;Lord of Light&lt;/title&gt;
24
+ &lt;author&gt;Roger Zelazny&lt;/author&gt;
25
+ &lt;year&gt;1967&lt;/year&gt;
26
+ &lt;description&gt;&lt;![CDATA[This is a great book, really!!]]&gt;&lt;/description&gt;
27
+ &lt;/book&gt;
28
+ &lt;/books&gt;
29
+ &lt;/catalog&gt;
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>&lt;?xml version="1.0" encoding="utf-8" standalone="no" ?&gt;</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");