1 // ========================================================================== 2 // Project: Greenhouse 3 // ========================================================================== 4 /*globals Greenhouse */ 5 /* 6 7 JS Beautifier 8 --------------- 9 10 11 Written by Einar Lielmanis, <einar@jsbeautifier.org> 12 http://jsbeautifier.org/ 13 14 Originally converted to javascript by Vital, <vital76@gmail.com> 15 16 You are free to use this in any way you want, in case you find this useful or working for you. 17 18 Usage: 19 js_beautify(js_source_text); 20 js_beautify(js_source_text, options); 21 22 The options are: 23 indent_size (default 4) — indentation size, 24 indent_char (default space) — character to indent with, 25 preserve_newlines (default true) — whether existing line breaks should be preserved, 26 indent_level (default 0) — initial indentation level, you probably won't need this ever, 27 28 space_after_anon_function (default false) — if true, then space is added between "function ()" 29 (jslint is happy about this); if false, then the common "function()" output is used. 30 braces_on_own_line (default false) - ANSI / Allman brace style, each opening/closing brace gets its own line. 31 32 e.g 33 34 js_beautify(js_source_text, {indent_size: 1, indent_char: '\t'}); 35 36 37 38 Copyright (c) 2009 Einar Lielmanis 39 40 Permission is hereby granted, free of charge, to any person 41 obtaining a copy of this software and associated documentation 42 files (the "Software"), to deal in the Software without 43 restriction, including without limitation the rights to use, 44 copy, modify, merge, publish, distribute, sublicense, and/or sell 45 copies of the Software, and to permit persons to whom the 46 Software is furnished to do so, subject to the following 47 conditions: 48 49 The above copyright notice and this permission notice shall be 50 included in all copies or substantial portions of the Software. 51 52 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 53 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 54 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 55 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 56 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 57 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 58 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 59 OTHER DEALINGS IN THE SOFTWARE. 60 */ 61 function js_beautify(js_source_text, options) { 62 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 68 69 // Some interpreters have unexpected results with foo = baz || bar; 70 options = options ? options : {}; 71 var opt_braces_on_own_line = options.braces_on_own_line ? options.braces_on_own_line : false; 72 var opt_indent_size = options.indent_size ? options.indent_size : 2; 73 var opt_indent_char = options.indent_char ? options.indent_char : ' '; 74 var opt_preserve_newlines = typeof options.preserve_newlines === 'undefined' ? true : options.preserve_newlines; 75 var opt_indent_level = options.indent_level ? options.indent_level : 0; // starting indentation 76 var opt_space_after_anon_function = options.space_after_anon_function === 'undefined' ? false : options.space_after_anon_function; 77 var opt_keep_array_indentation = typeof options.keep_array_indentation === 'undefined' ? true : options.keep_array_indentation; 78 79 just_added_newline = false; 80 81 // cache the source's length. 82 var input_length = js_source_text.length; 83 84 function trim_output() { 85 while (output.length && (output[output.length - 1] === ' ' || output[output.length - 1] === indent_string)) { 86 output.pop(); 87 } 88 } 89 90 function print_newline(ignore_repeated) { 91 92 flags.eat_next_space = false; 93 if (opt_keep_array_indentation && is_array(flags.mode)) { 94 return; 95 } 96 97 ignore_repeated = typeof ignore_repeated === 'undefined' ? true : ignore_repeated; 98 99 flags.if_line = false; 100 trim_output(); 101 102 if (!output.length) { 103 return; // no newline on start of file 104 } 105 106 if (output[output.length - 1] !== "\n" || !ignore_repeated) { 107 just_added_newline = true; 108 output.push("\n"); 109 } 110 for (var i = 0; i < flags.indentation_level; i += 1) { 111 output.push(indent_string); 112 } 113 } 114 115 116 117 function print_single_space() { 118 if (flags.eat_next_space) { 119 flags.eat_next_space = false; 120 return; 121 } 122 var last_output = ' '; 123 if (output.length) { 124 last_output = output[output.length - 1]; 125 } 126 if (last_output !== ' ' && last_output !== '\n' && last_output !== indent_string) { // prevent occassional duplicate space 127 output.push(' '); 128 } 129 } 130 131 132 function print_token() { 133 just_added_newline = false; 134 flags.eat_next_space = false; 135 output.push(token_text); 136 } 137 138 function indent() { 139 flags.indentation_level += 1; 140 } 141 142 143 function remove_indent() { 144 if (output.length && output[output.length - 1] === indent_string) { 145 output.pop(); 146 } 147 } 148 149 function set_mode(mode) { 150 if (flags) { 151 flag_store.push(flags); 152 } 153 flags = { 154 mode: mode, 155 var_line: false, 156 var_line_tainted: false, 157 var_line_reindented: false, 158 in_html_comment: false, 159 if_line: false, 160 in_case: false, 161 eat_next_space: false, 162 indentation_baseline: -1, 163 indentation_level: (flags ? flags.indentation_level + (flags.var_line_reindented ? 1 : 0) : opt_indent_level) 164 }; 165 } 166 167 function is_expression(mode) { 168 return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]' || mode === '(EXPRESSION)'; 169 } 170 171 function is_array(mode) { 172 return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]'; 173 } 174 175 function restore_mode() { 176 do_block_just_closed = flags.mode === 'DO_BLOCK'; 177 if (flag_store.length > 0) { 178 flags = flag_store.pop(); 179 } 180 } 181 182 183 function in_array(what, arr) { 184 for (var i = 0; i < arr.length; i += 1) { 185 if (arr[i] === what) { 186 return true; 187 } 188 } 189 return false; 190 } 191 192 // Walk backwards from the colon to find a '?' (colon is part of a ternary op) 193 // or a '{' (colon is part of a class literal). Along the way, keep track of 194 // the blocks and expressions we pass so we only trigger on those chars in our 195 // own level, and keep track of the colons so we only trigger on the matching '?'. 196 197 198 function is_ternary_op() { 199 var level = 0, 200 colon_count = 0; 201 for (var i = output.length - 1; i >= 0; i--) { 202 switch (output[i]) { 203 case ':': 204 if (level === 0) { 205 colon_count++; 206 } 207 break; 208 case '?': 209 if (level === 0) { 210 if (colon_count === 0) { 211 return true; 212 } else { 213 colon_count--; 214 } 215 } 216 break; 217 case '{': 218 if (level === 0) { 219 return false; 220 } 221 level--; 222 break; 223 case '(': 224 case '[': 225 level--; 226 break; 227 case ')': 228 case ']': 229 case '}': 230 level++; 231 break; 232 } 233 } 234 } 235 236 function get_next_token() { 237 n_newlines = 0; 238 239 if (parser_pos >= input_length) { 240 return ['', 'TK_EOF']; 241 } 242 243 wanted_newline = false; 244 245 var c = input.charAt(parser_pos); 246 parser_pos += 1; 247 248 249 var keep_whitespace = opt_keep_array_indentation && is_array(flags.mode); 250 251 if (keep_whitespace) { 252 253 // 254 // slight mess to allow nice preservation of array indentation and reindent that correctly 255 // first time when we get to the arrays: 256 // var a = [ 257 // ....'something' 258 // we make note of whitespace_count = 4 into flags.indentation_baseline 259 // so we know that 4 whitespaces in original source match indent_level of reindented source 260 // 261 // and afterwards, when we get to 262 // 'something, 263 // .......'something else' 264 // we know that this should be indented to indent_level + (7 - indentation_baseline) spaces 265 // 266 var whitespace_count = 0; 267 268 while (in_array(c, whitespace)) { 269 270 if (c === "\n") { 271 trim_output(); 272 output.push("\n"); 273 just_added_newline = true; 274 whitespace_count = 0; 275 } else { 276 if (c === '\t') { 277 whitespace_count += 4; 278 } else { 279 whitespace_count += 1; 280 } 281 } 282 283 if (parser_pos >= input_length) { 284 return ['', 'TK_EOF']; 285 } 286 287 c = input.charAt(parser_pos); 288 parser_pos += 1; 289 290 } 291 if (flags.indentation_baseline === -1) { 292 flags.indentation_baseline = whitespace_count; 293 } 294 295 if (just_added_newline) { 296 for (var i = 0; i < flags.indentation_level + 1; i += 1) { 297 output.push(indent_string); 298 } 299 if (flags.indentation_baseline !== -1) { 300 for (var i = 0; i < whitespace_count - flags.indentation_baseline; i++) { 301 output.push(' '); 302 } 303 } 304 } 305 306 } else { 307 while (in_array(c, whitespace)) { 308 309 if (c === "\n") { 310 n_newlines += 1; 311 } 312 313 314 if (parser_pos >= input_length) { 315 return ['', 'TK_EOF']; 316 } 317 318 c = input.charAt(parser_pos); 319 parser_pos += 1; 320 321 } 322 323 if (opt_preserve_newlines) { 324 if (n_newlines > 1) { 325 for (var i = 0; i < n_newlines; i += 1) { 326 print_newline(i === 0); 327 just_added_newline = true; 328 } 329 } 330 } 331 wanted_newline = n_newlines > 0; 332 } 333 334 335 if (in_array(c, wordchar)) { 336 if (parser_pos < input_length) { 337 while (in_array(input.charAt(parser_pos), wordchar)) { 338 c += input.charAt(parser_pos); 339 parser_pos += 1; 340 if (parser_pos === input_length) { 341 break; 342 } 343 } 344 } 345 346 // small and surprisingly unugly hack for 1E-10 representation 347 if (parser_pos !== input_length && c.match(/^[0-9]+[Ee]$/) && (input.charAt(parser_pos) === '-' || input.charAt(parser_pos) === '+')) { 348 349 var sign = input.charAt(parser_pos); 350 parser_pos += 1; 351 352 var t = get_next_token(parser_pos); 353 c += sign + t[0]; 354 return [c, 'TK_WORD']; 355 } 356 357 if (c === 'in') { // hack for 'in' operator 358 return [c, 'TK_OPERATOR']; 359 } 360 if (wanted_newline && last_type !== 'TK_OPERATOR' && !flags.if_line && (opt_preserve_newlines || last_text !== 'var')) { 361 print_newline(); 362 } 363 return [c, 'TK_WORD']; 364 } 365 366 if (c === '(' || c === '[') { 367 return [c, 'TK_START_EXPR']; 368 } 369 370 if (c === ')' || c === ']') { 371 return [c, 'TK_END_EXPR']; 372 } 373 374 if (c === '{') { 375 return [c, 'TK_START_BLOCK']; 376 } 377 378 if (c === '}') { 379 return [c, 'TK_END_BLOCK']; 380 } 381 382 if (c === ';') { 383 return [c, 'TK_SEMICOLON']; 384 } 385 386 if (c === '/') { 387 var comment = ''; 388 // peek for comment /* ... */ 389 var inline_comment = true; 390 if (input.charAt(parser_pos) === '*') { 391 parser_pos += 1; 392 if (parser_pos < input_length) { 393 while (! (input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/') && parser_pos < input_length) { 394 c = input.charAt(parser_pos); 395 comment += c; 396 if (c === '\x0d' || c === '\x0a') { 397 inline_comment = false; 398 } 399 parser_pos += 1; 400 if (parser_pos >= input_length) { 401 break; 402 } 403 } 404 } 405 parser_pos += 2; 406 if (inline_comment) { 407 return ['/*' + comment + '*/', 'TK_INLINE_COMMENT']; 408 } else { 409 return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT']; 410 } 411 } 412 // peek for comment // ... 413 if (input.charAt(parser_pos) === '/') { 414 comment = c; 415 while (input.charAt(parser_pos) !== "\x0d" && input.charAt(parser_pos) !== "\x0a") { 416 comment += input.charAt(parser_pos); 417 parser_pos += 1; 418 if (parser_pos >= input_length) { 419 break; 420 } 421 } 422 parser_pos += 1; 423 if (wanted_newline) { 424 print_newline(); 425 } 426 return [comment, 'TK_COMMENT']; 427 } 428 429 } 430 431 if (c === "'" || // string 432 c === '"' || // string 433 (c === '/' && ((last_type === 'TK_WORD' && in_array(last_text, ['return', 'do'])) || (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 434 var sep = c; 435 var esc = false; 436 var resulting_string = c; 437 438 if (parser_pos < input_length) { 439 if (sep === '/') { 440 // 441 // handle regexp separately... 442 // 443 var in_char_class = false; 444 while (esc || in_char_class || input.charAt(parser_pos) !== sep) { 445 resulting_string += input.charAt(parser_pos); 446 if (!esc) { 447 esc = input.charAt(parser_pos) === '\\'; 448 if (input.charAt(parser_pos) === '[') { 449 in_char_class = true; 450 } else if (input.charAt(parser_pos) === ']') { 451 in_char_class = false; 452 } 453 } else { 454 esc = false; 455 } 456 parser_pos += 1; 457 if (parser_pos >= input_length) { 458 // incomplete string/rexp when end-of-file reached. 459 // bail out with what had been received so far. 460 return [resulting_string, 'TK_STRING']; 461 } 462 } 463 464 } else { 465 // 466 // and handle string also separately 467 // 468 while (esc || input.charAt(parser_pos) !== sep) { 469 resulting_string += input.charAt(parser_pos); 470 if (!esc) { 471 esc = input.charAt(parser_pos) === '\\'; 472 } else { 473 esc = false; 474 } 475 parser_pos += 1; 476 if (parser_pos >= input_length) { 477 // incomplete string/rexp when end-of-file reached. 478 // bail out with what had been received so far. 479 return [resulting_string, 'TK_STRING']; 480 } 481 } 482 } 483 484 485 486 } 487 488 parser_pos += 1; 489 490 resulting_string += sep; 491 492 if (sep === '/') { 493 // regexps may have modifiers /regexp/MOD , so fetch those, too 494 while (parser_pos < input_length && in_array(input.charAt(parser_pos), wordchar)) { 495 resulting_string += input.charAt(parser_pos); 496 parser_pos += 1; 497 } 498 } 499 return [resulting_string, 'TK_STRING']; 500 } 501 502 if (c === '#') { 503 // Spidermonkey-specific sharp variables for circular references 504 // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript 505 // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935 506 var sharp = '#'; 507 if (parser_pos < input_length && in_array(input.charAt(parser_pos), digits)) { 508 do { 509 c = input.charAt(parser_pos); 510 sharp += c; 511 parser_pos += 1; 512 } while (parser_pos < input_length && c !== '#' && c !== '='); 513 if (c === '#') { 514 // 515 } else if (input.charAt(parser_pos) == '[' && input.charAt(parser_pos + 1) === ']') { 516 sharp += '[]'; 517 parser_pos += 2; 518 } else if (input.charAt(parser_pos) == '{' && input.charAt(parser_pos + 1) === '}') { 519 sharp += '{}'; 520 parser_pos += 2; 521 } 522 return [sharp, 'TK_WORD']; 523 } 524 } 525 526 if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') { 527 parser_pos += 3; 528 flags.in_html_comment = true; 529 return ['<!--', 'TK_COMMENT']; 530 } 531 532 if (c === '-' && flags.in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') { 533 flags.in_html_comment = false; 534 parser_pos += 2; 535 if (wanted_newline) { 536 print_newline(); 537 } 538 return ['-->', 'TK_COMMENT']; 539 } 540 541 if (in_array(c, punct)) { 542 while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) { 543 c += input.charAt(parser_pos); 544 parser_pos += 1; 545 if (parser_pos >= input_length) { 546 break; 547 } 548 } 549 550 if (c === '=') { 551 return [c, 'TK_EQUALS']; 552 } else { 553 return [c, 'TK_OPERATOR']; 554 } 555 } 556 557 return [c, 'TK_UNKNOWN']; 558 } 559 560 //---------------------------------- 561 indent_string = ''; 562 while (opt_indent_size > 0) { 563 indent_string += opt_indent_char; 564 opt_indent_size -= 1; 565 } 566 567 input = js_source_text; 568 569 last_word = ''; // last 'TK_WORD' passed 570 last_type = 'TK_START_EXPR'; // last token type 571 last_text = ''; // last token text 572 last_last_text = ''; // pre-last token text 573 output = []; 574 575 do_block_just_closed = false; 576 577 whitespace = "\n\r\t ".split(''); 578 wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split(''); 579 digits = '0123456789'.split(''); 580 581 punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::'.split(' '); 582 583 // words which should always start on new line. 584 line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(','); 585 586 // states showing if we are currently in expression (i.e. "if" case) - 'EXPRESSION', or in usual block (like, procedure), 'BLOCK'. 587 // some formatting depends on that. 588 flag_store = []; 589 set_mode('BLOCK'); 590 591 parser_pos = 0; 592 while (true) { 593 var t = get_next_token(parser_pos); 594 token_text = t[0]; 595 token_type = t[1]; 596 if (token_type === 'TK_EOF') { 597 break; 598 } 599 600 switch (token_type) { 601 602 case 'TK_START_EXPR': 603 604 if (token_text === '[') { 605 606 if (last_type === 'TK_WORD' || last_text === ')') { 607 // this is array index specifier, break immediately 608 // a[x], fn()[x] 609 if (in_array(last_text, line_starters)) { 610 print_single_space(); 611 } 612 set_mode('(EXPRESSION)'); 613 print_token(); 614 break; 615 } 616 617 if (flags.mode === '[EXPRESSION]' || flags.mode === '[INDENTED-EXPRESSION]') { 618 if (last_last_text === ']' && last_text === ',') { 619 // ], [ goes to new line 620 if (flags.mode === '[EXPRESSION]') { 621 flags.mode = '[INDENTED-EXPRESSION]'; 622 if (!opt_keep_array_indentation) { 623 indent(); 624 } 625 } 626 set_mode('[EXPRESSION]'); 627 if (!opt_keep_array_indentation) { 628 print_newline(); 629 } 630 } else if (last_text === '[') { 631 if (flags.mode === '[EXPRESSION]') { 632 flags.mode = '[INDENTED-EXPRESSION]'; 633 if (!opt_keep_array_indentation) { 634 indent(); 635 } 636 } 637 set_mode('[EXPRESSION]'); 638 639 if (!opt_keep_array_indentation) { 640 print_newline(); 641 } 642 } else { 643 set_mode('[EXPRESSION]'); 644 } 645 } else { 646 set_mode('[EXPRESSION]'); 647 } 648 649 650 651 } else { 652 set_mode('(EXPRESSION)'); 653 } 654 655 if (last_text === ';' || last_type === 'TK_START_BLOCK') { 656 print_newline(); 657 } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK') { 658 // do nothing on (( and )( and ][ and ]( .. 659 } else if (last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') { 660 print_single_space(); 661 } else if (last_word === 'function') { 662 // function() vs function () 663 if (opt_space_after_anon_function) { 664 print_single_space(); 665 } 666 } else if (in_array(last_text, line_starters) || last_text === 'catch') { 667 print_single_space(); 668 } 669 print_token(); 670 671 break; 672 673 case 'TK_END_EXPR': 674 if (token_text === ']') { 675 if (opt_keep_array_indentation) { 676 if (last_text === '}') { 677 // trim_output(); 678 // print_newline(true); 679 remove_indent(); 680 print_token(); 681 restore_mode(); 682 break; 683 } 684 } else { 685 if (flags.mode === '[INDENTED-EXPRESSION]') { 686 if (last_text === ']') { 687 restore_mode(); 688 print_newline(); 689 print_token(); 690 break; 691 } 692 } 693 } 694 } 695 restore_mode(); 696 print_token(); 697 break; 698 699 case 'TK_START_BLOCK': 700 701 if (last_word === 'do') { 702 set_mode('DO_BLOCK'); 703 } else { 704 set_mode('BLOCK'); 705 } 706 if (opt_braces_on_own_line) { 707 if (last_type !== 'TK_OPERATOR') { 708 print_newline(true); 709 } 710 print_token(); 711 indent(); 712 } else { 713 if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') { 714 if (last_type === 'TK_START_BLOCK') { 715 print_newline(); 716 } else { 717 print_single_space(); 718 } 719 } 720 indent(); 721 print_token(); 722 } 723 724 break; 725 726 case 'TK_END_BLOCK': 727 restore_mode(); 728 if (opt_braces_on_own_line) { 729 print_newline(); 730 if (flags.var_line_reindented) { 731 output.push(indent_string); 732 } 733 print_token(); 734 } else { 735 if (last_type === 'TK_START_BLOCK') { 736 // nothing 737 if (just_added_newline) { 738 remove_indent(); 739 } else { 740 // {} 741 trim_output(); 742 } 743 } else { 744 print_newline(); 745 if (flags.var_line_reindented) { 746 output.push(indent_string); 747 } 748 } 749 print_token(); 750 } 751 break; 752 753 case 'TK_WORD': 754 755 // no, it's not you. even I have problems understanding how this works 756 // and what does what. 757 if (do_block_just_closed) { 758 // do {} ## while () 759 print_single_space(); 760 print_token(); 761 print_single_space(); 762 do_block_just_closed = false; 763 break; 764 } 765 766 if (token_text === 'function') { 767 if ((just_added_newline || last_text == ';') && last_text !== '{') { 768 // make sure there is a nice clean space of at least one blank line 769 // before a new function definition 770 n_newlines = just_added_newline ? n_newlines : 0; 771 772 for (var i = 0; i < 2 - n_newlines; i++) { 773 print_newline(false); 774 } 775 776 } 777 } 778 if (token_text === 'case' || token_text === 'default') { 779 if (last_text === ':') { 780 // switch cases following one another 781 remove_indent(); 782 } else { 783 // case statement starts in the same line where switch 784 flags.indentation_level--; 785 print_newline(); 786 flags.indentation_level++; 787 } 788 print_token(); 789 flags.in_case = true; 790 break; 791 } 792 793 prefix = 'NONE'; 794 795 if (last_type === 'TK_END_BLOCK') { 796 if (!in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) { 797 prefix = 'NEWLINE'; 798 } else { 799 if (opt_braces_on_own_line) { 800 prefix = 'NEWLINE'; 801 } else { 802 prefix = 'SPACE'; 803 print_single_space(); 804 } 805 } 806 } else if (last_type === 'TK_SEMICOLON' && (flags.mode === 'BLOCK' || flags.mode === 'DO_BLOCK')) { 807 prefix = 'NEWLINE'; 808 } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) { 809 prefix = 'SPACE'; 810 } else if (last_type === 'TK_STRING') { 811 prefix = 'NEWLINE'; 812 } else if (last_type === 'TK_WORD') { 813 prefix = 'SPACE'; 814 } else if (last_type === 'TK_START_BLOCK') { 815 prefix = 'NEWLINE'; 816 } else if (last_type === 'TK_END_EXPR') { 817 print_single_space(); 818 prefix = 'NEWLINE'; 819 } 820 821 if (last_type !== 'TK_END_BLOCK' && in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) { 822 print_newline(); 823 } else if (in_array(token_text, line_starters) || prefix === 'NEWLINE') { 824 if (last_text === 'else') { 825 // no need to force newline on else break 826 print_single_space(); 827 } else if ((last_type === 'TK_START_EXPR' || last_text === '=' || last_text === ',') && token_text === 'function') { 828 // no need to force newline on 'function': (function 829 // DONOTHING 830 } else if (last_text === 'return' || last_text === 'throw') { 831 // no newline between 'return nnn' 832 print_single_space(); 833 } else if (last_type !== 'TK_END_EXPR') { 834 if ((last_type !== 'TK_START_EXPR' || token_text !== 'var') && last_text !== ':') { 835 // no need to force newline on 'var': for (var x = 0...) 836 if (token_text === 'if' && last_word === 'else' && last_text !== '{') { 837 // no newline for } else if { 838 print_single_space(); 839 } else { 840 print_newline(); 841 } 842 } 843 } else { 844 if (in_array(token_text, line_starters) && last_text !== ')') { 845 print_newline(); 846 } 847 } 848 } else if (prefix === 'SPACE') { 849 print_single_space(); 850 } 851 print_token(); 852 last_word = token_text; 853 854 if (token_text === 'var') { 855 flags.var_line = true; 856 flags.var_line_tainted = false; 857 } 858 859 if (token_text === 'if' || token_text === 'else') { 860 flags.if_line = true; 861 } 862 863 break; 864 865 case 'TK_SEMICOLON': 866 867 print_token(); 868 flags.var_line = false; 869 break; 870 871 case 'TK_STRING': 872 873 if (last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_SEMICOLON') { 874 print_newline(); 875 } else if (last_type === 'TK_WORD') { 876 print_single_space(); 877 } 878 print_token(); 879 break; 880 881 case 'TK_EQUALS': 882 if (flags.var_line) { 883 // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done 884 flags.var_line_tainted = true; 885 } 886 print_single_space(); 887 print_token(); 888 print_single_space(); 889 break; 890 891 case 'TK_OPERATOR': 892 893 var space_before = true; 894 var space_after = true; 895 896 if (flags.var_line && token_text === ',' && (is_expression(flags.mode))) { 897 // do not break on comma, for(var a = 1, b = 2) 898 flags.var_line_tainted = false; 899 } 900 901 if (flags.var_line) { 902 if (token_text === ',') { 903 if (flags.var_line_tainted) { 904 print_token(); 905 print_newline(); 906 output.push(indent_string); 907 flags.var_line_reindented = true; 908 flags.var_line_tainted = false; 909 break; 910 } else { 911 flags.var_line_tainted = false; 912 } 913 // } else if (token_text === ':') { 914 // hmm, when does this happen? tests don't catch this 915 // flags.var_line = false; 916 } 917 } 918 919 if (last_text === 'return' || last_text === 'throw') { 920 // "return" had a special handling in TK_WORD. Now we need to return the favor 921 print_single_space(); 922 print_token(); 923 break; 924 } 925 926 if (token_text === ':' && flags.in_case) { 927 print_token(); // colon really asks for separate treatment 928 print_newline(); 929 flags.in_case = false; 930 break; 931 } 932 933 if (token_text === '::') { 934 // no spaces around exotic namespacing syntax operator 935 print_token(); 936 break; 937 } 938 939 if (token_text === ',') { 940 if (flags.var_line) { 941 if (flags.var_line_tainted) { 942 print_token(); 943 print_newline(); 944 flags.var_line_tainted = false; 945 } else { 946 print_token(); 947 print_single_space(); 948 } 949 } else if (last_type === 'TK_END_BLOCK' && flags.mode !== "(EXPRESSION)") { 950 print_token(); 951 print_newline(); 952 } else { 953 if (flags.mode === 'BLOCK') { 954 print_token(); 955 print_newline(); 956 } else { 957 // EXPR or DO_BLOCK 958 print_token(); 959 print_single_space(); 960 } 961 } 962 break; 963 // } else if (in_array(token_text, ['--', '++', '!']) || (in_array(token_text, ['-', '+']) && (in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS']) || in_array(last_text, line_starters) || in_array(last_text, ['==', '!=', '+=', '-=', '*=', '/=', '+', '-'])))) { 964 } else 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)))) { 965 // unary operators (and binary +/- pretending to be unary) special cases 966 967 space_before = false; 968 space_after = false; 969 970 if (last_text === ';' && is_expression(flags.mode)) { 971 // for (;; ++i) 972 // ^^^ 973 space_before = true; 974 } 975 if (last_type === 'TK_WORD' && in_array(last_text, line_starters)) { 976 space_before = true; 977 } 978 979 if (flags.mode === 'BLOCK' && (last_text === '{' || last_text === ';')) { 980 // { foo; --i } 981 // foo(); --bar; 982 print_newline(); 983 } 984 } else if (token_text === '.') { 985 // decimal digits or object.property 986 space_before = false; 987 988 } else if (token_text === ':') { 989 if ( ! is_ternary_op()) { 990 space_before = false; 991 } 992 } 993 if (space_before) { 994 print_single_space(); 995 } 996 997 print_token(); 998 999 if (space_after) { 1000 print_single_space(); 1001 } 1002 1003 if (token_text === '!') { 1004 // flags.eat_next_space = true; 1005 } 1006 1007 break; 1008 1009 case 'TK_BLOCK_COMMENT': 1010 1011 var lines = token_text.split(/\x0a|\x0d\x0a/); 1012 1013 print_newline(); 1014 output.push(lines[0]); 1015 for (var i = 1, l = lines.length; i < l; i++) { 1016 print_newline(); 1017 output.push(' '); 1018 output.push(lines[i].replace(/^\s\s*|\s\s*$/, '')); 1019 } 1020 1021 print_newline(); 1022 break; 1023 1024 case 'TK_INLINE_COMMENT': 1025 1026 print_single_space(); 1027 print_token(); 1028 if (is_expression(flags.mode)) { 1029 print_single_space(); 1030 } else { 1031 print_newline(); 1032 } 1033 break; 1034 1035 case 'TK_COMMENT': 1036 1037 // print_newline(); 1038 if (wanted_newline) { 1039 print_newline(); 1040 } else { 1041 print_single_space(); 1042 } 1043 print_token(); 1044 print_newline(); 1045 break; 1046 1047 case 'TK_UNKNOWN': 1048 print_token(); 1049 break; 1050 } 1051 1052 last_last_text = last_text; 1053 last_type = token_type; 1054 last_text = token_text; 1055 } 1056 1057 return output.join('').replace(/[\n ]+$/, ''); 1058 } 1059