├── README.md
├── example
├── app.js
├── codemirror
│ ├── css
│ │ ├── csscolors.css
│ │ ├── docs.css
│ │ ├── jscolors.css
│ │ ├── people.jpg
│ │ ├── sparqlcolors.css
│ │ └── xmlcolors.css
│ └── js
│ │ ├── codemirror.js
│ │ ├── editor.js
│ │ ├── highlight.js
│ │ ├── mirrorframe.js
│ │ ├── parsecss.js
│ │ ├── parsedummy.js
│ │ ├── parsehtmlmixed.js
│ │ ├── parsejavascript.js
│ │ ├── parsesparql.js
│ │ ├── parsexml.js
│ │ ├── select.js
│ │ ├── stringstream.js
│ │ ├── tokenize.js
│ │ ├── tokenizejavascript.js
│ │ ├── undo.js
│ │ └── util.js
├── example.html
└── parser.browser.js
├── javascript.pegjs
├── js2cs.js
├── out
├── js2cs.coffee
├── js2cs.recompiled.js
└── test.coffee
├── parser.js
└── test
├── jquery-1.4.2.js
└── test.js
/README.md:
--------------------------------------------------------------------------------
1 | Usage
2 | ----------
3 |
4 | [CoffeeScript](http://www.coffeescript.com)
5 |
6 |
7 | node js2cs.js file_to_convert
8 |
9 |
10 | Options: --debug, --ilevel, --convert
11 |
12 | --debug: Print the AST tree to STDIO.
13 |
14 | --ilevel: Show node types, names and indent levels.
15 |
16 | --convert: Output converted program to stdout.
17 |
18 | Browser demo is in /example
19 |
20 | Known Limitations
21 | --------------------
22 |
23 | * Limited by PEG.js's ability to read your syntax. You may (will) have to refactor it to use this tool.
24 |
25 | * No support for LabelledStatement. Only used with BreakStatement (break) which is not used in Coffee.
26 |
27 | * Untested. Not symbol-for-symbol, token-for-token.
28 |
29 | * For Statements are turned into While statements in Coffee because Coffee's For is one-way to JavaScript (can't be translated back to that.)
30 |
31 | * Comma operator (,) compiles to \n. Not supported in CoffeeScript.
32 |
33 | * Postfix expression like ++i is not handled yet.
34 |
35 | * Else if is NOT supported by the PEGjs grammar. You can use if { stuff; } else { if() { stuff; } }.
36 |
37 | * No one line if statements.
38 |
39 | --
40 | Jonathan Silverman ("jsilver")
41 |
--------------------------------------------------------------------------------
/example/app.js:
--------------------------------------------------------------------------------
1 | String.prototype.trim = function () {
2 | return this.replace(/^\s*/, "").replace(/\s*$/, "");
3 | }
4 |
5 | var parseJs = function(js) {
6 | return parser.parse(js);
7 | }
8 | var output = '';
9 | var error = '';
10 | var iteration = 0;
11 | var indent_level = 0;
12 | var increaseIndent = function() {
13 | indent_level = indent_level + 1;
14 | }
15 | var decreaseIndent = function() {
16 | indent_level = indent_level - 1;
17 | }
18 | var indent = function()
19 | {
20 | for(var c = 0; c < indent_level; c++)
21 | {
22 | addToOut(" ");
23 | }
24 | }
25 | var addToOut = function(out) {
26 | output += out;
27 | }
28 | var removeBlankLines = function(out) {
29 | var return_me = out.replace(/\n\n/g, "\n");
30 | while (return_me.indexOf("\n\n") > 0)
31 | {
32 | return_me = return_me.replace(/\n\n/g, "\n");
33 | }
34 | return return_me;
35 | }
36 |
37 | /* calls parseNode on a collection of child nodes (statements, elements, properties, clauses) */
38 | var parseChildNodes = function(nodes) {
39 | for(var i = 0; i < nodes.length; i++) {
40 | /* some logic */
41 | _node = nodes[i];
42 | is_last_statement = (i < nodes.length -1);
43 | is_just_var = (is_last_statement && (_node.type == "Variable")); /* variables are not declared this way in coffee */
44 | is_break = (_node.type == "BreakStatement"); /* not used in coffee */
45 | /* also don't parse labelledStatement. it's not used and we can't have empty cases if we wanna self host */
46 | is_labelled_statement = (_node.type == "LabelledStatement");
47 | /* indenter */
48 |
49 | if(!(is_break) && !(is_labelled_statement)) {
50 | indent();
51 | }
52 |
53 | /* token parser */
54 | if(!(is_just_var) && !(is_break) && !(is_labelled_statement))
55 | {
56 | parseNode(_node);
57 | }
58 | /* line breaker */
59 | /*if((is_last_statement) && !(is_break) && !(is_just_var))
60 | {
61 | addToOut("\n");
62 | }
63 | */
64 | addToOut("\n");
65 | }
66 | }
67 |
68 | /* eats tokens and makes coffee */
69 | var parseNode = function(node) {
70 | switch(node.type)
71 | {
72 | case("Program"):
73 | if(node.elements)
74 | {
75 | parseChildNodes(node.elements);
76 | }
77 | break;
78 | case("This"):
79 | addToOut("@");
80 | break;
81 | case("Function"):
82 | if(node.params.length > 0)
83 | {
84 | addToOut("(");
85 | for(var i = 0; i < node.params.length; i++)
86 | {
87 | /* this tokenizer is probably broken. node.params node should be parsed. */
88 |
89 | addToOut(node.params[i]);
90 |
91 | /*arseNode(node.params[i]);*/
92 | if(i < node.params.length - 1)
93 | {
94 | addToOut(", ");
95 | }
96 | }
97 | addToOut(")");
98 | }
99 | addToOut("->\n");
100 | increaseIndent();
101 | if(node.elements)
102 | {
103 | parseChildNodes(node.elements);
104 | }
105 | decreaseIndent();
106 | break;
107 | case("Block"):
108 | increaseIndent();
109 | if(node.statements)
110 | {
111 | parseChildNodes(node.statements);
112 | }
113 | decreaseIndent();
114 | break;
115 | case("SwitchStatement"):
116 | addToOut("switch ");
117 | parseNode(node.expression);
118 | addToOut("\n");
119 | increaseIndent();
120 | parseChildNodes(node.clauses);
121 | decreaseIndent();
122 | break;
123 | case("CaseClause"):
124 | addToOut("when ");
125 | parseNode(node.selector);
126 | /* 2 is the minimum because break; is a statement too
127 | if((node.statements.length > 2) || (node.statements.length == 1))
128 | {
129 | */
130 | addToOut("\n");
131 | increaseIndent();
132 | if(node.statements)
133 | {
134 | parseChildNodes(node.statements);
135 | }
136 | decreaseIndent();
137 | /*
138 | }
139 | else
140 | {
141 | if(node.statements.length == 2)
142 | {
143 | addToOut(" then ");
144 | if(node.statements)
145 | {
146 | parseNode(node.statements[0]);
147 | }
148 | }
149 | }
150 | */
151 | break;
152 | case("DefaultClause"):
153 | addToOut("else ");
154 | if(node.statements.length > 1)
155 | {
156 | addToOut("\n");
157 | increaseIndent();
158 | if(node.statements)
159 | {
160 | parseChildNodes(node.statements);
161 | }
162 | decreaseIndent();
163 | }
164 | else
165 | {
166 | if(node.statements.length == 1)
167 | {
168 | if(node.statements)
169 | {
170 | parseNode(node.statements[0]);
171 | }
172 | }
173 | }
174 | break;
175 | case("IfStatement"):
176 | /* condition */
177 | if(node.condition.operator != "!")
178 | {
179 | addToOut("if ");
180 | parseNode(node.condition);
181 | }
182 | else
183 | {
184 | addToOut("unless ");
185 | /* skip next node, it's "not" */
186 | parseNode(node.condition.expression);
187 | }
188 | addToOut("\n");
189 | /* statements */
190 | increaseIndent();
191 | if(node.ifStatement.statements)
192 | {
193 | parseChildNodes(node.ifStatement.statements);
194 | }
195 | decreaseIndent();
196 | if(node.elseStatement != null) {
197 | addToOut("\n");
198 | indent();
199 | addToOut("else"); /* limitation: javascript.pegjs doesnt know else
200 | if */
201 | addToOut("\n");
202 | increaseIndent();
203 | if(node.elseStatement.statements)
204 | {
205 | parseChildNodes(node.elseStatement.statements);
206 | }
207 | decreaseIndent();
208 | }
209 | break;
210 | case("ForStatement"):
211 | /* converts to while because this mode is unsupported */
212 | parseNode(node.initializer);
213 | addToOut("\n");
214 | indent();
215 | addToOut("while ");
216 | parseNode(node.test);
217 | addToOut("\n");
218 | increaseIndent();
219 | indent();
220 | parseNode(node.counter); /* if possible do a test on the counter to see if it's ++i or i++ (they are different) */
221 | decreaseIndent();
222 | if(node.statement)
223 | {
224 | parseNode(node.statement);
225 | }
226 | break;
227 | case("WhileStatement"):
228 | addToOut("while ");
229 | parseNode(node.condition);
230 | addToOut("\n");
231 | if(node.statement)
232 | {
233 | parseNode(node.statement);
234 | }
235 | break;
236 | case("TryStatement"):
237 | /* epic self translating challenge here */
238 | addToOut("try\n");
239 | parseNode(node.block);
240 | addToOut("\n");
241 | if(node['catch']) {
242 | addToOut("catch ");
243 | parseNode(node['catch']);
244 | }
245 | if(node['finally']) {
246 | addToOut("finally\n");
247 | parseNode(node['finally']);
248 | }
249 | break;
250 | case("Catch"):
251 | if(node.identifier)
252 | {
253 | addToOut(node.identifier);
254 | }
255 | addToOut("\n");
256 | parseNode(node.block);
257 | addToOut("\n");
258 | break;
259 | case("Finally"):
260 | parseNode(node.block);
261 | break;
262 | case("AssignmentExpression"):
263 | parseNode(node.left);
264 | addToOut(": ");
265 | parseNode(node.right);
266 | break;
267 | case("PropertyAssignment"):
268 | /*addToOut(node.name);*/
269 | parseNode(node.name);
270 | addToOut(": ");
271 | parseNode(node.value);
272 | break;
273 | case("PropertyAccess"):
274 | parseNode(node.base);
275 | if(node.name.type)
276 | {
277 | /*
278 | var node_dot_name_is_numeric_literal = (node.name.type == "NumericLiteral");
279 | var node_dot_name_is_string_literal = (node.name.type == "StringLiteral");
280 | if(node.base.type != "This" && !(node_dot_name_is_numeric_literal) && !(node_dot_name_is_string_literal)) { addToOut("."); }
281 |
282 | if(node_dot_name_is_numeric_literal || node_dot_name_is_string_literal) { addToOut("["); }
283 |
284 | if(node.base.type == "Variable")
285 | {
286 | addToOut(".");
287 | parseNode(node.name);
288 | }
289 | else
290 | { */
291 | if(node.base.type != "This") {
292 | if(node.name.type != "FunctionCall")
293 | {
294 | addToOut("[");
295 | parseNode(node.name);
296 | addToOut("]");
297 | }
298 | else
299 | {
300 | addToOut(".");
301 | parseNode(node.name);
302 | }
303 | }
304 | else
305 | {
306 | parseNode(node.name);
307 | }
308 | /*
309 | else
310 | {
311 | parseNode(node.name);
312 | }
313 | }
314 |
315 | if(node_dot_name_is_numeric_literal || node_dot_name_is_string_literal) { addToOut("]"); }
316 | */
317 | }
318 | else
319 | {
320 | if(node.name.type == undefined || node.name.type == "null")
321 | {
322 | if(node.base.type != "This") { addToOut("."); }
323 | addToOut(node.name.trim());
324 | /*if(node.base.type != "This") { addToOut("']"); }*/
325 | }
326 | }
327 |
328 | break;
329 | case("BinaryExpression"):
330 | parseNode(node.left);
331 | addToOut(" ");
332 | switch(node.operator)
333 | {
334 | /* switch to "not" and "isnt" or something here */
335 | case("!"):
336 | addToOut("not ");
337 | break;
338 | case("==="):
339 | addToOut("is ");
340 | break;
341 | case("=="):
342 | addToOut("is ");
343 | break;
344 | case("!=="):
345 | addToOut("isnt ");
346 | break;
347 | case("&&"):
348 | addToOut("and ");
349 | break;
350 | case("||"):
351 | addToOut("or ");
352 | break;
353 | case(","):
354 | addToOut("\n"); /* no support for that operator yet. try to evaluate on seperate lines. */
355 | break;
356 | default:
357 | addToOut(node.operator);
358 | addToOut(" ");
359 | }
360 | parseNode(node.right);
361 | break;
362 | case("UnaryExpression"):
363 | switch(node.operator)
364 | {
365 | case('!'):
366 | addToOut("not ");
367 | break;
368 | default:
369 | addToOut(node.operator);
370 | }
371 | parseNode(node.expression);
372 | break;
373 | case("ConditionalExpression"):
374 | addToOut("if ");
375 | parseNode(node.condition);
376 | addToOut(" ");
377 | parseNode(node.trueExpression);
378 | addToOut(" else ");
379 | parseNode(node.falseExpression);
380 | break;
381 | case("PostfixExpression"):
382 | switch(node.operator)
383 | {
384 | case('++'):
385 | parseNode(node.expression);
386 | addToOut(" = ");
387 | parseNode(node.expression);
388 | addToOut(" + 1");
389 | break;
390 | case('--'):
391 | parseNode(node.expression);
392 | addToOut(" = ");
393 | parseNode(node.expression);
394 | addToOut(" - 1");
395 | break;
396 | }
397 | addToOut("\n");
398 | break;
399 | case("Variable"):
400 | if(!(node.name.substr(0, 3) == "var"))
401 | {
402 | addToOut(node.name.trim());
403 | }
404 | else
405 | {
406 | if(node.name.substr(0, 3) == "var")
407 | {
408 | /* -5 because of 4 for "var " and 1 for " " after */
409 | addToOut(node.name.substr(4, node.name.length - 4).trim());
410 | }
411 | }
412 | break;
413 | case("FunctionCall"):
414 | parseNode(node.name);
415 | addToOut("(");
416 | if(node.arguments.length > 0)
417 | {
418 |
419 | for(var i = 0; i < node.arguments.length; i++)
420 | {
421 | parseNode(node.arguments[i]);
422 | if(i < node.arguments.length - 1)
423 | {
424 | addToOut(", ");
425 | }
426 | }
427 | }
428 | addToOut(")");
429 | break;
430 | case('StringLiteral'):
431 | /* be sure to escape any control characters you need here with \ */
432 | var escapedValue = node.value.replace(/\n/g, "\\n");
433 | addToOut('"' + escapedValue + '"');
434 | break;
435 | case('NumericLiteral'):
436 | addToOut(node.value);
437 | break;
438 | case('RegularExpressionLiteral'):
439 | addToOut("/");
440 | addToOut(node.body);
441 | addToOut("/" + node.flags);
442 | break;
443 | case('NullLiteral'):
444 | addToOut("null");
445 | break;
446 | case('ArrayLiteral'):
447 | if(node.elements.length > 0)
448 | {
449 | addToOut("[");
450 | for(var i = 0; i < node.elements.length; i++)
451 | {
452 | parseNode(node.elements[i]);
453 | if(i < node.elements.length - 1)
454 | {
455 | addToOut(", ");
456 | }
457 | }
458 | addToOut("]");
459 | }
460 | break;
461 | case('ObjectLiteral'):
462 | if(node.properties.length > 0)
463 | {
464 | addToOut("{\n");
465 | increaseIndent();
466 | if(node.properties)
467 | {
468 | parseChildNodes(node.properties);
469 | }
470 | decreaseIndent();
471 | addToOut("\n}");
472 | }
473 | break;
474 | case('BooleanLiteral'):
475 | if(node.value == true)
476 | {
477 | addToOut("yes");
478 | }
479 | else
480 | {
481 | if(node.value == false)
482 | {
483 | addToOut("no");
484 | }
485 | }
486 | break;
487 | }
488 | }
489 |
490 | /*
491 | parseJs(js);
492 | now i have ast
493 | parseNode(ast);
494 | */
495 | /* now i have output */
496 |
497 |
498 |
499 |
--------------------------------------------------------------------------------
/example/codemirror/css/csscolors.css:
--------------------------------------------------------------------------------
1 | html {
2 | cursor: text;
3 | }
4 |
5 | .editbox {
6 | margin: .4em;
7 | padding: 0;
8 | font-family: monospace;
9 | font-size: 10pt;
10 | color: black;
11 | }
12 |
13 | pre.code, .editbox {
14 | color: #666;
15 | }
16 |
17 | .editbox p {
18 | margin: 0;
19 | }
20 |
21 | span.css-at {
22 | color: #708;
23 | }
24 |
25 | span.css-unit {
26 | color: #281;
27 | }
28 |
29 | span.css-value {
30 | color: #708;
31 | }
32 |
33 | span.css-identifier {
34 | color: black;
35 | }
36 |
37 | span.css-selector {
38 | color: #11B;
39 | }
40 |
41 | span.css-important {
42 | color: #00F;
43 | }
44 |
45 | span.css-colorcode {
46 | color: #299;
47 | }
48 |
49 | span.css-comment {
50 | color: #A70;
51 | }
52 |
53 | span.css-string {
54 | color: #A22;
55 | }
56 |
--------------------------------------------------------------------------------
/example/codemirror/css/docs.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 3em 6em;
4 | color: black;
5 | max-width: 50em;
6 | }
7 |
8 | h1 {
9 | font-size: 22pt;
10 | }
11 |
12 | .underline {
13 | border-bottom: 3px solid #C44;
14 | }
15 |
16 | h2 {
17 | font-size: 14pt;
18 | }
19 |
20 | h3 {
21 | font-size: 12pt;
22 | }
23 |
24 | p.rel {
25 | padding-left: 2em;
26 | text-indent: -2em;
27 | }
28 |
29 | div.border {
30 | border: 1px solid black;
31 | }
32 |
33 | code {
34 | font-family: courier, monospace;
35 | font-size: 90%;
36 | color: #144;
37 | }
38 |
39 | pre.code {
40 | margin: 1.1em 12px;
41 | border: 1px solid #CCCCCC;
42 | color: black;
43 | padding: .4em;
44 | font-family: courier, monospace;
45 | }
46 |
47 | .warn {
48 | color: #C00;
49 | }
50 |
--------------------------------------------------------------------------------
/example/codemirror/css/jscolors.css:
--------------------------------------------------------------------------------
1 | html {
2 | cursor: text;
3 | }
4 |
5 | .editbox {
6 | margin: .4em;
7 | padding: 0;
8 | font-family: monospace;
9 | font-size: 10pt;
10 | color: black;
11 | }
12 |
13 | pre.code, .editbox {
14 | color: #666666;
15 | }
16 |
17 | .editbox p {
18 | margin: 0;
19 | }
20 |
21 | span.js-punctuation {
22 | color: #666666;
23 | }
24 |
25 | span.js-operator {
26 | color: #666666;
27 | }
28 |
29 | span.js-keyword {
30 | color: #770088;
31 | }
32 |
33 | span.js-atom {
34 | color: #228811;
35 | }
36 |
37 | span.js-variable {
38 | color: black;
39 | }
40 |
41 | span.js-variabledef {
42 | color: #0000FF;
43 | }
44 |
45 | span.js-localvariable {
46 | color: #004499;
47 | }
48 |
49 | span.js-property {
50 | color: black;
51 | }
52 |
53 | span.js-comment {
54 | color: #AA7700;
55 | }
56 |
57 | span.js-string {
58 | color: #AA2222;
59 | }
60 |
--------------------------------------------------------------------------------
/example/codemirror/css/people.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mixflame/js2cs/211fc0c4b32402f1b06e01cfcfea5a159fa8380c/example/codemirror/css/people.jpg
--------------------------------------------------------------------------------
/example/codemirror/css/sparqlcolors.css:
--------------------------------------------------------------------------------
1 | html {
2 | cursor: text;
3 | }
4 |
5 | .editbox {
6 | margin: .4em;
7 | padding: 0;
8 | font-family: monospace;
9 | font-size: 10pt;
10 | color: black;
11 | }
12 |
13 | .editbox p {
14 | margin: 0;
15 | }
16 |
17 | span.sp-keyword {
18 | color: #708;
19 | }
20 |
21 | span.sp-prefixed {
22 | color: #5d1;
23 | }
24 |
25 | span.sp-var {
26 | color: #00c;
27 | }
28 |
29 | span.sp-comment {
30 | color: #a70;
31 | }
32 |
33 | span.sp-literal {
34 | color: #a22;
35 | }
36 |
37 | span.sp-uri {
38 | color: #292;
39 | }
40 |
41 | span.sp-operator {
42 | color: #088;
43 | }
44 |
--------------------------------------------------------------------------------
/example/codemirror/css/xmlcolors.css:
--------------------------------------------------------------------------------
1 | html {
2 | cursor: text;
3 | }
4 |
5 | .editbox {
6 | margin: .4em;
7 | padding: 0;
8 | font-family: monospace;
9 | font-size: 10pt;
10 | color: black;
11 | }
12 |
13 | .editbox p {
14 | margin: 0;
15 | }
16 |
17 | span.xml-tagname {
18 | color: #A0B;
19 | }
20 |
21 | span.xml-attribute {
22 | color: #281;
23 | }
24 |
25 | span.xml-punctuation {
26 | color: black;
27 | }
28 |
29 | span.xml-attname {
30 | color: #00F;
31 | }
32 |
33 | span.xml-comment {
34 | color: #A70;
35 | }
36 |
37 | span.xml-cdata {
38 | color: #48A;
39 | }
40 |
41 | span.xml-processing {
42 | color: #999;
43 | }
44 |
45 | span.xml-entity {
46 | color: #A22;
47 | }
48 |
49 | span.xml-error {
50 | color: #F00 !important;
51 | }
52 |
53 | span.xml-text {
54 | color: black;
55 | }
56 |
--------------------------------------------------------------------------------
/example/codemirror/js/codemirror.js:
--------------------------------------------------------------------------------
1 | /* CodeMirror main module
2 | *
3 | * Implements the CodeMirror constructor and prototype, which take care
4 | * of initializing the editor frame, and providing the outside interface.
5 | */
6 |
7 | // The CodeMirrorConfig object is used to specify a default
8 | // configuration. If you specify such an object before loading this
9 | // file, the values you put into it will override the defaults given
10 | // below. You can also assign to it after loading.
11 | var CodeMirrorConfig = window.CodeMirrorConfig || {};
12 |
13 | var CodeMirror = (function(){
14 | function setDefaults(object, defaults) {
15 | for (var option in defaults) {
16 | if (!object.hasOwnProperty(option))
17 | object[option] = defaults[option];
18 | }
19 | }
20 | function forEach(array, action) {
21 | for (var i = 0; i < array.length; i++)
22 | action(array[i]);
23 | }
24 |
25 | // These default options can be overridden by passing a set of
26 | // options to a specific CodeMirror constructor. See manual.html for
27 | // their meaning.
28 | setDefaults(CodeMirrorConfig, {
29 | stylesheet: [],
30 | path: "",
31 | parserfile: [],
32 | basefiles: ["util.js", "stringstream.js", "select.js", "undo.js", "editor.js", "tokenize.js"],
33 | iframeClass: null,
34 | passDelay: 200,
35 | passTime: 50,
36 | lineNumberDelay: 200,
37 | lineNumberTime: 50,
38 | continuousScanning: false,
39 | saveFunction: null,
40 | onChange: null,
41 | undoDepth: 50,
42 | undoDelay: 800,
43 | disableSpellcheck: true,
44 | textWrapping: true,
45 | readOnly: false,
46 | width: "",
47 | height: "300px",
48 | minHeight: 100,
49 | autoMatchParens: false,
50 | parserConfig: null,
51 | tabMode: "indent", // or "spaces", "default", "shift"
52 | reindentOnLoad: false,
53 | activeTokens: null,
54 | cursorActivity: null,
55 | lineNumbers: false,
56 | indentUnit: 2,
57 | domain: null
58 | });
59 |
60 | function addLineNumberDiv(container) {
61 | var nums = document.createElement("DIV"),
62 | scroller = document.createElement("DIV");
63 | nums.style.position = "absolute";
64 | nums.style.height = "100%";
65 | if (nums.style.setExpression) {
66 | try {nums.style.setExpression("height", "this.previousSibling.offsetHeight + 'px'");}
67 | catch(e) {} // Seems to throw 'Not Implemented' on some IE8 versions
68 | }
69 | nums.style.top = "0px";
70 | nums.style.left = "0px";
71 | nums.style.overflow = "hidden";
72 | container.appendChild(nums);
73 | scroller.className = "CodeMirror-line-numbers";
74 | nums.appendChild(scroller);
75 | scroller.innerHTML = "
1
";
76 | return nums;
77 | }
78 |
79 | function frameHTML(options) {
80 | if (typeof options.parserfile == "string")
81 | options.parserfile = [options.parserfile];
82 | if (typeof options.stylesheet == "string")
83 | options.stylesheet = [options.stylesheet];
84 |
85 | var html = [""];
86 | // Hack to work around a bunch of IE8-specific problems.
87 | html.push("");
88 | forEach(options.stylesheet, function(file) {
89 | html.push("");
90 | });
91 | forEach(options.basefiles.concat(options.parserfile), function(file) {
92 | if (!/^https?:/.test(file)) file = options.path + file;
93 | html.push("= 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 | function expressions(type){
230 | return pass(expression, expressions);
231 | }
232 | // Dispatches various types of statements based on the type of the
233 | // current token.
234 | function statement(type){
235 | if (type == "var") cont(pushlex("vardef"), vardef1, expect(";"), poplex);
236 | else if (type == "keyword a") cont(pushlex("form"), expression, statement, poplex);
237 | else if (type == "keyword b") cont(pushlex("form"), statement, poplex);
238 | else if (type == "{") cont(pushlex("}"), block, poplex);
239 | else if (type == "function") cont(functiondef);
240 | else if (type == "for") cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), poplex, statement, poplex);
241 | else if (type == "variable") cont(pushlex("stat"), maybelabel);
242 | else if (type == "switch") cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), block, poplex, poplex);
243 | else if (type == "case") cont(expression, expect(":"));
244 | else if (type == "default") cont(expect(":"));
245 | else if (type == "catch") cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), statement, poplex, popcontext);
246 | else pass(pushlex("stat"), expression, expect(";"), poplex);
247 | }
248 | // Dispatch expression types.
249 | function expression(type){
250 | if (atomicTypes.hasOwnProperty(type)) cont(maybeoperator);
251 | else if (type == "function") cont(functiondef);
252 | else if (type == "keyword c") cont(expression);
253 | else if (type == "(") cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator);
254 | else if (type == "operator") cont(expression);
255 | else if (type == "[") cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
256 | else if (type == "{") cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
257 | else cont();
258 | }
259 | // Called for places where operators, function calls, or
260 | // subscripts are valid. Will skip on to the next action if none
261 | // is found.
262 | function maybeoperator(type){
263 | if (type == "operator") cont(expression);
264 | else if (type == "(") cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
265 | else if (type == ".") cont(property, maybeoperator);
266 | else if (type == "[") cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
267 | }
268 | // When a statement starts with a variable name, it might be a
269 | // label. If no colon follows, it's a regular statement.
270 | function maybelabel(type){
271 | if (type == ":") cont(poplex, statement);
272 | else pass(maybeoperator, expect(";"), poplex);
273 | }
274 | // Property names need to have their style adjusted -- the
275 | // tokenizer thinks they are variables.
276 | function property(type){
277 | if (type == "variable") {mark("js-property"); cont();}
278 | }
279 | // This parses a property and its value in an object literal.
280 | function objprop(type){
281 | if (type == "variable") mark("js-property");
282 | if (atomicTypes.hasOwnProperty(type)) cont(expect(":"), expression);
283 | }
284 | // Parses a comma-separated list of the things that are recognized
285 | // by the 'what' argument.
286 | function commasep(what, end){
287 | function proceed(type) {
288 | if (type == ",") cont(what, proceed);
289 | else if (type == end) cont();
290 | else cont(expect(end));
291 | }
292 | return function commaSeparated(type) {
293 | if (type == end) cont();
294 | else pass(what, proceed);
295 | };
296 | }
297 | // Look for statements until a closing brace is found.
298 | function block(type){
299 | if (type == "}") cont();
300 | else pass(statement, block);
301 | }
302 | // Variable definitions are split into two actions -- 1 looks for
303 | // a name or the end of the definition, 2 looks for an '=' sign or
304 | // a comma.
305 | function vardef1(type, value){
306 | if (type == "variable"){register(value); cont(vardef2);}
307 | else cont();
308 | }
309 | function vardef2(type, value){
310 | if (value == "=") cont(expression, vardef2);
311 | else if (type == ",") cont(vardef1);
312 | }
313 | // For loops.
314 | function forspec1(type){
315 | if (type == "var") cont(vardef1, forspec2);
316 | else if (type == ";") pass(forspec2);
317 | else if (type == "variable") cont(formaybein);
318 | else pass(forspec2);
319 | }
320 | function formaybein(type, value){
321 | if (value == "in") cont(expression);
322 | else cont(maybeoperator, forspec2);
323 | }
324 | function forspec2(type, value){
325 | if (type == ";") cont(forspec3);
326 | else if (value == "in") cont(expression);
327 | else cont(expression, expect(";"), forspec3);
328 | }
329 | function forspec3(type) {
330 | if (type == ")") pass();
331 | else cont(expression);
332 | }
333 | // A function definition creates a new context, and the variables
334 | // in its argument list have to be added to this context.
335 | function functiondef(type, value){
336 | if (type == "variable"){register(value); cont(functiondef);}
337 | else if (type == "(") cont(pushcontext, commasep(funarg, ")"), statement, popcontext);
338 | }
339 | function funarg(type, value){
340 | if (type == "variable"){register(value); cont();}
341 | }
342 |
343 | return parser;
344 | }
345 |
346 | return {
347 | make: parseJS,
348 | electricChars: "{}:",
349 | configure: function(obj) {
350 | if (obj.json != null) json = obj.json;
351 | }
352 | };
353 | })();
354 |
--------------------------------------------------------------------------------
/example/codemirror/js/parsesparql.js:
--------------------------------------------------------------------------------
1 | var SparqlParser = Editor.Parser = (function() {
2 | function wordRegexp(words) {
3 | return new RegExp("^(?:" + words.join("|") + ")$", "i");
4 | }
5 | var ops = wordRegexp(["str", "lang", "langmatches", "datatype", "bound", "sameterm", "isiri", "isuri",
6 | "isblank", "isliteral", "union", "a"]);
7 | var keywords = wordRegexp(["base", "prefix", "select", "distinct", "reduced", "construct", "describe",
8 | "ask", "from", "named", "where", "order", "limit", "offset", "filter", "optional",
9 | "graph", "by", "asc", "desc"]);
10 | var operatorChars = /[*+\-<>=&|]/;
11 |
12 | var tokenizeSparql = (function() {
13 | function normal(source, setState) {
14 | var ch = source.next();
15 | if (ch == "$" || ch == "?") {
16 | source.nextWhileMatches(/[\w\d]/);
17 | return "sp-var";
18 | }
19 | else if (ch == "<" && !source.matches(/[\s\u00a0=]/)) {
20 | source.nextWhileMatches(/[^\s\u00a0>]/);
21 | if (source.equals(">")) source.next();
22 | return "sp-uri";
23 | }
24 | else if (ch == "\"" || ch == "'") {
25 | setState(inLiteral(ch));
26 | return null;
27 | }
28 | else if (/[{}\(\),\.;\[\]]/.test(ch)) {
29 | return "sp-punc";
30 | }
31 | else if (ch == "#") {
32 | while (!source.endOfLine()) source.next();
33 | return "sp-comment";
34 | }
35 | else if (operatorChars.test(ch)) {
36 | source.nextWhileMatches(operatorChars);
37 | return "sp-operator";
38 | }
39 | else if (ch == ":") {
40 | source.nextWhileMatches(/[\w\d\._\-]/);
41 | return "sp-prefixed";
42 | }
43 | else {
44 | source.nextWhileMatches(/[_\w\d]/);
45 | if (source.equals(":")) {
46 | source.next();
47 | source.nextWhileMatches(/[\w\d_\-]/);
48 | return "sp-prefixed";
49 | }
50 | var word = source.get(), type;
51 | if (ops.test(word))
52 | type = "sp-operator";
53 | else if (keywords.test(word))
54 | type = "sp-keyword";
55 | else
56 | type = "sp-word";
57 | return {style: type, content: word};
58 | }
59 | }
60 |
61 | function inLiteral(quote) {
62 | return function(source, setState) {
63 | var escaped = false;
64 | while (!source.endOfLine()) {
65 | var ch = source.next();
66 | if (ch == quote && !escaped) {
67 | setState(normal);
68 | break;
69 | }
70 | escaped = !escaped && ch == "\\";
71 | }
72 | return "sp-literal";
73 | };
74 | }
75 |
76 | return function(source, startState) {
77 | return tokenizer(source, startState || normal);
78 | };
79 | })();
80 |
81 | function indentSparql(context) {
82 | return function(nextChars) {
83 | var firstChar = nextChars && nextChars.charAt(0);
84 | if (/[\]\}]/.test(firstChar))
85 | while (context && context.type == "pattern") context = context.prev;
86 |
87 | var closing = context && firstChar == matching[context.type];
88 | if (!context)
89 | return 0;
90 | else if (context.type == "pattern")
91 | return context.col;
92 | else if (context.align)
93 | return context.col - (closing ? context.width : 0);
94 | else
95 | return context.indent + (closing ? 0 : indentUnit);
96 | }
97 | }
98 |
99 | function parseSparql(source) {
100 | var tokens = tokenizeSparql(source);
101 | var context = null, indent = 0, col = 0;
102 | function pushContext(type, width) {
103 | context = {prev: context, indent: indent, col: col, type: type, width: width};
104 | }
105 | function popContext() {
106 | context = context.prev;
107 | }
108 |
109 | var iter = {
110 | next: function() {
111 | var token = tokens.next(), type = token.style, content = token.content, width = token.value.length;
112 |
113 | if (content == "\n") {
114 | token.indentation = indentSparql(context);
115 | indent = col = 0;
116 | if (context && context.align == null) context.align = false;
117 | }
118 | else if (type == "whitespace" && col == 0) {
119 | indent = width;
120 | }
121 | else if (type != "sp-comment" && context && context.align == null) {
122 | context.align = true;
123 | }
124 |
125 | if (content != "\n") col += width;
126 |
127 | if (/[\[\{\(]/.test(content)) {
128 | pushContext(content, width);
129 | }
130 | else if (/[\]\}\)]/.test(content)) {
131 | while (context && context.type == "pattern")
132 | popContext();
133 | if (context && content == matching[context.type])
134 | popContext();
135 | }
136 | else if (content == "." && context && context.type == "pattern") {
137 | popContext();
138 | }
139 | else if ((type == "sp-word" || type == "sp-prefixed" || type == "sp-uri" || type == "sp-var" || type == "sp-literal") &&
140 | context && /[\{\[]/.test(context.type)) {
141 | pushContext("pattern", width);
142 | }
143 |
144 | return token;
145 | },
146 |
147 | copy: function() {
148 | var _context = context, _indent = indent, _col = col, _tokenState = tokens.state;
149 | return function(source) {
150 | tokens = tokenizeSparql(source, _tokenState);
151 | context = _context;
152 | indent = _indent;
153 | col = _col;
154 | return iter;
155 | };
156 | }
157 | };
158 | return iter;
159 | }
160 |
161 | return {make: parseSparql, electricChars: "}]"};
162 | })();
163 |
--------------------------------------------------------------------------------
/example/codemirror/js/parsexml.js:
--------------------------------------------------------------------------------
1 | /* This file defines an XML parser, with a few kludges to make it
2 | * useable for HTML. autoSelfClosers defines a set of tag names that
3 | * are expected to not have a closing tag, and doNotIndent specifies
4 | * the tags inside of which no indentation should happen (see Config
5 | * object). These can be disabled by passing the editor an object like
6 | * {useHTMLKludges: false} as parserConfig option.
7 | */
8 |
9 | var XMLParser = Editor.Parser = (function() {
10 | var Kludges = {
11 | autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
12 | "meta": true, "col": true, "frame": true, "base": true, "area": true},
13 | doNotIndent: {"pre": true, "!cdata": true}
14 | };
15 | var NoKludges = {autoSelfClosers: {}, doNotIndent: {"!cdata": true}};
16 | var UseKludges = Kludges;
17 | var alignCDATA = false;
18 |
19 | // Simple stateful tokenizer for XML documents. Returns a
20 | // MochiKit-style iterator, with a state property that contains a
21 | // function encapsulating the current state. See tokenize.js.
22 | var tokenizeXML = (function() {
23 | function inText(source, setState) {
24 | var ch = source.next();
25 | if (ch == "<") {
26 | if (source.equals("!")) {
27 | source.next();
28 | if (source.equals("[")) {
29 | if (source.lookAhead("[CDATA[", true)) {
30 | setState(inBlock("xml-cdata", "]]>"));
31 | return null;
32 | }
33 | else {
34 | return "xml-text";
35 | }
36 | }
37 | else if (source.lookAhead("--", true)) {
38 | setState(inBlock("xml-comment", "-->"));
39 | return null;
40 | }
41 | else {
42 | return "xml-text";
43 | }
44 | }
45 | else if (source.equals("?")) {
46 | source.next();
47 | source.nextWhileMatches(/[\w\._\-]/);
48 | setState(inBlock("xml-processing", "?>"));
49 | return "xml-processing";
50 | }
51 | else {
52 | if (source.equals("/")) source.next();
53 | setState(inTag);
54 | return "xml-punctuation";
55 | }
56 | }
57 | else if (ch == "&") {
58 | while (!source.endOfLine()) {
59 | if (source.next() == ";")
60 | break;
61 | }
62 | return "xml-entity";
63 | }
64 | else {
65 | source.nextWhileMatches(/[^&<\n]/);
66 | return "xml-text";
67 | }
68 | }
69 |
70 | function inTag(source, setState) {
71 | var ch = source.next();
72 | if (ch == ">") {
73 | setState(inText);
74 | return "xml-punctuation";
75 | }
76 | else if (/[?\/]/.test(ch) && source.equals(">")) {
77 | source.next();
78 | setState(inText);
79 | return "xml-punctuation";
80 | }
81 | else if (ch == "=") {
82 | return "xml-punctuation";
83 | }
84 | else if (/[\'\"]/.test(ch)) {
85 | setState(inAttribute(ch));
86 | return null;
87 | }
88 | else {
89 | source.nextWhileMatches(/[^\s\u00a0=<>\"\'\/?]/);
90 | return "xml-name";
91 | }
92 | }
93 |
94 | function inAttribute(quote) {
95 | return function(source, setState) {
96 | while (!source.endOfLine()) {
97 | if (source.next() == quote) {
98 | setState(inTag);
99 | break;
100 | }
101 | }
102 | return "xml-attribute";
103 | };
104 | }
105 |
106 | function inBlock(style, terminator) {
107 | return function(source, setState) {
108 | while (!source.endOfLine()) {
109 | if (source.lookAhead(terminator, true)) {
110 | setState(inText);
111 | break;
112 | }
113 | source.next();
114 | }
115 | return style;
116 | };
117 | }
118 |
119 | return function(source, startState) {
120 | return tokenizer(source, startState || inText);
121 | };
122 | })();
123 |
124 | // The parser. The structure of this function largely follows that of
125 | // parseJavaScript in parsejavascript.js (there is actually a bit more
126 | // shared code than I'd like), but it is quite a bit simpler.
127 | function parseXML(source) {
128 | var tokens = tokenizeXML(source), token;
129 | var cc = [base];
130 | var tokenNr = 0, indented = 0;
131 | var currentTag = null, context = null;
132 | var consume;
133 |
134 | function push(fs) {
135 | for (var i = fs.length - 1; i >= 0; i--)
136 | cc.push(fs[i]);
137 | }
138 | function cont() {
139 | push(arguments);
140 | consume = true;
141 | }
142 | function pass() {
143 | push(arguments);
144 | consume = false;
145 | }
146 |
147 | function markErr() {
148 | token.style += " xml-error";
149 | }
150 | function expect(text) {
151 | return function(style, content) {
152 | if (content == text) cont();
153 | else {markErr(); cont(arguments.callee);}
154 | };
155 | }
156 |
157 | function pushContext(tagname, startOfLine) {
158 | var noIndent = UseKludges.doNotIndent.hasOwnProperty(tagname) || (context && context.noIndent);
159 | context = {prev: context, name: tagname, indent: indented, startOfLine: startOfLine, noIndent: noIndent};
160 | }
161 | function popContext() {
162 | context = context.prev;
163 | }
164 | function computeIndentation(baseContext) {
165 | return function(nextChars, current) {
166 | var context = baseContext;
167 | if (context && context.noIndent)
168 | return current;
169 | if (alignCDATA && /"));
189 | else if (style == "xml-cdata") {
190 | if (!context || context.name != "!cdata") pushContext("!cdata");
191 | if (/\]\]>$/.test(content)) popContext();
192 | cont();
193 | }
194 | else if (harmlessTokens.hasOwnProperty(style)) cont();
195 | else {markErr(); cont();}
196 | }
197 | function tagname(style, content) {
198 | if (style == "xml-name") {
199 | currentTag = content.toLowerCase();
200 | token.style = "xml-tagname";
201 | cont();
202 | }
203 | else {
204 | currentTag = null;
205 | pass();
206 | }
207 | }
208 | function closetagname(style, content) {
209 | if (style == "xml-name") {
210 | token.style = "xml-tagname";
211 | if (context && content.toLowerCase() == context.name) popContext();
212 | else markErr();
213 | }
214 | cont();
215 | }
216 | function endtag(startOfLine) {
217 | return function(style, content) {
218 | if (content == "/>" || (content == ">" && UseKludges.autoSelfClosers.hasOwnProperty(currentTag))) cont();
219 | else if (content == ">") {pushContext(currentTag, startOfLine); cont();}
220 | else {markErr(); cont(arguments.callee);}
221 | };
222 | }
223 | function attributes(style) {
224 | if (style == "xml-name") {token.style = "xml-attname"; cont(attribute, attributes);}
225 | else pass();
226 | }
227 | function attribute(style, content) {
228 | if (content == "=") cont(value);
229 | else if (content == ">" || content == "/>") pass(endtag);
230 | else pass();
231 | }
232 | function value(style) {
233 | if (style == "xml-attribute") cont(value);
234 | else pass();
235 | }
236 |
237 | return {
238 | indentation: function() {return indented;},
239 |
240 | next: function(){
241 | token = tokens.next();
242 | if (token.style == "whitespace" && tokenNr == 0)
243 | indented = token.value.length;
244 | else
245 | tokenNr++;
246 | if (token.content == "\n") {
247 | indented = tokenNr = 0;
248 | token.indentation = computeIndentation(context);
249 | }
250 |
251 | if (token.style == "whitespace" || token.type == "xml-comment")
252 | return token;
253 |
254 | while(true){
255 | consume = false;
256 | cc.pop()(token.style, token.content);
257 | if (consume) return token;
258 | }
259 | },
260 |
261 | copy: function(){
262 | var _cc = cc.concat([]), _tokenState = tokens.state, _context = context;
263 | var parser = this;
264 |
265 | return function(input){
266 | cc = _cc.concat([]);
267 | tokenNr = indented = 0;
268 | context = _context;
269 | tokens = tokenizeXML(input, _tokenState);
270 | return parser;
271 | };
272 | }
273 | };
274 | }
275 |
276 | return {
277 | make: parseXML,
278 | electricChars: "/",
279 | configure: function(config) {
280 | if (config.useHTMLKludges != null)
281 | UseKludges = config.useHTMLKludges ? Kludges : NoKludges;
282 | if (config.alignCDATA)
283 | alignCDATA = config.alignCDATA;
284 | }
285 | };
286 | })();
287 |
--------------------------------------------------------------------------------
/example/codemirror/js/select.js:
--------------------------------------------------------------------------------
1 | /* Functionality for finding, storing, and restoring selections
2 | *
3 | * This does not provide a generic API, just the minimal functionality
4 | * required by the CodeMirror system.
5 | */
6 |
7 | // Namespace object.
8 | var select = {};
9 |
10 | (function() {
11 | select.ie_selection = document.selection && document.selection.createRangeCollection;
12 |
13 | // Find the 'top-level' (defined as 'a direct child of the node
14 | // passed as the top argument') node that the given node is
15 | // contained in. Return null if the given node is not inside the top
16 | // node.
17 | function topLevelNodeAt(node, top) {
18 | while (node && node.parentNode != top)
19 | node = node.parentNode;
20 | return node;
21 | }
22 |
23 | // Find the top-level node that contains the node before this one.
24 | function topLevelNodeBefore(node, top) {
25 | while (!node.previousSibling && node.parentNode != top)
26 | node = node.parentNode;
27 | return topLevelNodeAt(node.previousSibling, top);
28 | }
29 |
30 | var fourSpaces = "\u00a0\u00a0\u00a0\u00a0";
31 |
32 | select.scrollToNode = function(node, cursor) {
33 | if (!node) return;
34 | var element = node,
35 | doc = element.ownerDocument, body = doc.body,
36 | win = (doc.defaultView || doc.parentWindow),
37 | html = doc.documentElement,
38 | atEnd = !element.nextSibling || !element.nextSibling.nextSibling
39 | || !element.nextSibling.nextSibling.nextSibling;
40 | // In Opera (and recent Webkit versions), BR elements *always*
41 | // have a offsetTop property of zero.
42 | var compensateHack = 0;
43 | while (element && !element.offsetTop) {
44 | compensateHack++;
45 | element = element.previousSibling;
46 | }
47 | // atEnd is another kludge for these browsers -- if the cursor is
48 | // at the end of the document, and the node doesn't have an
49 | // offset, just scroll to the end.
50 | if (compensateHack == 0) atEnd = false;
51 |
52 | // WebKit has a bad habit of (sometimes) happily returning bogus
53 | // offsets when the document has just been changed. This seems to
54 | // always be 5/5, so we don't use those.
55 | if (webkit && element && element.offsetTop == 5 && element.offsetLeft == 5)
56 | return;
57 |
58 | var y = compensateHack * (element ? element.offsetHeight : 0), x = 0,
59 | width = (node ? node.offsetWidth : 0), pos = element;
60 | while (pos && pos.offsetParent) {
61 | y += pos.offsetTop;
62 | // Don't count X offset for
nodes
63 | if (!isBR(pos))
64 | x += pos.offsetLeft;
65 | pos = pos.offsetParent;
66 | }
67 |
68 | var scroll_x = body.scrollLeft || html.scrollLeft || 0,
69 | scroll_y = body.scrollTop || html.scrollTop || 0,
70 | scroll = false, screen_width = win.innerWidth || html.clientWidth || 0;
71 |
72 | if (cursor || width < screen_width) {
73 | if (cursor) {
74 | var off = select.offsetInNode(win, node), size = nodeText(node).length;
75 | if (size) x += width * (off / size);
76 | }
77 | var screen_x = x - scroll_x;
78 | if (screen_x < 0 || screen_x > screen_width) {
79 | scroll_x = x;
80 | scroll = true;
81 | }
82 | }
83 | var screen_y = y - scroll_y;
84 | if (screen_y < 0 || atEnd || screen_y > (win.innerHeight || html.clientHeight || 0) - 50) {
85 | scroll_y = atEnd ? 1e6 : y;
86 | scroll = true;
87 | }
88 | if (scroll) win.scrollTo(scroll_x, scroll_y);
89 | };
90 |
91 | select.scrollToCursor = function(container) {
92 | select.scrollToNode(select.selectionTopNode(container, true) || container.firstChild, true);
93 | };
94 |
95 | // Used to prevent restoring a selection when we do not need to.
96 | var currentSelection = null;
97 |
98 | select.snapshotChanged = function() {
99 | if (currentSelection) currentSelection.changed = true;
100 | };
101 |
102 | // This is called by the code in editor.js whenever it is replacing
103 | // a text node. The function sees whether the given oldNode is part
104 | // of the current selection, and updates this selection if it is.
105 | // Because nodes are often only partially replaced, the length of
106 | // the part that gets replaced has to be taken into account -- the
107 | // selection might stay in the oldNode if the newNode is smaller
108 | // than the selection's offset. The offset argument is needed in
109 | // case the selection does move to the new object, and the given
110 | // length is not the whole length of the new node (part of it might
111 | // have been used to replace another node).
112 | select.snapshotReplaceNode = function(from, to, length, offset) {
113 | if (!currentSelection) return;
114 |
115 | function replace(point) {
116 | if (from == point.node) {
117 | currentSelection.changed = true;
118 | if (length && point.offset > length) {
119 | point.offset -= length;
120 | }
121 | else {
122 | point.node = to;
123 | point.offset += (offset || 0);
124 | }
125 | }
126 | }
127 | replace(currentSelection.start);
128 | replace(currentSelection.end);
129 | };
130 |
131 | select.snapshotMove = function(from, to, distance, relative, ifAtStart) {
132 | if (!currentSelection) return;
133 |
134 | function move(point) {
135 | if (from == point.node && (!ifAtStart || point.offset == 0)) {
136 | currentSelection.changed = true;
137 | point.node = to;
138 | if (relative) point.offset = Math.max(0, point.offset + distance);
139 | else point.offset = distance;
140 | }
141 | }
142 | move(currentSelection.start);
143 | move(currentSelection.end);
144 | };
145 |
146 | // Most functions are defined in two ways, one for the IE selection
147 | // model, one for the W3C one.
148 | if (select.ie_selection) {
149 | function selectionNode(win, start) {
150 | var range = win.document.selection.createRange();
151 | range.collapse(start);
152 |
153 | function nodeAfter(node) {
154 | var found = null;
155 | while (!found && node) {
156 | found = node.nextSibling;
157 | node = node.parentNode;
158 | }
159 | return nodeAtStartOf(found);
160 | }
161 |
162 | function nodeAtStartOf(node) {
163 | while (node && node.firstChild) node = node.firstChild;
164 | return {node: node, offset: 0};
165 | }
166 |
167 | var containing = range.parentElement();
168 | if (!isAncestor(win.document.body, containing)) return null;
169 | if (!containing.firstChild) return nodeAtStartOf(containing);
170 |
171 | var working = range.duplicate();
172 | working.moveToElementText(containing);
173 | working.collapse(true);
174 | for (var cur = containing.firstChild; cur; cur = cur.nextSibling) {
175 | if (cur.nodeType == 3) {
176 | var size = cur.nodeValue.length;
177 | working.move("character", size);
178 | }
179 | else {
180 | working.moveToElementText(cur);
181 | working.collapse(false);
182 | }
183 |
184 | var dir = range.compareEndPoints("StartToStart", working);
185 | if (dir == 0) return nodeAfter(cur);
186 | if (dir == 1) continue;
187 | if (cur.nodeType != 3) return nodeAtStartOf(cur);
188 |
189 | working.setEndPoint("StartToEnd", range);
190 | return {node: cur, offset: size - working.text.length};
191 | }
192 | return nodeAfter(containing);
193 | }
194 |
195 | select.markSelection = function(win) {
196 | currentSelection = null;
197 | var sel = win.document.selection;
198 | if (!sel) return;
199 | var start = selectionNode(win, true),
200 | end = selectionNode(win, false);
201 | if (!start || !end) return;
202 | currentSelection = {start: start, end: end, window: win, changed: false};
203 | };
204 |
205 | select.selectMarked = function() {
206 | if (!currentSelection || !currentSelection.changed) return;
207 | var win = currentSelection.window, doc = win.document;
208 |
209 | function makeRange(point) {
210 | var range = doc.body.createTextRange(),
211 | node = point.node;
212 | if (!node) {
213 | range.moveToElementText(currentSelection.window.document.body);
214 | range.collapse(false);
215 | }
216 | else if (node.nodeType == 3) {
217 | range.moveToElementText(node.parentNode);
218 | var offset = point.offset;
219 | while (node.previousSibling) {
220 | node = node.previousSibling;
221 | offset += (node.innerText || "").length;
222 | }
223 | range.move("character", offset);
224 | }
225 | else {
226 | range.moveToElementText(node);
227 | range.collapse(true);
228 | }
229 | return range;
230 | }
231 |
232 | var start = makeRange(currentSelection.start), end = makeRange(currentSelection.end);
233 | start.setEndPoint("StartToEnd", end);
234 | start.select();
235 | };
236 |
237 | select.offsetInNode = function(win, node) {
238 | var sel = win.document.selection;
239 | if (!sel) return 0;
240 | var range = sel.createRange(), range2 = range.duplicate();
241 | try {range2.moveToElementText(node);} catch(e){return 0;}
242 | range.setEndPoint("StartToStart", range2);
243 | return range.text.length;
244 | };
245 |
246 | // Get the top-level node that one end of the cursor is inside or
247 | // after. Note that this returns false for 'no cursor', and null
248 | // for 'start of document'.
249 | select.selectionTopNode = function(container, start) {
250 | var selection = container.ownerDocument.selection;
251 | if (!selection) return false;
252 |
253 | var range = selection.createRange(), range2 = range.duplicate();
254 | range.collapse(start);
255 | var around = range.parentElement();
256 | if (around && isAncestor(container, around)) {
257 | // Only use this node if the selection is not at its start.
258 | range2.moveToElementText(around);
259 | if (range.compareEndPoints("StartToStart", range2) == 1)
260 | return topLevelNodeAt(around, container);
261 | }
262 |
263 | // Move the start of a range to the start of a node,
264 | // compensating for the fact that you can't call
265 | // moveToElementText with text nodes.
266 | function moveToNodeStart(range, node) {
267 | if (node.nodeType == 3) {
268 | var count = 0, cur = node.previousSibling;
269 | while (cur && cur.nodeType == 3) {
270 | count += cur.nodeValue.length;
271 | cur = cur.previousSibling;
272 | }
273 | if (cur) {
274 | try{range.moveToElementText(cur);}
275 | catch(e){return false;}
276 | range.collapse(false);
277 | }
278 | else range.moveToElementText(node.parentNode);
279 | if (count) range.move("character", count);
280 | }
281 | else {
282 | try{range.moveToElementText(node);}
283 | catch(e){return false;}
284 | }
285 | return true;
286 | }
287 |
288 | // Do a binary search through the container object, comparing
289 | // the start of each node to the selection
290 | var start = 0, end = container.childNodes.length - 1;
291 | while (start < end) {
292 | var middle = Math.ceil((end + start) / 2), node = container.childNodes[middle];
293 | if (!node) return false; // Don't ask. IE6 manages this sometimes.
294 | if (!moveToNodeStart(range2, node)) return false;
295 | if (range.compareEndPoints("StartToStart", range2) == 1)
296 | start = middle;
297 | else
298 | end = middle - 1;
299 | }
300 | return container.childNodes[start] || null;
301 | };
302 |
303 | // Place the cursor after this.start. This is only useful when
304 | // manually moving the cursor instead of restoring it to its old
305 | // position.
306 | select.focusAfterNode = function(node, container) {
307 | var range = container.ownerDocument.body.createTextRange();
308 | range.moveToElementText(node || container);
309 | range.collapse(!node);
310 | range.select();
311 | };
312 |
313 | select.somethingSelected = function(win) {
314 | var sel = win.document.selection;
315 | return sel && (sel.createRange().text != "");
316 | };
317 |
318 | function insertAtCursor(window, html) {
319 | var selection = window.document.selection;
320 | if (selection) {
321 | var range = selection.createRange();
322 | range.pasteHTML(html);
323 | range.collapse(false);
324 | range.select();
325 | }
326 | }
327 |
328 | // Used to normalize the effect of the enter key, since browsers
329 | // do widely different things when pressing enter in designMode.
330 | select.insertNewlineAtCursor = function(window) {
331 | insertAtCursor(window, "
");
332 | };
333 |
334 | select.insertTabAtCursor = function(window) {
335 | insertAtCursor(window, fourSpaces);
336 | };
337 |
338 | // Get the BR node at the start of the line on which the cursor
339 | // currently is, and the offset into the line. Returns null as
340 | // node if cursor is on first line.
341 | select.cursorPos = function(container, start) {
342 | var selection = container.ownerDocument.selection;
343 | if (!selection) return null;
344 |
345 | var topNode = select.selectionTopNode(container, start);
346 | while (topNode && !isBR(topNode))
347 | topNode = topNode.previousSibling;
348 |
349 | var range = selection.createRange(), range2 = range.duplicate();
350 | range.collapse(start);
351 | if (topNode) {
352 | range2.moveToElementText(topNode);
353 | range2.collapse(false);
354 | }
355 | else {
356 | // When nothing is selected, we can get all kinds of funky errors here.
357 | try { range2.moveToElementText(container); }
358 | catch (e) { return null; }
359 | range2.collapse(true);
360 | }
361 | range.setEndPoint("StartToStart", range2);
362 |
363 | return {node: topNode, offset: range.text.length};
364 | };
365 |
366 | select.setCursorPos = function(container, from, to) {
367 | function rangeAt(pos) {
368 | var range = container.ownerDocument.body.createTextRange();
369 | if (!pos.node) {
370 | range.moveToElementText(container);
371 | range.collapse(true);
372 | }
373 | else {
374 | range.moveToElementText(pos.node);
375 | range.collapse(false);
376 | }
377 | range.move("character", pos.offset);
378 | return range;
379 | }
380 |
381 | var range = rangeAt(from);
382 | if (to && to != from)
383 | range.setEndPoint("EndToEnd", rangeAt(to));
384 | range.select();
385 | }
386 |
387 | // Some hacks for storing and re-storing the selection when the editor loses and regains focus.
388 | select.getBookmark = function (container) {
389 | var from = select.cursorPos(container, true), to = select.cursorPos(container, false);
390 | if (from && to) return {from: from, to: to};
391 | };
392 |
393 | // Restore a stored selection.
394 | select.setBookmark = function(container, mark) {
395 | if (!mark) return;
396 | select.setCursorPos(container, mark.from, mark.to);
397 | };
398 | }
399 | // W3C model
400 | else {
401 | // Find the node right at the cursor, not one of its
402 | // ancestors with a suitable offset. This goes down the DOM tree
403 | // until a 'leaf' is reached (or is it *up* the DOM tree?).
404 | function innerNode(node, offset) {
405 | while (node.nodeType != 3 && !isBR(node)) {
406 | var newNode = node.childNodes[offset] || node.nextSibling;
407 | offset = 0;
408 | while (!newNode && node.parentNode) {
409 | node = node.parentNode;
410 | newNode = node.nextSibling;
411 | }
412 | node = newNode;
413 | if (!newNode) break;
414 | }
415 | return {node: node, offset: offset};
416 | }
417 |
418 | // Store start and end nodes, and offsets within these, and refer
419 | // back to the selection object from those nodes, so that this
420 | // object can be updated when the nodes are replaced before the
421 | // selection is restored.
422 | select.markSelection = function (win) {
423 | var selection = win.getSelection();
424 | if (!selection || selection.rangeCount == 0)
425 | return (currentSelection = null);
426 | var range = selection.getRangeAt(0);
427 |
428 | currentSelection = {
429 | start: innerNode(range.startContainer, range.startOffset),
430 | end: innerNode(range.endContainer, range.endOffset),
431 | window: win,
432 | changed: false
433 | };
434 | };
435 |
436 | select.selectMarked = function () {
437 | var cs = currentSelection;
438 | // on webkit-based browsers, it is apparently possible that the
439 | // selection gets reset even when a node that is not one of the
440 | // endpoints get messed with. the most common situation where
441 | // this occurs is when a selection is deleted or overwitten. we
442 | // check for that here.
443 | function focusIssue() {
444 | if (cs.start.node == cs.end.node && cs.start.offset == cs.end.offset) {
445 | var selection = cs.window.getSelection();
446 | if (!selection || selection.rangeCount == 0) return true;
447 | var range = selection.getRangeAt(0), point = innerNode(range.startContainer, range.startOffset);
448 | return cs.start.node != point.node || cs.start.offset != point.offset;
449 | }
450 | }
451 | if (!cs || !(cs.changed || (webkit && focusIssue()))) return;
452 | var win = cs.window, range = win.document.createRange();
453 |
454 | function setPoint(point, which) {
455 | if (point.node) {
456 | // Some magic to generalize the setting of the start and end
457 | // of a range.
458 | if (point.offset == 0)
459 | range["set" + which + "Before"](point.node);
460 | else
461 | range["set" + which](point.node, point.offset);
462 | }
463 | else {
464 | range.setStartAfter(win.document.body.lastChild || win.document.body);
465 | }
466 | }
467 |
468 | setPoint(cs.end, "End");
469 | setPoint(cs.start, "Start");
470 | selectRange(range, win);
471 | };
472 |
473 | // Helper for selecting a range object.
474 | function selectRange(range, window) {
475 | var selection = window.getSelection();
476 | if (!selection) return;
477 | selection.removeAllRanges();
478 | selection.addRange(range);
479 | }
480 | function selectionRange(window) {
481 | var selection = window.getSelection();
482 | if (!selection || selection.rangeCount == 0)
483 | return false;
484 | else
485 | return selection.getRangeAt(0);
486 | }
487 |
488 | // Finding the top-level node at the cursor in the W3C is, as you
489 | // can see, quite an involved process.
490 | select.selectionTopNode = function(container, start) {
491 | var range = selectionRange(container.ownerDocument.defaultView);
492 | if (!range) return false;
493 |
494 | var node = start ? range.startContainer : range.endContainer;
495 | var offset = start ? range.startOffset : range.endOffset;
496 | // Work around (yet another) bug in Opera's selection model.
497 | if (window.opera && !start && range.endContainer == container && range.endOffset == range.startOffset + 1 &&
498 | container.childNodes[range.startOffset] && isBR(container.childNodes[range.startOffset]))
499 | offset--;
500 |
501 | // For text nodes, we look at the node itself if the cursor is
502 | // inside, or at the node before it if the cursor is at the
503 | // start.
504 | if (node.nodeType == 3){
505 | if (offset > 0)
506 | return topLevelNodeAt(node, container);
507 | else
508 | return topLevelNodeBefore(node, container);
509 | }
510 | // Occasionally, browsers will return the HTML node as
511 | // selection. If the offset is 0, we take the start of the frame
512 | // ('after null'), otherwise, we take the last node.
513 | else if (node.nodeName.toUpperCase() == "HTML") {
514 | return (offset == 1 ? null : container.lastChild);
515 | }
516 | // If the given node is our 'container', we just look up the
517 | // correct node by using the offset.
518 | else if (node == container) {
519 | return (offset == 0) ? null : node.childNodes[offset - 1];
520 | }
521 | // In any other case, we have a regular node. If the cursor is
522 | // at the end of the node, we use the node itself, if it is at
523 | // the start, we use the node before it, and in any other
524 | // case, we look up the child before the cursor and use that.
525 | else {
526 | if (offset == node.childNodes.length)
527 | return topLevelNodeAt(node, container);
528 | else if (offset == 0)
529 | return topLevelNodeBefore(node, container);
530 | else
531 | return topLevelNodeAt(node.childNodes[offset - 1], container);
532 | }
533 | };
534 |
535 | select.focusAfterNode = function(node, container) {
536 | var win = container.ownerDocument.defaultView,
537 | range = win.document.createRange();
538 | range.setStartBefore(container.firstChild || container);
539 | // In Opera, setting the end of a range at the end of a line
540 | // (before a BR) will cause the cursor to appear on the next
541 | // line, so we set the end inside of the start node when
542 | // possible.
543 | if (node && !node.firstChild)
544 | range.setEndAfter(node);
545 | else if (node)
546 | range.setEnd(node, node.childNodes.length);
547 | else
548 | range.setEndBefore(container.firstChild || container);
549 | range.collapse(false);
550 | selectRange(range, win);
551 | };
552 |
553 | select.somethingSelected = function(win) {
554 | var range = selectionRange(win);
555 | return range && !range.collapsed;
556 | };
557 |
558 | select.offsetInNode = function(win, node) {
559 | var range = selectionRange(win);
560 | if (!range) return 0;
561 | range = range.cloneRange();
562 | range.setStartBefore(node);
563 | return range.toString().length;
564 | };
565 |
566 | function insertNodeAtCursor(window, node) {
567 | var range = selectionRange(window);
568 | if (!range) return;
569 |
570 | range.deleteContents();
571 | range.insertNode(node);
572 |
573 | // work around weirdness where Opera will magically insert a new
574 | // BR node when a BR node inside a span is moved around. makes
575 | // sure the BR ends up outside of spans.
576 | if (window.opera && isBR(node) && isSpan(node.parentNode)) {
577 | var next = node.nextSibling, p = node.parentNode, outer = p.parentNode;
578 | outer.insertBefore(node, p.nextSibling);
579 | var textAfter = "";
580 | for (; next && next.nodeType == 3; next = next.nextSibling) {
581 | textAfter += next.nodeValue;
582 | removeElement(next);
583 | }
584 | outer.insertBefore(makePartSpan(textAfter, window.document), node.nextSibling);
585 | }
586 | range = window.document.createRange();
587 | range.selectNode(node);
588 | range.collapse(false);
589 | selectRange(range, window);
590 | }
591 |
592 | select.insertNewlineAtCursor = function(window) {
593 | if (webkit)
594 | document.execCommand('insertLineBreak');
595 | else
596 | insertNodeAtCursor(window, window.document.createElement("BR"));
597 | };
598 |
599 | select.insertTabAtCursor = function(window) {
600 | insertNodeAtCursor(window, window.document.createTextNode(fourSpaces));
601 | };
602 |
603 | select.cursorPos = function(container, start) {
604 | var range = selectionRange(window);
605 | if (!range) return;
606 |
607 | var topNode = select.selectionTopNode(container, start);
608 | while (topNode && !isBR(topNode))
609 | topNode = topNode.previousSibling;
610 |
611 | range = range.cloneRange();
612 | range.collapse(start);
613 | if (topNode)
614 | range.setStartAfter(topNode);
615 | else
616 | range.setStartBefore(container);
617 |
618 | return {node: topNode, offset: range.toString().length};
619 | };
620 |
621 | select.setCursorPos = function(container, from, to) {
622 | var win = container.ownerDocument.defaultView,
623 | range = win.document.createRange();
624 |
625 | function setPoint(node, offset, side) {
626 | if (offset == 0 && node && !node.nextSibling) {
627 | range["set" + side + "After"](node);
628 | return true;
629 | }
630 |
631 | if (!node)
632 | node = container.firstChild;
633 | else
634 | node = node.nextSibling;
635 |
636 | if (!node) return;
637 |
638 | if (offset == 0) {
639 | range["set" + side + "Before"](node);
640 | return true;
641 | }
642 |
643 | var backlog = []
644 | function decompose(node) {
645 | if (node.nodeType == 3)
646 | backlog.push(node);
647 | else
648 | forEach(node.childNodes, decompose);
649 | }
650 | while (true) {
651 | while (node && !backlog.length) {
652 | decompose(node);
653 | node = node.nextSibling;
654 | }
655 | var cur = backlog.shift();
656 | if (!cur) return false;
657 |
658 | var length = cur.nodeValue.length;
659 | if (length >= offset) {
660 | range["set" + side](cur, offset);
661 | return true;
662 | }
663 | offset -= length;
664 | }
665 | }
666 |
667 | to = to || from;
668 | if (setPoint(to.node, to.offset, "End") && setPoint(from.node, from.offset, "Start"))
669 | selectRange(range, win);
670 | };
671 | }
672 | })();
673 |
--------------------------------------------------------------------------------
/example/codemirror/js/stringstream.js:
--------------------------------------------------------------------------------
1 | /* String streams are the things fed to parsers (which can feed them
2 | * to a tokenizer if they want). They provide peek and next methods
3 | * for looking at the current character (next 'consumes' this
4 | * character, peek does not), and a get method for retrieving all the
5 | * text that was consumed since the last time get was called.
6 | *
7 | * An easy mistake to make is to let a StopIteration exception finish
8 | * the token stream while there are still characters pending in the
9 | * string stream (hitting the end of the buffer while parsing a
10 | * token). To make it easier to detect such errors, the stringstreams
11 | * throw an exception when this happens.
12 | */
13 |
14 | // Make a stringstream stream out of an iterator that returns strings.
15 | // This is applied to the result of traverseDOM (see codemirror.js),
16 | // and the resulting stream is fed to the parser.
17 | var stringStream = function(source){
18 | // String that's currently being iterated over.
19 | var current = "";
20 | // Position in that string.
21 | var pos = 0;
22 | // Accumulator for strings that have been iterated over but not
23 | // get()-ed yet.
24 | var accum = "";
25 | // Make sure there are more characters ready, or throw
26 | // StopIteration.
27 | function ensureChars() {
28 | while (pos == current.length) {
29 | accum += current;
30 | current = ""; // In case source.next() throws
31 | pos = 0;
32 | try {current = source.next();}
33 | catch (e) {
34 | if (e != StopIteration) throw e;
35 | else return false;
36 | }
37 | }
38 | return true;
39 | }
40 |
41 | return {
42 | // Return the next character in the stream.
43 | peek: function() {
44 | if (!ensureChars()) return null;
45 | return current.charAt(pos);
46 | },
47 | // Get the next character, throw StopIteration if at end, check
48 | // for unused content.
49 | next: function() {
50 | if (!ensureChars()) {
51 | if (accum.length > 0)
52 | throw "End of stringstream reached without emptying buffer ('" + accum + "').";
53 | else
54 | throw StopIteration;
55 | }
56 | return current.charAt(pos++);
57 | },
58 | // Return the characters iterated over since the last call to
59 | // .get().
60 | get: function() {
61 | var temp = accum;
62 | accum = "";
63 | if (pos > 0){
64 | temp += current.slice(0, pos);
65 | current = current.slice(pos);
66 | pos = 0;
67 | }
68 | return temp;
69 | },
70 | // Push a string back into the stream.
71 | push: function(str) {
72 | current = current.slice(0, pos) + str + current.slice(pos);
73 | },
74 | lookAhead: function(str, consume, skipSpaces, caseInsensitive) {
75 | function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
76 | str = cased(str);
77 | var found = false;
78 |
79 | var _accum = accum, _pos = pos;
80 | if (skipSpaces) this.nextWhileMatches(/[\s\u00a0]/);
81 |
82 | while (true) {
83 | var end = pos + str.length, left = current.length - pos;
84 | if (end <= current.length) {
85 | found = str == cased(current.slice(pos, end));
86 | pos = end;
87 | break;
88 | }
89 | else if (str.slice(0, left) == cased(current.slice(pos))) {
90 | accum += current; current = "";
91 | try {current = source.next();}
92 | catch (e) {break;}
93 | pos = 0;
94 | str = str.slice(left);
95 | }
96 | else {
97 | break;
98 | }
99 | }
100 |
101 | if (!(found && consume)) {
102 | current = accum.slice(_accum.length) + current;
103 | pos = _pos;
104 | accum = _accum;
105 | }
106 |
107 | return found;
108 | },
109 |
110 | // Utils built on top of the above
111 | more: function() {
112 | return this.peek() !== null;
113 | },
114 | applies: function(test) {
115 | var next = this.peek();
116 | return (next !== null && test(next));
117 | },
118 | nextWhile: function(test) {
119 | var next;
120 | while ((next = this.peek()) !== null && test(next))
121 | this.next();
122 | },
123 | matches: function(re) {
124 | var next = this.peek();
125 | return (next !== null && re.test(next));
126 | },
127 | nextWhileMatches: function(re) {
128 | var next;
129 | while ((next = this.peek()) !== null && re.test(next))
130 | this.next();
131 | },
132 | equals: function(ch) {
133 | return ch === this.peek();
134 | },
135 | endOfLine: function() {
136 | var next = this.peek();
137 | return next == null || next == "\n";
138 | }
139 | };
140 | };
141 |
--------------------------------------------------------------------------------
/example/codemirror/js/tokenize.js:
--------------------------------------------------------------------------------
1 | // A framework for simple tokenizers. Takes care of newlines and
2 | // white-space, and of getting the text from the source stream into
3 | // the token object. A state is a function of two arguments -- a
4 | // string stream and a setState function. The second can be used to
5 | // change the tokenizer's state, and can be ignored for stateless
6 | // tokenizers. This function should advance the stream over a token
7 | // and return a string or object containing information about the next
8 | // token, or null to pass and have the (new) state be called to finish
9 | // the token. When a string is given, it is wrapped in a {style, type}
10 | // object. In the resulting object, the characters consumed are stored
11 | // under the content property. Any whitespace following them is also
12 | // automatically consumed, and added to the value property. (Thus,
13 | // content is the actual meaningful part of the token, while value
14 | // contains all the text it spans.)
15 |
16 | function tokenizer(source, state) {
17 | // Newlines are always a separate token.
18 | function isWhiteSpace(ch) {
19 | // The messy regexp is because IE's regexp matcher is of the
20 | // opinion that non-breaking spaces are no whitespace.
21 | return ch != "\n" && /^[\s\u00a0]*$/.test(ch);
22 | }
23 |
24 | var tokenizer = {
25 | state: state,
26 |
27 | take: function(type) {
28 | if (typeof(type) == "string")
29 | type = {style: type, type: type};
30 |
31 | type.content = (type.content || "") + source.get();
32 | if (!/\n$/.test(type.content))
33 | source.nextWhile(isWhiteSpace);
34 | type.value = type.content + source.get();
35 | return type;
36 | },
37 |
38 | next: function () {
39 | if (!source.more()) throw StopIteration;
40 |
41 | var type;
42 | if (source.equals("\n")) {
43 | source.next();
44 | return this.take("whitespace");
45 | }
46 |
47 | if (source.applies(isWhiteSpace))
48 | type = "whitespace";
49 | else
50 | while (!type)
51 | type = this.state(source, function(s) {tokenizer.state = s;});
52 |
53 | return this.take(type);
54 | }
55 | };
56 | return tokenizer;
57 | }
58 |
--------------------------------------------------------------------------------
/example/codemirror/js/tokenizejavascript.js:
--------------------------------------------------------------------------------
1 | /* Tokenizer for JavaScript code */
2 |
3 | var tokenizeJavaScript = (function() {
4 | // Advance the stream until the given character (not preceded by a
5 | // backslash) is encountered, or the end of the line is reached.
6 | function nextUntilUnescaped(source, end) {
7 | var escaped = false;
8 | while (!source.endOfLine()) {
9 | var next = source.next();
10 | if (next == end && !escaped)
11 | return false;
12 | escaped = !escaped && next == "\\";
13 | }
14 | return escaped;
15 | }
16 |
17 | // A map of JavaScript's keywords. The a/b/c keyword distinction is
18 | // very rough, but it gives the parser enough information to parse
19 | // correct code correctly (we don't care that much how we parse
20 | // incorrect code). The style information included in these objects
21 | // is used by the highlighter to pick the correct CSS style for a
22 | // token.
23 | var keywords = function(){
24 | function result(type, style){
25 | return {type: type, style: "js-" + style};
26 | }
27 | // keywords that take a parenthised expression, and then a
28 | // statement (if)
29 | var keywordA = result("keyword a", "keyword");
30 | // keywords that take just a statement (else)
31 | var keywordB = result("keyword b", "keyword");
32 | // keywords that optionally take an expression, and form a
33 | // statement (return)
34 | var keywordC = result("keyword c", "keyword");
35 | var operator = result("operator", "keyword");
36 | var atom = result("atom", "atom");
37 | return {
38 | "if": keywordA, "while": keywordA, "with": keywordA,
39 | "else": keywordB, "do": keywordB, "try": keywordB, "finally": keywordB,
40 | "return": keywordC, "break": keywordC, "continue": keywordC, "new": keywordC, "delete": keywordC, "throw": keywordC,
41 | "in": operator, "typeof": operator, "instanceof": operator,
42 | "var": result("var", "keyword"), "function": result("function", "keyword"), "catch": result("catch", "keyword"),
43 | "for": result("for", "keyword"), "switch": result("switch", "keyword"),
44 | "case": result("case", "keyword"), "default": result("default", "keyword"),
45 | "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
46 | };
47 | }();
48 |
49 | // Some helper regexps
50 | var isOperatorChar = /[+\-*&%=<>!?|]/;
51 | var isHexDigit = /[0-9A-Fa-f]/;
52 | var isWordChar = /[\w\$_]/;
53 |
54 | // Wrapper around jsToken that helps maintain parser state (whether
55 | // we are inside of a multi-line comment and whether the next token
56 | // could be a regular expression).
57 | function jsTokenState(inside, regexp) {
58 | return function(source, setState) {
59 | var newInside = inside;
60 | var type = jsToken(inside, regexp, source, function(c) {newInside = c;});
61 | var newRegexp = type.type == "operator" || type.type == "keyword c" || type.type.match(/^[\[{}\(,;:]$/);
62 | if (newRegexp != regexp || newInside != inside)
63 | setState(jsTokenState(newInside, newRegexp));
64 | return type;
65 | };
66 | }
67 |
68 | // The token reader, intended to be used by the tokenizer from
69 | // tokenize.js (through jsTokenState). Advances the source stream
70 | // over a token, and returns an object containing the type and style
71 | // of that token.
72 | function jsToken(inside, regexp, source, setInside) {
73 | function readHexNumber(){
74 | source.next(); // skip the 'x'
75 | source.nextWhileMatches(isHexDigit);
76 | return {type: "number", style: "js-atom"};
77 | }
78 |
79 | function readNumber() {
80 | source.nextWhileMatches(/[0-9]/);
81 | if (source.equals(".")){
82 | source.next();
83 | source.nextWhileMatches(/[0-9]/);
84 | }
85 | if (source.equals("e") || source.equals("E")){
86 | source.next();
87 | if (source.equals("-"))
88 | source.next();
89 | source.nextWhileMatches(/[0-9]/);
90 | }
91 | return {type: "number", style: "js-atom"};
92 | }
93 | // Read a word, look it up in keywords. If not found, it is a
94 | // variable, otherwise it is a keyword of the type found.
95 | function readWord() {
96 | source.nextWhileMatches(isWordChar);
97 | var word = source.get();
98 | var known = keywords.hasOwnProperty(word) && keywords.propertyIsEnumerable(word) && keywords[word];
99 | return known ? {type: known.type, style: known.style, content: word} :
100 | {type: "variable", style: "js-variable", content: word};
101 | }
102 | function readRegexp() {
103 | nextUntilUnescaped(source, "/");
104 | source.nextWhileMatches(/[gi]/);
105 | return {type: "regexp", style: "js-string"};
106 | }
107 | // Mutli-line comments are tricky. We want to return the newlines
108 | // embedded in them as regular newline tokens, and then continue
109 | // returning a comment token for every line of the comment. So
110 | // some state has to be saved (inside) to indicate whether we are
111 | // inside a /* */ sequence.
112 | function readMultilineComment(start){
113 | var newInside = "/*";
114 | var maybeEnd = (start == "*");
115 | while (true) {
116 | if (source.endOfLine())
117 | break;
118 | var next = source.next();
119 | if (next == "/" && maybeEnd){
120 | newInside = null;
121 | break;
122 | }
123 | maybeEnd = (next == "*");
124 | }
125 | setInside(newInside);
126 | return {type: "comment", style: "js-comment"};
127 | }
128 | function readOperator() {
129 | source.nextWhileMatches(isOperatorChar);
130 | return {type: "operator", style: "js-operator"};
131 | }
132 | function readString(quote) {
133 | var endBackSlash = nextUntilUnescaped(source, quote);
134 | setInside(endBackSlash ? quote : null);
135 | return {type: "string", style: "js-string"};
136 | }
137 |
138 | // Fetch the next token. Dispatches on first character in the
139 | // stream, or first two characters when the first is a slash.
140 | if (inside == "\"" || inside == "'")
141 | return readString(inside);
142 | var ch = source.next();
143 | if (inside == "/*")
144 | return readMultilineComment(ch);
145 | else if (ch == "\"" || ch == "'")
146 | return readString(ch);
147 | // with punctuation, the type of the token is the symbol itself
148 | else if (/[\[\]{}\(\),;\:\.]/.test(ch))
149 | return {type: ch, style: "js-punctuation"};
150 | else if (ch == "0" && (source.equals("x") || source.equals("X")))
151 | return readHexNumber();
152 | else if (/[0-9]/.test(ch))
153 | return readNumber();
154 | else if (ch == "/"){
155 | if (source.equals("*"))
156 | { source.next(); return readMultilineComment(ch); }
157 | else if (source.equals("/"))
158 | { nextUntilUnescaped(source, null); return {type: "comment", style: "js-comment"};}
159 | else if (regexp)
160 | return readRegexp();
161 | else
162 | return readOperator();
163 | }
164 | else if (isOperatorChar.test(ch))
165 | return readOperator();
166 | else
167 | return readWord();
168 | }
169 |
170 | // The external interface to the tokenizer.
171 | return function(source, startState) {
172 | return tokenizer(source, startState || jsTokenState(false, true));
173 | };
174 | })();
175 |
--------------------------------------------------------------------------------
/example/codemirror/js/undo.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Storage and control for undo information within a CodeMirror
3 | * editor. 'Why on earth is such a complicated mess required for
4 | * that?', I hear you ask. The goal, in implementing this, was to make
5 | * the complexity of storing and reverting undo information depend
6 | * only on the size of the edited or restored content, not on the size
7 | * of the whole document. This makes it necessary to use a kind of
8 | * 'diff' system, which, when applied to a DOM tree, causes some
9 | * complexity and hackery.
10 | *
11 | * In short, the editor 'touches' BR elements as it parses them, and
12 | * the UndoHistory stores these. When nothing is touched in commitDelay
13 | * milliseconds, the changes are committed: It goes over all touched
14 | * nodes, throws out the ones that did not change since last commit or
15 | * are no longer in the document, and assembles the rest into zero or
16 | * more 'chains' -- arrays of adjacent lines. Links back to these
17 | * chains are added to the BR nodes, while the chain that previously
18 | * spanned these nodes is added to the undo history. Undoing a change
19 | * means taking such a chain off the undo history, restoring its
20 | * content (text is saved per line) and linking it back into the
21 | * document.
22 | */
23 |
24 | // A history object needs to know about the DOM container holding the
25 | // document, the maximum amount of undo levels it should store, the
26 | // delay (of no input) after which it commits a set of changes, and,
27 | // unfortunately, the 'parent' window -- a window that is not in
28 | // designMode, and on which setTimeout works in every browser.
29 | function UndoHistory(container, maxDepth, commitDelay, editor) {
30 | this.container = container;
31 | this.maxDepth = maxDepth; this.commitDelay = commitDelay;
32 | this.editor = editor; this.parent = editor.parent;
33 | // This line object represents the initial, empty editor.
34 | var initial = {text: "", from: null, to: null};
35 | // As the borders between lines are represented by BR elements, the
36 | // start of the first line and the end of the last one are
37 | // represented by null. Since you can not store any properties
38 | // (links to line objects) in null, these properties are used in
39 | // those cases.
40 | this.first = initial; this.last = initial;
41 | // Similarly, a 'historyTouched' property is added to the BR in
42 | // front of lines that have already been touched, and 'firstTouched'
43 | // is used for the first line.
44 | this.firstTouched = false;
45 | // History is the set of committed changes, touched is the set of
46 | // nodes touched since the last commit.
47 | this.history = []; this.redoHistory = []; this.touched = [];
48 | }
49 |
50 | UndoHistory.prototype = {
51 | // Schedule a commit (if no other touches come in for commitDelay
52 | // milliseconds).
53 | scheduleCommit: function() {
54 | var self = this;
55 | this.parent.clearTimeout(this.commitTimeout);
56 | this.commitTimeout = this.parent.setTimeout(function(){self.tryCommit();}, this.commitDelay);
57 | },
58 |
59 | // Mark a node as touched. Null is a valid argument.
60 | touch: function(node) {
61 | this.setTouched(node);
62 | this.scheduleCommit();
63 | },
64 |
65 | // Undo the last change.
66 | undo: function() {
67 | // Make sure pending changes have been committed.
68 | this.commit();
69 |
70 | if (this.history.length) {
71 | // Take the top diff from the history, apply it, and store its
72 | // shadow in the redo history.
73 | var item = this.history.pop();
74 | this.redoHistory.push(this.updateTo(item, "applyChain"));
75 | this.notifyEnvironment();
76 | return this.chainNode(item);
77 | }
78 | },
79 |
80 | // Redo the last undone change.
81 | redo: function() {
82 | this.commit();
83 | if (this.redoHistory.length) {
84 | // The inverse of undo, basically.
85 | var item = this.redoHistory.pop();
86 | this.addUndoLevel(this.updateTo(item, "applyChain"));
87 | this.notifyEnvironment();
88 | return this.chainNode(item);
89 | }
90 | },
91 |
92 | clear: function() {
93 | this.history = [];
94 | this.redoHistory = [];
95 | },
96 |
97 | // Ask for the size of the un/redo histories.
98 | historySize: function() {
99 | return {undo: this.history.length, redo: this.redoHistory.length};
100 | },
101 |
102 | // Push a changeset into the document.
103 | push: function(from, to, lines) {
104 | var chain = [];
105 | for (var i = 0; i < lines.length; i++) {
106 | var end = (i == lines.length - 1) ? to : this.container.ownerDocument.createElement("BR");
107 | chain.push({from: from, to: end, text: cleanText(lines[i])});
108 | from = end;
109 | }
110 | this.pushChains([chain], from == null && to == null);
111 | this.notifyEnvironment();
112 | },
113 |
114 | pushChains: function(chains, doNotHighlight) {
115 | this.commit(doNotHighlight);
116 | this.addUndoLevel(this.updateTo(chains, "applyChain"));
117 | this.redoHistory = [];
118 | },
119 |
120 | // Retrieve a DOM node from a chain (for scrolling to it after undo/redo).
121 | chainNode: function(chains) {
122 | for (var i = 0; i < chains.length; i++) {
123 | var start = chains[i][0], node = start && (start.from || start.to);
124 | if (node) return node;
125 | }
126 | },
127 |
128 | // Clear the undo history, make the current document the start
129 | // position.
130 | reset: function() {
131 | this.history = []; this.redoHistory = [];
132 | },
133 |
134 | textAfter: function(br) {
135 | return this.after(br).text;
136 | },
137 |
138 | nodeAfter: function(br) {
139 | return this.after(br).to;
140 | },
141 |
142 | nodeBefore: function(br) {
143 | return this.before(br).from;
144 | },
145 |
146 | // Commit unless there are pending dirty nodes.
147 | tryCommit: function() {
148 | if (!window.UndoHistory) return; // Stop when frame has been unloaded
149 | if (this.editor.highlightDirty()) this.commit(true);
150 | else this.scheduleCommit();
151 | },
152 |
153 | // Check whether the touched nodes hold any changes, if so, commit
154 | // them.
155 | commit: function(doNotHighlight) {
156 | this.parent.clearTimeout(this.commitTimeout);
157 | // Make sure there are no pending dirty nodes.
158 | if (!doNotHighlight) this.editor.highlightDirty(true);
159 | // Build set of chains.
160 | var chains = this.touchedChains(), self = this;
161 |
162 | if (chains.length) {
163 | this.addUndoLevel(this.updateTo(chains, "linkChain"));
164 | this.redoHistory = [];
165 | this.notifyEnvironment();
166 | }
167 | },
168 |
169 | // [ end of public interface ]
170 |
171 | // Update the document with a given set of chains, return its
172 | // shadow. updateFunc should be "applyChain" or "linkChain". In the
173 | // second case, the chains are taken to correspond the the current
174 | // document, and only the state of the line data is updated. In the
175 | // first case, the content of the chains is also pushed iinto the
176 | // document.
177 | updateTo: function(chains, updateFunc) {
178 | var shadows = [], dirty = [];
179 | for (var i = 0; i < chains.length; i++) {
180 | shadows.push(this.shadowChain(chains[i]));
181 | dirty.push(this[updateFunc](chains[i]));
182 | }
183 | if (updateFunc == "applyChain")
184 | this.notifyDirty(dirty);
185 | return shadows;
186 | },
187 |
188 | // Notify the editor that some nodes have changed.
189 | notifyDirty: function(nodes) {
190 | forEach(nodes, method(this.editor, "addDirtyNode"))
191 | this.editor.scheduleHighlight();
192 | },
193 |
194 | notifyEnvironment: function() {
195 | if (this.onChange) this.onChange();
196 | // Used by the line-wrapping line-numbering code.
197 | if (window.frameElement && window.frameElement.CodeMirror.updateNumbers)
198 | window.frameElement.CodeMirror.updateNumbers();
199 | },
200 |
201 | // Link a chain into the DOM nodes (or the first/last links for null
202 | // nodes).
203 | linkChain: function(chain) {
204 | for (var i = 0; i < chain.length; i++) {
205 | var line = chain[i];
206 | if (line.from) line.from.historyAfter = line;
207 | else this.first = line;
208 | if (line.to) line.to.historyBefore = line;
209 | else this.last = line;
210 | }
211 | },
212 |
213 | // Get the line object after/before a given node.
214 | after: function(node) {
215 | return node ? node.historyAfter : this.first;
216 | },
217 | before: function(node) {
218 | return node ? node.historyBefore : this.last;
219 | },
220 |
221 | // Mark a node as touched if it has not already been marked.
222 | setTouched: function(node) {
223 | if (node) {
224 | if (!node.historyTouched) {
225 | this.touched.push(node);
226 | node.historyTouched = true;
227 | }
228 | }
229 | else {
230 | this.firstTouched = true;
231 | }
232 | },
233 |
234 | // Store a new set of undo info, throw away info if there is more of
235 | // it than allowed.
236 | addUndoLevel: function(diffs) {
237 | this.history.push(diffs);
238 | if (this.history.length > this.maxDepth)
239 | this.history.shift();
240 | },
241 |
242 | // Build chains from a set of touched nodes.
243 | touchedChains: function() {
244 | var self = this;
245 |
246 | // The temp system is a crummy hack to speed up determining
247 | // whether a (currently touched) node has a line object associated
248 | // with it. nullTemp is used to store the object for the first
249 | // line, other nodes get it stored in their historyTemp property.
250 | var nullTemp = null;
251 | function temp(node) {return node ? node.historyTemp : nullTemp;}
252 | function setTemp(node, line) {
253 | if (node) node.historyTemp = line;
254 | else nullTemp = line;
255 | }
256 |
257 | function buildLine(node) {
258 | var text = [];
259 | for (var cur = node ? node.nextSibling : self.container.firstChild;
260 | cur && !isBR(cur); cur = cur.nextSibling)
261 | if (cur.currentText) text.push(cur.currentText);
262 | return {from: node, to: cur, text: cleanText(text.join(""))};
263 | }
264 |
265 | // Filter out unchanged lines and nodes that are no longer in the
266 | // document. Build up line objects for remaining nodes.
267 | var lines = [];
268 | if (self.firstTouched) self.touched.push(null);
269 | forEach(self.touched, function(node) {
270 | if (node && node.parentNode != self.container) return;
271 |
272 | if (node) node.historyTouched = false;
273 | else self.firstTouched = false;
274 |
275 | var line = buildLine(node), shadow = self.after(node);
276 | if (!shadow || shadow.text != line.text || shadow.to != line.to) {
277 | lines.push(line);
278 | setTemp(node, line);
279 | }
280 | });
281 |
282 | // Get the BR element after/before the given node.
283 | function nextBR(node, dir) {
284 | var link = dir + "Sibling", search = node[link];
285 | while (search && !isBR(search))
286 | search = search[link];
287 | return search;
288 | }
289 |
290 | // Assemble line objects into chains by scanning the DOM tree
291 | // around them.
292 | var chains = []; self.touched = [];
293 | forEach(lines, function(line) {
294 | // Note that this makes the loop skip line objects that have
295 | // been pulled into chains by lines before them.
296 | if (!temp(line.from)) return;
297 |
298 | var chain = [], curNode = line.from, safe = true;
299 | // Put any line objects (referred to by temp info) before this
300 | // one on the front of the array.
301 | while (true) {
302 | var curLine = temp(curNode);
303 | if (!curLine) {
304 | if (safe) break;
305 | else curLine = buildLine(curNode);
306 | }
307 | chain.unshift(curLine);
308 | setTemp(curNode, null);
309 | if (!curNode) break;
310 | safe = self.after(curNode);
311 | curNode = nextBR(curNode, "previous");
312 | }
313 | curNode = line.to; safe = self.before(line.from);
314 | // Add lines after this one at end of array.
315 | while (true) {
316 | if (!curNode) break;
317 | var curLine = temp(curNode);
318 | if (!curLine) {
319 | if (safe) break;
320 | else curLine = buildLine(curNode);
321 | }
322 | chain.push(curLine);
323 | setTemp(curNode, null);
324 | safe = self.before(curNode);
325 | curNode = nextBR(curNode, "next");
326 | }
327 | chains.push(chain);
328 | });
329 |
330 | return chains;
331 | },
332 |
333 | // Find the 'shadow' of a given chain by following the links in the
334 | // DOM nodes at its start and end.
335 | shadowChain: function(chain) {
336 | var shadows = [], next = this.after(chain[0].from), end = chain[chain.length - 1].to;
337 | while (true) {
338 | shadows.push(next);
339 | var nextNode = next.to;
340 | if (!nextNode || nextNode == end)
341 | break;
342 | else
343 | next = nextNode.historyAfter || this.before(end);
344 | // (The this.before(end) is a hack -- FF sometimes removes
345 | // properties from BR nodes, in which case the best we can hope
346 | // for is to not break.)
347 | }
348 | return shadows;
349 | },
350 |
351 | // Update the DOM tree to contain the lines specified in a given
352 | // chain, link this chain into the DOM nodes.
353 | applyChain: function(chain) {
354 | // Some attempt is made to prevent the cursor from jumping
355 | // randomly when an undo or redo happens. It still behaves a bit
356 | // strange sometimes.
357 | var cursor = select.cursorPos(this.container, false), self = this;
358 |
359 | // Remove all nodes in the DOM tree between from and to (null for
360 | // start/end of container).
361 | function removeRange(from, to) {
362 | var pos = from ? from.nextSibling : self.container.firstChild;
363 | while (pos != to) {
364 | var temp = pos.nextSibling;
365 | removeElement(pos);
366 | pos = temp;
367 | }
368 | }
369 |
370 | var start = chain[0].from, end = chain[chain.length - 1].to;
371 | // Clear the space where this change has to be made.
372 | removeRange(start, end);
373 |
374 | // Insert the content specified by the chain into the DOM tree.
375 | for (var i = 0; i < chain.length; i++) {
376 | var line = chain[i];
377 | // The start and end of the space are already correct, but BR
378 | // tags inside it have to be put back.
379 | if (i > 0)
380 | self.container.insertBefore(line.from, end);
381 |
382 | // Add the text.
383 | var node = makePartSpan(fixSpaces(line.text), this.container.ownerDocument);
384 | self.container.insertBefore(node, end);
385 | // See if the cursor was on this line. Put it back, adjusting
386 | // for changed line length, if it was.
387 | if (cursor && cursor.node == line.from) {
388 | var cursordiff = 0;
389 | var prev = this.after(line.from);
390 | if (prev && i == chain.length - 1) {
391 | // Only adjust if the cursor is after the unchanged part of
392 | // the line.
393 | for (var match = 0; match < cursor.offset &&
394 | line.text.charAt(match) == prev.text.charAt(match); match++);
395 | if (cursor.offset > match)
396 | cursordiff = line.text.length - prev.text.length;
397 | }
398 | select.setCursorPos(this.container, {node: line.from, offset: Math.max(0, cursor.offset + cursordiff)});
399 | }
400 | // Cursor was in removed line, this is last new line.
401 | else if (cursor && (i == chain.length - 1) && cursor.node && cursor.node.parentNode != this.container) {
402 | select.setCursorPos(this.container, {node: line.from, offset: line.text.length});
403 | }
404 | }
405 |
406 | // Anchor the chain in the DOM tree.
407 | this.linkChain(chain);
408 | return start;
409 | }
410 | };
411 |
--------------------------------------------------------------------------------
/example/codemirror/js/util.js:
--------------------------------------------------------------------------------
1 | /* A few useful utility functions. */
2 |
3 | // Capture a method on an object.
4 | function method(obj, name) {
5 | return function() {obj[name].apply(obj, arguments);};
6 | }
7 |
8 | // The value used to signal the end of a sequence in iterators.
9 | var StopIteration = {toString: function() {return "StopIteration"}};
10 |
11 | // Apply a function to each element in a sequence.
12 | function forEach(iter, f) {
13 | if (iter.next) {
14 | try {while (true) f(iter.next());}
15 | catch (e) {if (e != StopIteration) throw e;}
16 | }
17 | else {
18 | for (var i = 0; i < iter.length; i++)
19 | f(iter[i]);
20 | }
21 | }
22 |
23 | // Map a function over a sequence, producing an array of results.
24 | function map(iter, f) {
25 | var accum = [];
26 | forEach(iter, function(val) {accum.push(f(val));});
27 | return accum;
28 | }
29 |
30 | // Create a predicate function that tests a string againsts a given
31 | // regular expression. No longer used but might be used by 3rd party
32 | // parsers.
33 | function matcher(regexp){
34 | return function(value){return regexp.test(value);};
35 | }
36 |
37 | // Test whether a DOM node has a certain CSS class. Much faster than
38 | // the MochiKit equivalent, for some reason.
39 | function hasClass(element, className){
40 | var classes = element.className;
41 | return classes && new RegExp("(^| )" + className + "($| )").test(classes);
42 | }
43 |
44 | // Insert a DOM node after another node.
45 | function insertAfter(newNode, oldNode) {
46 | var parent = oldNode.parentNode;
47 | parent.insertBefore(newNode, oldNode.nextSibling);
48 | return newNode;
49 | }
50 |
51 | function removeElement(node) {
52 | if (node.parentNode)
53 | node.parentNode.removeChild(node);
54 | }
55 |
56 | function clearElement(node) {
57 | while (node.firstChild)
58 | node.removeChild(node.firstChild);
59 | }
60 |
61 | // Check whether a node is contained in another one.
62 | function isAncestor(node, child) {
63 | while (child = child.parentNode) {
64 | if (node == child)
65 | return true;
66 | }
67 | return false;
68 | }
69 |
70 | // The non-breaking space character.
71 | var nbsp = "\u00a0";
72 | var matching = {"{": "}", "[": "]", "(": ")",
73 | "}": "{", "]": "[", ")": "("};
74 |
75 | // Standardize a few unportable event properties.
76 | function normalizeEvent(event) {
77 | if (!event.stopPropagation) {
78 | event.stopPropagation = function() {this.cancelBubble = true;};
79 | event.preventDefault = function() {this.returnValue = false;};
80 | }
81 | if (!event.stop) {
82 | event.stop = function() {
83 | this.stopPropagation();
84 | this.preventDefault();
85 | };
86 | }
87 |
88 | if (event.type == "keypress") {
89 | event.code = (event.charCode == null) ? event.keyCode : event.charCode;
90 | event.character = String.fromCharCode(event.code);
91 | }
92 | return event;
93 | }
94 |
95 | // Portably register event handlers.
96 | function addEventHandler(node, type, handler, removeFunc) {
97 | function wrapHandler(event) {
98 | handler(normalizeEvent(event || window.event));
99 | }
100 | if (typeof node.addEventListener == "function") {
101 | node.addEventListener(type, wrapHandler, false);
102 | if (removeFunc) return function() {node.removeEventListener(type, wrapHandler, false);};
103 | }
104 | else {
105 | node.attachEvent("on" + type, wrapHandler);
106 | if (removeFunc) return function() {node.detachEvent("on" + type, wrapHandler);};
107 | }
108 | }
109 |
110 | function nodeText(node) {
111 | return node.textContent || node.innerText || node.nodeValue || "";
112 | }
113 |
114 | function nodeTop(node) {
115 | var top = 0;
116 | while (node.offsetParent) {
117 | top += node.offsetTop;
118 | node = node.offsetParent;
119 | }
120 | return top;
121 | }
122 |
123 | function isBR(node) {
124 | var nn = node.nodeName;
125 | return nn == "BR" || nn == "br";
126 | }
127 | function isSpan(node) {
128 | var nn = node.nodeName;
129 | return nn == "SPAN" || nn == "span";
130 | }
131 |
--------------------------------------------------------------------------------
/example/example.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 | JS2CS Converter
8 |
9 |
10 |
11 |
12 |
13 |
42 |
43 |
44 |
45 |
46 |
JavaScript to CoffeeScript Compiler
47 |
Syntax error:
48 |
49 |
Input JavaScript
50 |
615 |
616 |
Resultant CoffeeScript
617 |
618 |
619 |
this is a product of mindynamics and jsilver. made with love
620 |
621 |
622 |
623 |
--------------------------------------------------------------------------------
/js2cs.js:
--------------------------------------------------------------------------------
1 | /*#!/usr/bin/env node*/
2 | var pegjs = require('./parser');
3 | var parser = pegjs.parser;
4 | var sys = require('sys');
5 | var fs = require('fs');
6 |
7 | /* object inspect method */
8 | var p = function(obj)
9 | {
10 | var obj_inspect = sys.inspect(obj, true, 100);
11 | sys.puts(obj_inspect);
12 | }
13 |
14 | /* the missing trim method */
15 | String.prototype.trim = function()
16 | {
17 | var str_to_return = this.replace(/^\s*/, "");
18 | str_to_return = str_to_return.replace(/\s*$/, "");
19 | return str_to_return;
20 | }
21 |
22 | /* argument section */
23 | var _runmode;
24 | var _filename = process.argv[process.argv.length - 1];
25 | if(process.argv[process.argv.length - 2].substr(0,2) == "--")
26 | {
27 | _runmode = process.argv[process.argv.length - 2];
28 | }
29 | else
30 | {
31 | _runmode = "--convert";
32 | }
33 |
34 | /* read input (sync) */
35 | try
36 | {
37 | var string_raw_js = fs.readFileSync(_filename, "utf8");
38 | } catch(e) {
39 | sys.log("Failed to read input file.. Did you specify one?");
40 | process.exit(1);
41 | }
42 |
43 |
44 | /* parse section */
45 | try{
46 | var ast = parser.parse(string_raw_js);
47 | } catch(e) {
48 | sys.log(e.name + " on line " + e.line + " on column " + e.column + ": " + e.message);
49 | process.exit(1);
50 | }
51 |
52 | var output = '';
53 | var iteration = 0;
54 | var indent_level = 0;
55 | var increaseIndent = function() {
56 | indent_level = indent_level + 1;
57 | }
58 | var decreaseIndent = function() {
59 | indent_level = indent_level - 1;
60 | }
61 | var indent = function()
62 | {
63 | for(var c = 0; c < indent_level; c++)
64 | {
65 | addToOut(" ");
66 | }
67 | }
68 | var addToOut = function(out) {
69 | output += out;
70 | }
71 | var removeBlankLines = function(out) {
72 | var return_me = out.replace(/\n\n/g, "\n");
73 | while (!(return_me.indexOf("\n\n") == -1))
74 | {
75 | return_me = return_me.replace(/\n\n/g, "\n");
76 | }
77 | return return_me;
78 | }
79 |
80 | /* calls parseNode on a collection of child nodes (statements, elements, properties, clauses) */
81 | var parseChildNodes = function(nodes) {
82 | for(var i = 0; i < nodes.length; i++) {
83 | /* some logic */
84 | _node = nodes[i];
85 | is_last_statement = (i < nodes.length -1);
86 | is_just_var = (is_last_statement && (_node.type == "Variable")); /* variables are not declared this way in coffee */
87 | is_break = (_node.type == "BreakStatement"); /* not used in coffee */
88 | /* also don't parse labelledStatement. it's not used and we can't have empty cases if we wanna self host */
89 | is_labelled_statement = (_node.type == "LabelledStatement");
90 | /* indenter */
91 |
92 | if(!(is_break) && !(is_labelled_statement)) {
93 | indent();
94 | }
95 |
96 | /* token parser */
97 | if(!(is_just_var) && !(is_break) && !(is_labelled_statement))
98 | {
99 | parseNode(_node);
100 | }
101 | /* line breaker */
102 | /*if((is_last_statement) && !(is_break) && !(is_just_var))
103 | {
104 | addToOut("\n");
105 | }
106 | */
107 | addToOut("\n");
108 | }
109 | }
110 |
111 | /* eats tokens and makes coffee */
112 | var parseNode = function(node) {
113 | iteration = iteration + 1;
114 |
115 | if(_runmode == "--debug")
116 | {
117 | sys.puts(iteration + " " + node.type);
118 | p(node);
119 | }
120 |
121 | if(_runmode == "--ilevel")
122 | {
123 | sys.puts(iteration + " (" + indent_level + ") " + node.type + " - " + node.name);
124 | }
125 |
126 | switch(node.type)
127 | {
128 | case("Program"):
129 | if(node.elements)
130 | {
131 | parseChildNodes(node.elements);
132 | }
133 | break;
134 | case("This"):
135 | addToOut("@");
136 | break;
137 | case("Function"):
138 | if(node.params.length > 0)
139 | {
140 | addToOut("(");
141 | for(var i = 0; i < node.params.length; i++)
142 | {
143 | addToOut(node.params[i]);
144 | if(i < node.params.length - 1)
145 | {
146 | addToOut(", ");
147 | }
148 | }
149 | addToOut(")");
150 | }
151 | addToOut("->\n");
152 | increaseIndent();
153 | if(node.elements)
154 | {
155 | parseChildNodes(node.elements);
156 | }
157 | decreaseIndent();
158 | break;
159 | case("Block"):
160 | increaseIndent();
161 | if(node.statements)
162 | {
163 | parseChildNodes(node.statements);
164 | }
165 | decreaseIndent();
166 | break;
167 | case("SwitchStatement"):
168 | addToOut("switch ");
169 | parseNode(node.expression);
170 | addToOut("\n");
171 | increaseIndent();
172 | parseChildNodes(node.clauses);
173 | decreaseIndent();
174 | break;
175 | case("CaseClause"):
176 | addToOut("when ");
177 | parseNode(node.selector);
178 | addToOut("\n");
179 | increaseIndent();
180 | if(node.statements)
181 | {
182 | parseChildNodes(node.statements);
183 | }
184 | decreaseIndent();
185 | break;
186 | case("DefaultClause"):
187 | addToOut("else ");
188 | if(node.statements.length > 1)
189 | {
190 | addToOut("\n");
191 | increaseIndent();
192 | if(node.statements)
193 | {
194 | parseChildNodes(node.statements);
195 | }
196 | decreaseIndent();
197 | }
198 | else
199 | {
200 | if(node.statements.length == 1)
201 | {
202 | if(node.statements)
203 | {
204 | parseNode(node.statements[0]);
205 | }
206 | }
207 | }
208 | break;
209 | case("IfStatement"):
210 | /* condition */
211 | if(node.condition.operator != "!")
212 | {
213 | addToOut("if ");
214 | parseNode(node.condition);
215 | }
216 | else
217 | {
218 | addToOut("unless ");
219 | /* skip next node, it's "not" */
220 | parseNode(node.condition.expression);
221 | }
222 | addToOut("\n");
223 | /* statements */
224 | increaseIndent();
225 | if(node.ifStatement.statements)
226 | {
227 | parseChildNodes(node.ifStatement.statements);
228 | }
229 | decreaseIndent();
230 | if(node.elseStatement != null) {
231 | addToOut("\n");
232 | indent();
233 | addToOut("else");
234 | addToOut("\n");
235 | increaseIndent();
236 | if(node.elseStatement.statements)
237 | {
238 | parseChildNodes(node.elseStatement.statements);
239 | }
240 | decreaseIndent();
241 | }
242 | break;
243 | case("ForStatement"):
244 | parseNode(node.initializer);
245 | addToOut("\n");
246 | indent();
247 | addToOut("while ");
248 | parseNode(node.test);
249 | addToOut("\n");
250 | increaseIndent();
251 | indent();
252 | parseNode(node.counter);
253 | decreaseIndent();
254 | if(node.statement)
255 | {
256 | parseNode(node.statement);
257 | }
258 | break;
259 | case("WhileStatement"):
260 | addToOut("while ");
261 | parseNode(node.condition);
262 | addToOut("\n");
263 | if(node.statement)
264 | {
265 | parseNode(node.statement);
266 | }
267 | break;
268 | case("TryStatement"):
269 | addToOut("try\n");
270 | parseNode(node.block);
271 | addToOut("\n");
272 | if(node['catch']) {
273 | addToOut("catch ");
274 | parseNode(node['catch']);
275 | }
276 | if(node['finally']) {
277 | addToOut("finally\n");
278 | parseNode(node['finally']);
279 | }
280 | break;
281 | case("Catch"):
282 | if(node.identifier)
283 | {
284 | addToOut(node.identifier);
285 | }
286 | addToOut("\n");
287 | parseNode(node.block);
288 | addToOut("\n");
289 | break;
290 | case("Finally"):
291 | parseNode(node.block);
292 | break;
293 | case("AssignmentExpression"):
294 | parseNode(node.left);
295 | addToOut(": ");
296 | parseNode(node.right);
297 | break;
298 | case("PropertyAssignment"):
299 | parseNode(node.name);
300 | addToOut(": ");
301 | parseNode(node.value);
302 | break;
303 | case("PropertyAccess"):
304 | parseNode(node.base);
305 | if(node.name.type)
306 | {
307 | if(node.base.type != "This") {
308 | if(node.name.type != "FunctionCall")
309 | {
310 | addToOut("[");
311 | parseNode(node.name);
312 | addToOut("]");
313 | }
314 | else
315 | {
316 | addToOut(".");
317 | parseNode(node.name);
318 | }
319 | }
320 | else
321 | {
322 | parseNode(node.name);
323 | }
324 | }
325 | else
326 | {
327 | if(node.name.type == undefined || node.name.type == "null")
328 | {
329 | if(node.base.type != "This") { addToOut("."); }
330 | addToOut(node.name.trim());
331 | }
332 | }
333 |
334 | break;
335 | case("BinaryExpression"):
336 | parseNode(node.left);
337 | switch(node.operator)
338 | {
339 | /* switch to "not" and "isnt" or something here */
340 | case("!"):
341 | addToOut(" not ");
342 | break;
343 | case("==="):
344 | addToOut(" is ");
345 | break;
346 | case("=="):
347 | addToOut(" is ");
348 | break;
349 | case("!=="):
350 | addToOut(" isnt ");
351 | break;
352 | case("&&"):
353 | addToOut(" and ");
354 | break;
355 | case("||"):
356 | addToOut(" or ");
357 | break;
358 | case(","):
359 | addToOut(", "); /* normal mode , for loop \n */
360 | break;
361 | default:
362 | addToOut(" ");
363 | addToOut(node.operator);
364 | addToOut(" ");
365 | }
366 | parseNode(node.right);
367 | break;
368 | case("UnaryExpression"):
369 | switch(node.operator)
370 | {
371 | case('!'):
372 | addToOut("not ");
373 | break;
374 | default:
375 | addToOut(node.operator);
376 | }
377 | parseNode(node.expression);
378 | break;
379 | case("ConditionalExpression"):
380 | addToOut("if ");
381 | parseNode(node.condition);
382 | addToOut(" ");
383 | parseNode(node.trueExpression);
384 | addToOut(" else ");
385 | parseNode(node.falseExpression);
386 | break;
387 | case("PostfixExpression"):
388 | switch(node.operator)
389 | {
390 | case('++'):
391 | parseNode(node.expression);
392 | addToOut(" = ");
393 | parseNode(node.expression);
394 | addToOut(" + 1");
395 | break;
396 | case('--'):
397 | parseNode(node.expression);
398 | addToOut(" = ");
399 | parseNode(node.expression);
400 | addToOut(" - 1");
401 | break;
402 | }
403 | addToOut("\n");
404 | break;
405 | case("Variable"):
406 | if(!(node.name.substr(0, 3) == "var"))
407 | {
408 | addToOut(node.name.trim());
409 | }
410 | else
411 | {
412 | if(node.name.substr(0, 3) == "var")
413 | {
414 | addToOut(node.name.substr(4, node.name.length - 4).trim());
415 | }
416 | }
417 | break;
418 | case("FunctionCall"):
419 | parseNode(node.name);
420 | addToOut("(");
421 | if(node.arguments.length > 0)
422 | {
423 |
424 | for(var i = 0; i < node.arguments.length; i++)
425 | {
426 | parseNode(node.arguments[i]);
427 | if(i < node.arguments.length - 1)
428 | {
429 | addToOut(", ");
430 | }
431 | }
432 | }
433 | addToOut(")");
434 | break;
435 | case('StringLiteral'):
436 | var escapedValue = node.value.replace(/\n/g, "\\n");
437 | addToOut('"' + escapedValue + '"');
438 | break;
439 | case('NumericLiteral'):
440 | addToOut(node.value);
441 | break;
442 | case('RegularExpressionLiteral'):
443 | addToOut("/");
444 | addToOut(node.body);
445 | addToOut("/" + node.flags);
446 | break;
447 | case('NullLiteral'):
448 | addToOut("null");
449 | break;
450 | case('ArrayLiteral'):
451 | if(node.elements.length > 0)
452 | {
453 | addToOut("[");
454 | for(var i = 0; i < node.elements.length; i++)
455 | {
456 | parseNode(node.elements[i]);
457 | if(i < node.elements.length - 1)
458 | {
459 | addToOut(", ");
460 | }
461 | }
462 | addToOut("]");
463 | }
464 | break;
465 | case('ObjectLiteral'):
466 | if(node.properties.length > 0)
467 | {
468 | addToOut("{\n");
469 | increaseIndent();
470 | if(node.properties)
471 | {
472 | parseChildNodes(node.properties);
473 | }
474 | decreaseIndent();
475 | addToOut("\n}");
476 | }
477 | break;
478 | case('BooleanLiteral'):
479 | if(node.value == true)
480 | {
481 | addToOut("yes");
482 | }
483 | else
484 | {
485 | if(node.value == false)
486 | {
487 | addToOut("no");
488 | }
489 | }
490 | break;
491 | }
492 | }
493 |
494 | parseNode(ast);
495 |
496 | if(_runmode == "--convert")
497 | {
498 | sys.puts(removeBlankLines(output));
499 | }
500 | else
501 | {
502 | if(_runmode == "--showjs")
503 | {
504 | sys.puts("Original JavaScript: ");
505 | sys.puts(string_raw_js);
506 | sys.puts("Generated CoffeeScript: ");
507 | sys.puts(output);
508 | }
509 | }
510 |
--------------------------------------------------------------------------------
/out/js2cs.coffee:
--------------------------------------------------------------------------------
1 | pegjs: require("./parser")
2 | parser: pegjs.parser
3 | sys: require("sys")
4 | fs: require("fs")
5 | p: (obj)->
6 | obj_inspect: sys.inspect(obj, yes, 100)
7 | sys.puts(obj_inspect)
8 | String.prototype.trim: ->
9 | str_to_return: @replace(/^\s*/, "")
10 | str_to_return: str_to_return.replace(/\s*$/, "")
11 | return str_to_return
12 | _filename: process.argv[process.argv.length - 1]
13 | if process.argv[process.argv.length - 2].substr(0, 2) is "--"
14 | _runmode: process.argv[process.argv.length - 2]
15 | else
16 | _runmode: "--convert"
17 | try
18 | string_raw_js: fs.readFileSync(_filename, "utf8")
19 | catch e
20 | sys.log("Failed to read input file.. Did you specify one?")
21 | process.exit(1)
22 | try
23 | ast: parser.parse(string_raw_js)
24 | catch e
25 | sys.log(e.name + " on line " + e.line + " on column " + e.column + ": " + e.message)
26 | process.exit(1)
27 | output: ""
28 | iteration: 0
29 | indent_level: 0
30 | increaseIndent: ->
31 | indent_level: indent_level + 1
32 | decreaseIndent: ->
33 | indent_level: indent_level - 1
34 | indent: ->
35 | c: 0
36 | while c < indent_level
37 | c = c + 1
38 | addToOut(" ")
39 | addToOut: (out)->
40 | output: out
41 | removeBlankLines: (out)->
42 | return_me: out.replace(/\n\n/g, "\n")
43 | while(return_me.indexOf("\n\n") > 0)
44 | return_me: return_me.replace(/\n\n/g, "\n")
45 | return return_me
46 | parseChildNodes: (nodes)->
47 | i: 0
48 | while i < nodes.length
49 | i = i + 1
50 | _node: nodes[i]
51 | is_last_statement: i < nodes.length - 1
52 | is_just_var: is_last_statement and _node.type is "Variable"
53 | is_break: _node.type is "BreakStatement"
54 | is_labelled_statement: _node.type is "LabelledStatement"
55 | if not is_break and not is_labelled_statement
56 | indent()
57 | if not is_just_var and not is_break and not is_labelled_statement
58 | parseNode(_node)
59 | addToOut("\n")
60 | parseNode: (node)->
61 | iteration: iteration + 1
62 | if _runmode is "--debug"
63 | sys.puts(iteration + " " + node.type)
64 | p(node)
65 | if _runmode is "--ilevel"
66 | sys.puts(iteration + " (" + indent_level + ") " + node.type + " - " + node.name)
67 | switch node.type
68 | when "Program"
69 | if node.elements
70 | parseChildNodes(node.elements)
71 | when "This"
72 | addToOut("@")
73 | when "Function"
74 | if node.params.length > 0
75 | addToOut("(")
76 | i: 0
77 | while i < node.params.length
78 | i = i + 1
79 | addToOut(node.params[i])
80 | if i < node.params.length - 1
81 | addToOut(", ")
82 | addToOut(")")
83 | addToOut("->\n")
84 | increaseIndent()
85 | if node.elements
86 | parseChildNodes(node.elements)
87 | decreaseIndent()
88 | when "Block"
89 | increaseIndent()
90 | if node.statements
91 | parseChildNodes(node.statements)
92 | decreaseIndent()
93 | when "SwitchStatement"
94 | addToOut("switch ")
95 | parseNode(node.expression)
96 | addToOut("\n")
97 | increaseIndent()
98 | parseChildNodes(node.clauses)
99 | decreaseIndent()
100 | when "CaseClause"
101 | addToOut("when ")
102 | parseNode(node.selector)
103 | addToOut("\n")
104 | increaseIndent()
105 | if node.statements
106 | parseChildNodes(node.statements)
107 | decreaseIndent()
108 | when "DefaultClause"
109 | addToOut("else ")
110 | if node.statements.length > 1
111 | addToOut("\n")
112 | increaseIndent()
113 | if node.statements
114 | parseChildNodes(node.statements)
115 | decreaseIndent()
116 | else
117 | if node.statements.length is 1
118 | if node.statements
119 | parseNode(node.statements[0])
120 | when "IfStatement"
121 | if node.condition.operator != "!"
122 | addToOut("if ")
123 | parseNode(node.condition)
124 | else
125 | addToOut("unless ")
126 | parseNode(node.condition.expression)
127 | addToOut("\n")
128 | increaseIndent()
129 | if node.ifStatement.statements
130 | parseChildNodes(node.ifStatement.statements)
131 | decreaseIndent()
132 | if node.elseStatement != null
133 | addToOut("\n")
134 | indent()
135 | addToOut("else")
136 | addToOut("\n")
137 | increaseIndent()
138 | if node.elseStatement.statements
139 | parseChildNodes(node.elseStatement.statements)
140 | decreaseIndent()
141 | when "ForStatement"
142 | parseNode(node.initializer)
143 | addToOut("\n")
144 | indent()
145 | addToOut("while ")
146 | parseNode(node.test)
147 | addToOut("\n")
148 | increaseIndent()
149 | indent()
150 | parseNode(node.counter)
151 | decreaseIndent()
152 | if node.statement
153 | parseNode(node.statement)
154 | when "WhileStatement"
155 | addToOut("while ")
156 | parseNode(node.condition)
157 | addToOut("\n")
158 | if node.statement
159 | parseNode(node.statement)
160 | when "TryStatement"
161 | addToOut("try\n")
162 | parseNode(node.block)
163 | addToOut("\n")
164 | if node["catch"]
165 | addToOut("catch ")
166 | parseNode(node["catch"])
167 | if node["finally"]
168 | addToOut("finally\n")
169 | parseNode(node["finally"])
170 | when "Catch"
171 | if node.identifier
172 | addToOut(node.identifier)
173 | addToOut("\n")
174 | parseNode(node.block)
175 | addToOut("\n")
176 | when "Finally"
177 | parseNode(node.block)
178 | when "AssignmentExpression"
179 | parseNode(node.left)
180 | addToOut(": ")
181 | parseNode(node.right)
182 | when "PropertyAssignment"
183 | parseNode(node.name)
184 | addToOut(": ")
185 | parseNode(node.value)
186 | when "PropertyAccess"
187 | parseNode(node.base)
188 | if node.name.type
189 | if node.base.type != "This"
190 | if node.name.type != "FunctionCall"
191 | addToOut("[")
192 | parseNode(node.name)
193 | addToOut("]")
194 | else
195 | addToOut(".")
196 | parseNode(node.name)
197 | else
198 | parseNode(node.name)
199 | else
200 | if node.name.type is undefined or node.name.type is "null"
201 | if node.base.type != "This"
202 | addToOut(".")
203 | addToOut(node.name.trim())
204 | when "BinaryExpression"
205 | parseNode(node.left)
206 | addToOut(" ")
207 | switch node.operator
208 | when "!"
209 | addToOut("not ")
210 | when "==="
211 | addToOut("is ")
212 | when "=="
213 | addToOut("is ")
214 | when "!=="
215 | addToOut("isnt ")
216 | when "&&"
217 | addToOut("and ")
218 | when "||"
219 | addToOut("or ")
220 | when ","
221 | addToOut("\n")
222 | else
223 | addToOut(node.operator)
224 | addToOut(" ")
225 | parseNode(node.right)
226 | when "UnaryExpression"
227 | switch node.operator
228 | when "!"
229 | addToOut("not ")
230 | else addToOut(node.operator)
231 | parseNode(node.expression)
232 | when "ConditionalExpression"
233 | addToOut("if ")
234 | parseNode(node.condition)
235 | addToOut(" ")
236 | parseNode(node.trueExpression)
237 | addToOut(" else ")
238 | parseNode(node.falseExpression)
239 | when "PostfixExpression"
240 | switch node.operator
241 | when "++"
242 | parseNode(node.expression)
243 | addToOut(" = ")
244 | parseNode(node.expression)
245 | addToOut(" + 1")
246 | when "--"
247 | parseNode(node.expression)
248 | addToOut(" = ")
249 | parseNode(node.expression)
250 | addToOut(" - 1")
251 | addToOut("\n")
252 | when "Variable"
253 | unless node.name.substr(0, 3) is "var"
254 | addToOut(node.name.trim())
255 | else
256 | if node.name.substr(0, 3) is "var"
257 | addToOut()
258 | when "FunctionCall"
259 | parseNode(node.name)
260 | addToOut("(")
261 | if node.arguments.length > 0
262 | i: 0
263 | while i < node.arguments.length
264 | i = i + 1
265 | parseNode(node.arguments[i])
266 | if i < node.arguments.length - 1
267 | addToOut(", ")
268 | addToOut(")")
269 | when "StringLiteral"
270 | escapedValue: node.value.replace(/\n/g, "\n")
271 | addToOut(""" + escapedValue + """)
272 | when "NumericLiteral"
273 | addToOut(node.value)
274 | when "RegularExpressionLiteral"
275 | addToOut("/")
276 | addToOut(node.body)
277 | addToOut("/" + node.flags)
278 | when "NullLiteral"
279 | addToOut("null")
280 | when "ArrayLiteral"
281 | if node.elements.length > 0
282 | addToOut("[")
283 | i: 0
284 | while i < node.elements.length
285 | i = i + 1
286 | parseNode(node.elements[i])
287 | if i < node.elements.length - 1
288 | addToOut(", ")
289 | addToOut("]")
290 | when "ObjectLiteral"
291 | if node.properties.length > 0
292 | addToOut("{\n")
293 | increaseIndent()
294 | if node.properties
295 | parseChildNodes(node.properties)
296 | decreaseIndent()
297 | addToOut("\n}")
298 | when "BooleanLiteral"
299 | if node.value is yes
300 | addToOut("yes")
301 | else
302 | if node.value is no
303 | addToOut("no")
304 | parseNode(ast)
305 | if _runmode is "--convert"
306 | sys.puts(removeBlankLines(output))
307 | else
308 | if _runmode is "--showjs"
309 | sys.puts("Original JavaScript: ")
310 | sys.puts(string_raw_js)
311 | sys.puts("Generated CoffeeScript: ")
312 | sys.puts(output)
313 |
314 |
--------------------------------------------------------------------------------
/out/js2cs.recompiled.js:
--------------------------------------------------------------------------------
1 | var _filename, _runmode, addToOut, ast, decreaseIndent, fs, increaseIndent, indent, indent_level, iteration, output, p, parseChildNodes, parseNode, parser, pegjs, removeBlankLines, string_raw_js, sys;
2 | pegjs = require("./parser");
3 | parser = pegjs.parser;
4 | sys = require("sys");
5 | fs = require("fs");
6 | p = function(obj) {
7 | var obj_inspect;
8 | obj_inspect = sys.inspect(obj, true, 100);
9 | return sys.puts(obj_inspect);
10 | };
11 | String.prototype.trim = function() {
12 | var str_to_return;
13 | str_to_return = this.replace(/^\s*/, "");
14 | str_to_return = str_to_return.replace(/\s*$/, "");
15 | return str_to_return;
16 | };
17 | _filename = process.argv[process.argv.length - 1];
18 | process.argv[process.argv.length - 2].substr(0, 2) === "--" ? (_runmode = process.argv[process.argv.length - 2]) : (_runmode = "--convert");
19 | try {
20 | string_raw_js = fs.readFileSync(_filename, "utf8");
21 | } catch (e) {
22 | sys.log("Failed to read input file.. Did you specify one?");
23 | process.exit(1);
24 | }
25 | try {
26 | ast = parser.parse(string_raw_js);
27 | } catch (e) {
28 | sys.log(e.name + " on line " + e.line + " on column " + e.column + ": " + e.message);
29 | process.exit(1);
30 | }
31 | output = "";
32 | iteration = 0;
33 | indent_level = 0;
34 | increaseIndent = function() {
35 | indent_level = indent_level + 1;
36 | return indent_level;
37 | };
38 | decreaseIndent = function() {
39 | indent_level = indent_level - 1;
40 | return indent_level;
41 | };
42 | indent = function() {
43 | var _a, c;
44 | c = 0;
45 | _a = [];
46 | while (c < indent_level) {
47 | _a.push((function() {
48 | c = c + 1;
49 | return addToOut(" ");
50 | })());
51 | }
52 | return _a;
53 | };
54 | addToOut = function(out) {
55 | output = out;
56 | return output;
57 | };
58 | removeBlankLines = function(out) {
59 | var return_me;
60 | return_me = out.replace(/\n\n/g, "\n");
61 | while ((return_me.indexOf("\n\n") > 0)) {
62 | return_me = return_me.replace(/\n\n/g, "\n");
63 | }
64 | return return_me;
65 | };
66 | parseChildNodes = function(nodes) {
67 | var _a, _node, i, is_break, is_just_var, is_labelled_statement, is_last_statement;
68 | i = 0;
69 | _a = [];
70 | while (i < nodes.length) {
71 | _a.push((function() {
72 | i = i + 1;
73 | _node = nodes[i];
74 | is_last_statement = i < nodes.length - 1;
75 | is_just_var = is_last_statement && _node.type === "Variable";
76 | is_break = _node.type === "BreakStatement";
77 | is_labelled_statement = _node.type === "LabelledStatement";
78 | !is_break && !is_labelled_statement ? indent() : null;
79 | !is_just_var && !is_break && !is_labelled_statement ? parseNode(_node) : null;
80 | return addToOut("\n");
81 | })());
82 | }
83 | return _a;
84 | };
85 | parseNode = function(node) {
86 | var _a, _b, _c, _d, escapedValue, i;
87 | iteration = iteration + 1;
88 | if (_runmode === "--debug") {
89 | sys.puts(iteration + " " + node.type);
90 | p(node);
91 | }
92 | _runmode === "--ilevel" ? sys.puts(iteration + " (" + indent_level + ") " + node.type + " - " + node.name) : null;
93 | if ((_a = node.type) === "Program") {
94 | return node.elements ? parseChildNodes(node.elements) : null;
95 | } else if (_a === "This") {
96 | return addToOut("@");
97 | } else if (_a === "Function") {
98 | if (node.params.length > 0) {
99 | addToOut("(");
100 | i = 0;
101 | while (i < node.params.length) {
102 | i = i + 1;
103 | addToOut(node.params[i]);
104 | i < node.params.length - 1 ? addToOut(", ") : null;
105 | }
106 | addToOut(")");
107 | }
108 | addToOut("->\n");
109 | increaseIndent();
110 | node.elements ? parseChildNodes(node.elements) : null;
111 | return decreaseIndent();
112 | } else if (_a === "Block") {
113 | increaseIndent();
114 | node.statements ? parseChildNodes(node.statements) : null;
115 | return decreaseIndent();
116 | } else if (_a === "SwitchStatement") {
117 | addToOut("switch ");
118 | parseNode(node.expression);
119 | addToOut("\n");
120 | increaseIndent();
121 | parseChildNodes(node.clauses);
122 | return decreaseIndent();
123 | } else if (_a === "CaseClause") {
124 | addToOut("when ");
125 | parseNode(node.selector);
126 | addToOut("\n");
127 | increaseIndent();
128 | node.statements ? parseChildNodes(node.statements) : null;
129 | return decreaseIndent();
130 | } else if (_a === "DefaultClause") {
131 | addToOut("else ");
132 | if (node.statements.length > 1) {
133 | addToOut("\n");
134 | increaseIndent();
135 | node.statements ? parseChildNodes(node.statements) : null;
136 | return decreaseIndent();
137 | } else {
138 | return node.statements.length === 1 ? node.statements ? parseNode(node.statements[0]) : null : null;
139 | }
140 | } else if (_a === "IfStatement") {
141 | if (node.condition.operator !== "!") {
142 | addToOut("if ");
143 | parseNode(node.condition);
144 | } else {
145 | addToOut("unless ");
146 | parseNode(node.condition.expression);
147 | }
148 | addToOut("\n");
149 | increaseIndent();
150 | node.ifStatement.statements ? parseChildNodes(node.ifStatement.statements) : null;
151 | decreaseIndent();
152 | if (node.elseStatement !== null) {
153 | addToOut("\n");
154 | indent();
155 | addToOut("else");
156 | addToOut("\n");
157 | increaseIndent();
158 | node.elseStatement.statements ? parseChildNodes(node.elseStatement.statements) : null;
159 | return decreaseIndent();
160 | }
161 | } else if (_a === "ForStatement") {
162 | parseNode(node.initializer);
163 | addToOut("\n");
164 | indent();
165 | addToOut("while ");
166 | parseNode(node.test);
167 | addToOut("\n");
168 | increaseIndent();
169 | indent();
170 | parseNode(node.counter);
171 | decreaseIndent();
172 | return node.statement ? parseNode(node.statement) : null;
173 | } else if (_a === "WhileStatement") {
174 | addToOut("while ");
175 | parseNode(node.condition);
176 | addToOut("\n");
177 | return node.statement ? parseNode(node.statement) : null;
178 | } else if (_a === "TryStatement") {
179 | addToOut("try\n");
180 | parseNode(node.block);
181 | addToOut("\n");
182 | if (node["catch"]) {
183 | addToOut("catch ");
184 | parseNode(node["catch"]);
185 | }
186 | if (node["finally"]) {
187 | addToOut("finally\n");
188 | return parseNode(node["finally"]);
189 | }
190 | } else if (_a === "Catch") {
191 | node.identifier ? addToOut(node.identifier) : null;
192 | addToOut("\n");
193 | parseNode(node.block);
194 | return addToOut("\n");
195 | } else if (_a === "Finally") {
196 | return parseNode(node.block);
197 | } else if (_a === "AssignmentExpression") {
198 | parseNode(node.left);
199 | addToOut(": ");
200 | return parseNode(node.right);
201 | } else if (_a === "PropertyAssignment") {
202 | parseNode(node.name);
203 | addToOut(": ");
204 | return parseNode(node.value);
205 | } else if (_a === "PropertyAccess") {
206 | parseNode(node.base);
207 | if (node.name.type) {
208 | if (node.base.type !== "This") {
209 | if (node.name.type !== "FunctionCall") {
210 | addToOut("[");
211 | parseNode(node.name);
212 | return addToOut("]");
213 | } else {
214 | addToOut(".");
215 | return parseNode(node.name);
216 | }
217 | } else {
218 | return parseNode(node.name);
219 | }
220 | } else {
221 | if (node.name.type === undefined || node.name.type === "null") {
222 | node.base.type !== "This" ? addToOut(".") : null;
223 | return addToOut(node.name.trim());
224 | }
225 | }
226 | } else if (_a === "BinaryExpression") {
227 | parseNode(node.left);
228 | addToOut(" ");
229 | if ((_b = node.operator) === "!") {
230 | addToOut("not ");
231 | } else if (_b === "===") {
232 | addToOut("is ");
233 | } else if (_b === "==") {
234 | addToOut("is ");
235 | } else if (_b === "!==") {
236 | addToOut("isnt ");
237 | } else if (_b === "&&") {
238 | addToOut("and ");
239 | } else if (_b === "||") {
240 | addToOut("or ");
241 | } else if (_b === ",") {
242 | addToOut("\n");
243 | } else {
244 | addToOut(node.operator);
245 | addToOut(" ");
246 | }
247 | return parseNode(node.right);
248 | } else if (_a === "UnaryExpression") {
249 | if ((_c = node.operator) === "!") {
250 | addToOut("not ");
251 | } else {
252 | addToOut(node.operator);
253 | }
254 | return parseNode(node.expression);
255 | } else if (_a === "ConditionalExpression") {
256 | addToOut("if ");
257 | parseNode(node.condition);
258 | addToOut(" ");
259 | parseNode(node.trueExpression);
260 | addToOut(" else ");
261 | return parseNode(node.falseExpression);
262 | } else if (_a === "PostfixExpression") {
263 | if ((_d = node.operator) === "++") {
264 | parseNode(node.expression);
265 | addToOut(" = ");
266 | parseNode(node.expression);
267 | addToOut(" + 1");
268 | } else if (_d === "--") {
269 | parseNode(node.expression);
270 | addToOut(" = ");
271 | parseNode(node.expression);
272 | addToOut(" - 1");
273 | }
274 | return addToOut("\n");
275 | } else if (_a === "Variable") {
276 | return !(node.name.substr(0, 3) === "var") ? addToOut(node.name.trim()) : node.name.substr(0, 3) === "var" ? addToOut() : null;
277 | } else if (_a === "FunctionCall") {
278 | parseNode(node.name);
279 | addToOut("(");
280 | if (node.arguments.length > 0) {
281 | i = 0;
282 | while (i < node.arguments.length) {
283 | i = i + 1;
284 | parseNode(node.arguments[i]);
285 | i < node.arguments.length - 1 ? addToOut(", ") : null;
286 | }
287 | }
288 | return addToOut(")");
289 | } else if (_a === "StringLiteral") {
290 | escapedValue = node.value.replace(/\n/g, "\n");
291 | return addToOut("+ escapedValue +");
292 | } else if (_a === "NumericLiteral") {
293 | return addToOut(node.value);
294 | } else if (_a === "RegularExpressionLiteral") {
295 | addToOut("/");
296 | addToOut(node.body);
297 | return addToOut("/" + node.flags);
298 | } else if (_a === "NullLiteral") {
299 | return addToOut("null");
300 | } else if (_a === "ArrayLiteral") {
301 | if (node.elements.length > 0) {
302 | addToOut("[");
303 | i = 0;
304 | while (i < node.elements.length) {
305 | i = i + 1;
306 | parseNode(node.elements[i]);
307 | i < node.elements.length - 1 ? addToOut(", ") : null;
308 | }
309 | return addToOut("]");
310 | }
311 | } else if (_a === "ObjectLiteral") {
312 | if (node.properties.length > 0) {
313 | addToOut("{\n");
314 | increaseIndent();
315 | node.properties ? parseChildNodes(node.properties) : null;
316 | decreaseIndent();
317 | return addToOut("\n}");
318 | }
319 | } else if (_a === "BooleanLiteral") {
320 | return node.value === true ? addToOut("yes") : node.value === false ? addToOut("no") : null;
321 | }
322 | };
323 | parseNode(ast);
324 | if (_runmode === "--convert") {
325 | sys.puts(removeBlankLines(output));
326 | } else {
327 | if (_runmode === "--showjs") {
328 | sys.puts("Original JavaScript: ");
329 | sys.puts(string_raw_js);
330 | sys.puts("Generated CoffeeScript: ");
331 | sys.puts(output);
332 | }
333 | }
334 |
--------------------------------------------------------------------------------
/out/test.coffee:
--------------------------------------------------------------------------------
1 | _e: 0, _g: _f.length
2 | while _e < _g
3 | _e = _e + 1
4 | thing()
5 |
6 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | for(_e = 0, _g = _f.length; _e < _g; _e++)
2 | {
3 | thing();
4 | }
5 |
--------------------------------------------------------------------------------