| /*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ |
| /* |
| |
| The MIT License (MIT) |
| |
| Copyright (c) 2007-2013 Einar Lielmanis and contributors. |
| |
| Permission is hereby granted, free of charge, to any person |
| obtaining a copy of this software and associated documentation files |
| (the "Software"), to deal in the Software without restriction, |
| including without limitation the rights to use, copy, modify, merge, |
| publish, distribute, sublicense, and/or sell copies of the Software, |
| and to permit persons to whom the Software is furnished to do so, |
| subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be |
| included in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| SOFTWARE. |
| |
| JS Beautifier |
| --------------- |
| |
| |
| Written by Einar Lielmanis, <einar@jsbeautifier.org> |
| http://jsbeautifier.org/ |
| |
| Originally converted to javascript by Vital, <vital76@gmail.com> |
| "End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com> |
| Parsing improvements for brace-less statements by Liam Newman <bitwiseman@gmail.com> |
| |
| |
| Usage: |
| js_beautify(js_source_text); |
| js_beautify(js_source_text, options); |
| |
| The options are: |
| indent_size (default 4) - indentation size, |
| indent_char (default space) - character to indent with, |
| preserve_newlines (default true) - whether existing line breaks should be preserved, |
| max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk, |
| |
| jslint_happy (default false) - if true, then jslint-stricter mode is enforced. |
| |
| jslint_happy !jslint_happy |
| --------------------------------- |
| function () function() |
| |
| brace_style (default "collapse") - "collapse" | "expand" | "end-expand" |
| 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. |
| |
| space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)", |
| |
| unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65" |
| |
| wrap_line_length (default unlimited) - lines should wrap at next opportunity after this number of characters. |
| NOTE: This is not a hard limit. Lines will continue until a point where a newline would |
| be preserved if it were present. |
| |
| e.g |
| |
| js_beautify(js_source_text, { |
| 'indent_size': 1, |
| 'indent_char': '\t' |
| }); |
| |
| */ |
| |
| |
| (function() { |
| function js_beautify(js_source_text, options) { |
| "use strict"; |
| var beautifier = new Beautifier(js_source_text, options); |
| return beautifier.beautify(); |
| } |
| |
| function Beautifier(js_source_text, options) { |
| "use strict"; |
| var input, output_lines; |
| var token_text, token_type, last_type, last_last_text, indent_string; |
| var flags, previous_flags, flag_store; |
| var whitespace, wordchar, punct, parser_pos, line_starters, digits; |
| var prefix; |
| var input_wanted_newline; |
| var output_wrapped, output_space_before_token; |
| var input_length, n_newlines, whitespace_before_token; |
| var handlers, MODE, opt; |
| var preindent_string = ''; |
| |
| whitespace = "\n\r\t ".split(''); |
| wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split(''); |
| digits = '0123456789'.split(''); |
| |
| punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::'; |
| punct += ' <%= <% %> <?= <? ?>'; // try to be a good boy and try not to break the markup language identifiers |
| punct = punct.split(' '); |
| |
| // words which should always start on new line. |
| line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(','); |
| |
| MODE = { |
| BlockStatement: 'BlockStatement', // 'BLOCK' |
| Statement: 'Statement', // 'STATEMENT' |
| ObjectLiteral: 'ObjectLiteral', // 'OBJECT', |
| ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]', |
| ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)', |
| Conditional: 'Conditional', //'(COND-EXPRESSION)', |
| Expression: 'Expression' //'(EXPRESSION)' |
| }; |
| |
| handlers = { |
| 'TK_START_EXPR': handle_start_expr, |
| 'TK_END_EXPR': handle_end_expr, |
| 'TK_START_BLOCK': handle_start_block, |
| 'TK_END_BLOCK': handle_end_block, |
| 'TK_WORD': handle_word, |
| 'TK_SEMICOLON': handle_semicolon, |
| 'TK_STRING': handle_string, |
| 'TK_EQUALS': handle_equals, |
| 'TK_OPERATOR': handle_operator, |
| 'TK_COMMA': handle_comma, |
| 'TK_BLOCK_COMMENT': handle_block_comment, |
| 'TK_INLINE_COMMENT': handle_inline_comment, |
| 'TK_COMMENT': handle_comment, |
| 'TK_DOT': handle_dot, |
| 'TK_UNKNOWN': handle_unknown |
| }; |
| |
| function create_flags(flags_base, mode) { |
| var next_indent_level = 0; |
| if (flags_base) { |
| next_indent_level = flags_base.indentation_level; |
| next_indent_level += (flags_base.var_line && flags_base.var_line_reindented) ? 1 : 0; |
| if (!just_added_newline() && |
| flags_base.line_indent_level > next_indent_level) { |
| next_indent_level = flags_base.line_indent_level; |
| } |
| } |
| |
| var next_flags = { |
| mode: mode, |
| parent: flags_base, |
| last_text: flags_base ? flags_base.last_text : '', // last token text |
| last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed |
| var_line: false, |
| var_line_tainted: false, |
| var_line_reindented: false, |
| in_html_comment: false, |
| multiline_frame: false, |
| if_block: false, |
| do_block: false, |
| do_while: false, |
| in_case_statement: false, // switch(..){ INSIDE HERE } |
| in_case: false, // we're on the exact line with "case 0:" |
| case_body: false, // the indented case-action block |
| indentation_level: next_indent_level, |
| line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level, |
| start_line_index: output_lines.length, |
| had_comment: false, |
| ternary_depth: 0 |
| } |
| return next_flags; |
| } |
| |
| // Using object instead of string to allow for later expansion of info about each line |
| |
| function create_output_line() { |
| return { |
| text: [] |
| }; |
| } |
| |
| // Some interpreters have unexpected results with foo = baz || bar; |
| options = options ? options : {}; |
| opt = {}; |
| |
| // compatibility |
| if (options.space_after_anon_function !== undefined && options.jslint_happy === undefined) { |
| options.jslint_happy = options.space_after_anon_function; |
| } |
| if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option |
| opt.brace_style = options.braces_on_own_line ? "expand" : "collapse"; |
| } |
| opt.brace_style = options.brace_style ? options.brace_style : (opt.brace_style ? opt.brace_style : "collapse"); |
| |
| // graceful handling of deprecated option |
| if (opt.brace_style === "expand-strict") { |
| opt.brace_style = "expand"; |
| } |
| |
| |
| opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4; |
| opt.indent_char = options.indent_char ? options.indent_char : ' '; |
| opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines; |
| opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods; |
| opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10); |
| opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren; |
| opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy; |
| opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation; |
| opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional; |
| opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings; |
| opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10); |
| opt.e4x = (options.e4x === undefined) ? false : options.e4x; |
| |
| if(options.indent_with_tabs){ |
| opt.indent_char = '\t'; |
| opt.indent_size = 1; |
| } |
| |
| //---------------------------------- |
| indent_string = ''; |
| while (opt.indent_size > 0) { |
| indent_string += opt.indent_char; |
| opt.indent_size -= 1; |
| } |
| |
| while (js_source_text && (js_source_text.charAt(0) === ' ' || js_source_text.charAt(0) === '\t')) { |
| preindent_string += js_source_text.charAt(0); |
| js_source_text = js_source_text.substring(1); |
| } |
| input = js_source_text; |
| // cache the source's length. |
| input_length = js_source_text.length; |
| |
| last_type = 'TK_START_BLOCK'; // last token type |
| last_last_text = ''; // pre-last token text |
| output_lines = [create_output_line()]; |
| output_wrapped = false; |
| output_space_before_token = false; |
| whitespace_before_token = []; |
| |
| // Stack of parsing/formatting states, including MODE. |
| // We tokenize, parse, and output in an almost purely a forward-only stream of token input |
| // and formatted output. This makes the beautifier less accurate than full parsers |
| // but also far more tolerant of syntax errors. |
| // |
| // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type |
| // MODE.BlockStatement on the the stack, even though it could be object literal. If we later |
| // encounter a ":", we'll switch to to MODE.ObjectLiteral. If we then see a ";", |
| // most full parsers would die, but the beautifier gracefully falls back to |
| // MODE.BlockStatement and continues on. |
| flag_store = []; |
| set_mode(MODE.BlockStatement); |
| |
| parser_pos = 0; |
| |
| this.beautify = function() { |
| /*jshint onevar:true */ |
| var t, i, keep_whitespace, sweet_code; |
| |
| while (true) { |
| t = get_next_token(); |
| token_text = t[0]; |
| token_type = t[1]; |
| |
| if (token_type === 'TK_EOF') { |
| break; |
| } |
| |
| keep_whitespace = opt.keep_array_indentation && is_array(flags.mode); |
| input_wanted_newline = n_newlines > 0; |
| |
| if (keep_whitespace) { |
| for (i = 0; i < n_newlines; i += 1) { |
| print_newline(i > 0); |
| } |
| } else { |
| if (opt.max_preserve_newlines && n_newlines > opt.max_preserve_newlines) { |
| n_newlines = opt.max_preserve_newlines; |
| } |
| |
| if (opt.preserve_newlines) { |
| if (n_newlines > 1) { |
| print_newline(); |
| for (i = 1; i < n_newlines; i += 1) { |
| print_newline(true); |
| } |
| } |
| } |
| } |
| |
| handlers[token_type](); |
| |
| // The cleanest handling of inline comments is to treat them as though they aren't there. |
| // Just continue formatting and the behavior should be logical. |
| // Also ignore unknown tokens. Again, this should result in better behavior. |
| if (token_type !== 'TK_INLINE_COMMENT' && token_type !== 'TK_COMMENT' && |
| token_type !== 'TK_BLOCK_COMMENT' && token_type !== 'TK_UNKNOWN') { |
| last_last_text = flags.last_text; |
| last_type = token_type; |
| flags.last_text = token_text; |
| } |
| flags.had_comment = (token_type === 'TK_INLINE_COMMENT' || token_type === 'TK_COMMENT' |
| || token_type === 'TK_BLOCK_COMMENT'); |
| } |
| |
| |
| sweet_code = output_lines[0].text.join(''); |
| for (var line_index = 1; line_index < output_lines.length; line_index++) { |
| sweet_code += '\n' + output_lines[line_index].text.join(''); |
| } |
| sweet_code = sweet_code.replace(/[\r\n ]+$/, ''); |
| return sweet_code; |
| }; |
| |
| function trim_output(eat_newlines) { |
| eat_newlines = (eat_newlines === undefined) ? false : eat_newlines; |
| |
| if (output_lines.length) { |
| trim_output_line(output_lines[output_lines.length - 1], eat_newlines); |
| |
| while (eat_newlines && output_lines.length > 1 && |
| output_lines[output_lines.length - 1].text.length === 0) { |
| output_lines.pop(); |
| trim_output_line(output_lines[output_lines.length - 1], eat_newlines); |
| } |
| } |
| } |
| |
| function trim_output_line(line) { |
| while (line.text.length && |
| (line.text[line.text.length - 1] === ' ' || |
| line.text[line.text.length - 1] === indent_string || |
| line.text[line.text.length - 1] === preindent_string)) { |
| line.text.pop(); |
| } |
| } |
| |
| function trim(s) { |
| return s.replace(/^\s+|\s+$/g, ''); |
| } |
| |
| // we could use just string.split, but |
| // IE doesn't like returning empty strings |
| |
| function split_newlines(s) { |
| //return s.split(/\x0d\x0a|\x0a/); |
| |
| s = s.replace(/\x0d/g, ''); |
| var out = [], |
| idx = s.indexOf("\n"); |
| while (idx !== -1) { |
| out.push(s.substring(0, idx)); |
| s = s.substring(idx + 1); |
| idx = s.indexOf("\n"); |
| } |
| if (s.length) { |
| out.push(s); |
| } |
| return out; |
| } |
| |
| function just_added_newline() { |
| var line = output_lines[output_lines.length - 1]; |
| return line.text.length === 0; |
| } |
| |
| function just_added_blankline() { |
| if (just_added_newline()) { |
| if (output_lines.length === 1) { |
| return true; // start of the file and newline = blank |
| } |
| |
| var line = output_lines[output_lines.length - 2]; |
| return line.text.length === 0; |
| } |
| return false; |
| } |
| |
| function allow_wrap_or_preserved_newline(force_linewrap) { |
| force_linewrap = (force_linewrap === undefined) ? false : force_linewrap; |
| if (opt.wrap_line_length && !force_linewrap) { |
| var line = output_lines[output_lines.length - 1]; |
| var proposed_line_length = 0; |
| // never wrap the first token of a line. |
| if (line.text.length > 0) { |
| proposed_line_length = line.text.join('').length + token_text.length + |
| (output_space_before_token ? 1 : 0); |
| if (proposed_line_length >= opt.wrap_line_length) { |
| force_linewrap = true; |
| } |
| } |
| } |
| if (((opt.preserve_newlines && input_wanted_newline) || force_linewrap) && !just_added_newline()) { |
| print_newline(false, true); |
| |
| // Expressions and array literals already indent their contents. |
| if (!(is_array(flags.mode) || is_expression(flags.mode))) { |
| output_wrapped = true; |
| } |
| } |
| } |
| |
| function print_newline(force_newline, preserve_statement_flags) { |
| output_wrapped = false; |
| output_space_before_token = false; |
| |
| if (!preserve_statement_flags) { |
| if (flags.last_text !== ';') { |
| while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) { |
| restore_mode(); |
| } |
| } |
| } |
| |
| if (output_lines.length === 1 && just_added_newline()) { |
| return; // no newline on start of file |
| } |
| |
| if (force_newline || !just_added_newline()) { |
| flags.multiline_frame = true; |
| output_lines.push(create_output_line()); |
| } |
| } |
| |
| function print_token_line_indentation() { |
| if (just_added_newline()) { |
| var line = output_lines[output_lines.length - 1]; |
| if (opt.keep_array_indentation && is_array(flags.mode) && input_wanted_newline) { |
| // prevent removing of this whitespace as redundant |
| line.text.push(''); |
| for (var i = 0; i < whitespace_before_token.length; i += 1) { |
| line.text.push(whitespace_before_token[i]); |
| } |
| } else { |
| if (preindent_string) { |
| line.text.push(preindent_string); |
| } |
| |
| print_indent_string(flags.indentation_level + |
| (flags.var_line && flags.var_line_reindented ? 1 : 0) + |
| (output_wrapped ? 1 : 0)); |
| } |
| } |
| } |
| |
| function print_indent_string(level) { |
| // Never indent your first output indent at the start of the file |
| if (output_lines.length > 1) { |
| var line = output_lines[output_lines.length - 1]; |
| |
| flags.line_indent_level = level; |
| for (var i = 0; i < level; i += 1) { |
| line.text.push(indent_string); |
| } |
| } |
| } |
| |
| function print_token_space_before() { |
| var line = output_lines[output_lines.length - 1]; |
| if (output_space_before_token && line.text.length) { |
| var last_output = line.text[line.text.length - 1]; |
| if (last_output !== ' ' && last_output !== indent_string) { // prevent occassional duplicate space |
| line.text.push(' '); |
| } |
| } |
| } |
| |
| function print_token(printable_token) { |
| printable_token = printable_token || token_text; |
| print_token_line_indentation(); |
| output_wrapped = false; |
| print_token_space_before(); |
| output_space_before_token = false; |
| output_lines[output_lines.length - 1].text.push(printable_token); |
| } |
| |
| function indent() { |
| flags.indentation_level += 1; |
| } |
| |
| function deindent() { |
| if (flags.indentation_level > 0 && |
| ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level)) |
| flags.indentation_level -= 1; |
| } |
| |
| function remove_redundant_indentation(frame) { |
| // This implementation is effective but has some issues: |
| // - less than great performance due to array splicing |
| // - can cause line wrap to happen too soon due to indent removal |
| // after wrap points are calculated |
| // These issues are minor compared to ugly indentation. |
| |
| if (frame.multiline_frame) return; |
| |
| // remove one indent from each line inside this section |
| var index = frame.start_line_index; |
| var splice_index = 0; |
| var line; |
| |
| while (index < output_lines.length) { |
| line = output_lines[index]; |
| index++; |
| |
| // skip empty lines |
| if (line.text.length === 0) { |
| continue; |
| } |
| |
| // skip the preindent string if present |
| if (preindent_string && line.text[0] === preindent_string) { |
| splice_index = 1; |
| } else { |
| splice_index = 0; |
| } |
| |
| // remove one indent, if present |
| if (line.text[splice_index] === indent_string) { |
| line.text.splice(splice_index, 1); |
| } |
| } |
| } |
| |
| function set_mode(mode) { |
| if (flags) { |
| flag_store.push(flags); |
| previous_flags = flags; |
| } else { |
| previous_flags = create_flags(null, mode); |
| } |
| |
| flags = create_flags(previous_flags, mode); |
| } |
| |
| function is_array(mode) { |
| return mode === MODE.ArrayLiteral; |
| } |
| |
| function is_expression(mode) { |
| return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]); |
| } |
| |
| function restore_mode() { |
| if (flag_store.length > 0) { |
| previous_flags = flags; |
| flags = flag_store.pop(); |
| } |
| } |
| |
| function start_of_object_property() { |
| return flags.mode === MODE.ObjectLiteral && flags.last_text === ':' && |
| flags.ternary_depth === 0; |
| } |
| |
| function start_of_statement() { |
| if ( |
| (flags.last_text === 'do' || |
| (flags.last_text === 'else' && token_text !== 'if') || |
| (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)))) { |
| // Issue #276: |
| // If starting a new statement with [if, for, while, do], push to a new line. |
| // if (a) if (b) if(c) d(); else e(); else f(); |
| allow_wrap_or_preserved_newline( |
| in_array(token_text, ['do', 'for', 'if', 'while'])); |
| |
| set_mode(MODE.Statement); |
| // Issue #275: |
| // If starting on a newline, all of a statement should be indented. |
| // if not, use line wrapping logic for indent. |
| if (just_added_newline()) { |
| indent(); |
| output_wrapped = false; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| function all_lines_start_with(lines, c) { |
| for (var i = 0; i < lines.length; i++) { |
| var line = trim(lines[i]); |
| if (line.charAt(0) !== c) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| function is_special_word(word) { |
| return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']); |
| } |
| |
| function in_array(what, arr) { |
| for (var i = 0; i < arr.length; i += 1) { |
| if (arr[i] === what) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| function unescape_string(s) { |
| var esc = false, |
| out = '', |
| pos = 0, |
| s_hex = '', |
| escaped = 0, |
| c; |
| |
| while (esc || pos < s.length) { |
| |
| c = s.charAt(pos); |
| pos++; |
| |
| if (esc) { |
| esc = false; |
| if (c === 'x') { |
| // simple hex-escape \x24 |
| s_hex = s.substr(pos, 2); |
| pos += 2; |
| } else if (c === 'u') { |
| // unicode-escape, \u2134 |
| s_hex = s.substr(pos, 4); |
| pos += 4; |
| } else { |
| // some common escape, e.g \n |
| out += '\\' + c; |
| continue; |
| } |
| if (!s_hex.match(/^[0123456789abcdefABCDEF]+$/)) { |
| // some weird escaping, bail out, |
| // leaving whole string intact |
| return s; |
| } |
| |
| escaped = parseInt(s_hex, 16); |
| |
| if (escaped >= 0x00 && escaped < 0x20) { |
| // leave 0x00...0x1f escaped |
| if (c === 'x') { |
| out += '\\x' + s_hex; |
| } else { |
| out += '\\u' + s_hex; |
| } |
| continue; |
| } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) { |
| // single-quote, apostrophe, backslash - escape these |
| out += '\\' + String.fromCharCode(escaped); |
| } else if (c === 'x' && escaped > 0x7e && escaped <= 0xff) { |
| // we bail out on \x7f..\xff, |
| // leaving whole string escaped, |
| // as it's probably completely binary |
| return s; |
| } else { |
| out += String.fromCharCode(escaped); |
| } |
| } else if (c === '\\') { |
| esc = true; |
| } else { |
| out += c; |
| } |
| } |
| return out; |
| } |
| |
| function is_next(find) { |
| var local_pos = parser_pos; |
| var c = input.charAt(local_pos); |
| while (in_array(c, whitespace) && c !== find) { |
| local_pos++; |
| if (local_pos >= input_length) { |
| return false; |
| } |
| c = input.charAt(local_pos); |
| } |
| return c === find; |
| } |
| |
| function get_next_token() { |
| var i, resulting_string; |
| |
| n_newlines = 0; |
| |
| if (parser_pos >= input_length) { |
| return ['', 'TK_EOF']; |
| } |
| |
| input_wanted_newline = false; |
| whitespace_before_token = []; |
| |
| var c = input.charAt(parser_pos); |
| parser_pos += 1; |
| |
| while (in_array(c, whitespace)) { |
| |
| if (c === '\n') { |
| n_newlines += 1; |
| whitespace_before_token = []; |
| } else if (n_newlines) { |
| if (c === indent_string) { |
| whitespace_before_token.push(indent_string); |
| } else if (c !== '\r') { |
| whitespace_before_token.push(' '); |
| } |
| } |
| |
| if (parser_pos >= input_length) { |
| return ['', 'TK_EOF']; |
| } |
| |
| c = input.charAt(parser_pos); |
| parser_pos += 1; |
| } |
| |
| if (in_array(c, wordchar)) { |
| if (parser_pos < input_length) { |
| while (in_array(input.charAt(parser_pos), wordchar)) { |
| c += input.charAt(parser_pos); |
| parser_pos += 1; |
| if (parser_pos === input_length) { |
| break; |
| } |
| } |
| } |
| |
| // small and surprisingly unugly hack for 1E-10 representation |
| if (parser_pos !== input_length && c.match(/^[0-9]+[Ee]$/) && (input.charAt(parser_pos) === '-' || input.charAt(parser_pos) === '+')) { |
| |
| var sign = input.charAt(parser_pos); |
| parser_pos += 1; |
| |
| var t = get_next_token(); |
| c += sign + t[0]; |
| return [c, 'TK_WORD']; |
| } |
| |
| if (c === 'in') { // hack for 'in' operator |
| return [c, 'TK_OPERATOR']; |
| } |
| return [c, 'TK_WORD']; |
| } |
| |
| if (c === '(' || c === '[') { |
| return [c, 'TK_START_EXPR']; |
| } |
| |
| if (c === ')' || c === ']') { |
| return [c, 'TK_END_EXPR']; |
| } |
| |
| if (c === '{') { |
| return [c, 'TK_START_BLOCK']; |
| } |
| |
| if (c === '}') { |
| return [c, 'TK_END_BLOCK']; |
| } |
| |
| if (c === ';') { |
| return [c, 'TK_SEMICOLON']; |
| } |
| |
| if (c === '/') { |
| var comment = ''; |
| // peek for comment /* ... */ |
| var inline_comment = true; |
| if (input.charAt(parser_pos) === '*') { |
| parser_pos += 1; |
| if (parser_pos < input_length) { |
| while (parser_pos < input_length && !(input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/')) { |
| c = input.charAt(parser_pos); |
| comment += c; |
| if (c === "\n" || c === "\r") { |
| inline_comment = false; |
| } |
| parser_pos += 1; |
| if (parser_pos >= input_length) { |
| break; |
| } |
| } |
| } |
| parser_pos += 2; |
| if (inline_comment && n_newlines === 0) { |
| return ['/*' + comment + '*/', 'TK_INLINE_COMMENT']; |
| } else { |
| return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT']; |
| } |
| } |
| // peek for comment // ... |
| if (input.charAt(parser_pos) === '/') { |
| comment = c; |
| while (input.charAt(parser_pos) !== '\r' && input.charAt(parser_pos) !== '\n') { |
| comment += input.charAt(parser_pos); |
| parser_pos += 1; |
| if (parser_pos >= input_length) { |
| break; |
| } |
| } |
| return [comment, 'TK_COMMENT']; |
| } |
| |
| } |
| |
| |
| if (c === "'" || c === '"' || // string |
| ( |
| (c === '/') || // regexp |
| (opt.e4x && c === "<" && input.slice(parser_pos - 1).match(/^<([-a-zA-Z:0-9_.]+|{[^{}]*}|!\[CDATA\[[\s\S]*?\]\])\s*([-a-zA-Z:0-9_.]+=('[^']*'|"[^"]*"|{[^{}]*})\s*)*\/?\s*>/)) // xml |
| ) && ( // regex and xml can only appear in specific locations during parsing |
| (last_type === 'TK_WORD' && is_special_word(flags.last_text)) || |
| (last_type === 'TK_END_EXPR' && in_array(previous_flags.mode, [MODE.Conditional, MODE.ForInitializer])) || |
| (in_array(last_type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', |
| 'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA' |
| ])) |
| )) { |
| |
| var sep = c, |
| esc = false, |
| has_char_escapes = false; |
| |
| resulting_string = c; |
| |
| if (parser_pos < input_length) { |
| if (sep === '/') { |
| // |
| // handle regexp |
| // |
| var in_char_class = false; |
| while (esc || in_char_class || input.charAt(parser_pos) !== sep) { |
| resulting_string += input.charAt(parser_pos); |
| if (!esc) { |
| esc = input.charAt(parser_pos) === '\\'; |
| if (input.charAt(parser_pos) === '[') { |
| in_char_class = true; |
| } else if (input.charAt(parser_pos) === ']') { |
| in_char_class = false; |
| } |
| } else { |
| esc = false; |
| } |
| parser_pos += 1; |
| if (parser_pos >= input_length) { |
| // incomplete string/rexp when end-of-file reached. |
| // bail out with what had been received so far. |
| return [resulting_string, 'TK_STRING']; |
| } |
| } |
| } else if (opt.e4x && sep === '<') { |
| // |
| // handle e4x xml literals |
| // |
| var xmlRegExp = /<(\/?)([-a-zA-Z:0-9_.]+|{[^{}]*}|!\[CDATA\[[\s\S]*?\]\])\s*([-a-zA-Z:0-9_.]+=('[^']*'|"[^"]*"|{[^{}]*})\s*)*(\/?)\s*>/g; |
| var xmlStr = input.slice(parser_pos - 1); |
| var match = xmlRegExp.exec(xmlStr); |
| if (match && match.index === 0) { |
| var rootTag = match[2]; |
| var depth = 0; |
| while (match) { |
| var isEndTag = !! match[1]; |
| var tagName = match[2]; |
| var isSingletonTag = ( !! match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA["); |
| if (tagName === rootTag && !isSingletonTag) { |
| if (isEndTag) { |
| --depth; |
| } else { |
| ++depth; |
| } |
| } |
| if (depth <= 0) { |
| break; |
| } |
| match = xmlRegExp.exec(xmlStr); |
| } |
| var xmlLength = match ? match.index + match[0].length : xmlStr.length; |
| parser_pos += xmlLength - 1; |
| return [xmlStr.slice(0, xmlLength), "TK_STRING"]; |
| } |
| } else { |
| // |
| // handle string |
| // |
| while (esc || input.charAt(parser_pos) !== sep) { |
| resulting_string += input.charAt(parser_pos); |
| if (esc) { |
| if (input.charAt(parser_pos) === 'x' || input.charAt(parser_pos) === 'u') { |
| has_char_escapes = true; |
| } |
| esc = false; |
| } else { |
| esc = input.charAt(parser_pos) === '\\'; |
| } |
| parser_pos += 1; |
| if (parser_pos >= input_length) { |
| // incomplete string/rexp when end-of-file reached. |
| // bail out with what had been received so far. |
| return [resulting_string, 'TK_STRING']; |
| } |
| } |
| |
| } |
| } |
| |
| parser_pos += 1; |
| resulting_string += sep; |
| |
| if (has_char_escapes && opt.unescape_strings) { |
| resulting_string = unescape_string(resulting_string); |
| } |
| |
| if (sep === '/') { |
| // regexps may have modifiers /regexp/MOD , so fetch those, too |
| while (parser_pos < input_length && in_array(input.charAt(parser_pos), wordchar)) { |
| resulting_string += input.charAt(parser_pos); |
| parser_pos += 1; |
| } |
| } |
| return [resulting_string, 'TK_STRING']; |
| } |
| |
| if (c === '#') { |
| |
| |
| if (output_lines.length === 1 && output_lines[0].text.length === 0 && |
| input.charAt(parser_pos) === '!') { |
| // shebang |
| resulting_string = c; |
| while (parser_pos < input_length && c !== '\n') { |
| c = input.charAt(parser_pos); |
| resulting_string += c; |
| parser_pos += 1; |
| } |
| return [trim(resulting_string) + '\n', 'TK_UNKNOWN']; |
| } |
| |
| |
| |
| // Spidermonkey-specific sharp variables for circular references |
| // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript |
| // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935 |
| var sharp = '#'; |
| if (parser_pos < input_length && in_array(input.charAt(parser_pos), digits)) { |
| do { |
| c = input.charAt(parser_pos); |
| sharp += c; |
| parser_pos += 1; |
| } while (parser_pos < input_length && c !== '#' && c !== '='); |
| if (c === '#') { |
| // |
| } else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') { |
| sharp += '[]'; |
| parser_pos += 2; |
| } else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') { |
| sharp += '{}'; |
| parser_pos += 2; |
| } |
| return [sharp, 'TK_WORD']; |
| } |
| } |
| |
| if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') { |
| parser_pos += 3; |
| c = '<!--'; |
| while (input.charAt(parser_pos) !== '\n' && parser_pos < input_length) { |
| c += input.charAt(parser_pos); |
| parser_pos++; |
| } |
| flags.in_html_comment = true; |
| return [c, 'TK_COMMENT']; |
| } |
| |
| if (c === '-' && flags.in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') { |
| flags.in_html_comment = false; |
| parser_pos += 2; |
| return ['-->', 'TK_COMMENT']; |
| } |
| |
| if (c === '.') { |
| return [c, 'TK_DOT']; |
| } |
| |
| if (in_array(c, punct)) { |
| while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) { |
| c += input.charAt(parser_pos); |
| parser_pos += 1; |
| if (parser_pos >= input_length) { |
| break; |
| } |
| } |
| |
| if (c === ',') { |
| return [c, 'TK_COMMA']; |
| } else if (c === '=') { |
| return [c, 'TK_EQUALS']; |
| } else { |
| return [c, 'TK_OPERATOR']; |
| } |
| } |
| |
| return [c, 'TK_UNKNOWN']; |
| } |
| |
| function handle_start_expr() { |
| if (start_of_statement()) { |
| // The conditional starts the statement if appropriate. |
| } |
| |
| var next_mode = MODE.Expression; |
| if (token_text === '[') { |
| |
| if (last_type === 'TK_WORD' || flags.last_text === ')') { |
| // this is array index specifier, break immediately |
| // a[x], fn()[x] |
| if (in_array(flags.last_text, line_starters)) { |
| output_space_before_token = true; |
| } |
| set_mode(next_mode); |
| print_token(); |
| indent(); |
| if (opt.space_in_paren) { |
| output_space_before_token = true; |
| } |
| return; |
| } |
| |
| next_mode = MODE.ArrayLiteral; |
| if (is_array(flags.mode)) { |
| if (flags.last_text === '[' || |
| (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) { |
| // ], [ goes to new line |
| // }, [ goes to new line |
| if (!opt.keep_array_indentation) { |
| print_newline(); |
| } |
| } |
| } |
| |
| } else { |
| if (flags.last_text === 'for') { |
| next_mode = MODE.ForInitializer; |
| } else if (in_array(flags.last_text, ['if', 'while'])) { |
| next_mode = MODE.Conditional; |
| } else { |
| // next_mode = MODE.Expression; |
| } |
| } |
| |
| if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') { |
| print_newline(); |
| } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') { |
| // TODO: Consider whether forcing this is required. Review failing tests when removed. |
| allow_wrap_or_preserved_newline(input_wanted_newline); |
| output_wrapped = false; |
| // do nothing on (( and )( and ][ and ]( and .( |
| } else if (last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') { |
| output_space_before_token = true; |
| } else if (flags.last_word === 'function' || flags.last_word === 'typeof') { |
| // function() vs function () |
| if (opt.jslint_happy) { |
| output_space_before_token = true; |
| } |
| } else if (in_array(flags.last_text, line_starters) || flags.last_text === 'catch') { |
| if (opt.space_before_conditional) { |
| output_space_before_token = true; |
| } |
| } |
| |
| // Support of this kind of newline preservation. |
| // a = (b && |
| // (c || d)); |
| if (token_text === '(') { |
| if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') { |
| if (!start_of_object_property()) { |
| allow_wrap_or_preserved_newline(); |
| } |
| } |
| } |
| |
| set_mode(next_mode); |
| print_token(); |
| if (opt.space_in_paren) { |
| output_space_before_token = true; |
| } |
| |
| // In all cases, if we newline while inside an expression it should be indented. |
| indent(); |
| } |
| |
| function handle_end_expr() { |
| // statements inside expressions are not valid syntax, but... |
| // statements must all be closed when their container closes |
| while (flags.mode === MODE.Statement) { |
| restore_mode(); |
| } |
| |
| if (token_text === ']' && is_array(flags.mode) && flags.multiline_frame && !opt.keep_array_indentation) { |
| print_newline(); |
| } |
| |
| if (flags.multiline_frame) { |
| allow_wrap_or_preserved_newline(); |
| } |
| if (opt.space_in_paren) { |
| if (last_type === 'TK_START_EXPR') { |
| // () [] no inner space in empty parens like these, ever, ref #320 |
| trim_output(); |
| output_space_before_token = false; |
| } else { |
| output_space_before_token = true; |
| } |
| } |
| if (token_text === ']' && opt.keep_array_indentation) { |
| print_token(); |
| restore_mode(); |
| } else { |
| restore_mode(); |
| print_token(); |
| } |
| remove_redundant_indentation(previous_flags); |
| |
| // do {} while () // no statement required after |
| if (flags.do_while && previous_flags.mode === MODE.Conditional) { |
| previous_flags.mode = MODE.Expression; |
| flags.do_block = false; |
| flags.do_while = false; |
| |
| } |
| } |
| |
| function handle_start_block() { |
| set_mode(MODE.BlockStatement); |
| |
| var empty_braces = is_next('}'); |
| var empty_anonymous_function = empty_braces && flags.last_word === 'function' && |
| last_type === 'TK_END_EXPR'; |
| |
| if (opt.brace_style === "expand") { |
| if (last_type !== 'TK_OPERATOR' && |
| (empty_anonymous_function || |
| last_type === 'TK_EQUALS' || |
| (is_special_word(flags.last_text) && flags.last_text !== 'else'))) { |
| output_space_before_token = true; |
| } else { |
| print_newline(); |
| } |
| } else { // collapse |
| if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') { |
| if (last_type === 'TK_START_BLOCK') { |
| print_newline(); |
| } else { |
| output_space_before_token = true; |
| } |
| } else { |
| // if TK_OPERATOR or TK_START_EXPR |
| if (is_array(previous_flags.mode) && flags.last_text === ',') { |
| if (last_last_text === '}') { |
| // }, { in array context |
| output_space_before_token = true; |
| } else { |
| print_newline(); // [a, b, c, { |
| } |
| } |
| } |
| } |
| print_token(); |
| indent(); |
| } |
| |
| function handle_end_block() { |
| // statements must all be closed when their container closes |
| while (flags.mode === MODE.Statement) { |
| restore_mode(); |
| } |
| var empty_braces = last_type === 'TK_START_BLOCK'; |
| |
| if (opt.brace_style === "expand") { |
| if (!empty_braces) { |
| print_newline(); |
| } |
| } else { |
| // skip {} |
| if (!empty_braces) { |
| if (is_array(flags.mode) && opt.keep_array_indentation) { |
| // we REALLY need a newline here, but newliner would skip that |
| opt.keep_array_indentation = false; |
| print_newline(); |
| opt.keep_array_indentation = true; |
| |
| } else { |
| print_newline(); |
| } |
| } |
| } |
| restore_mode(); |
| print_token(); |
| } |
| |
| function handle_word() { |
| if (start_of_statement()) { |
| // The conditional starts the statement if appropriate. |
| } else if (input_wanted_newline && !is_expression(flags.mode) && |
| (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) && |
| last_type !== 'TK_EQUALS' && |
| (opt.preserve_newlines || flags.last_text !== 'var')) { |
| |
| print_newline(); |
| } |
| |
| if (flags.do_block && !flags.do_while) { |
| if (token_text === 'while') { |
| // do {} ## while () |
| output_space_before_token = true; |
| print_token(); |
| output_space_before_token = true; |
| flags.do_while = true; |
| return; |
| } else { |
| // do {} should always have while as the next word. |
| // if we don't see the expected while, recover |
| print_newline(); |
| flags.do_block = false; |
| } |
| } |
| |
| // if may be followed by else, or not |
| // Bare/inline ifs are tricky |
| // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e(); |
| if (flags.if_block) { |
| if (token_text !== 'else') { |
| while (flags.mode === MODE.Statement) { |
| restore_mode(); |
| } |
| flags.if_block = false; |
| } |
| } |
| |
| if (token_text === 'case' || (token_text === 'default' && flags.in_case_statement)) { |
| print_newline(); |
| if (flags.case_body || opt.jslint_happy) { |
| // switch cases following one another |
| deindent(); |
| flags.case_body = false; |
| } |
| print_token(); |
| flags.in_case = true; |
| flags.in_case_statement = true; |
| return; |
| } |
| |
| if (token_text === 'function') { |
| if (flags.var_line && last_type !== 'TK_EQUALS') { |
| flags.var_line_reindented = true; |
| } |
| if (in_array(flags.last_text, ['}', ';']) || (just_added_newline() && ! in_array(flags.last_text, ['{', ':', '=', ',']))) { |
| // make sure there is a nice clean space of at least one blank line |
| // before a new function definition |
| if ( ! just_added_blankline() && ! flags.had_comment) { |
| print_newline(); |
| print_newline(true); |
| } |
| } |
| if (last_type === 'TK_WORD') { |
| if (flags.last_text === 'get' || flags.last_text === 'set' || flags.last_text === 'new' || flags.last_text === 'return') { |
| output_space_before_token = true; |
| } else { |
| print_newline(); |
| } |
| } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') { |
| // foo = function |
| output_space_before_token = true; |
| } else if (is_expression(flags.mode)) { |
| // (function |
| } else { |
| print_newline(); |
| } |
| } |
| |
| if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') { |
| if (!start_of_object_property()) { |
| allow_wrap_or_preserved_newline(); |
| } |
| } |
| |
| if (token_text === 'function') { |
| print_token(); |
| flags.last_word = token_text; |
| return; |
| } |
| |
| prefix = 'NONE'; |
| |
| if (last_type === 'TK_END_BLOCK') { |
| if (!in_array(token_text, ['else', 'catch', 'finally'])) { |
| prefix = 'NEWLINE'; |
| } else { |
| if (opt.brace_style === "expand" || opt.brace_style === "end-expand") { |
| prefix = 'NEWLINE'; |
| } else { |
| prefix = 'SPACE'; |
| output_space_before_token = true; |
| } |
| } |
| } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) { |
| // TODO: Should this be for STATEMENT as well? |
| prefix = 'NEWLINE'; |
| } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) { |
| prefix = 'SPACE'; |
| } else if (last_type === 'TK_STRING') { |
| prefix = 'NEWLINE'; |
| } else if (last_type === 'TK_WORD') { |
| prefix = 'SPACE'; |
| } else if (last_type === 'TK_START_BLOCK') { |
| prefix = 'NEWLINE'; |
| } else if (last_type === 'TK_END_EXPR') { |
| output_space_before_token = true; |
| prefix = 'NEWLINE'; |
| } |
| |
| if (in_array(token_text, line_starters) && flags.last_text !== ')') { |
| if (flags.last_text === 'else') { |
| prefix = 'SPACE'; |
| } else { |
| prefix = 'NEWLINE'; |
| } |
| |
| } |
| |
| if (in_array(token_text, ['else', 'catch', 'finally'])) { |
| if (last_type !== 'TK_END_BLOCK' || opt.brace_style === "expand" || opt.brace_style === "end-expand") { |
| print_newline(); |
| } else { |
| trim_output(true); |
| var line = output_lines[output_lines.length - 1]; |
| // If we trimmed and there's something other than a close block before us |
| // put a newline back in. Handles '} // comment' scenario. |
| if (line.text[line.text.length - 1] !== '}') { |
| print_newline(); |
| } |
| output_space_before_token = true; |
| } |
| } else if (prefix === 'NEWLINE') { |
| if (is_special_word(flags.last_text)) { |
| // no newline between 'return nnn' |
| output_space_before_token = true; |
| } else if (last_type !== 'TK_END_EXPR') { |
| if ((last_type !== 'TK_START_EXPR' || token_text !== 'var') && flags.last_text !== ':') { |
| // no need to force newline on 'var': for (var x = 0...) |
| if (token_text === 'if' && flags.last_word === 'else' && flags.last_text !== '{') { |
| // no newline for } else if { |
| output_space_before_token = true; |
| } else { |
| flags.var_line = false; |
| flags.var_line_reindented = false; |
| print_newline(); |
| } |
| } |
| } else if (in_array(token_text, line_starters) && flags.last_text !== ')') { |
| flags.var_line = false; |
| flags.var_line_reindented = false; |
| print_newline(); |
| } |
| } else if (is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') { |
| print_newline(); // }, in lists get a newline treatment |
| } else if (prefix === 'SPACE') { |
| output_space_before_token = true; |
| } |
| print_token(); |
| flags.last_word = token_text; |
| |
| if (token_text === 'var') { |
| flags.var_line = true; |
| flags.var_line_reindented = false; |
| flags.var_line_tainted = false; |
| } |
| |
| if (token_text === 'do') { |
| flags.do_block = true; |
| } |
| |
| if (token_text === 'if') { |
| flags.if_block = true; |
| } |
| } |
| |
| function handle_semicolon() { |
| if (start_of_statement()) { |
| // The conditional starts the statement if appropriate. |
| // Semicolon can be the start (and end) of a statement |
| output_space_before_token = false; |
| } |
| while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) { |
| restore_mode(); |
| } |
| print_token(); |
| flags.var_line = false; |
| flags.var_line_reindented = false; |
| if (flags.mode === MODE.ObjectLiteral) { |
| // if we're in OBJECT mode and see a semicolon, its invalid syntax |
| // recover back to treating this as a BLOCK |
| flags.mode = MODE.BlockStatement; |
| } |
| } |
| |
| function handle_string() { |
| if (start_of_statement()) { |
| // The conditional starts the statement if appropriate. |
| // One difference - strings want at least a space before |
| output_space_before_token = true; |
| } else if (last_type === 'TK_WORD') { |
| output_space_before_token = true; |
| } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') { |
| if (!start_of_object_property()) { |
| allow_wrap_or_preserved_newline(); |
| } |
| } else { |
| print_newline(); |
| } |
| print_token(); |
| } |
| |
| function handle_equals() { |
| if (flags.var_line) { |
| // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done |
| flags.var_line_tainted = true; |
| } |
| output_space_before_token = true; |
| print_token(); |
| output_space_before_token = true; |
| } |
| |
| function handle_comma() { |
| if (flags.var_line) { |
| if (is_expression(flags.mode) || last_type === 'TK_END_BLOCK') { |
| // do not break on comma, for(var a = 1, b = 2) |
| flags.var_line_tainted = false; |
| } |
| |
| if (flags.var_line) { |
| flags.var_line_reindented = true; |
| } |
| |
| print_token(); |
| |
| if (flags.var_line_tainted) { |
| flags.var_line_tainted = false; |
| print_newline(); |
| } else { |
| output_space_before_token = true; |
| } |
| return; |
| } |
| |
| if (last_type === 'TK_END_BLOCK' && flags.mode !== MODE.Expression) { |
| print_token(); |
| if (flags.mode === MODE.ObjectLiteral && flags.last_text === '}') { |
| print_newline(); |
| } else { |
| output_space_before_token = true; |
| } |
| } else { |
| if (flags.mode === MODE.ObjectLiteral) { |
| print_token(); |
| print_newline(); |
| } else { |
| // EXPR or DO_BLOCK |
| print_token(); |
| output_space_before_token = true; |
| } |
| } |
| } |
| |
| function handle_operator() { |
| var space_before = true; |
| var space_after = true; |
| if (is_special_word(flags.last_text)) { |
| // "return" had a special handling in TK_WORD. Now we need to return the favor |
| output_space_before_token = true; |
| print_token(); |
| return; |
| } |
| |
| // hack for actionscript's import .*; |
| if (token_text === '*' && last_type === 'TK_DOT' && !last_last_text.match(/^\d+$/)) { |
| print_token(); |
| return; |
| } |
| |
| if (token_text === ':' && flags.in_case) { |
| flags.case_body = true; |
| indent(); |
| print_token(); |
| print_newline(); |
| flags.in_case = false; |
| return; |
| } |
| |
| if (token_text === '::') { |
| // no spaces around exotic namespacing syntax operator |
| print_token(); |
| return; |
| } |
| |
| // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1 |
| // if there is a newline between -- or ++ and anything else we should preserve it. |
| if (input_wanted_newline && (token_text === '--' || token_text === '++')) { |
| print_newline(); |
| } |
| |
| 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(flags.last_text, line_starters) || flags.last_text === ','))) { |
| // unary operators (and binary +/- pretending to be unary) special cases |
| |
| space_before = false; |
| space_after = false; |
| |
| if (flags.last_text === ';' && is_expression(flags.mode)) { |
| // for (;; ++i) |
| // ^^^ |
| space_before = true; |
| } |
| |
| if (last_type === 'TK_WORD' && in_array(flags.last_text, line_starters)) { |
| space_before = true; |
| } |
| |
| if ((flags.mode === MODE.BlockStatement || flags.mode === MODE.Statement) && (flags.last_text === '{' || flags.last_text === ';')) { |
| // { foo; --i } |
| // foo(); --bar; |
| print_newline(); |
| } |
| } else if (token_text === ':') { |
| if (flags.ternary_depth === 0) { |
| if (flags.mode === MODE.BlockStatement) { |
| flags.mode = MODE.ObjectLiteral; |
| } |
| space_before = false; |
| } else { |
| flags.ternary_depth -= 1; |
| } |
| } else if (token_text === '?') { |
| flags.ternary_depth += 1; |
| } |
| output_space_before_token = output_space_before_token || space_before; |
| print_token(); |
| output_space_before_token = space_after; |
| } |
| |
| function handle_block_comment() { |
| var lines = split_newlines(token_text); |
| var j; // iterator for this case |
| var javadoc = false; |
| |
| // block comment starts with a new line |
| print_newline(false, true); |
| if (lines.length > 1) { |
| if (all_lines_start_with(lines.slice(1), '*')) { |
| javadoc = true; |
| } |
| } |
| |
| // first line always indented |
| print_token(lines[0]); |
| for (j = 1; j < lines.length; j++) { |
| print_newline(false, true); |
| if (javadoc) { |
| // javadoc: reformat and re-indent |
| print_token(' ' + trim(lines[j])); |
| } else { |
| // normal comments output raw |
| output_lines[output_lines.length - 1].text.push(lines[j]); |
| } |
| } |
| |
| // for comments of more than one line, make sure there's a new line after |
| print_newline(false, true); |
| } |
| |
| function handle_inline_comment() { |
| output_space_before_token = true; |
| print_token(); |
| output_space_before_token = true; |
| } |
| |
| function handle_comment() { |
| if (input_wanted_newline) { |
| print_newline(false, true); |
| } else { |
| trim_output(true); |
| } |
| |
| output_space_before_token = true; |
| print_token(); |
| print_newline(false, true); |
| } |
| |
| function handle_dot() { |
| if (is_special_word(flags.last_text)) { |
| output_space_before_token = true; |
| } else { |
| // allow preserved newlines before dots in general |
| // force newlines on dots after close paren when break_chained - for bar().baz() |
| allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods); |
| } |
| |
| print_token(); |
| } |
| |
| function handle_unknown() { |
| print_token(); |
| |
| if (token_text[token_text.length - 1] === '\n') { |
| print_newline(); |
| } |
| } |
| } |
| |
| |
| if (typeof define === "function" && define.amd) { |
| define([], function() { |
| return js_beautify; |
| }); |
| } else if (typeof exports !== "undefined") { |
| // Add support for CommonJS. Just put this file somewhere on your require.paths |
| // and you will be able to `var js_beautify = require("beautify").js_beautify`. |
| exports.js_beautify = js_beautify; |
| } else if (typeof window !== "undefined") { |
| // If we're running a web page and don't have either of the above, add our one global |
| window.js_beautify = js_beautify; |
| } else if (typeof global !== "undefined") { |
| // If we don't even have window, try global. |
| global.js_beautify = js_beautify; |
| } |
| |
| }()); |