w2tags 0.9.55 → 0.9.56

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 (37) hide show
  1. data/Manifest.txt +31 -0
  2. data/lib/w2tags/parser.rb +2 -5
  3. data/lib/w2tags/try/public/css/csscolors.css +47 -0
  4. data/lib/w2tags/try/public/css/jquery.cluetip.css +130 -0
  5. data/lib/w2tags/try/public/css/jscolors.css +55 -0
  6. data/lib/w2tags/try/public/css/rubycolors.css +82 -0
  7. data/lib/w2tags/try/public/css/xmlcolors.css +51 -0
  8. data/lib/w2tags/try/public/img/loading.gif +0 -0
  9. data/lib/w2tags/try/public/js/codemirror.js +308 -0
  10. data/lib/w2tags/try/public/js/editor.js +1340 -0
  11. data/lib/w2tags/try/public/js/highlight.js +68 -0
  12. data/lib/w2tags/try/public/js/jquery.cluetip.js +470 -0
  13. data/lib/w2tags/try/public/js/jquery.js +4376 -0
  14. data/lib/w2tags/try/public/js/mirrorframe.js +81 -0
  15. data/lib/w2tags/try/public/js/parsecss.js +155 -0
  16. data/lib/w2tags/try/public/js/parsehtmlmixed.js +74 -0
  17. data/lib/w2tags/try/public/js/parsejavascript.js +350 -0
  18. data/lib/w2tags/try/public/js/parsew2tags.js +157 -0
  19. data/lib/w2tags/try/public/js/parsexml.js +292 -0
  20. data/lib/w2tags/try/public/js/select.js +607 -0
  21. data/lib/w2tags/try/public/js/stringstream.js +140 -0
  22. data/lib/w2tags/try/public/js/tokenize.js +57 -0
  23. data/lib/w2tags/try/public/js/tokenizejavascript.js +175 -0
  24. data/lib/w2tags/try/public/js/undo.js +404 -0
  25. data/lib/w2tags/try/public/js/util.js +134 -0
  26. data/lib/w2tags/try/public/w2/basic.w2erb +8 -2
  27. data/lib/w2tags/try/public/w2/erb_base.hot.html +167 -0
  28. data/lib/w2tags/try/public/w2/erb_rails.hot.html +59 -0
  29. data/lib/w2tags/try/public/w2/html.hot.html +1 -0
  30. data/lib/w2tags/try/public/w2/rails.hot.html +37 -0
  31. data/lib/w2tags/try/public/w2/try.rb.hot.html +50 -0
  32. data/lib/w2tags/try/try.rb +3 -2
  33. data/lib/w2tags/try/views/index.erb +85 -15
  34. data/lib/w2tags/try/views/layout.erb +4 -5
  35. data/lib/w2tags/try/views/parse.erb +1 -0
  36. data/tasks/setup.rb +1 -1
  37. metadata +58 -2
