diff options
Diffstat (limited to 'imports/codemirror/mode/xmlpure')
-rwxr-xr-x | imports/codemirror/mode/xmlpure/index.html | 59 | ||||
-rwxr-xr-x | imports/codemirror/mode/xmlpure/xmlpure.js | 485 |
2 files changed, 544 insertions, 0 deletions
diff --git a/imports/codemirror/mode/xmlpure/index.html b/imports/codemirror/mode/xmlpure/index.html new file mode 100755 index 00000000..721834ac --- /dev/null +++ b/imports/codemirror/mode/xmlpure/index.html | |||
@@ -0,0 +1,59 @@ | |||
1 | <!doctype html> | ||
2 | <html> | ||
3 | <head> | ||
4 | <title>CodeMirror: 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 | <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style> | ||
9 | <link rel="stylesheet" href="../../doc/docs.css"> | ||
10 | </head> | ||
11 | <body> | ||
12 | <h1>CodeMirror: XML mode</h1> | ||
13 | <form><textarea id="code" name="code"> | ||
14 | <?xml version="1.0" encoding="UTF-8" standalone="no" ?> | ||
15 | |||
16 | <!-- This is the pure XML mode, | ||
17 | and we're inside a comment! --> | ||
18 | |||
19 | <catalog> | ||
20 | <books> | ||
21 | <book id="bk01"> | ||
22 | <title>Lord of Light</title> | ||
23 | <author>Roger Zelazny</author> | ||
24 | <year>1967</year> | ||
25 | <description><![CDATA[This is a great book, really!!]]></description> | ||
26 | </book> | ||
27 | </books> | ||
28 | </catalog> | ||
29 | </textarea></form> | ||
30 | <script> | ||
31 | var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: {name: "xmlpure"}}); | ||
32 | </script> | ||
33 | |||
34 | <p>This is my XML parser, based on the original:</p> | ||
35 | <ul> | ||
36 | <li>No html mode - this is pure xml</li> | ||
37 | <li>Illegal attributes and element names are errors</li> | ||
38 | <li>Attributes must have a value</li> | ||
39 | <li>XML declaration supported (e.g.: <b><?xml version="1.0" encoding="utf-8" standalone="no" ?></b>)</li> | ||
40 | <li>CDATA and comment blocks are not indented (except for their start-tag)</li> | ||
41 | <li>Better handling of errors per line with the state object - provides good infrastructure for extending it</li> | ||
42 | </ul> | ||
43 | |||
44 | <p>What's missing:</p> | ||
45 | <ul> | ||
46 | <li>Make sure only a single root element exists at the document level</li> | ||
47 | <li>Multi-line attributes should NOT indent</li> | ||
48 | <li>Start tags are not painted red when they have no matching end tags (is this really wrong?)</li> | ||
49 | </ul> | ||
50 | |||
51 | <p><strong>MIME types defined:</strong> <code>application/xml</code>, <code>text/xml</code>.</p> | ||
52 | |||
53 | <p><b>@author</b>: Dror BG (<i>deebug.dev[at]gmail.com</i>)<br/> | ||
54 | <p><b>@date</b>: August, 2011<br/> | ||
55 | <p><b>@github</b>: <a href='https://github.com/deebugger/CodeMirror2' target='blank'>https://github.com/deebugger/CodeMirror2</a></p> | ||
56 | |||
57 | <p><strong>MIME types defined:</strong> <code>application/xml</code>, <code>text/xml</code>.</p> | ||
58 | </body> | ||
59 | </html> | ||
diff --git a/imports/codemirror/mode/xmlpure/xmlpure.js b/imports/codemirror/mode/xmlpure/xmlpure.js new file mode 100755 index 00000000..6102f51d --- /dev/null +++ b/imports/codemirror/mode/xmlpure/xmlpure.js | |||
@@ -0,0 +1,485 @@ | |||
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; | ||