liquid_cms 0.3.1.0 → 0.3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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");