Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /*jslint onevar: false, plusplus: false */
2 /*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
3 /*
5 JS Beautifier
6 ---------------
9 Written by Einar Lielmanis, <einar@jsbeautifier.org>
10 http://jsbeautifier.org/
12 Originally converted to javascript by Vital, <vital76@gmail.com>
13 "End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com>
15 You are free to use this in any way you want, in case you find this useful or working for you.
17 Usage:
18 js_beautify(js_source_text);
19 js_beautify(js_source_text, options);
21 The options are:
22 indent_size (default 4) - indentation size,
23 indent_char (default space) - character to indent with,
24 preserve_newlines (default true) - whether existing line breaks should be preserved,
25 max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk,
27 jslint_happy (default false) - if true, then jslint-stricter mode is enforced.
29 jslint_happy !jslint_happy
30 ---------------------------------
31 function () function()
33 brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "expand-strict"
34 put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line.
36 expand-strict: put brace on own line even in such cases:
38 var a =
39 {
40 a: 5,
41 b: 6
42 }
43 This mode may break your scripts - e.g "return { a: 1 }" will be broken into two lines, so beware.
45 space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)",
47 unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65"
49 e.g
51 js_beautify(js_source_text, {
52 'indent_size': 1,
53 'indent_char': '\t'
54 });
57 */
59 this.EXPORTED_SYMBOLS = ["js_beautify"];
61 this.js_beautify = function js_beautify(js_source_text, options) {
63 var input, output, token_text, last_type, last_text, last_last_text, last_word, flags, flag_store, indent_string;
64 var whitespace, wordchar, punct, parser_pos, line_starters, digits;
65 var prefix, token_type, do_block_just_closed;
66 var wanted_newline, just_added_newline, n_newlines;
67 var preindent_string = '';
70 // Some interpreters have unexpected results with foo = baz || bar;
71 options = options ? options : {};
73 var opt_brace_style;
75 // compatibility
76 if (options.space_after_anon_function !== undefined && options.jslint_happy === undefined) {
77 options.jslint_happy = options.space_after_anon_function;
78 }
79 if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
80 opt_brace_style = options.braces_on_own_line ? "expand" : "collapse";
81 }
82 opt_brace_style = options.brace_style ? options.brace_style : (opt_brace_style ? opt_brace_style : "collapse");
85 var opt_indent_size = options.indent_size ? options.indent_size : 4;
86 var opt_indent_char = options.indent_char ? options.indent_char : ' ';
87 var opt_preserve_newlines = typeof options.preserve_newlines === 'undefined' ? true : options.preserve_newlines;
88 var opt_max_preserve_newlines = typeof options.max_preserve_newlines === 'undefined' ? false : options.max_preserve_newlines;
89 var opt_jslint_happy = options.jslint_happy === 'undefined' ? false : options.jslint_happy;
90 var opt_keep_array_indentation = typeof options.keep_array_indentation === 'undefined' ? false : options.keep_array_indentation;
91 var opt_space_before_conditional = typeof options.space_before_conditional === 'undefined' ? true : options.space_before_conditional;
92 var opt_indent_case = typeof options.indent_case === 'undefined' ? false : options.indent_case;
93 var opt_unescape_strings = typeof options.unescape_strings === 'undefined' ? false : options.unescape_strings;
95 just_added_newline = false;
97 // cache the source's length.
98 var input_length = js_source_text.length;
100 function trim_output(eat_newlines) {
101 eat_newlines = typeof eat_newlines === 'undefined' ? false : eat_newlines;
102 while (output.length && (output[output.length - 1] === ' '
103 || output[output.length - 1] === indent_string
104 || output[output.length - 1] === preindent_string
105 || (eat_newlines && (output[output.length - 1] === '\n' || output[output.length - 1] === '\r')))) {
106 output.pop();
107 }
108 }
110 function trim(s) {
111 return s.replace(/^\s\s*|\s\s*$/, '');
112 }
114 // we could use just string.split, but
115 // IE doesn't like returning empty strings
116 function split_newlines(s) {
117 return s.split(/\x0d\x0a|\x0a/);
118 }
120 function force_newline() {
121 var old_keep_array_indentation = opt_keep_array_indentation;
122 opt_keep_array_indentation = false;
123 print_newline();
124 opt_keep_array_indentation = old_keep_array_indentation;
125 }
127 function print_newline(ignore_repeated) {
129 flags.eat_next_space = false;
130 if (opt_keep_array_indentation && is_array(flags.mode)) {
131 return;
132 }
134 ignore_repeated = typeof ignore_repeated === 'undefined' ? true : ignore_repeated;
136 flags.if_line = false;
137 trim_output();
139 if (!output.length) {
140 return; // no newline on start of file
141 }
143 if (output[output.length - 1] !== "\n" || !ignore_repeated) {
144 just_added_newline = true;
145 output.push("\n");
146 }
147 if (preindent_string) {
148 output.push(preindent_string);
149 }
150 for (var i = 0; i < flags.indentation_level; i += 1) {
151 output.push(indent_string);
152 }
153 if (flags.var_line && flags.var_line_reindented) {
154 output.push(indent_string); // skip space-stuffing, if indenting with a tab
155 }
156 if (flags.case_body) {
157 output.push(indent_string);
158 }
159 }
163 function print_single_space() {
165 if (last_type === 'TK_COMMENT') {
166 return print_newline();
167 }
168 if (flags.eat_next_space) {
169 flags.eat_next_space = false;
170 return;
171 }
172 var last_output = ' ';
173 if (output.length) {
174 last_output = output[output.length - 1];
175 }
176 if (last_output !== ' ' && last_output !== '\n' && last_output !== indent_string) { // prevent occassional duplicate space
177 output.push(' ');
178 }
179 }
182 function print_token() {
183 just_added_newline = false;
184 flags.eat_next_space = false;
185 output.push(token_text);
186 }
188 function indent() {
189 flags.indentation_level += 1;
190 }
193 function remove_indent() {
194 if (output.length && output[output.length - 1] === indent_string) {
195 output.pop();
196 }
197 }
199 function set_mode(mode) {
200 if (flags) {
201 flag_store.push(flags);
202 }
203 flags = {
204 previous_mode: flags ? flags.mode : 'BLOCK',
205 mode: mode,
206 var_line: false,
207 var_line_tainted: false,
208 var_line_reindented: false,
209 in_html_comment: false,
210 if_line: false,
211 in_case_statement: false, // switch(..){ INSIDE HERE }
212 in_case: false, // we're on the exact line with "case 0:"
213 case_body: false, // the indented case-action block
214 eat_next_space: false,
215 indentation_baseline: -1,
216 indentation_level: (flags ? flags.indentation_level + (flags.case_body ? 1 : 0) + ((flags.var_line && flags.var_line_reindented) ? 1 : 0) : 0),
217 ternary_depth: 0
218 };
219 }
221 function is_array(mode) {
222 return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]';
223 }
225 function is_expression(mode) {
226 return in_array(mode, ['[EXPRESSION]', '(EXPRESSION)', '(FOR-EXPRESSION)', '(COND-EXPRESSION)']);
227 }
229 function restore_mode() {
230 do_block_just_closed = flags.mode === 'DO_BLOCK';
231 if (flag_store.length > 0) {
232 var mode = flags.mode;
233 flags = flag_store.pop();
234 flags.previous_mode = mode;
235 }
236 }
238 function all_lines_start_with(lines, c) {
239 for (var i = 0; i < lines.length; i++) {
240 var line = trim(lines[i]);
241 if (line.charAt(0) !== c) {
242 return false;
243 }
244 }
245 return true;
246 }
248 function is_special_word(word) {
249 return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
250 }
252 function in_array(what, arr) {
253 for (var i = 0; i < arr.length; i += 1) {
254 if (arr[i] === what) {
255 return true;
256 }
257 }
258 return false;
259 }
261 function look_up(exclude) {
262 var local_pos = parser_pos;
263 var c = input.charAt(local_pos);
264 while (in_array(c, whitespace) && c !== exclude) {
265 local_pos++;
266 if (local_pos >= input_length) {
267 return 0;
268 }
269 c = input.charAt(local_pos);
270 }
271 return c;
272 }
274 function get_next_token() {
275 var i;
276 var resulting_string;
278 n_newlines = 0;
280 if (parser_pos >= input_length) {
281 return ['', 'TK_EOF'];
282 }
284 wanted_newline = false;
286 var c = input.charAt(parser_pos);
287 parser_pos += 1;
290 var keep_whitespace = opt_keep_array_indentation && is_array(flags.mode);
292 if (keep_whitespace) {
294 //
295 // slight mess to allow nice preservation of array indentation and reindent that correctly
296 // first time when we get to the arrays:
297 // var a = [
298 // ....'something'
299 // we make note of whitespace_count = 4 into flags.indentation_baseline
300 // so we know that 4 whitespaces in original source match indent_level of reindented source
301 //
302 // and afterwards, when we get to
303 // 'something,
304 // .......'something else'
305 // we know that this should be indented to indent_level + (7 - indentation_baseline) spaces
306 //
307 var whitespace_count = 0;
309 while (in_array(c, whitespace)) {
311 if (c === "\n") {
312 trim_output();
313 output.push("\n");
314 just_added_newline = true;
315 whitespace_count = 0;
316 } else {
317 if (c === '\t') {
318 whitespace_count += 4;
319 } else if (c === '\r') {
320 // nothing
321 } else {
322 whitespace_count += 1;
323 }
324 }
326 if (parser_pos >= input_length) {
327 return ['', 'TK_EOF'];
328 }
330 c = input.charAt(parser_pos);
331 parser_pos += 1;
333 }
334 if (flags.indentation_baseline === -1) {
335 flags.indentation_baseline = whitespace_count;
336 }
338 if (just_added_newline) {
339 for (i = 0; i < flags.indentation_level + 1; i += 1) {
340 output.push(indent_string);
341 }
342 if (flags.indentation_baseline !== -1) {
343 for (i = 0; i < whitespace_count - flags.indentation_baseline; i++) {
344 output.push(' ');
345 }
346 }
347 }
349 } else {
350 while (in_array(c, whitespace)) {
352 if (c === "\n") {
353 n_newlines += ((opt_max_preserve_newlines) ? (n_newlines <= opt_max_preserve_newlines) ? 1 : 0 : 1);
354 }
357 if (parser_pos >= input_length) {
358 return ['', 'TK_EOF'];
359 }
361 c = input.charAt(parser_pos);
362 parser_pos += 1;
364 }
366 if (opt_preserve_newlines) {
367 if (n_newlines > 1) {
368 for (i = 0; i < n_newlines; i += 1) {
369 print_newline(i === 0);
370 just_added_newline = true;
371 }
372 }
373 }
374 wanted_newline = n_newlines > 0;
375 }
378 if (in_array(c, wordchar)) {
379 if (parser_pos < input_length) {
380 while (in_array(input.charAt(parser_pos), wordchar)) {
381 c += input.charAt(parser_pos);
382 parser_pos += 1;
383 if (parser_pos === input_length) {
384 break;
385 }
386 }
387 }
389 // small and surprisingly unugly hack for 1E-10 representation
390 if (parser_pos !== input_length && c.match(/^[0-9]+[Ee]$/) && (input.charAt(parser_pos) === '-' || input.charAt(parser_pos) === '+')) {
392 var sign = input.charAt(parser_pos);
393 parser_pos += 1;
395 var t = get_next_token();
396 c += sign + t[0];
397 return [c, 'TK_WORD'];
398 }
400 if (c === 'in') { // hack for 'in' operator
401 return [c, 'TK_OPERATOR'];
402 }
403 if (wanted_newline && last_type !== 'TK_OPERATOR'
404 && last_type !== 'TK_EQUALS'
405 && !flags.if_line && (opt_preserve_newlines || last_text !== 'var')) {
406 print_newline();
407 }
408 return [c, 'TK_WORD'];
409 }
411 if (c === '(' || c === '[') {
412 return [c, 'TK_START_EXPR'];
413 }
415 if (c === ')' || c === ']') {
416 return [c, 'TK_END_EXPR'];
417 }
419 if (c === '{') {
420 return [c, 'TK_START_BLOCK'];
421 }
423 if (c === '}') {
424 return [c, 'TK_END_BLOCK'];
425 }
427 if (c === ';') {
428 return [c, 'TK_SEMICOLON'];
429 }
431 if (c === '/') {
432 var comment = '';
433 // peek for comment /* ... */
434 var inline_comment = true;
435 if (input.charAt(parser_pos) === '*') {
436 parser_pos += 1;
437 if (parser_pos < input_length) {
438 while (parser_pos < input_length &&
439 ! (input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/')) {
440 c = input.charAt(parser_pos);
441 comment += c;
442 if (c === "\n" || c === "\r") {
443 inline_comment = false;
444 }
445 parser_pos += 1;
446 if (parser_pos >= input_length) {
447 break;
448 }
449 }
450 }
451 parser_pos += 2;
452 if (inline_comment && n_newlines === 0) {
453 return ['/*' + comment + '*/', 'TK_INLINE_COMMENT'];
454 } else {
455 return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT'];
456 }
457 }
458 // peek for comment // ...
459 if (input.charAt(parser_pos) === '/') {
460 comment = c;
461 while (input.charAt(parser_pos) !== '\r' && input.charAt(parser_pos) !== '\n') {
462 comment += input.charAt(parser_pos);
463 parser_pos += 1;
464 if (parser_pos >= input_length) {
465 break;
466 }
467 }
468 if (wanted_newline) {
469 print_newline();
470 }
471 return [comment, 'TK_COMMENT'];
472 }
474 }
476 if (c === "'" || // string
477 c === '"' || // string
478 (c === '/' &&
479 ((last_type === 'TK_WORD' && is_special_word(last_text)) ||
480 (last_text === ')' && in_array(flags.previous_mode, ['(COND-EXPRESSION)', '(FOR-EXPRESSION)'])) ||
481 (last_type === 'TK_COMMA' || last_type === 'TK_COMMENT' || last_type === 'TK_START_EXPR' || last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_OPERATOR' || last_type === 'TK_EQUALS' || last_type === 'TK_EOF' || last_type === 'TK_SEMICOLON')))) { // regexp
482 var sep = c;
483 var esc = false;
484 var esc1 = 0;
485 var esc2 = 0;
486 resulting_string = c;
488 if (parser_pos < input_length) {
489 if (sep === '/') {
490 //
491 // handle regexp separately...
492 //
493 var in_char_class = false;
494 while (esc || in_char_class || input.charAt(parser_pos) !== sep) {
495 resulting_string += input.charAt(parser_pos);
496 if (!esc) {
497 esc = input.charAt(parser_pos) === '\\';
498 if (input.charAt(parser_pos) === '[') {
499 in_char_class = true;
500 } else if (input.charAt(parser_pos) === ']') {
501 in_char_class = false;
502 }
503 } else {
504 esc = false;
505 }
506 parser_pos += 1;
507 if (parser_pos >= input_length) {
508 // incomplete string/rexp when end-of-file reached.
509 // bail out with what had been received so far.
510 return [resulting_string, 'TK_STRING'];
511 }
512 }
514 } else {
515 //
516 // and handle string also separately
517 //
518 while (esc || input.charAt(parser_pos) !== sep) {
519 resulting_string += input.charAt(parser_pos);
520 if (esc1 && esc1 >= esc2) {
521 esc1 = parseInt(resulting_string.substr(-esc2), 16);
522 if (esc1 && esc1 >= 0x20 && esc1 <= 0x7e) {
523 esc1 = String.fromCharCode(esc1);
524 resulting_string = resulting_string.substr(0, resulting_string.length - esc2 - 2) + (((esc1 === sep) || (esc1 === '\\')) ? '\\' : '') + esc1;
525 }
526 esc1 = 0;
527 }
528 if (esc1) {
529 esc1++;
530 } else if (!esc) {
531 esc = input.charAt(parser_pos) === '\\';
532 } else {
533 esc = false;
534 if (opt_unescape_strings) {
535 if (input.charAt(parser_pos) === 'x') {
536 esc1++;
537 esc2 = 2;
538 } else if (input.charAt(parser_pos) === 'u') {
539 esc1++;
540 esc2 = 4;
541 }
542 }
543 }
544 parser_pos += 1;
545 if (parser_pos >= input_length) {
546 // incomplete string/rexp when end-of-file reached.
547 // bail out with what had been received so far.
548 return [resulting_string, 'TK_STRING'];
549 }
550 }
551 }
555 }
557 parser_pos += 1;
559 resulting_string += sep;
561 if (sep === '/') {
562 // regexps may have modifiers /regexp/MOD , so fetch those, too
563 while (parser_pos < input_length && in_array(input.charAt(parser_pos), wordchar)) {
564 resulting_string += input.charAt(parser_pos);
565 parser_pos += 1;
566 }
567 }
568 return [resulting_string, 'TK_STRING'];
569 }
571 if (c === '#') {
574 if (output.length === 0 && input.charAt(parser_pos) === '!') {
575 // shebang
576 resulting_string = c;
577 while (parser_pos < input_length && c !== '\n') {
578 c = input.charAt(parser_pos);
579 resulting_string += c;
580 parser_pos += 1;
581 }
582 output.push(trim(resulting_string) + '\n');
583 print_newline();
584 return get_next_token();
585 }
589 // Spidermonkey-specific sharp variables for circular references
590 // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
591 // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
592 var sharp = '#';
593 if (parser_pos < input_length && in_array(input.charAt(parser_pos), digits)) {
594 do {
595 c = input.charAt(parser_pos);
596 sharp += c;
597 parser_pos += 1;
598 } while (parser_pos < input_length && c !== '#' && c !== '=');
599 if (c === '#') {
600 //
601 } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') {
602 sharp += '[]';
603 parser_pos += 2;
604 } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') {
605 sharp += '{}';
606 parser_pos += 2;
607 }
608 return [sharp, 'TK_WORD'];
609 }
610 }
612 if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') {
613 parser_pos += 3;
614 c = '<!--';
615 while (input.charAt(parser_pos) !== '\n' && parser_pos < input_length) {
616 c += input.charAt(parser_pos);
617 parser_pos++;
618 }
619 flags.in_html_comment = true;
620 return [c, 'TK_COMMENT'];
621 }
623 if (c === '-' && flags.in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') {
624 flags.in_html_comment = false;
625 parser_pos += 2;
626 if (wanted_newline) {
627 print_newline();
628 }
629 return ['-->', 'TK_COMMENT'];
630 }
632 if (in_array(c, punct)) {
633 while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) {
634 c += input.charAt(parser_pos);
635 parser_pos += 1;
636 if (parser_pos >= input_length) {
637 break;
638 }
639 }
641 if (c === ',') {
642 return [c, 'TK_COMMA'];
643 } else if (c === '=') {
644 return [c, 'TK_EQUALS'];
645 } else {
646 return [c, 'TK_OPERATOR'];
647 }
648 }
650 return [c, 'TK_UNKNOWN'];
651 }
653 //----------------------------------
654 indent_string = '';
655 while (opt_indent_size > 0) {
656 indent_string += opt_indent_char;
657 opt_indent_size -= 1;
658 }
660 while (js_source_text && (js_source_text.charAt(0) === ' ' || js_source_text.charAt(0) === '\t')) {
661 preindent_string += js_source_text.charAt(0);
662 js_source_text = js_source_text.substring(1);
663 }
664 input = js_source_text;
666 last_word = ''; // last 'TK_WORD' passed
667 last_type = 'TK_START_EXPR'; // last token type
668 last_text = ''; // last token text
669 last_last_text = ''; // pre-last token text
670 output = [];
672 do_block_just_closed = false;
674 whitespace = "\n\r\t ".split('');
675 wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split('');
676 digits = '0123456789'.split('');
678 punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::';
679 punct += ' <%= <% %> <?= <? ?>'; // try to be a good boy and try not to break the markup language identifiers
680 punct = punct.split(' ');
682 // words which should always start on new line.
683 line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(',');
685 // states showing if we are currently in expression (i.e. "if" case) - 'EXPRESSION', or in usual block (like, procedure), 'BLOCK'.
686 // some formatting depends on that.
687 flag_store = [];
688 set_mode('BLOCK');
690 parser_pos = 0;
691 while (true) {
692 var t = get_next_token();
693 token_text = t[0];
694 token_type = t[1];
695 if (token_type === 'TK_EOF') {
696 break;
697 }
699 switch (token_type) {
701 case 'TK_START_EXPR':
703 if (token_text === '[') {
705 if (last_type === 'TK_WORD' || last_text === ')') {
706 // this is array index specifier, break immediately
707 // a[x], fn()[x]
708 if (in_array(last_text, line_starters)) {
709 print_single_space();
710 }
711 set_mode('(EXPRESSION)');
712 print_token();
713 break;
714 }
716 if (flags.mode === '[EXPRESSION]' || flags.mode === '[INDENTED-EXPRESSION]') {
717 if (last_last_text === ']' && last_text === ',') {
718 // ], [ goes to new line
719 if (flags.mode === '[EXPRESSION]') {
720 flags.mode = '[INDENTED-EXPRESSION]';
721 if (!opt_keep_array_indentation) {
722 indent();
723 }
724 }
725 set_mode('[EXPRESSION]');
726 if (!opt_keep_array_indentation) {
727 print_newline();
728 }
729 } else if (last_text === '[') {
730 if (flags.mode === '[EXPRESSION]') {
731 flags.mode = '[INDENTED-EXPRESSION]';
732 if (!opt_keep_array_indentation) {
733 indent();
734 }
735 }
736 set_mode('[EXPRESSION]');
738 if (!opt_keep_array_indentation) {
739 print_newline();
740 }
741 } else {
742 set_mode('[EXPRESSION]');
743 }
744 } else {
745 set_mode('[EXPRESSION]');
746 }
750 } else {
751 if (last_word === 'for') {
752 set_mode('(FOR-EXPRESSION)');
753 } else if (in_array(last_word, ['if', 'while'])) {
754 set_mode('(COND-EXPRESSION)');
755 } else {
756 set_mode('(EXPRESSION)');
757 }
758 }
760 if (last_text === ';' || last_type === 'TK_START_BLOCK') {
761 print_newline();
762 } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || last_text === '.') {
763 if (wanted_newline) {
764 print_newline();
765 }
766 // do nothing on (( and )( and ][ and ]( and .(
767 } else if (last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
768 print_single_space();
769 } else if (last_word === 'function' || last_word === 'typeof') {
770 // function() vs function ()
771 if (opt_jslint_happy) {
772 print_single_space();
773 }
774 } else if (in_array(last_text, line_starters) || last_text === 'catch') {
775 if (opt_space_before_conditional) {
776 print_single_space();
777 }
778 }
779 print_token();
781 break;
783 case 'TK_END_EXPR':
784 if (token_text === ']') {
785 if (opt_keep_array_indentation) {
786 if (last_text === '}') {
787 // trim_output();
788 // print_newline(true);
789 remove_indent();
790 print_token();
791 restore_mode();
792 break;
793 }
794 } else {
795 if (flags.mode === '[INDENTED-EXPRESSION]') {
796 if (last_text === ']') {
797 restore_mode();
798 print_newline();
799 print_token();
800 break;
801 }
802 }
803 }
804 }
805 restore_mode();
806 print_token();
807 break;
809 case 'TK_START_BLOCK':
811 if (last_word === 'do') {
812 set_mode('DO_BLOCK');
813 } else {
814 set_mode('BLOCK');
815 }
816 if (opt_brace_style === "expand" || opt_brace_style === "expand-strict") {
817 var empty_braces = false;
818 if (opt_brace_style === "expand-strict") {
819 empty_braces = (look_up() === '}');
820 if (!empty_braces) {
821 print_newline(true);
822 }
823 } else {
824 if (last_type !== 'TK_OPERATOR') {
825 if (last_text === '=' || (is_special_word(last_text) && last_text !== 'else')) {
826 print_single_space();
827 } else {
828 print_newline(true);
829 }
830 }
831 }
832 print_token();
833 if (!empty_braces) {
834 indent();
835 }
836 } else {
837 if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
838 if (last_type === 'TK_START_BLOCK') {
839 print_newline();
840 } else {
841 print_single_space();
842 }
843 } else {
844 // if TK_OPERATOR or TK_START_EXPR
845 if (is_array(flags.previous_mode) && last_text === ',') {
846 if (last_last_text === '}') {
847 // }, { in array context
848 print_single_space();
849 } else {
850 print_newline(); // [a, b, c, {
851 }
852 }
853 }
854 indent();
855 print_token();
856 }
858 break;
860 case 'TK_END_BLOCK':
861 restore_mode();
862 if (opt_brace_style === "expand" || opt_brace_style === "expand-strict") {
863 if (last_text !== '{') {
864 print_newline();
865 }
866 print_token();
867 } else {
868 if (last_type === 'TK_START_BLOCK') {
869 // nothing
870 if (just_added_newline) {
871 remove_indent();
872 } else {
873 // {}
874 trim_output();
875 }
876 } else {
877 if (is_array(flags.mode) && opt_keep_array_indentation) {
878 // we REALLY need a newline here, but newliner would skip that
879 opt_keep_array_indentation = false;
880 print_newline();
881 opt_keep_array_indentation = true;
883 } else {
884 print_newline();
885 }
886 }
887 print_token();
888 }
889 break;
891 case 'TK_WORD':
893 // no, it's not you. even I have problems understanding how this works
894 // and what does what.
895 if (do_block_just_closed) {
896 // do {} ## while ()
897 print_single_space();
898 print_token();
899 print_single_space();
900 do_block_just_closed = false;
901 break;
902 }
904 prefix = 'NONE';
906 if (token_text === 'function') {
907 if (flags.var_line && last_type !== 'TK_EQUALS' ) {
908 flags.var_line_reindented = true;
909 }
910 if ((just_added_newline || last_text === ';') && last_text !== '{'
911 && last_type !== 'TK_BLOCK_COMMENT' && last_type !== 'TK_COMMENT') {
912 // make sure there is a nice clean space of at least one blank line
913 // before a new function definition
914 n_newlines = just_added_newline ? n_newlines : 0;
915 if (!opt_preserve_newlines) {
916 n_newlines = 1;
917 }
919 for (var i = 0; i < 2 - n_newlines; i++) {
920 print_newline(false);
921 }
922 }
923 if (last_type === 'TK_WORD') {
924 if (last_text === 'get' || last_text === 'set' || last_text === 'new' || last_text === 'return') {
925 print_single_space();
926 } else {
927 print_newline();
928 }
929 } else if (last_type === 'TK_OPERATOR' || last_text === '=') {
930 // foo = function
931 print_single_space();
932 } else if (is_expression(flags.mode)) {
933 //รครค print nothing
934 } else {
935 print_newline();
936 }
938 print_token();
939 last_word = token_text;
940 break;
941 }
943 if (token_text === 'case' || (token_text === 'default' && flags.in_case_statement)) {
944 if (last_text === ':' || flags.case_body) {
945 // switch cases following one another
946 remove_indent();
947 } else {
948 // case statement starts in the same line where switch
949 if (!opt_indent_case) {
950 flags.indentation_level--;
951 }
952 print_newline();
953 if (!opt_indent_case) {
954 flags.indentation_level++;
955 }
956 }
957 print_token();
958 flags.in_case = true;
959 flags.in_case_statement = true;
960 flags.case_body = false;
961 break;
962 }
964 if (last_type === 'TK_END_BLOCK') {
966 if (!in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) {
967 prefix = 'NEWLINE';
968 } else {
969 if (opt_brace_style === "expand" || opt_brace_style === "end-expand" || opt_brace_style === "expand-strict") {
970 prefix = 'NEWLINE';
971 } else {
972 prefix = 'SPACE';
973 print_single_space();
974 }
975 }
976 } else if (last_type === 'TK_SEMICOLON' && (flags.mode === 'BLOCK' || flags.mode === 'DO_BLOCK')) {
977 prefix = 'NEWLINE';
978 } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
979 prefix = 'SPACE';
980 } else if (last_type === 'TK_STRING') {
981 prefix = 'NEWLINE';
982 } else if (last_type === 'TK_WORD') {
983 if (last_text === 'else') {
984 // eat newlines between ...else *** some_op...
985 // won't preserve extra newlines in this place (if any), but don't care that much
986 trim_output(true);
987 }
988 prefix = 'SPACE';
989 } else if (last_type === 'TK_START_BLOCK') {
990 prefix = 'NEWLINE';
991 } else if (last_type === 'TK_END_EXPR') {
992 print_single_space();
993 prefix = 'NEWLINE';
994 }
996 if (in_array(token_text, line_starters) && last_text !== ')') {
997 if (last_text === 'else') {
998 prefix = 'SPACE';
999 } else {
1000 prefix = 'NEWLINE';
1001 }
1003 }
1005 if (flags.if_line && last_type === 'TK_END_EXPR') {
1006 flags.if_line = false;
1007 }
1008 if (in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) {
1009 if (last_type !== 'TK_END_BLOCK' || opt_brace_style === "expand" || opt_brace_style === "end-expand" || opt_brace_style === "expand-strict") {
1010 print_newline();
1011 } else {
1012 trim_output(true);
1013 print_single_space();
1014 }
1015 } else if (prefix === 'NEWLINE') {
1016 if (is_special_word(last_text)) {
1017 // no newline between 'return nnn'
1018 print_single_space();
1019 } else if (last_type !== 'TK_END_EXPR') {
1020 if ((last_type !== 'TK_START_EXPR' || token_text !== 'var') && last_text !== ':') {
1021 // no need to force newline on 'var': for (var x = 0...)
1022 if (token_text === 'if' && last_word === 'else' && last_text !== '{') {
1023 // no newline for } else if {
1024 print_single_space();
1025 } else {
1026 flags.var_line = false;
1027 flags.var_line_reindented = false;
1028 print_newline();
1029 }
1030 }
1031 } else if (in_array(token_text, line_starters) && last_text !== ')') {
1032 flags.var_line = false;
1033 flags.var_line_reindented = false;
1034 print_newline();
1035 }
1036 } else if (is_array(flags.mode) && last_text === ',' && last_last_text === '}') {
1037 print_newline(); // }, in lists get a newline treatment
1038 } else if (prefix === 'SPACE') {
1039 print_single_space();
1040 }
1041 print_token();
1042 last_word = token_text;
1044 if (token_text === 'var') {
1045 flags.var_line = true;
1046 flags.var_line_reindented = false;
1047 flags.var_line_tainted = false;
1048 }
1050 if (token_text === 'if') {
1051 flags.if_line = true;
1052 }
1053 if (token_text === 'else') {
1054 flags.if_line = false;
1055 }
1057 break;
1059 case 'TK_SEMICOLON':
1061 print_token();
1062 flags.var_line = false;
1063 flags.var_line_reindented = false;
1064 if (flags.mode === 'OBJECT') {
1065 // OBJECT mode is weird and doesn't get reset too well.
1066 flags.mode = 'BLOCK';
1067 }
1068 break;
1070 case 'TK_STRING':
1072 if (last_type === 'TK_END_EXPR' && in_array(flags.previous_mode, ['(COND-EXPRESSION)', '(FOR-EXPRESSION)'])) {
1073 print_single_space();
1074 } else if (last_type === 'TK_COMMENT' || last_type === 'TK_STRING' || last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_SEMICOLON') {
1075 print_newline();
1076 } else if (last_type === 'TK_WORD') {
1077 print_single_space();
1078 }
1079 print_token();
1080 break;
1082 case 'TK_EQUALS':
1083 if (flags.var_line) {
1084 // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
1085 flags.var_line_tainted = true;
1086 }
1087 print_single_space();
1088 print_token();
1089 print_single_space();
1090 break;
1092 case 'TK_COMMA':
1093 if (flags.var_line) {
1094 if (is_expression(flags.mode) || last_type === 'TK_END_BLOCK' ) {
1095 // do not break on comma, for(var a = 1, b = 2)
1096 flags.var_line_tainted = false;
1097 }
1098 if (flags.var_line_tainted) {
1099 print_token();
1100 flags.var_line_reindented = true;
1101 flags.var_line_tainted = false;
1102 print_newline();
1103 break;
1104 } else {
1105 flags.var_line_tainted = false;
1106 }
1108 print_token();
1109 print_single_space();
1110 break;
1111 }
1113 if (last_type === 'TK_COMMENT') {
1114 print_newline();
1115 }
1117 if (last_type === 'TK_END_BLOCK' && flags.mode !== "(EXPRESSION)") {
1118 print_token();
1119 if (flags.mode === 'OBJECT' && last_text === '}') {
1120 print_newline();
1121 } else {
1122 print_single_space();
1123 }
1124 } else {
1125 if (flags.mode === 'OBJECT') {
1126 print_token();
1127 print_newline();
1128 } else {
1129 // EXPR or DO_BLOCK
1130 print_token();
1131 print_single_space();
1132 }
1133 }
1134 break;
1137 case 'TK_OPERATOR':
1139 var space_before = true;
1140 var space_after = true;
1142 if (is_special_word(last_text)) {
1143 // "return" had a special handling in TK_WORD. Now we need to return the favor
1144 print_single_space();
1145 print_token();
1146 break;
1147 }
1149 // hack for actionscript's import .*;
1150 if (token_text === '*' && last_type === 'TK_UNKNOWN' && !last_last_text.match(/^\d+$/)) {
1151 print_token();
1152 break;
1153 }
1155 if (token_text === ':' && flags.in_case) {
1156 if (opt_indent_case) {
1157 flags.case_body = true;
1158 }
1159 print_token(); // colon really asks for separate treatment
1160 print_newline();
1161 flags.in_case = false;
1162 break;
1163 }
1165 if (token_text === '::') {
1166 // no spaces around exotic namespacing syntax operator
1167 print_token();
1168 break;
1169 }
1171 if (in_array(token_text, ['--', '++', '!']) || (in_array(token_text, ['-', '+']) && (in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) || in_array(last_text, line_starters)))) {
1172 // unary operators (and binary +/- pretending to be unary) special cases
1174 space_before = false;
1175 space_after = false;
1177 if (last_text === ';' && is_expression(flags.mode)) {
1178 // for (;; ++i)
1179 // ^^^
1180 space_before = true;
1181 }
1182 if (last_type === 'TK_WORD' && in_array(last_text, line_starters)) {
1183 space_before = true;
1184 }
1186 if (flags.mode === 'BLOCK' && (last_text === '{' || last_text === ';')) {
1187 // { foo; --i }
1188 // foo(); --bar;
1189 print_newline();
1190 }
1191 } else if (token_text === '.') {
1192 // decimal digits or object.property
1193 space_before = false;
1195 } else if (token_text === ':') {
1196 if (flags.ternary_depth === 0) {
1197 if (flags.mode === 'BLOCK') {
1198 flags.mode = 'OBJECT';
1199 }
1200 space_before = false;
1201 } else {
1202 flags.ternary_depth -= 1;
1203 }
1204 } else if (token_text === '?') {
1205 flags.ternary_depth += 1;
1206 }
1207 if (space_before) {
1208 print_single_space();
1209 }
1211 print_token();
1213 if (space_after) {
1214 print_single_space();
1215 }
1217 break;
1219 case 'TK_BLOCK_COMMENT':
1221 var lines = split_newlines(token_text);
1222 var j; // iterator for this case
1224 if (all_lines_start_with(lines.slice(1), '*')) {
1225 // javadoc: reformat and reindent
1226 print_newline();
1227 output.push(lines[0]);
1228 for (j = 1; j < lines.length; j++) {
1229 print_newline();
1230 output.push(' ');
1231 output.push(trim(lines[j]));
1232 }
1234 } else {
1236 // simple block comment: leave intact
1237 if (lines.length > 1) {
1238 // multiline comment block starts with a new line
1239 print_newline();
1240 } else {
1241 // single-line /* comment */ stays where it is
1242 if (last_type === 'TK_END_BLOCK') {
1243 print_newline();
1244 } else {
1245 print_single_space();
1246 }
1248 }
1250 for (j = 0; j < lines.length; j++) {
1251 output.push(lines[j]);
1252 output.push("\n");
1253 }
1255 }
1256 if (look_up('\n') !== '\n') {
1257 print_newline();
1258 }
1259 break;
1261 case 'TK_INLINE_COMMENT':
1262 print_single_space();
1263 print_token();
1264 if (is_expression(flags.mode)) {
1265 print_single_space();
1266 } else {
1267 force_newline();
1268 }
1269 break;
1271 case 'TK_COMMENT':
1273 if (last_text === ',' && !wanted_newline) {
1274 trim_output(true);
1275 }
1276 if (last_type !== 'TK_COMMENT') {
1277 if (wanted_newline) {
1278 print_newline();
1279 } else {
1280 print_single_space();
1281 }
1282 }
1283 print_token();
1284 print_newline();
1285 break;
1287 case 'TK_UNKNOWN':
1288 if (is_special_word(last_text)) {
1289 print_single_space();
1290 }
1291 print_token();
1292 break;
1293 }
1295 last_last_text = last_text;
1296 last_type = token_type;
1297 last_text = token_text;
1298 }
1300 var sweet_code = preindent_string + output.join('').replace(/[\r\n ]+$/, '');
1301 return sweet_code;
1303 }