@@ -0,0 +1,81 @@
1
+ /* Demonstration of embedding CodeMirror in a bigger application. The
2
+ * interface defined here is a mess of prompts and confirms, and
3
+ * should probably not be used in a real project.
4
+ */
5
+
6
+ function MirrorFrame(place, options) {
7
+ this.home = document.createElement("DIV");
8
+ if (place.appendChild)
9
+ place.appendChild(this.home);
10
+ else
11
+ place(this.home);
12
+
13
+ var self = this;
14
+ function makeButton(name, action) {
15
+ var button = document.createElement("INPUT");
16
+ button.type = "button";
17
+ button.value = name;
18
+ self.home.appendChild(button);
19
+ button.onclick = function(){self[action].call(self);};
20
+ }
21
+
22
+ makeButton("Search", "search");
23
+ makeButton("Replace", "replace");
24
+ makeButton("Current line", "line");
25
+ makeButton("Jump to line", "jump");
26
+ makeButton("Insert constructor", "macro");
27
+ makeButton("Indent all", "reindent");
28
+
29
+ this.mirror = new CodeMirror(this.home, options);
30
+ }
31
+
32
+ MirrorFrame.prototype = {
33
+ search: function() {
34
+ var text = prompt("Enter search term:", "");
35
+ if (!text) return;
36
+
37
+ var first = true;
38
+ do {
39
+ var cursor = this.mirror.getSearchCursor(text, first, true);
40
+ first = false;
41
+ while (cursor.findNext()) {
42
+ cursor.select();
43
+ if (!confirm("Search again?"))
44
+ return;
45
+ }
46
+ } while (confirm("End of document reached. Start over?"));
47
+ },
48
+
49
+ replace: function() {
50
+ // This is a replace-all, but it is possible to implement a
51
+ // prompting replace.
52
+ var from = prompt("Enter search string:", ""), to;
53
+ if (from) to = prompt("What should it be replaced with?", "");
54
+ if (to == null) return;
55
+
56
+ var cursor = this.mirror.getSearchCursor(from, false);
57
+ while (cursor.findNext())
58
+ cursor.replace(to);
59
+ },
60
+
61
+ jump: function() {
62
+ var line = prompt("Jump to line:", "");
63
+ if (line && !isNaN(Number(line)))
64
+ this.mirror.jumpToLine(Number(line));
65
+ },
66
+
67
+ line: function() {
68
+ alert("The cursor is currently at line " + this.mirror.currentLine());
69
+ this.mirror.focus();
70
+ },
71
+
72
+ macro: function() {
73
+ var name = prompt("Name your constructor:", "");
74
+ if (name)
75
+ this.mirror.replaceSelection("function " + name + "() {\n \n}\n\n" + name + ".prototype = {\n \n};\n");
76
+ },
77
+
78
+ reindent: function() {
79
+ this.mirror.reindent();
80
+ }
81
+ };
@@ -0,0 +1,155 @@
1
+ /* Simple parser for CSS */
2
+
3
+ var CSSParser = Editor.Parser = (function() {
4
+ var tokenizeCSS = (function() {
5
+ function normal(source, setState) {
6
+ var ch = source.next();
7
+ if (ch == "@") {
8
+ source.nextWhileMatches(/\w/);
9
+ return "css-at";
10
+ }
11
+ else if (ch == "/" && source.equals("*")) {
12
+ setState(inCComment);
13
+ return null;
14
+ }
15
+ else if (ch == "<" && source.equals("!")) {
16
+ setState(inSGMLComment);
17
+ return null;
18
+ }
19
+ else if (ch == "=") {
20
+ return "css-compare";
21
+ }
22
+ else if (source.equals("=") && (ch == "~" || ch == "|")) {
23
+ source.next();
24
+ return "css-compare";
25
+ }
26
+ else if (ch == "\"" || ch == "'") {
27
+ setState(inString(ch));
28
+ return null;
29
+ }
30
+ else if (ch == "#") {
31
+ source.nextWhileMatches(/\w/);
32
+ return "css-hash";
33
+ }
34
+ else if (ch == "!") {
35
+ source.nextWhileMatches(/[ \t]/);
36
+ source.nextWhileMatches(/\w/);
37
+ return "css-important";
38
+ }
39
+ else if (/\d/.test(ch)) {
40
+ source.nextWhileMatches(/[\w.%]/);
41
+ return "css-unit";
42
+ }
43
+ else if (/[,.+>*\/]/.test(ch)) {
44
+ return "css-select-op";
45
+ }
46
+ else if (/[;{}:\[\]]/.test(ch)) {
47
+ return "css-punctuation";
48
+ }
49
+ else {
50
+ source.nextWhileMatches(/[\w\\\-_]/);
51
+ return "css-identifier";
52
+ }
53
+ }
54
+
55
+ function inCComment(source, setState) {
56
+ var maybeEnd = false;
57
+ while (!source.endOfLine()) {
58
+ var ch = source.next();
59
+ if (maybeEnd && ch == "/") {
60
+ setState(normal);
61
+ break;
62
+ }
63
+ maybeEnd = (ch == "*");
64
+ }
65
+ return "css-comment";
66
+ }
67
+
68
+ function inSGMLComment(source, setState) {
69
+ var dashes = 0;
70
+ while (!source.endOfLine()) {
71
+ var ch = source.next();
72
+ if (dashes >= 2 && ch == ">") {
73
+ setState(normal);
74
+ break;
75
+ }
76
+ dashes = (ch == "-") ? dashes + 1 : 0;
77
+ }
78
+ return "css-comment";
79
+ }
80
+
81
+ function inString(quote) {
82
+ return function(source, setState) {
83
+ var escaped = false;
84
+ while (!source.endOfLine()) {
85
+ var ch = source.next();
86
+ if (ch == quote && !escaped)
87
+ break;
88
+ escaped = !escaped && ch == "\\";
89
+ }
90
+ if (!escaped)
91
+ setState(normal);
92
+ return "css-string";
93
+ };
94
+ }
95
+
96
+ return function(source, startState) {
97
+ return tokenizer(source, startState || normal);
98
+ };
99
+ })();
100
+
101
+ function indentCSS(inBraces, inRule, base) {
102
+ return function(nextChars) {
103
+ if (!inBraces || /^\}/.test(nextChars)) return base;
104
+ else if (inRule) return base + indentUnit * 2;
105
+ else return base + indentUnit;
106
+ };
107
+ }
108
+
109
+ // This is a very simplistic parser -- since CSS does not really
110
+ // nest, it works acceptably well, but some nicer colouroing could
111
+ // be provided with a more complicated parser.
112
+ function parseCSS(source, basecolumn) {
113
+ basecolumn = basecolumn || 0;
114
+ var tokens = tokenizeCSS(source);
115
+ var inBraces = false, inRule = false;
116
+
117
+ var iter = {
118
+ next: function() {
119
+ var token = tokens.next(), style = token.style, content = token.content;
120
+
121
+ if (style == "css-identifier" && inRule)
122
+ token.style = "css-value";
123
+ if (style == "css-hash")
124
+ token.style = inRule ? "css-colorcode" : "css-identifier";
125
+
126
+ if (content == "\n")
127
+ token.indentation = indentCSS(inBraces, inRule, basecolumn);
128
+
129
+ if (content == "{")
130
+ inBraces = true;
131
+ else if (content == "}")
132
+ inBraces = inRule = false;
133
+ else if (inBraces && content == ";")
134
+ inRule = false;
135
+ else if (inBraces && style != "css-comment" && style != "whitespace")
136
+ inRule = true;
137
+
138
+ return token;
139
+ },
140
+
141
+ copy: function() {
142
+ var _inBraces = inBraces, _inRule = inRule, _tokenState = tokens.state;
143
+ return function(source) {
144
+ tokens = tokenizeCSS(source, _tokenState);
145
+ inBraces = _inBraces;
146
+ inRule = _inRule;
147
+ return iter;
148
+ };
149
+ }
150
+ };
151
+ return iter;
152
+ }
153
+
154
+ return {make: parseCSS, electricChars: "}"};
155
+ })();
@@ -0,0 +1,74 @@
1
+ var HTMLMixedParser = Editor.Parser = (function() {
2
+ if (!(CSSParser && JSParser && XMLParser))
3
+ throw new Error("CSS, JS, and XML parsers must be loaded for HTML mixed mode to work.");
4
+ XMLParser.configure({useHTMLKludges: true});
5
+
6
+ function parseMixed(stream) {
7
+ var htmlParser = XMLParser.make(stream), localParser = null, inTag = false;
8
+ var iter = {next: top, copy: copy};
9
+
10
+ function top() {
11
+ var token = htmlParser.next();
12
+ if (token.content == "<")
13
+ inTag = true;
14
+ else if (token.style == "xml-tagname" && inTag === true)
15
+ inTag = token.content.toLowerCase();
16
+ else if (token.content == ">") {
17
+ if (inTag == "script")
18
+ iter.next = local(JSParser, "</script");
19
+ else if (inTag == "style")
20
+ iter.next = local(CSSParser, "</style");
21
+ inTag = false;
22
+ }
23
+ return token;
24
+ }
25
+ function local(parser, tag) {
26
+ var baseIndent = htmlParser.indentation();
27
+ localParser = parser.make(stream, baseIndent + indentUnit);
28
+ return function() {
29
+ if (stream.lookAhead(tag, false, false, true)) {
30
+ localParser = null;
31
+ iter.next = top;
32
+ return top();
33
+ }
34
+
35
+ var token = localParser.next();
36
+ var lt = token.value.lastIndexOf("<"), sz = Math.min(token.value.length - lt, tag.length);
37
+ if (lt != -1 && token.value.slice(lt, lt + sz).toLowerCase() == tag.slice(0, sz) &&
38
+ stream.lookAhead(tag.slice(sz), false, false, true)) {
39
+ stream.push(token.value.slice(lt));
40
+ token.value = token.value.slice(0, lt);
41
+ }
42
+
43
+ if (token.indentation) {
44
+ var oldIndent = token.indentation;
45
+ token.indentation = function(chars) {
46
+ if (chars == "</")
47
+ return baseIndent;
48
+ else
49
+ return oldIndent(chars);
50
+ }
51
+ }
52
+
53
+ return token;
54
+ };
55
+ }
56
+
57
+ function copy() {
58
+ var _html = htmlParser.copy(), _local = localParser && localParser.copy(),
59
+ _next = iter.next, _inTag = inTag;
60
+ return function(_stream) {
61
+ stream = _stream;
62
+ htmlParser = _html(_stream);
63
+ localParser = _local && _local(_stream);
64
+ iter.next = _next;
65
+ inTag = _inTag;
66
+ return iter;
67
+ };
68
+ }
69
+ return iter;
70
+ }
71
+
72
+ return {make: parseMixed, electricChars: "{}/:"};
73
+
74
+ })();
@@ -0,0 +1,350 @@
1
+ /* Parse function for JavaScript. Makes use of the tokenizer from
2
+ * tokenizejavascript.js. Note that your parsers do not have to be
3
+ * this complicated -- if you don't want to recognize local variables,
4
+ * in many languages it is enough to just look for braces, semicolons,
5
+ * parentheses, etc, and know when you are inside a string or comment.
6
+ *
7
+ * See manual.html for more info about the parser interface.
8
+ */
9
+
10
+ var JSParser = Editor.Parser = (function() {
11
+ // Token types that can be considered to be atoms.
12
+ var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
13
+ // Setting that can be used to have JSON data indent properly.
14
+ var json = false;
15
+ // Constructor for the lexical context objects.
16
+ function JSLexical(indented, column, type, align, prev, info) {
17
+ // indentation at start of this line
18
+ this.indented = indented;
19
+ // column at which this scope was opened
20
+ this.column = column;
21
+ // type of scope ('vardef', 'stat' (statement), 'form' (special form), '[', '{', or '(')
22
+ this.type = type;
23
+ // '[', '{', or '(' blocks that have any text after their opening
24
+ // character are said to be 'aligned' -- any lines below are
25
+ // indented all the way to the opening character.
26
+ if (align != null)
27
+ this.align = align;
28
+ // Parent scope, if any.
29
+ this.prev = prev;
30
+ this.info = info;
31
+ }
32
+
33
+ // My favourite JavaScript indentation rules.
34
+ function indentJS(lexical) {
35
+ return function(firstChars) {
36
+ var firstChar = firstChars && firstChars.charAt(0), type = lexical.type;
37
+ var closing = firstChar == type;
38
+ if (type == "vardef")
39
+ return lexical.indented + 4;
40
+ else if (type == "form" && firstChar == "{")
41
+ return lexical.indented;
42
+ else if (type == "stat" || type == "form")
43
+ return lexical.indented + indentUnit;
44
+ else if (lexical.info == "switch" && !closing)
45
+ return lexical.indented + (/^(?:case|default)\b/.test(firstChars) ? indentUnit : 2 * indentUnit);
46
+ else if (lexical.align)
47
+ return lexical.column - (closing ? 1 : 0);
48
+ else
49
+ return lexical.indented + (closing ? 0 : indentUnit);
50
+ };
51
+ }
52
+
53
+ // The parser-iterator-producing function itself.
54
+ function parseJS(input, basecolumn) {
55
+ // Wrap the input in a token stream
56
+ var tokens = tokenizeJavaScript(input);
57
+ // The parser state. cc is a stack of actions that have to be
58
+ // performed to finish the current statement. For example we might
59
+ // know that we still need to find a closing parenthesis and a
60
+ // semicolon. Actions at the end of the stack go first. It is
61
+ // initialized with an infinitely looping action that consumes
62
+ // whole statements.
63
+ var cc = [statements];
64
+ // Context contains information about the current local scope, the
65
+ // variables defined in that, and the scopes above it.
66
+ var context = null;
67
+ // The lexical scope, used mostly for indentation.
68
+ var lexical = new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false);
69
+ // Current column, and the indentation at the start of the current
70
+ // line. Used to create lexical scope objects.
71
+ var column = 0;
72
+ var indented = 0;
73
+ // Variables which are used by the mark, cont, and pass functions
74
+ // below to communicate with the driver loop in the 'next'
75
+ // function.
76
+ var consume, marked;
77
+
78
+ // The iterator object.
79
+ var parser = {next: next, copy: copy};
80
+
81
+ function next(){
82
+ // Start by performing any 'lexical' actions (adjusting the
83
+ // lexical variable), or the operations below will be working
84
+ // with the wrong lexical state.
85
+ while(cc[cc.length - 1].lex)
86
+ cc.pop()();
87
+
88
+ // Fetch a token.
89
+ var token = tokens.next();
90
+
91
+ // Adjust column and indented.
92
+ if (token.type == "whitespace" && column == 0)
93
+ indented = token.value.length;
94
+ column += token.value.length;
95
+ if (token.content == "\n"){
96
+ indented = column = 0;
97
+ // If the lexical scope's align property is still undefined at
98
+ // the end of the line, it is an un-aligned scope.
99
+ if (!("align" in lexical))
100
+ lexical.align = false;
101
+ // Newline tokens get an indentation function associated with
102
+ // them.
103
+ token.indentation = indentJS(lexical);
104
+ }
105
+ // No more processing for meaningless tokens.
106
+ if (token.type == "whitespace" || token.type == "comment")
107
+ return token;
108
+ // When a meaningful token is found and the lexical scope's
109
+ // align is undefined, it is an aligned scope.
110
+ if (!("align" in lexical))
111
+ lexical.align = true;
112
+
113
+ // Execute actions until one 'consumes' the token and we can
114
+ // return it.
115
+ while(true) {
116
+ consume = marked = false;
117
+ // Take and execute the topmost action.
118
+ cc.pop()(token.type, token.content);
119
+ if (consume){
120
+ // Marked is used to change the style of the current token.
121
+ if (marked)
122
+ token.style = marked;
123
+ // Here we differentiate between local and global variables.
124
+ else if (token.type == "variable" && inScope(token.content))
125
+ token.style = "js-localvariable";
126
+ return token;
127
+ }
128
+ }
129
+ }
130
+
131
+ // This makes a copy of the parser state. It stores all the
132
+ // stateful variables in a closure, and returns a function that
133
+ // will restore them when called with a new input stream. Note
134
+ // that the cc array has to be copied, because it is contantly
135
+ // being modified. Lexical objects are not mutated, and context
136
+ // objects are not mutated in a harmful way, so they can be shared
137
+ // between runs of the parser.
138
+ function copy(){
139
+ var _context = context, _lexical = lexical, _cc = cc.concat([]), _tokenState = tokens.state;
140
+
141
+ return function copyParser(input){
142
+ context = _context;
143
+ lexical = _lexical;
144
+ cc = _cc.concat([]); // copies the array
145
+ column = indented = 0;
146
+ tokens = tokenizeJavaScript(input, _tokenState);
147
+ return parser;
148
+ };
149
+ }
150
+
151
+ // Helper function for pushing a number of actions onto the cc
152
+ // stack in reverse order.
153
+ function push(fs){
154
+ for (var i = fs.length - 1; i >= 0; i--)
155
+ cc.push(fs[i]);
156
+ }
157
+ // cont and pass are used by the action functions to add other
158
+ // actions to the stack. cont will cause the current token to be
159
+ // consumed, pass will leave it for the next action.
160
+ function cont(){
161
+ push(arguments);
162
+ consume = true;
163
+ }
164
+ function pass(){
165
+ push(arguments);
166
+ consume = false;
167
+ }
168
+ // Used to change the style of the current token.
169
+ function mark(style){
170
+ marked = style;
171
+ }
172
+
173
+ // Push a new scope. Will automatically link the current scope.
174
+ function pushcontext(){
175
+ context = {prev: context, vars: {"this": true, "arguments": true}};
176
+ }
177
+ // Pop off the current scope.
178
+ function popcontext(){
179
+ context = context.prev;
180
+ }
181
+ // Register a variable in the current scope.
182
+ function register(varname){
183
+ if (context){
184
+ mark("js-variabledef");
185
+ context.vars[varname] = true;
186
+ }
187
+ }
188
+ // Check whether a variable is defined in the current scope.
189
+ function inScope(varname){
190
+ var cursor = context;
191
+ while (cursor) {
192
+ if (cursor.vars[varname])
193
+ return true;
194
+ cursor = cursor.prev;
195
+ }
196
+ return false;
197
+ }
198
+
199
+ // Push a new lexical context of the given type.
200
+ function pushlex(type, info) {
201
+ var result = function(){
202
+ lexical = new JSLexical(indented, column, type, null, lexical, info)
203
+ };
204
+ result.lex = true;
205
+ return result;
206
+ }
207
+ // Pop off the current lexical context.
208
+ function poplex(){
209
+ lexical = lexical.prev;
210
+ }
211
+ poplex.lex = true;
212
+ // The 'lex' flag on these actions is used by the 'next' function
213
+ // to know they can (and have to) be ran before moving on to the
214
+ // next token.
215
+
216
+ // Creates an action that discards tokens until it finds one of
217
+ // the given type.
218
+ function expect(wanted){
219
+ return function expecting(type){
220
+ if (type == wanted) cont();
221
+ else cont(arguments.callee);
222
+ };
223
+ }
224
+
225
+ // Looks for a statement, and then calls itself.
226
+ function statements(type){
227
+ return pass(statement, statements);
228
+ }
229
+ // Dispatches various types of statements based on the type of the
230
+ // current token.
231
+ function statement(type){
232
+ if (type == "var") cont(pushlex("vardef"), vardef1, expect(";"), poplex);
233
+ else if (type == "keyword a") cont(pushlex("form"), expression, statement, poplex);
234
+ else if (type == "keyword b") cont(pushlex("form"), statement, poplex);
235
+ else if (type == "{" && json) cont(pushlex("}"), commasep(objprop, "}"), poplex);
236
+ else if (type == "{") cont(pushlex("}"), block, poplex);
237
+ else if (type == "function") cont(functiondef);
238
+ else if (type == "for") cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), poplex, statement, poplex);
239
+ else if (type == "variable") cont(pushlex("stat"), maybelabel);
240
+ else if (type == "switch") cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), block, poplex, poplex);
241
+ else if (type == "case") cont(expression, expect(":"));
242
+ else if (type == "default") cont(expect(":"));
243
+ else if (type == "catch") cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), statement, poplex, popcontext);
244
+ else pass(pushlex("stat"), expression, expect(";"), poplex);
245
+ }
246
+ // Dispatch expression types.
247
+ function expression(type){
248
+ if (atomicTypes.hasOwnProperty(type)) cont(maybeoperator);
249
+ else if (type == "function") cont(functiondef);
250
+ else if (type == "keyword c") cont(expression);
251
+ else if (type == "(") cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator);
252
+ else if (type == "operator") cont(expression);
253
+ else if (type == "[") cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
254
+ else if (type == "{") cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
255
+ }
256
+ // Called for places where operators, function calls, or
257
+ // subscripts are valid. Will skip on to the next action if none
258
+ // is found.
259
+ function maybeoperator(type){
260
+ if (type == "operator") cont(expression);
261
+ else if (type == "(") cont(pushlex(")"), expression, commasep(expression, ")"), poplex, maybeoperator);
262
+ else if (type == ".") cont(property, maybeoperator);
263
+ else if (type == "[") cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
264
+ }
265
+ // When a statement starts with a variable name, it might be a
266
+ // label. If no colon follows, it's a regular statement.
267
+ function maybelabel(type){
268
+ if (type == ":") cont(poplex, statement);
269
+ else pass(maybeoperator, expect(";"), poplex);
270
+ }
271
+ // Property names need to have their style adjusted -- the
272
+ // tokenizer thinks they are variables.
273
+ function property(type){
274
+ if (type == "variable") {mark("js-property"); cont();}
275
+ }
276
+ // This parses a property and its value in an object literal.
277
+ function objprop(type){
278
+ if (type == "variable") mark("js-property");
279
+ if (atomicTypes.hasOwnProperty(type)) cont(expect(":"), expression);
280
+ }
281
+ // Parses a comma-separated list of the things that are recognized
282
+ // by the 'what' argument.
283
+ function commasep(what, end){
284
+ function proceed(type) {
285
+ if (type == ",") cont(what, proceed);
286
+ else if (type == end) cont();
287
+ else cont(expect(end));
288
+ };
289
+ return function commaSeparated(type) {
290
+ if (type == end) cont();
291
+ else pass(what, proceed);
292
+ };
293
+ }
294
+ // Look for statements until a closing brace is found.
295
+ function block(type){
296
+ if (type == "}") cont();
297
+ else pass(statement, block);
298
+ }
299
+ // Variable definitions are split into two actions -- 1 looks for
300
+ // a name or the end of the definition, 2 looks for an '=' sign or
301
+ // a comma.
302
+ function vardef1(type, value){
303
+ if (type == "variable"){register(value); cont(vardef2);}
304
+ else cont();
305
+ }
306
+ function vardef2(type, value){
307
+ if (value == "=") cont(expression, vardef2);
308
+ else if (type == ",") cont(vardef1);
309
+ }
310
+ // For loops.
311
+ function forspec1(type){
312
+ if (type == "var") cont(vardef1, forspec2);
313
+ else if (type == ";") pass(forspec2);
314
+ else if (type == "variable") cont(formaybein);
315
+ else pass(forspec2);
316
+ }
317
+ function formaybein(type, value){
318
+ if (value == "in") cont(expression);
319
+ else cont(maybeoperator, forspec2);
320
+ }
321
+ function forspec2(type, value){
322
+ if (type == ";") cont(forspec3);
323
+ else if (value == "in") cont(expression);
324
+ else cont(expression, expect(";"), forspec3);
325
+ }
326
+ function forspec3(type) {
327
+ if (type == ")") pass();
328
+ else cont(expression);
329
+ }
330
+ // A function definition creates a new context, and the variables
331
+ // in its argument list have to be added to this context.
332
+ function functiondef(type, value){
333
+ if (type == "variable"){register(value); cont(functiondef);}
334
+ else if (type == "(") cont(pushcontext, commasep(funarg, ")"), statement, popcontext);
335
+ }
336
+ function funarg(type, value){
337
+ if (type == "variable"){register(value); cont();}
338
+ }
339
+
340
+ return parser;
341
+ }
342
+
343
+ return {
344
+ make: parseJS,
345
+ electricChars: "{}:",
346
+ configure: function(obj) {
347
+ if (obj.json != null) json = obj.json;
348
+ }
349
+ };
350
+ })();