1/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ 2/* 3 4 The MIT License (MIT) 5 6 Copyright (c) 2007-2013 Einar Lielmanis and contributors. 7 8 Permission is hereby granted, free of charge, to any person 9 obtaining a copy of this software and associated documentation files 10 (the "Software"), to deal in the Software without restriction, 11 including without limitation the rights to use, copy, modify, merge, 12 publish, distribute, sublicense, and/or sell copies of the Software, 13 and to permit persons to whom the Software is furnished to do so, 14 subject to the following conditions: 15 16 The above copyright notice and this permission notice shall be 17 included in all copies or substantial portions of the Software. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 23 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 24 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 25 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 SOFTWARE. 27 28 JS Beautifier 29 --------------- 30 31 32 Written by Einar Lielmanis, <[email protected]> 33 http://jsbeautifier.org/ 34 35 Originally converted to javascript by Vital, <[email protected]> 36 "End braces on own line" added by Chris J. Shull, <[email protected]> 37 Parsing improvements for brace-less statements by Liam Newman <[email protected]> 38 39 40 Usage: 41 js_beautify(js_source_text); 42 js_beautify(js_source_text, options); 43 44 The options are: 45 indent_size (default 4) - indentation size, 46 indent_char (default space) - character to indent with, 47 preserve_newlines (default true) - whether existing line breaks should be preserved, 48 max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk, 49 50 jslint_happy (default false) - if true, then jslint-stricter mode is enforced. 51 52 jslint_happy !jslint_happy 53 --------------------------------- 54 function () function() 55 56 switch () { switch() { 57 case 1: case 1: 58 break; break; 59 } } 60 61 space_after_anon_function (default false) - should the space before an anonymous function's parens be added, "function()" vs "function ()", 62 NOTE: This option is overriden by jslint_happy (i.e. if jslint_happy is true, space_after_anon_function is true by design) 63 64 brace_style (default "collapse") - "collapse-preserve-inline" | "collapse" | "expand" | "end-expand" | "none" 65 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, or attempt to keep them where they are. 66 67 space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)", 68 69 unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x6
695\x78\x61\x6d\x70\x6c\x65" 70 71 wrap_line_length (default unlimited) - lines should wrap at next opportunity after this number of characters. 72 NOTE: This is not a hard limit. Lines will continue until a point where a newline would 73 be preserved if it were present. 74 75 end_with_newline (default false) - end output with a newline 76 77 78 e.g 79 80 js_beautify(js_source_text, { 81 'indent_size': 1, 82 'indent_char': '\t' 83 }); 84 85 */ 86 87// Object.values polyfill found here: 88// http://tokenposts.blogspot.com.au/2012/04/javascript-objectkeys-browser.html 89if (!Object.values) { 90 Object.values = function(o) { 91 if (o !== Object(o)) { 92 throw new TypeError('Object.values called on a non-object'); 93 } 94 var k = [], 95 p; 96 for (p in o) { 97 if (Object.prototype.hasOwnProperty.call(o, p)) { 98 k.push(o[p]); 99 } 100 } 101 return k; 102 }; 103} 104 105(function() { 106 107 function js_beautify(js_source_text, options) { 108 109 var acorn = {}; 110 (function(exports) { 111 /* jshint curly: false */ 112 // This section of code is taken from acorn. 113 // 114 // Acorn was written by Marijn Haverbeke and released under an MIT 115 // license. The Unicode regexps (for identifiers and whitespace) were 116 // taken from [Esprima](http://esprima.org) by Ariya Hidayat. 117 // 118 // Git repositories for Acorn are available at 119 // 120 // http://marijnhaverbeke.nl/git/acorn 121 // https://github.com/marijnh/acorn.git 122 123 // ## Character categories 124 125 // Big ugly regular expressions that match characters in the 126 // whitespace, identifier, and identifier-start categories. These 127 // are only applied when a character is found to actually have a 128 // code point above 128. 129 130 var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line 131 var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u
13119c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; 132 var nonASCIIidentifierChars = "\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; 133 var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); 134 var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); 135 136 // Whether a single character denotes a newline. 137 138 exports.newline = /[\n\r\u2028\u2029]/; 139 140 // Matches a whole line break (where CRLF is considered a single 141 // line break). Used to count lines. 142 143 // in javascript, these two differ 144 // in python they are the same, different methods are called on them 145 exports.lineBreak = new RegExp('\r\n|' + exports.newline.source); 146 exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g'); 147 148 149 // Test whether a given character code starts an identifier. 150 151 exports.isIdentifierStart = function(code) { 152 // permit $ (36) and @ (64). @ is used in ES7 decorators. 153 if (code < 65) return code === 36 || code === 64; 154 // 65 through 91 are uppercase letters. 155 if (code < 91) return true; 156 // permit _ (95). 157 if (code < 97) return code === 95; 158 // 97 through 123 are lowercase letters. 159 if (code < 123) return true; 160 return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)); 161 }; 162 163 // Test whether a given character is part of an identifier. 164 165 exports.isIdentifierChar = function(code) { 166 if (code < 48) return code === 36; 167 if (code < 58) return true; 168 if (code < 65) return false;
169 if (code < 91) return true; 170 if (code < 97) return code === 95; 171 if (code < 123) return true; 172 return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)); 173 }; 174 })(acorn); 175 /* jshint curly: true */ 176 177 function in_array(what, arr) { 178 for (var i = 0; i < arr.length; i += 1) { 179 if (arr[i] === what) { 180 return true; 181 } 182 } 183 return false; 184 } 185 186 function trim(s) { 187 return s.replace(/^\s+|\s+$/g, ''); 188 } 189 190 function ltrim(s) { 191 return s.replace(/^\s+/g, ''); 192 } 193 194 // function rtrim(s) { 195 // return s.replace(/\s+$/g, ''); 196 // } 197 198 function sanitizeOperatorPosition(opPosition) { 199 opPosition = opPosition || OPERATOR_POSITION.before_newline; 200 201 var validPositionValues = Object.values(OPERATOR_POSITION); 202 203 if (!in_array(opPosition, validPositionValues)) { 204 throw new Error("Invalid Option Value: The option 'operator_position' must be one of the following values\n" + 205 validPositionValues + 206 "\nYou passed in: '" + opPosition + "'"); 207 } 208 209 return opPosition; 210 } 211 212 var OPERATOR_POSITION = { 213 before_newline: 'before-newline', 214 after_newline: 'after-newline', 215 preserve_newline: 'preserve-newline', 216 }; 217 218 var OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION.before_newline, OPERATOR_POSITION.preserve_newline]; 219 220 var MODE = { 221 BlockStatement: 'BlockStatement', // 'BLOCK' 222 Statement: 'Statement', // 'STATEMENT' 223 ObjectLiteral: 'ObjectLiteral', // 'OBJECT', 224 ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]', 225 ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)', 226 Conditional: 'Conditional', //'(COND-EXPRESSION)', 227 Expression: 'Expression' //'(EXPRESSION)' 228 }; 229 230 function Beautifier(js_source_text, options) { 231 "use strict"; 232 var output; 233 var tokens = [], 234 token_pos; 235 var Tokenizer; 236 var current_token; 237 var last_type, last_last_text, indent_string; 238 var flags, previous_flags, flag_store; 239 var prefix; 240 241 var handlers, opt; 242 var baseIndentString = ''; 243 244 handlers = { 245 'TK_START_EXPR': handle_start_expr, 246 'TK_END_EXPR': handle_end_expr, 247 'TK_START_BLOCK': handle_start_block, 248 'TK_END_BLOCK': handle_end_block, 249 'TK_WORD': handle_word, 250 'TK_RESERVED': handle_word, 251 'TK_SEMICOLON': handle_semicolon, 252 'TK_STRING': handle_string, 253 'TK_EQUALS': handle_equals, 254 'TK_OPERATOR': handle_operator, 255 'TK_COMMA': handle_comma, 256 'TK_BLOCK_COMMENT': handle_block_comment, 257 'TK_COMMENT': handle_comment, 258 'TK_DOT': handle_dot, 259 'TK_UNKNOWN': handle_unknown, 260 'TK_EOF': handle_eof 261 }; 262 263 function create_flags(flags_base, mode) { 264 var next_indent_level = 0; 265 if (flags_base) { 266 next_indent_level = flags_base.indentation_level; 267 if (!output.just_added_newline() && 268 flags_base.line_indent_level > next_indent_level) { 269 next_indent_level = flags_base.line_indent_level; 270 } 271 } 272 273 var next_flags = { 274 mode: mode, 275 parent: flags_base, 276 last_text: flags_base ? flags_base.last_text : '', // last token text 277 last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed 278 declaration_statement: false, 279 declaration_assignment: false, 280 multiline_frame: false, 281 inline_frame: false, 282 if_block: false, 283 else_block: false, 284 do_block: false, 285 do_while: false, 286 import_block: false, 287 in_case_statement: false, // switch(..){ INSIDE HERE } 288 in_case: false, // we're on the exact line with "case 0:" 289 case_body: false, // the indented case-action block 290 indentation_level: next_indent_level, 291 line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level, 292 start_line_index: output.get_line_number(), 293 ternary_depth: 0 294 }; 295 return next_flags; 296 } 297
298 // Some interpreters have unexpected results with foo = baz || bar; 299 options = options ? options : {}; 300 opt = {}; 301 302 // compatibility 303 if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option 304 opt.brace_style = options.braces_on_own_line ? "expand" : "collapse"; 305 } 306 opt.brace_style = options.brace_style ? options.brace_style : (opt.brace_style ? opt.brace_style : "collapse"); 307 308 // graceful handling of deprecated option 309 if (opt.brace_style === "expand-strict") { 310 opt.brace_style = "expand"; 311 } 312 313 opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4; 314 opt.indent_char = options.indent_char ? options.indent_char : ' '; 315 opt.eol = options.eol ? options.eol : 'auto'; 316 opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines; 317 opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods; 318 opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10); 319 opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren; 320 opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren; 321 opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy; 322 opt.space_after_anon_function = (options.space_after_anon_function === undefined) ? false : options.space_after_anon_function; 323 opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation; 324 opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional; 325 opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings; 326 opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10); 327 opt.e4x = (options.e4x === undefined) ? false : options.e4x; 328 opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; 329 opt.comma_first = (options.comma_first === undefined) ? false : options.comma_first; 330 opt.operator_position = sanitizeOperatorPosition(options.operator_position); 331 332 // For testing of beautify ignore:start directive 333 opt.test_output_raw = (options.test_output_raw === undefined) ? false : options.test_output_raw; 334 335 // force opt.space_after_anon_function to true if opt.jslint_happy 336 if (opt.jslint_happy) { 337 opt.space_after_anon_function = true; 338 } 339 340 if (options.indent_with_tabs) { 341 opt.indent_char = '\t'; 342 opt.indent_size = 1; 343 } 344 345 if (opt.eol === 'auto') { 346 opt.eol = '\n'; 347 if (js_source_text && acorn.lineBreak.test(js_source_text || '')) { 348 opt.eol = js_source_text.match(acorn.lineBreak)[0]; 349 } 350 } 351
352 opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n'); 353 354 //---------------------------------- 355 indent_string = ''; 356 while (opt.indent_size > 0) { 357 indent_string += opt.indent_char; 358 opt.indent_size -= 1; 359 } 360 361 var preindent_index = 0; 362 if (js_source_text && js_source_text.length) { 363 while ((js_source_text.charAt(preindent_index) === ' ' || 364 js_source_text.charAt(preindent_index) === '\t')) { 365 baseIndentString += js_source_text.charAt(preindent_index); 366 preindent_index += 1; 367 } 368 js_source_text = js_source_text.substring(preindent_index); 369 } 370 371 last_type = 'TK_START_BLOCK'; // last token type 372 last_last_text = ''; // pre-last token text 373 output = new Output(indent_string, baseIndentString); 374 375 // If testing the ignore directive, start with output disable set to true 376 output.raw = opt.test_output_raw; 377 378 379 // Stack of parsing/formatting states, including MODE. 380 // We tokenize, parse, and output in an almost purely a forward-only stream of token input 381 // and formatted output. This makes the beautifier less accurate than full parsers 382 // but also far more tolerant of syntax errors. 383 // 384 // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type 385 // MODE.BlockStatement on the the stack, even though it could be object literal. If we later 386 // encounter a ":", we'll switch to to MODE.ObjectLiteral. If we then see a ";", 387 // most full parsers would die, but the beautifier gracefully falls back to 388 // MODE.BlockStatement and continues on. 389 flag_store = []; 390 set_mode(MODE.BlockStatement); 391 392 this.beautify = function() { 393 394 /*jshint onevar:true */ 395 var local_token, sweet_code; 396 Tokenizer = new tokenizer(js_source_text, opt, indent_string); 397 tokens = Tokenizer.tokenize(); 398 token_pos = 0; 399 400 function get_local_token() { 401 local_token = get_token(); 402 return local_token; 403 } 404 405 while (get_local_token()) { 406 for (var i = 0; i < local_token.comments_before.length; i++) { 407 // The cleanest handling of inline comments is to treat them as though they aren't there. 408 // Just continue formatting and the behavior should be logical. 409 // Also ignore unknown tokens. Again, this should result in better behavior. 410 handle_token(local_token.comments_before[i]); 411 } 412 handle_token(local_token); 413 414 last_last_text = flags.last_text; 415 last_type = local_token.type; 416 flags.last_text = local_token.text; 417 418 token_pos += 1; 419 } 420 421 sweet_code = output.get_code(); 422 if (opt.end_with_newline) { 423 sweet_code += '\n'; 424 } 425 426 if (opt.eol !== '\n') { 427 sweet_code = sweet_code.replace(/[\n]/g, opt.eol); 428 } 429 430 return sweet_code; 431 }; 432 433 function handle_token(local_token) { 434 var newlines = local_token.newlines; 435 var keep_whitespace = opt.keep_array_indentation && is_array(flags.mode); 436 437 if (keep_whitespace) { 438 for (var i = 0; i < newlines; i += 1) { 439 print_newline(i > 0); 440 } 441 } else { 442 if (opt.max_preserve_newlines && newlines > opt.max_preserve_newlines) { 443 newlines = opt.max_preserve_newlines; 444 } 445 446 if (opt.preserve_newlines) { 447 if (local_token.newlines > 1) { 448 print_newline(); 449 for (var j = 1; j < newlines; j += 1) { 450 print_newline(true); 451 } 452 } 453 } 454 } 455 456 current_token = local_token; 457 handlers[current_token.type](); 458 } 459 460 // we could use just string.split, but 461 // IE doesn't like returning empty strings 462 function split_linebreaks(s) { 463 //return s.split(/\x0d\x0a|\x0a/); 464 465 s = s.replace(acorn.allLineBreaks, '\n'); 466 var out = [], 467 idx = s.indexOf("\n"); 468 while (idx !== -1) { 469 out.push(s.substring(0, idx)); 470 s = s.substring(idx + 1); 471 idx = s.indexOf("\n"); 472 } 473 if (s.length) { 474 out.push(s); 475 } 476 return out; 477 } 478 479 var newline_restricted_tokens = ['break', 'continue', 'return', 'throw']; 480 481 function allow_wrap_or_preserved_newline(force_linewrap) { 482 force_linewrap = (force_linewrap === undefined) ? false : force_linewrap; 483 484 // Never wrap the first token on a line 485 if (output.just_added_newline()) { 486 return; 487 } 488 489 var shouldPreserveOrForce = (opt.preserve_newlines && current_token.wanted_newline) || force_linewrap; 490 var operatorLogicApplies = in_array(flags.last_text, Tokenizer.positionable_operators) || in_array(current_token.text, Tokenizer.positionable_operators); 491
492 if (operatorLogicApplies) { 493 var shouldPrintOperatorNewline = ( 494 in_array(flags.last_text, Tokenizer.positionable_operators) && 495 in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE) 496 ) || 497 in_array(current_token.text, Tokenizer.positionable_operators); 498 shouldPreserveOrForce = shouldPreserveOrForce && shouldPrintOperatorNewline; 499 } 500 501 if (shouldPreserveOrForce) { 502 print_newline(false, true); 503 } else if (opt.wrap_line_length) { 504 if (last_type === 'TK_RESERVED' && in_array(flags.last_text, newline_restricted_tokens)) { 505 // These tokens should never have a newline inserted 506 // between them and the following expression. 507 return; 508 } 509 var proposed_line_length = output.current_line.get_character_count() + current_token.text.length + 510 (output.space_before_token ? 1 : 0); 511 if (proposed_line_length >= opt.wrap_line_length) { 512 print_newline(false, true); 513 } 514 } 515 } 516 517 function print_newline(force_newline, preserve_statement_flags) { 518 if (!preserve_statement_flags) { 519 if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') { 520 while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) { 521 restore_mode(); 522 } 523 } 524 } 525 526 if (output.add_new_line(force_newline)) { 527 flags.multiline_frame = true; 528 } 529 } 530 531 function print_token_line_indentation() { 532 if (output.just_added_newline()) { 533 if (opt.keep_array_indentation && is_array(flags.mode) && current_token.wanted_newline) { 534 output.current_line.push(current_token.whitespace_before); 535 output.space_before_token = false; 536 } else if (output.set_indent(flags.indentation_level)) { 537 flags.line_indent_level = flags.indentation_level; 538 } 539 } 540 } 541 542 function print_token(printable_token) { 543 if (output.raw) { 544 output.add_raw_token(current_token); 545 return; 546 } 547 548 if (opt.comma_first && last_type === 'TK_COMMA' && 549 output.just_added_newline()) { 550 if (output.previous_line.last() === ',') { 551 var popped = output.previous_line.pop(); 552 // if the comma was already at the start of the line, 553 // pull back onto that line and reprint the indentation 554 if (output.previous_line.is_empty()) { 555 output.previous_line.push(popped); 556 output.trim(true); 557 output.current_line.pop(); 558 output.trim(); 559 } 560 561 // add the comma in front of the next token 562 print_token_line_indentation(); 563 output.add_token(','); 564 output.space_before_token = true; 565 } 566 } 567 568 printable_token = printable_token || current_token.text; 569 print_token_line_indentation(); 570 output.add_token(printable_token); 571 } 572 573 function indent() { 574 flags.indentation_level += 1; 575 } 576 577 function deindent() { 578 if (flags.indentation_level > 0 && 579 ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level)) { 580 flags.indentation_level -= 1; 581 582 } 583 } 584 585 function set_mode(mode) { 586 if (flags) { 587 flag_store.push(flags); 588 previous_flags = flags; 589 } else { 590 previous_flags = create_flags(null, mode); 591 } 592 593 flags = create_flags(previous_flags, mode); 594 } 595 596 function is_array(mode) { 597 return mode === MODE.ArrayLiteral; 598 } 599 600 function is_expression(mode) { 601 return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]); 602 } 603 604 function restore_mode() { 605 if (flag_store.length > 0) { 606 previous_flags = flags; 607 flags = flag_store.pop(); 608 if (previous_flags.mode === MODE.Statement) { 609 output.remove_redundant_indentation(previous_flags); 610 } 611 } 612 } 613 614 function start_of_object_property() { 615 return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && ( 616 (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set']))); 617 } 618
619 function start_of_statement() { 620 if ( 621 (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') || 622 (last_type === 'TK_RESERVED' && flags.last_text === 'do') || 623 (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw']) && !current_token.wanted_newline) || 624 (last_type === 'TK_RESERVED' && flags.last_text === 'else' && !(current_token.type === 'TK_RESERVED' && current_token.text === 'if')) || 625 (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) || 626 (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement && 627 !flags.in_case && 628 !(current_token.text === '--' || current_token.text === '++') && 629 last_last_text !== 'function' && 630 current_token.type !== 'TK_WORD' && current_token.type !== 'TK_RESERVED') || 631 (flags.mode === MODE.ObjectLiteral && ( 632 (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set'])))) 633 ) { 634 635 set_mode(MODE.Statement); 636 indent(); 637 638 if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') { 639 flags.declaration_statement = true; 640 } 641 642 // Issue #276: 643 // If starting a new statement with [if, for, while, do], push to a new line. 644 // if (a) if (b) if(c) d(); else e(); else f(); 645 if (!start_of_object_property()) { 646 allow_wrap_or_preserved_newline( 647 current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['do', 'for', 'if', 'while'])); 648 } 649 650 return true; 651 } 652 return false; 653 } 654 655 function all_lines_start_with(lines, c) { 656 for (var i = 0; i < lines.length; i++) { 657 var line = trim(lines[i]); 658 if (line.charAt(0) !== c) { 659 return false; 660 } 661 } 662 return true; 663 } 664 665 function each_line_matches_indent(lines, indent) { 666 var i = 0, 667 len = lines.length, 668 line; 669 for (; i < len; i++) { 670 line = lines[i]; 671 // allow empty lines to pass through 672 if (line && line.indexOf(indent) !== 0) { 673 return false; 674 } 675 } 676 return true; 677 } 678 679 function is_special_word(word) { 680 return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']); 681 } 682 683 function get_token(offset) { 684 var index = token_pos + (offset || 0); 685 return (index < 0 || index >= tokens.length) ? null : tokens[index]; 686 } 687 688 function handle_start_expr() { 689 if (start_of_statement()) { 690 // The conditional starts the statement if appropriate. 691 } 692 693 var next_mode = MODE.Expression; 694 if (current_token.text === '[') { 695 696 if (last_type === 'TK_WORD' || flags.last_text === ')') { 697 // this is array index specifier, break immediately 698 // a[x], fn()[x] 699 if (last_type === 'TK_RESERVED' && in_array(flags.last_text, Tokenizer.line_starters)) { 700 output.space_before_token = true; 701 } 702 set_mode(next_mode); 703 print_token(); 704 indent(); 705 if (opt.space_in_paren) { 706 output.space_before_token = true; 707 } 708 return; 709 } 710 711 next_mode = MODE.ArrayLiteral; 712 if (is_array(flags.mode)) { 713 if (flags.last_text === '[' || 714 (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) { 715 // ], [ goes to new line 716 // }, [ goes to new line 717 if (!opt.keep_array_indentation) { 718 print_newline(); 719 } 720 } 721 } 722 723 } else { 724 if (last_type === 'TK_RESERVED' && flags.last_text === 'for') { 725 next_mode = MODE.ForInitializer; 726 } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) { 727 next_mode = MODE.Conditional; 728 } else { 729 // next_mode = MODE.Expression; 730 } 731 } 732 733 if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') { 734 print_newline(); 735 } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') { 736 // TODO: Consider whether forcing this is required. Review failing tests when removed. 737 allow_wrap_or_preserved_newline(current_token.wanted_newline); 738 // do nothing on (( and )( and ][ and ]( and .( 739 } else if (!(last_type === 'TK_RESERVED' && current_token.text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') { 740 output.space_before_token = true; 741 } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) || 742 (flags.last_text === '*' && 743 (in_array(last_last_text, ['function', 'yield']) || 744 (flags.mode === MODE.ObjectLiteral && in_array(last_last_text, ['{', ',']))))) { 745 // function() vs function () 746 // yield*() vs yield* () 747 // function*() vs function* () 748 if (opt.space_after_anon_function) { 749 output.space_before_token = true; 750 } 751 } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, Tokenizer.line_starters) || flags.last_text === 'catch')) { 752 if (opt.space_before_conditional) { 753 output.space_before_token = true; 754 } 755 } 756 757 // Should be a space between await and an IIFE 758 if (current_token.text === '(' && last_type === 'TK_RESERVED' && flags.last_word === 'await') { 759 output.space_before_token = true; 760 } 761
762 // Support of this kind of newline preservation. 763 // a = (b && 764 // (c || d)); 765 if (current_token.text === '(') { 766 if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') { 767 if (!start_of_object_property()) { 768 allow_wrap_or_preserved_newline(); 769 } 770 } 771 } 772 773 // Support preserving wrapped arrow function expressions 774 // a.b('c', 775 // () => d.e 776 // ) 777 if (current_token.text === '(' && last_type !== 'TK_WORD' && last_type !== 'TK_RESERVED') { 778 allow_wrap_or_preserved_newline(); 779 } 780 781 set_mode(next_mode); 782 print_token(); 783 if (opt.space_in_paren) { 784 output.space_before_token = true; 785 } 786 787 // In all cases, if we newline while inside an expression it should be indented. 788 indent(); 789 } 790 791 function handle_end_expr() { 792 // statements inside expressions are not valid syntax, but... 793 // statements must all be closed when their container closes 794 while (flags.mode === MODE.Statement) { 795 restore_mode(); 796 } 797 798 if (flags.multiline_frame) { 799 allow_wrap_or_preserved_newline(current_token.text === ']' && is_array(flags.mode) && !opt.keep_array_indentation); 800 } 801 802 if (opt.space_in_paren) { 803 if (last_type === 'TK_START_EXPR' && !opt.space_in_empty_paren) { 804 // () [] no inner space in empty parens like these, ever, ref #320 805 output.trim(); 806 output.space_before_token = false; 807 } else { 808 output.space_before_token = true; 809 } 810 } 811 if (current_token.text === ']' && opt.keep_array_indentation) { 812 print_token(); 813 restore_mode(); 814 } else { 815 restore_mode(); 816 print_token(); 817 } 818 output.remove_redundant_indentation(previous_flags); 819 820 // do {} while () // no statement required after 821 if (flags.do_while && previous_flags.mode === MODE.Conditional) { 822 previous_flags.mode = MODE.Expression; 823 flags.do_block = false; 824 flags.do_while = false; 825 826 } 827 } 828 829 function handle_start_block() { 830 // Check if this is should be treated as a ObjectLiteral 831 var next_token = get_token(1); 832 var second_token = get_token(2); 833 if (second_token && ( 834 (in_array(second_token.text, [':', ',']) && in_array(next_token.type, ['TK_STRING', 'TK_WORD', 'TK_RESERVED'])) || 835 (in_array(next_token.text, ['get', 'set']) && in_array(second_token.type, ['TK_WORD', 'TK_RESERVED'])) 836 )) { 837 // We don't support TypeScript,but we didn't break it for a very long time. 838 // We'll try to keep not breaking it. 839 if (!in_array(last_last_text, ['class', 'interface'])) { 840 set_mode(MODE.ObjectLiteral); 841 } else { 842 set_mode(MODE.BlockStatement); 843 } 844 } else if (last_type === 'TK_OPERATOR' && flags.last_text === '=>') { 845 // arrow function: (param1, paramN) => { statements } 846 set_mode(MODE.BlockStatement); 847 } else if (in_array(last_type, ['TK_EQUALS', 'TK_START_EXPR', 'TK_COMMA', 'TK_OPERATOR']) || 848 (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw', 'import', 'default'])) 849 ) { 850 // Detecting shorthand function syntax is difficult by scanning forward, 851 // so check the surrounding context. 852 // If the block is being returned, imported, export default, passed as arg, 853 // assigned with = or assigned in a nested object, treat as an ObjectLiteral. 854 set_mode(MODE.ObjectLiteral); 855 } else { 856 set_mode(MODE.BlockStatement); 857 } 858 859 var empty_braces = !next_token.comments_before.length && next_token.text === '}'; 860 var empty_anonymous_function = empty_braces && flags.last_word === 'function' && 861 last_type === 'TK_END_EXPR'; 862 863 864 if (opt.brace_style === "expand" || 865 (opt.brace_style === "none" && current_token.wanted_newline)) { 866 if (last_type !== 'TK_OPERATOR' && 867 (empty_anonymous_function || 868 last_type === 'TK_EQUALS' || 869 (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) { 870 output.space_before_token = true; 871 } else { 872 print_newline(false, true); 873 } 874 } else { // collapse 875 if (opt.brace_style === 'collapse-preserve-inline') { 876 // search forward for a newline wanted inside this block 877 var index = 0; 878 var check_token = null; 879 flags.inline_frame = true; 880 do { 881 index += 1; 882 check_token = get_token(index); 883 if (check_token.wanted_newline) { 884 flags.inline_frame = false;
885 break; 886 } 887 } while (check_token.type !== 'TK_EOF' && 888 !(check_token.type === 'TK_END_BLOCK' && check_token.opened === current_token)); 889 } 890 891 if (is_array(previous_flags.mode) && (last_type === 'TK_START_EXPR' || last_type === 'TK_COMMA')) { 892 // if we're preserving inline, 893 // allow newline between comma and next brace. 894 if (last_type === 'TK_COMMA' || opt.space_in_paren) { 895 output.space_before_token = true; 896 } 897 898 if (opt.brace_style === 'collapse-preserve-inline' && 899 (last_type === 'TK_COMMA' || (last_type === 'TK_START_EXPR' && flags.inline_frame))) { 900 allow_wrap_or_preserved_newline(); 901 previous_flags.multiline_frame = previous_flags.multiline_frame || flags.multiline_frame; 902 flags.multiline_frame = false; 903 } 904 } else if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') { 905 if (last_type === 'TK_START_BLOCK') { 906 print_newline(); 907 } else { 908 output.space_before_token = true; 909 } 910 } 911 } 912 print_token(); 913 indent(); 914 } 915 916 function handle_end_block() { 917 // statements must all be closed when their container closes 918 while (flags.mode === MODE.Statement) { 919 restore_mode(); 920 } 921 var empty_braces = last_type === 'TK_START_BLOCK'; 922 923 if (opt.brace_style === "expand") { 924 if (!empty_braces) { 925 print_newline(); 926 } 927 } else { 928 // skip {} 929 if (!empty_braces) { 930 if (flags.inline_frame) { 931 output.space_before_token = true; 932 } else if (is_array(flags.mode) && opt.keep_array_indentation) { 933 // we REALLY need a newline here, but newliner would skip that 934 opt.keep_array_indentation = false; 935 print_newline(); 936 opt.keep_array_indentation = true; 937 938 } else { 939 print_newline(); 940 } 941 } 942 } 943 restore_mode(); 944 print_token(); 945 } 946 947 function handle_word() { 948 if (current_token.type === 'TK_RESERVED') { 949 if (in_array(current_token.text, ['set', 'get']) && flags.mode !== MODE.ObjectLiteral) { 950 current_token.type = 'TK_WORD'; 951 } else if (in_array(current_token.text, ['as', 'from']) && !flags.import_block) { 952 current_token.type = 'TK_WORD'; 953 } else if (flags.mode === MODE.ObjectLiteral) { 954 var next_token = get_token(1); 955 if (next_token.text === ':') { 956 current_token.type = 'TK_WORD'; 957 } 958 } 959 } 960 961 if (start_of_statement()) { 962 // The conditional starts the statement if appropriate. 963 } else if (current_token.wanted_newline && !is_expression(flags.mode) && 964 (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) && 965 last_type !== 'TK_EQUALS' && 966 (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const', 'set', 'get'])))) { 967 968 print_newline(); 969 } 970 971 if (flags.do_block && !flags.do_while) { 972 if (current_token.type === 'TK_RESERVED' && current_token.text === 'while') { 973 // do {} ## while () 974 output.space_before_token = true; 975 print_token(); 976 output.space_before_token = true; 977 flags.do_while = true; 978 return; 979 } else { 980 // do {} should always have while as the next word. 981 // if we don't see the expected while, recover 982 print_newline(); 983 flags.do_block = false;
984 } 985 } 986 987 // if may be followed by else, or not 988 // Bare/inline ifs are tricky 989 // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e(); 990 if (flags.if_block) { 991 if (!flags.else_block && (current_token.type === 'TK_RESERVED' && current_token.text === 'else')) { 992 flags.else_block = true; 993 } else { 994 while (flags.mode === MODE.Statement) { 995 restore_mode(); 996 } 997 flags.if_block = false; 998 flags.else_block = false; 999 } 1000 } 1001 1002 if (current_token.type === 'TK_RESERVED' && (current_token.text === 'case' || (current_token.text === 'default' && flags.in_case_statement))) { 1003 print_newline(); 1004 if (flags.case_body || opt.jslint_happy) { 1005 // switch cases following one another 1006 deindent(); 1007 flags.case_body = false; 1008 } 1009 print_token(); 1010 flags.in_case = true; 1011 flags.in_case_statement = true; 1012 return; 1013 } 1014 1015 if (current_token.type === 'TK_RESERVED' && current_token.text === 'function') { 1016 if (in_array(flags.last_text, ['}', ';']) || (output.just_added_newline() && !in_array(flags.last_text, ['[', '{', ':', '=', ',']))) { 1017 // make sure there is a nice clean space of at least one blank line 1018 // before a new function definition 1019 if (!output.just_added_blankline() && !current_token.comments_before.length) { 1020 print_newline(); 1021 print_newline(true); 1022 } 1023 } 1024 if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') { 1025 if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return', 'export', 'async'])) { 1026 output.space_before_token = true; 1027 } else if (last_type === 'TK_RESERVED' && flags.last_text === 'default' && last_last_text === 'export') { 1028 output.space_before_token = true; 1029 } else { 1030 print_newline(); 1031 } 1032 } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') { 1033 // foo = function 1034 output.space_before_token = true; 1035 } else if (!flags.multiline_frame && (is_expression(flags.mode) || is_array(flags.mode))) { 1036 // (function 1037 } else { 1038 print_newline(); 1039 } 1040 } 1041 1042 if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') { 1043 if (!start_of_object_property()) { 1044 allow_wrap_or_preserved_newline(); 1045 } 1046 } 1047 1048 if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['function', 'get', 'set'])) { 1049 print_token(); 1050 flags.last_word = current_token.text; 1051 return; 1052 } 1053 1054 prefix = 'NONE'; 1055 1056 if (last_type === 'TK_END_BLOCK') { 1057 1058 if (!(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally', 'from']))) { 1059 prefix = 'NEWLINE'; 1060 } else { 1061 if (opt.brace_style === "expand" || 1062 opt.brace_style === "end-expand" || 1063 (opt.brace_style === "none" && current_token.wanted_newline)) { 1064 prefix = 'NEWLINE'; 1065 } else { 1066 prefix = 'SPACE'; 1067 output.space_before_token = true; 1068 } 1069 } 1070 } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) { 1071 // TODO: Should this be for STATEMENT as well? 1072 prefix = 'NEWLINE'; 1073 } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) { 1074 prefix = 'SPACE'; 1075 } else if (last_type === 'TK_STRING') { 1076 prefix = 'NEWLINE'; 1077 } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' || 1078 (flags.last_text === '*' && 1079 (in_array(last_last_text, ['function', 'yield']) || 1080 (flags.mode === MODE.ObjectLiteral && in_array(last_last_text, ['{', ',']))))) { 1081 prefix = 'SPACE'; 1082 } else if (last_type === 'TK_START_BLOCK') { 1083 if (flags.inline_frame) { 1084 prefix = 'SPACE'; 1085 } else { 1086 prefix = 'NEWLINE'; 1087 } 1088 } else if (last_type === 'TK_END_EXPR') { 1089 output.space_before_token = true; 1090 prefix = 'NEWLINE'; 1091 } 1092 1093 if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') { 1094 if (flags.inline_frame || flags.last_text === 'else' || flags.last_text === 'export') { 1095 prefix = 'SPACE'; 1096 } else { 1097 prefix = 'NEWLINE'; 1098 } 1099 1100 } 1101 1102 if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally'])) { 1103 if (!(last_type === 'TK_END_BLOCK' && previous_flags.mode === MODE.BlockStatement) || 1104 opt.brace_style === "expand" || 1105 opt.brace_style === "end-expand" || 1106 (opt.brace_style === "none" && current_token.wanted_newline)) { 1107 print_newline(); 1108 } else { 1109 output.trim(true); 1110 var line = output.current_line; 1111 // If we trimmed and there's something other than a close block before us 1112 // put a newline back in. Handles '} // comment' scenario. 1113 if (line.last() !== '}') { 1114 print_newline(); 1115 } 1116 output.space_before_token = true; 1117 } 1118 } else if (prefix === 'NEWLINE') { 1119 if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) { 1120 // no newline between 'return nnn' 1121 output.space_before_token = true; 1122 } else if (last_type !== 'TK_END_EXPR') { 1123 if ((last_type !== 'TK_START_EXPR' || !(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['var', 'let', 'const']))) && flags.last_text !== ':') { 1124 // no need to force newline on 'var': for (var x = 0...) 1125 if (current_token.type === 'TK_RESERVED' && current_token.text === 'if' && flags.last_text === 'else') { 1126 // no newline for } else if { 1127 output.space_before_token = true; 1128 } else { 1129 print_newline(); 1130 } 1131 } 1132 } else if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') { 1133 print_newline(); 1134 } 1135 } else if (flags.multiline_frame && is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') { 1136 print_newline(); // }, in lists get a newline treatment 1137 } else if (prefix === 'SPACE') { 1138 output.space_before_token = true; 1139 } 1140 print_token(); 1141 flags.last_word = current_token.text; 1142 1143 if (current_token.type === 'TK_RESERVED') { 1144 if (current_token.text === 'do') { 1145 flags.do_block = true; 1146 } else if (current_token.text === 'if') { 1147 flags.if_block = true; 1148 } else if (current_token.text === 'import') { 1149 flags.import_block = true; 1150 } else if (flags.import_block && current_token.type === 'TK_RESERVED' && current_token.text === 'from') { 1151 flags.import_block = false;
1152 } 1153 } 1154 } 1155 1156 function handle_semicolon() { 1157 if (start_of_statement()) { 1158 // The conditional starts the statement if appropriate. 1159 // Semicolon can be the start (and end) of a statement 1160 output.space_before_token = false; 1161 } 1162 while (flags.mode === MODE.Statement && !flags.if_block && !flags.do_block) { 1163 restore_mode(); 1164 } 1165 1166 // hacky but effective for the moment 1167 if (flags.import_block) { 1168 flags.import_block = false; 1169 } 1170 print_token(); 1171 } 1172 1173 function handle_string() { 1174 if (start_of_statement()) { 1175 // The conditional starts the statement if appropriate. 1176 // One difference - strings want at least a space before 1177 output.space_before_token = true; 1178 } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' || flags.inline_frame) { 1179 output.space_before_token = true; 1180 } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') { 1181 if (!start_of_object_property()) { 1182 allow_wrap_or_preserved_newline(); 1183 } 1184 } else { 1185 print_newline(); 1186 } 1187 print_token(); 1188 } 1189 1190 function handle_equals() { 1191 if (start_of_statement()) { 1192 // The conditional starts the statement if appropriate. 1193 } 1194 1195 if (flags.declaration_statement) { 1196 // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done 1197 flags.declaration_assignment = true; 1198 } 1199 output.space_before_token = true; 1200 print_token(); 1201 output.space_before_token = true; 1202 } 1203 1204 function handle_comma() { 1205 print_token(); 1206 output.space_before_token = true; 1207 if (flags.declaration_statement) { 1208 if (is_expression(flags.parent.mode)) { 1209 // do not break on comma, for(var a = 1, b = 2) 1210 flags.declaration_assignment = false; 1211 } 1212 1213 if (flags.declaration_assignment) { 1214 flags.declaration_assignment = false; 1215 print_newline(false, true); 1216 } else if (opt.comma_first) { 1217 // for comma-first, we want to allow a newline before the comma 1218 // to turn into a newline after the comma, which we will fixup later 1219 allow_wrap_or_preserved_newline(); 1220 } 1221 } else if (flags.mode === MODE.ObjectLiteral || 1222 (flags.mode === MODE.Statement && flags.parent.mode === MODE.ObjectLiteral)) { 1223 if (flags.mode === MODE.Statement) { 1224 restore_mode(); 1225 } 1226 1227 if (!flags.inline_frame) { 1228 print_newline(); 1229 } 1230 } else if (opt.comma_first) { 1231 // EXPR or DO_BLOCK 1232 // for comma-first, we want to allow a newline before the comma 1233 // to turn into a newline after the comma, which we will fixup later 1234 allow_wrap_or_preserved_newline(); 1235 } 1236 } 1237 1238 function handle_operator() { 1239 if (start_of_statement()) { 1240 // The conditional starts the statement if appropriate. 1241 } 1242 1243 if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) { 1244 // "return" had a special handling in TK_WORD. Now we need to return the favor 1245 output.space_before_token = true; 1246 print_token(); 1247 return; 1248 } 1249 1250 // hack for actionscript's import .*; 1251 if (current_token.text === '*' && last_type === 'TK_DOT') { 1252 print_token(); 1253 return; 1254 } 1255 1256 if (current_token.text === '::') { 1257 // no spaces around exotic namespacing syntax operator
1258 print_token(); 1259 return; 1260 } 1261 1262 // Allow line wrapping between operators when operator_position is 1263 // set to before or preserve 1264 if (last_type === 'TK_OPERATOR' && in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)) { 1265 allow_wrap_or_preserved_newline(); 1266 } 1267 1268 if (current_token.text === ':' && flags.in_case) { 1269 flags.case_body = true; 1270 indent(); 1271 print_token(); 1272 print_newline(); 1273 flags.in_case = false; 1274 return; 1275 } 1276 1277 var space_before = true; 1278 var space_after = true; 1279 var in_ternary = false; 1280 var isGeneratorAsterisk = current_token.text === '*' && 1281 ((last_type === 'TK_RESERVED' && in_array(flags.last_text, ['function', 'yield'])) || 1282 (flags.mode === MODE.ObjectLiteral && in_array(last_type, ['TK_START_BLOCK', 'TK_COMMA']))); 1283 var isUnary = in_array(current_token.text, ['-', '+']) && ( 1284 in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) || 1285 in_array(flags.last_text, Tokenizer.line_starters) || 1286 flags.last_text === ',' 1287 ); 1288 1289 if (current_token.text === ':') { 1290 if (flags.ternary_depth === 0) { 1291 // Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant. 1292 space_before = false; 1293 } else { 1294 flags.ternary_depth -= 1; 1295 in_ternary = true; 1296 } 1297 } else if (current_token.text === '?') { 1298 flags.ternary_depth += 1; 1299 } 1300 1301 // let's handle the operator_position option prior to any conflicting logic 1302 if (!isUnary && !isGeneratorAsterisk && opt.preserve_newlines && in_array(current_token.text, Tokenizer.positionable_operators)) { 1303 var isColon = current_token.text === ':'; 1304 var isTernaryColon = (isColon && in_ternary); 1305 var isOtherColon = (isColon && !in_ternary); 1306 1307 switch (opt.operator_position) { 1308 case OPERATOR_POSITION.before_newline: 1309 // if the current token is : and it's not a ternary statement then we set space_before to false 1310 output.space_before_token = !isOtherColon; 1311 1312 print_token(); 1313 1314 if (!isColon || isTernaryColon) { 1315 allow_wrap_or_preserved_newline(); 1316 } 1317 1318 output.space_before_token = true; 1319 return; 1320 1321 case OPERATOR_POSITION.after_newline: 1322 // if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement, 1323 // then print a newline. 1324 1325 output.space_before_token = true; 1326 1327 if (!isColon || isTernaryColon) { 1328 if (get_token(1).wanted_newline) { 1329 print_newline(false, true); 1330 } else { 1331 allow_wrap_or_preserved_newline(); 1332 } 1333 } else { 1334 output.space_before_token = false; 1335 } 1336 1337 print_token(); 1338 1339 output.space_before_token = true; 1340 return; 1341 1342 case OPERATOR_POSITION.preserve_newline: 1343 if (!isOtherColon) { 1344 allow_wrap_or_preserved_newline(); 1345 } 1346 1347 // if we just added a newline, or the current token is : and it's not a ternary statement, 1348 // then we set space_before to false 1349 space_before = !(output.just_added_newline() || isOtherColon); 1350 1351 output.space_before_token = space_before; 1352 print_token(); 1353 output.space_before_token = true; 1354 return; 1355 } 1356 } 1357 1358 if (in_array(current_token.text, ['--', '++', '!', '~']) || isUnary) { 1359 // unary operators (and binary +/- pretending to be unary) special cases 1360 1361 space_before = false;
1362 space_after = false; 1363 1364 // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1 1365 // if there is a newline between -- or ++ and anything else we should preserve it. 1366 if (current_token.wanted_newline && (current_token.text === '--' || current_token.text === '++')) { 1367 print_newline(false, true); 1368 } 1369 1370 if (flags.last_text === ';' && is_expression(flags.mode)) { 1371 // for (;; ++i) 1372 // ^^^ 1373 space_before = true; 1374 } 1375 1376 if (last_type === 'TK_RESERVED') { 1377 space_before = true; 1378 } else if (last_type === 'TK_END_EXPR') { 1379 space_before = !(flags.last_text === ']' && (current_token.text === '--' || current_token.text === '++')); 1380 } else if (last_type === 'TK_OPERATOR') { 1381 // a++ + ++b; 1382 // a - -b 1383 space_before = in_array(current_token.text, ['--', '-', '++', '+']) && in_array(flags.last_text, ['--', '-', '++', '+']); 1384 // + and - are not unary when preceeded by -- or ++ operator 1385 // a-- + b 1386 // a * +b 1387 // a - -b 1388 if (in_array(current_token.text, ['+', '-']) && in_array(flags.last_text, ['--', '++'])) { 1389 space_after = true; 1390 } 1391 } 1392 1393 1394 if (((flags.mode === MODE.BlockStatement && !flags.inline_frame) || flags.mode === MODE.Statement) && 1395 (flags.last_text === '{' || flags.last_text === ';')) { 1396 // { foo; --i } 1397 // foo(); --bar; 1398 print_newline(); 1399 } 1400 } else if (isGeneratorAsterisk) { 1401 allow_wrap_or_preserved_newline(); 1402 space_before = false; 1403 space_after = false; 1404 } 1405 output.space_before_token = output.space_before_token || space_before; 1406 print_token(); 1407 output.space_before_token = space_after; 1408 } 1409 1410 function handle_block_comment() { 1411 if (output.raw) { 1412 output.add_raw_token(current_token); 1413 if (current_token.directives && current_token.directives.preserve === 'end') { 1414 // If we're testing the raw output behavior, do not allow a directive to turn it off. 1415 output.raw = opt.test_output_raw; 1416 } 1417 return; 1418 } 1419 1420 if (current_token.directives) { 1421 print_newline(false, true); 1422 print_token(); 1423 if (current_token.directives.preserve === 'start') { 1424 output.raw = true; 1425 } 1426 print_newline(false, true); 1427 return; 1428 } 1429 1430 // inline block 1431 if (!acorn.newline.test(current_token.text) && !current_token.wanted_newline) { 1432 output.space_before_token = true; 1433 print_token(); 1434 output.space_before_token = true; 1435 return; 1436 } 1437 1438 var lines = split_linebreaks(current_token.text); 1439 var j; // iterator for this case 1440 var javadoc = false; 1441 var starless = false; 1442 var lastIndent = current_token.whitespace_before; 1443 var lastIndentLength = lastIndent.length; 1444 1445 // block comment starts with a new line 1446 print_newline(false, true); 1447 if (lines.length > 1) { 1448 javadoc = all_lines_start_with(lines.slice(1), '*'); 1449 starless = each_line_matches_indent(lines.slice(1), lastIndent); 1450 } 1451 1452 // first line always indented
1453 print_token(lines[0]); 1454 for (j = 1; j < lines.length; j++) { 1455 print_newline(false, true); 1456 if (javadoc) { 1457 // javadoc: reformat and re-indent 1458 print_token(' ' + ltrim(lines[j])); 1459 } else if (starless && lines[j].length > lastIndentLength) { 1460 // starless: re-indent non-empty content, avoiding trim 1461 print_token(lines[j].substring(lastIndentLength)); 1462 } else { 1463 // normal comments output raw 1464 output.add_token(lines[j]); 1465 } 1466 } 1467 1468 // for comments of more than one line, make sure there's a new line after 1469 print_newline(false, true); 1470 } 1471 1472 function handle_comment() { 1473 if (current_token.wanted_newline) { 1474 print_newline(false, true); 1475 } else { 1476 output.trim(true); 1477 } 1478 1479 output.space_before_token = true; 1480 print_token(); 1481 print_newline(false, true); 1482 } 1483 1484 function handle_dot() { 1485 if (start_of_statement()) { 1486 // The conditional starts the statement if appropriate. 1487 } 1488 1489 if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) { 1490 output.space_before_token = true; 1491 } else { 1492 // allow preserved newlines before dots in general 1493 // force newlines on dots after close paren when break_chained - for bar().baz() 1494 allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods); 1495 } 1496 1497 print_token(); 1498 } 1499 1500 function handle_unknown() { 1501 print_token(); 1502 1503 if (current_token.text[current_token.text.length - 1] === '\n') { 1504 print_newline(); 1505 } 1506 } 1507 1508 function handle_eof() { 1509 // Unwind any open statements 1510 while (flags.mode === MODE.Statement) { 1511 restore_mode(); 1512 } 1513 } 1514 } 1515 1516 1517 function OutputLine(parent) { 1518 var _character_count = 0; 1519 // use indent_count as a marker for lines that have preserved indentation 1520 var _indent_count = -1; 1521 1522 var _items = []; 1523 var _empty = true; 1524 1525 this.set_indent = function(level) { 1526 _character_count = parent.baseIndentLength + level * parent.indent_length; 1527 _indent_count = level; 1528 }; 1529 1530 this.get_character_count = function() { 1531 return _character_count; 1532 }; 1533 1534 this.is_empty = function() { 1535 return _empty; 1536 }; 1537 1538 this.last = function() { 1539 if (!this._empty) { 1540 return _items[_items.length - 1]; 1541 } else { 1542 return null; 1543 } 1544 }; 1545 1546 this.push = function(input) { 1547 _items.push(input); 1548 _character_count += input.length; 1549 _empty = false; 1550 }; 1551 1552 this.pop = function() { 1553 var item = null; 1554 if (!_empty) { 1555 item = _items.pop(); 1556 _character_count -= item.length; 1557 _empty = _items.length === 0; 1558 } 1559 return item; 1560 }; 1561 1562 this.remove_indent = function() { 1563 if (_indent_count > 0) { 1564 _indent_count -= 1; 1565 _character_count -= parent.indent_length; 1566 } 1567 }; 1568 1569 this.trim = function() { 1570 while (this.last() === ' ') { 1571 _items.pop(); 1572 _character_count -= 1; 1573 } 1574 _empty = _items.length === 0; 1575 }; 1576 1577 this.toString = function() { 1578 var result = ''; 1579 if (!this._empty) { 1580 if (_indent_count >= 0) { 1581 result = parent.indent_cache[_indent_count]; 1582 } 1583 result += _items.join(''); 1584 } 1585 return result; 1586 }; 1587 } 1588
1589 function Output(indent_string, baseIndentString) { 1590 baseIndentString = baseIndentString || ''; 1591 this.indent_cache = [baseIndentString]; 1592 this.baseIndentLength = baseIndentString.length; 1593 this.indent_length = indent_string.length; 1594 this.raw = false; 1595 1596 var lines = []; 1597 this.baseIndentString = baseIndentString; 1598 this.indent_string = indent_string; 1599 this.previous_line = null; 1600 this.current_line = null; 1601 this.space_before_token = false; 1602 1603 this.add_outputline = function() { 1604 this.previous_line = this.current_line; 1605 this.current_line = new OutputLine(this); 1606 lines.push(this.current_line); 1607 }; 1608 1609 // initialize 1610 this.add_outputline(); 1611 1612 1613 this.get_line_number = function() { 1614 return lines.length; 1615 }; 1616 1617 // Using object instead of string to allow for later expansion of info about each line 1618 this.add_new_line = function(force_newline) { 1619 if (this.get_line_number() === 1 && this.just_added_newline()) { 1620 return false; // no newline on start of file 1621 } 1622 1623 if (force_newline || !this.just_added_newline()) { 1624 if (!this.raw) { 1625 this.add_outputline(); 1626 } 1627 return true; 1628 } 1629 1630 return false; 1631 }; 1632 1633 this.get_code = function() { 1634 var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, ''); 1635 return sweet_code; 1636 }; 1637 1638 this.set_indent = function(level) { 1639 // Never indent your first output indent at the start of the file 1640 if (lines.length > 1) { 1641 while (level >= this.indent_cache.length) { 1642 this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string); 1643 } 1644 1645 this.current_line.set_indent(level); 1646 return true; 1647 } 1648 this.current_line.set_indent(0); 1649 return false; 1650 }; 1651 1652 this.add_raw_token = function(token) { 1653 for (var x = 0; x < token.newlines; x++) { 1654 this.add_outputline(); 1655 } 1656 this.current_line.push(token.whitespace_before); 1657 this.current_line.push(token.text); 1658 this.space_before_token = false; 1659 }; 1660 1661 this.add_token = function(printable_token) { 1662 this.add_space_before_token(); 1663 this.current_line.push(printable_token); 1664 }; 1665 1666 this.add_space_before_token = function() { 1667 if (this.space_before_token && !this.just_added_newline()) { 1668 this.current_line.push(' '); 1669 } 1670 this.space_before_token = false; 1671 }; 1672 1673 this.remove_redundant_indentation = function(frame) { 1674 // This implementation is effective but has some issues: 1675 // - can cause line wrap to happen too soon due to indent removal 1676 // after wrap points are calculated 1677 // These issues are minor compared to ugly indentation. 1678 1679 if (frame.multiline_frame || 1680 frame.mode === MODE.ForInitializer || 1681 frame.mode === MODE.Conditional) { 1682 return; 1683 } 1684 1685 // remove one indent from each line inside this section 1686 var index = frame.start_line_index; 1687 1688 var output_length = lines.length; 1689 while (index < output_length) { 1690 lines[index].remove_indent(); 1691 index++; 1692 } 1693 }; 1694 1695 this.trim = function(eat_newlines) { 1696 eat_newlines = (eat_newlines === undefined) ? false : eat_newlines; 1697 1698 this.current_line.trim(indent_string, baseIndentStr
1698ing); 1699 1700 while (eat_newlines && lines.length > 1 && 1701 this.current_line.is_empty()) { 1702 lines.pop(); 1703 this.current_line = lines[lines.length - 1]; 1704 this.current_line.trim(); 1705 } 1706 1707 this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null; 1708 }; 1709 1710 this.just_added_newline = function() { 1711 return this.current_line.is_empty(); 1712 }; 1713 1714 this.just_added_blankline = function() { 1715 if (this.just_added_newline()) { 1716 if (lines.length === 1) { 1717 return true; // start of the file and newline = blank 1718 } 1719 1720 var line = lines[lines.length - 2]; 1721 return line.is_empty(); 1722 } 1723 return false; 1724 }; 1725 } 1726 1727 var InputScanner = function(input) { 1728 var _input = input; 1729 var _input_length = _input.length; 1730 var _position = 0; 1731 1732 this.back = function() { 1733 _position -= 1; 1734 }; 1735 1736 this.hasNext = function() { 1737 return _position < _input_length; 1738 }; 1739 1740 this.next = function() { 1741 var val = null; 1742 if (this.hasNext()) { 1743 val = _input.charAt(_position); 1744 _position += 1; 1745 } 1746 return val; 1747 }; 1748 1749 this.peek = function(index) { 1750 var val = null; 1751 index = index || 0; 1752 index += _position; 1753 if (index >= 0 && index < _input_length) { 1754 val = _input.charAt(index); 1755 } 1756 return val; 1757 }; 1758 1759 this.peekCharCode = function(index) { 1760 var val = 0; 1761 index = index || 0; 1762 index += _position; 1763 if (index >= 0 && index < _input_length) { 1764 val = _input.charCodeAt(index); 1765 } 1766 return val; 1767 }; 1768 1769 this.test = function(pattern, index) { 1770 index = index || 0; 1771 pattern.lastIndex = _position + index; 1772 return pattern.test(_input); 1773 }; 1774 1775 this.testChar = function(pattern, index) { 1776 var val = this.peek(index); 1777 return val !== null && pattern.test(val); 1778 }; 1779 1780 this.match = function(pattern) { 1781 pattern.lastIndex = _position; 1782 var pattern_match = pattern.exec(_input); 1783 if (pattern_match && pattern_match.index === _position) { 1784 _position += pattern_match[0].length; 1785 } else { 1786 pattern_match = null; 1787 } 1788 return pattern_match; 1789 }; 1790 }; 1791 1792 var Token = function(type, text, newlines, whitespace_before, parent) { 1793 this.type = type; 1794 this.text = text; 1795 this.comments_before = []; 1796 this.newlines = newlines || 0; 1797 this.wanted_newline = newlines > 0; 1798 this.whitespace_before = whitespace_before || ''; 1799 this.parent = parent || null; 1800 this.opened = null; 1801 this.directives = null; 1802 }; 1803 1804 function tokenizer(input_string, opts) { 1805 1806 var whitespace = "\n\r\t ".split(''); 1807 var digit = /[0-9]/; 1808 var digit_bin = /[01]/; 1809 var digit_oct = /[01234567]/; 1810 var digit_hex = /[0123456789abcdefABCDEF]/; 1811 1812 this.positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' '); 1813 var punct = this.positionable_operators.concat( 1814 // non-positionable operators - these do not follow operator position settings 1815 '! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~'.split(' ')); 1816 1817 // words which should always start on new line. 1818 this.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(','); 1819 var reserved_words = this.line_starters.concat(['do', 'in', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as']); 1820 1821 // /* ... */ comment ends with nearest */ or end of file 1822 var block_comment_pattern = /([\s\S]*?)((?:\*\/)|$)/g; 1823 1824 // comment ends just before nearest linefeed or end of file 1825 var comment_pattern = /([^\n\r\u2028\u2029]*)/g; 1826 1827 var directives_block_pattern = /\/\* beautify( \w+[:]\w+)+ \*\//g; 1828 var directive_pattern = / (\w+)[:](\w+)/g; 1829 var directives_end_ignore_pattern = /([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)/g; 1830 1831 var template_pattern = /((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)/g; 1832 1833 var n_newlines, whitespace_before_token, in_html_comment, tokens; 1834 var input; 1835 1836 this.tokenize = function() { 1837 input = new InputScanner(input_string); 1838 in_html_comment = false;
1839 tokens = []; 1840 1841 var next, last; 1842 var token_values; 1843 var open = null; 1844 var open_stack = []; 1845 var comments = []; 1846 1847 while (!(last && last.type === 'TK_EOF')) { 1848 token_values = tokenize_next(); 1849 next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token); 1850 while (next.type === 'TK_COMMENT' || next.type === 'TK_BLOCK_COMMENT' || next.type === 'TK_UNKNOWN') { 1851 if (next.type === 'TK_BLOCK_COMMENT') { 1852 next.directives = token_values[2]; 1853 } 1854 comments.push(next); 1855 token_values = tokenize_next(); 1856 next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token); 1857 } 1858 1859 if (comments.length) { 1860 next.comments_before = comments; 1861 comments = []; 1862 } 1863 1864 if (next.type === 'TK_START_BLOCK' || next.type === 'TK_START_EXPR') { 1865 next.parent = last; 1866 open_stack.push(open); 1867 open = next; 1868 } else if ((next.type === 'TK_END_BLOCK' || next.type === 'TK_END_EXPR') && 1869 (open && ( 1870 (next.text === ']' && open.text === '[') || 1871 (next.text === ')' && open.text === '(') || 1872 (next.text === '}' && open.text === '{')))) { 1873 next.parent = open.parent; 1874 next.opened = open; 1875 1876 open = open_stack.pop(); 1877 } 1878 1879 tokens.push(next); 1880 last = next; 1881 } 1882 1883 return tokens; 1884 }; 1885 1886 function get_directives(text) { 1887 if (!text.match(directives_block_pattern)) { 1888 return null; 1889 } 1890 1891 var directives = {}; 1892 directive_pattern.lastIndex = 0; 1893 var directive_match = directive_pattern.exec(text); 1894 1895 while (directive_match) { 1896 directives[directive_match[1]] = directive_match[2]; 1897 directive_match = directive_pattern.exec(text); 1898 } 1899 1900 return directives; 1901 } 1902 1903 function tokenize_next() { 1904 var resulting_string; 1905 var whitespace_on_this_line = []; 1906 1907 n_newlines = 0; 1908 whitespace_before_token = ''; 1909 1910 var c = input.next(); 1911 1912 if (c === null) { 1913 return ['', 'TK_EOF']; 1914 } 1915 1916 var last_token; 1917 if (tokens.length) { 1918 last_token = tokens[tokens.length - 1]; 1919 } else { 1920 // For the sake of tokenizing we can pretend that there was on open brace to start 1921 last_token = new Token('TK_START_BLOCK', '{'); 1922 } 1923 1924 while (in_array(c, whitespace)) { 1925 1926 if (acorn.newline.test(c)) { 1927 if (!(c === '\n' && input.peek(-2) === '\r')) { 1928 n_newlines += 1; 1929 whitespace_on_this_line = []; 1930 } 1931 } else { 1932 whitespace_on_this_line.push(c); 1933 } 1934 1935 c = input.next(); 1936 1937 if (c === null) { 1938 return ['', 'TK_EOF']; 1939 } 1940 } 1941 1942 if (whitespace_on_this_line.length) { 1943 whitespace_before_token = whitespace_on_this_line.join(''); 1944 } 1945 1946 if (digit.test(c) || (c === '.' && input.testChar(digit))) { 1947 var allow_decimal = true; 1948 var allow_e = true; 1949 var local_digit = digit; 1950 1951 if (c === '0' && input.testChar(/[XxOoBb]/)) { 1952 // switch to hex/oct/bin number, no decimal or e, just hex/oct/bin digits 1953 allow_decimal = false;
1954 allow_e = false; 1955 if (input.testChar(/[Bb]/)) { 1956 local_digit = digit_bin; 1957 } else if (input.testChar(/[Oo]/)) { 1958 local_digit = digit_oct; 1959 } else { 1960 local_digit = digit_hex; 1961 } 1962 c += input.next(); 1963 } else if (c === '.') { 1964 // Already have a decimal for this literal, don't allow another 1965 allow_decimal = false; 1966 } else { 1967 // we know this first loop will run. It keeps the logic simpler. 1968 c = ''; 1969 input.back(); 1970 } 1971 1972 // Add the digits 1973 while (input.testChar(local_digit)) { 1974 c += input.next(); 1975 1976 if (allow_decimal && input.peek() === '.') { 1977 c += input.next(); 1978 allow_decimal = false; 1979 } 1980 1981 // a = 1.e-7 is valid, so we test for . then e in one loop 1982 if (allow_e && input.testChar(/[Ee]/)) { 1983 c += input.next(); 1984 1985 if (input.testChar(/[+-]/)) { 1986 c += input.next(); 1987 } 1988 1989 allow_e = false; 1990 allow_decimal = false; 1991 } 1992 } 1993 1994 return [c, 'TK_WORD']; 1995 } 1996 1997 if (acorn.isIdentifierStart(input.peekCharCode(-1))) { 1998 if (input.hasNext()) { 1999 while (acorn.isIdentifierChar(input.peekCharCode())) { 2000 c += input.next(); 2001 if (!input.hasNext()) { 2002 break; 2003 } 2004 } 2005 } 2006 2007 if (!(last_token.type === 'TK_DOT' || 2008 (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get']))) && 2009 in_array(c, reserved_words)) { 2010 if (c === 'in') { // hack for 'in' operator 2011 return [c, 'TK_OPERATOR']; 2012 } 2013 return [c, 'TK_RESERVED']; 2014 } 2015 2016 return [c, 'TK_WORD']; 2017 } 2018 2019 if (c === '(' || c === '[') { 2020 return [c, 'TK_START_EXPR']; 2021 } 2022 2023 if (c === ')' || c === ']') { 2024 return [c, 'TK_END_EXPR']; 2025 } 2026 2027 if (c === '{') { 2028 return [c, 'TK_START_BLOCK']; 2029 } 2030 2031 if (c === '}') { 2032 return [c, 'TK_END_BLOCK']; 2033 } 2034 2035 if (c === ';') { 2036 return [c, 'TK_SEMICOLON']; 2037 } 2038 2039 if (c === '/') { 2040 var comment = ''; 2041 var comment_match; 2042 // peek for comment /* ... */ 2043 if (input.peek() === '*') { 2044 input.next(); 2045 comment_match = input.match(block_comment_pattern); 2046 comment = '/*' + comment_match[0]; 2047 var directives = get_directives(comment); 2048 if (directives && directives.ignore === 'start') { 2049 comment_match = input.match(directives_end_ignore_pattern); 2050 comment += comment_match[0]; 2051 } 2052 comment = comment.replace(acorn.allLineBreaks, '\n'); 2053 return [comment, 'TK_BLOCK_COMMENT', directives]; 2054 } 2055 // peek for comment // ... 2056 if (input.peek() === '/') { 2057 input.next(); 2058 comment_match = input.match(comment_pattern); 2059 comment = '//' + comment_match[0]; 2060 return [comment, 'TK_COMMENT']; 2061 } 2062 2063 } 2064 2065 var startXmlRegExp = /<()([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}
2065|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{[\s\S]+?}))*\s*(\/?)\s*>/g; 2066 2067 if (c === '`' || c === "'" || c === '"' || // string 2068 ( 2069 (c === '/') || // regexp 2070 (opts.e4x && c === "<" && input.test(startXmlRegExp, -1)) // xml 2071 ) && ( // regex and xml can only appear in specific locations during parsing 2072 (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) || 2073 (last_token.type === 'TK_END_EXPR' && last_token.text === ')' && 2074 last_token.parent && last_token.parent.type === 'TK_RESERVED' && in_array(last_token.parent.text, ['if', 'while', 'for'])) || 2075 (in_array(last_token.type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 2076 'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA' 2077 ])) 2078 )) { 2079 2080 var sep = c, 2081 esc = false, 2082 has_char_escapes = false; 2083 2084 resulting_string = c; 2085 2086 if (sep === '/') { 2087 // 2088 // handle regexp 2089 // 2090 var in_char_class = false; 2091 while (input.hasNext() && 2092 ((esc || in_char_class || input.peek() !== sep) && 2093 !input.testChar(acorn.newline))) { 2094 resulting_string += input.peek(); 2095 if (!esc) { 2096 esc = input.peek() === '\\'; 2097 if (input.peek() === '[') { 2098 in_char_class = true; 2099 } else if (input.peek() === ']') { 2100 in_char_class = false; 2101 } 2102 } else { 2103 esc = false; 2104 } 2105 input.next(); 2106 } 2107 } else if (opts.e4x && sep === '<') { 2108 // 2109 // handle e4x xml literals 2110 // 2111 2112 var xmlRegExp = /[\s\S]*?<(\/?)([-a-zA-Z:0-9_.]+|{[\s\S]+?}|!\[CDATA\[[\s\S]*?\]\])(\s+{[\s\S]+?}|\s+[-a-zA-Z:0-9_.]+|\s+[-a-zA-Z:0-9_.]+\s*=\s*('[^']*'|"[^"]*"|{[\s\S]+?}))*\s*(\/?)\s*>/g; 2113 input.back(); 2114 var xmlStr = ''; 2115 var match = input.match(startXmlRegExp); 2116 if (match) { 2117 // Trim root tag to attempt to 2118 var rootTag = match[2].replace(/^{\s+/, '{').replace(/\s+}$/, '}'); 2119 var isCurlyRoot = rootTag.indexOf('{') === 0; 2120 var depth = 0; 2121 while (match) { 2122 var isEndTag = !!match[1]; 2123 var tagName = match[2]; 2124 var isSingletonTag = (!!match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA["); 2125 if (!isSingletonTag && 2126 (tagName === rootTag || (isCurlyRoot && tagName.replace(/^{\s+/, '{').replace(/\s+}$/, '}')))) { 2127 if (isEndTag) { 2128 --depth; 2129 } else { 2130 ++depth; 2131 } 2132 } 2133 xmlStr += match[0]; 2134 if (depth <= 0) { 2135 break; 2136 } 2137 match = input.match(xmlRegExp); 2138 } 2139 // if we didn't close correctly, keep unformatted. 2140 if (!match) { 2141 xmlStr += input.match(/[\s\S]*/g)[0]; 2142 } 2143 xmlStr = xmlStr.replace(acorn.allLineBreaks, '\n'); 2144 return [xmlStr, "TK_STRING"]; 2145 } 2146 } else { 2147 // 2148 // handle string 2149 // 2150 var parse_string = function(delimiter, allow_unescaped_newlines, start_sub) { 2151 // Template strings can travers lines without escape characters. 2152 // Other strings cannot 2153 var current_char; 2154 while (input.hasNext()) { 2155 current_char = input.peek(); 2156 if (!(esc || (current_char !== delimiter && 2157 (allow_unescaped_newlines || !acorn.newline.test(current_char))))) { 2158 break; 2159 } 2160 2161 // Handle \r\n linebreaks after escapes or in template strings 2162 if ((esc || allow_unescaped_newlines) && acorn.newline.test(current_char)) { 2163 if (current_char === '\r' && input.peek(1) === '\n') { 2164 input.next(); 2165 current_char = input.peek(); 2166 } 2167 resulting_string += '\n'; 2168 } else { 2169 resulting_string += current_char; 2170 } 2171 2172 if (esc) { 2173 if (current_char === 'x' || current_char === 'u') { 2174 has_char_escapes = true; 2175 } 2176 esc = false;
2177 } else { 2178 esc = current_char === '\\'; 2179 } 2180 2181 input.next(); 2182 2183 if (start_sub && resulting_string.indexOf(start_sub, resulting_string.length - start_sub.length) !== -1) { 2184 if (delimiter === '`') { 2185 parse_string('}', allow_unescaped_newlines, '`'); 2186 } else { 2187 parse_string('`', allow_unescaped_newlines, '${'); 2188 } 2189 } 2190 } 2191 }; 2192 2193 if (sep === '`') { 2194 parse_string('`', true, '${'); 2195 } else { 2196 parse_string(sep); 2197 } 2198 } 2199 2200 if (has_char_escapes && opts.unescape_strings) { 2201 resulting_string = unescape_string(resulting_string); 2202 } 2203 2204 if (input.peek() === sep) { 2205 resulting_string += sep; 2206 input.next(); 2207 2208 if (sep === '/') { 2209 // regexps may have modifiers /regexp/MOD , so fetch those, too 2210 // Only [gim] are valid, but if the user puts in garbage, do what we can to take it. 2211 while (input.hasNext() && acorn.isIdentifierStart(input.peekCharCode())) { 2212 resulting_string += input.next(); 2213 } 2214 } 2215 } 2216 return [resulting_string, 'TK_STRING']; 2217 } 2218 2219 if (c === '#') { 2220 2221 if (tokens.length === 0 && input.peek() === '!') { 2222 // shebang 2223 resulting_string = c; 2224 while (input.hasNext() && c !== '\n') { 2225 c = input.next(); 2226 resulting_string += c; 2227 } 2228 return [trim(resulting_string) + '\n', 'TK_UNKNOWN']; 2229 } 2230 2231 2232 2233 // Spidermonkey-specific sharp variables for circular references 2234 // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript 2235 // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935 2236 var sharp = '#'; 2237 if (input.hasNext() && input.testChar(digit)) { 2238 do { 2239 c = input.next(); 2240 sharp += c; 2241 } while (input.hasNext() && c !== '#' && c !== '='); 2242 if (c === '#') { 2243 // 2244 } else if (input.peek() === '[' && input.peek(1) === ']') { 2245 sharp += '[]'; 2246 input.next(); 2247 input.next(); 2248 } else if (input.peek() === '{' && input.peek(1) === '}') { 2249 sharp += '{}'; 2250 input.next(); 2251 input.next(); 2252 } 2253 return [sharp, 'TK_WORD']; 2254 } 2255 } 2256 2257 if (c === '<' && (input.peek() === '?' || input.peek() === '%')) { 2258 input.back(); 2259 var template_match = input.match(template_pattern); 2260 if (template_match) { 2261 c = template_match[0]; 2262 c = c.replace(acorn.allLineBreaks, '\n'); 2263 return [c, 'TK_STRING']; 2264 } 2265 } 2266 2267 if (c === '<' && input.match(/\!--/g)) { 2268 c = '<!--'; 2269 while (input.hasNext() && !input.testChar(acorn.newline)) { 2270 c += input.next(); 2271 } 2272 in_html_comment = true; 2273 return [c, 'TK_COMMENT']; 2274 } 2275 2276 if (c === '-' && in_html_comment && input.match(/->/g)) { 2277 in_html_comment = false;
2278 return ['-->', 'TK_COMMENT']; 2279 } 2280 2281 if (c === '.') { 2282 return [c, 'TK_DOT']; 2283 } 2284 2285 if (in_array(c, punct)) { 2286 while (input.hasNext() && in_array(c + input.peek(), punct)) { 2287 c += input.next(); 2288 if (!input.hasNext()) { 2289 break; 2290 } 2291 } 2292 2293 if (c === ',') { 2294 return [c, 'TK_COMMA']; 2295 } else if (c === '=') { 2296 return [c, 'TK_EQUALS']; 2297 } else { 2298 return [c, 'TK_OPERATOR']; 2299 } 2300 } 2301 2302 return [c, 'TK_UNKNOWN']; 2303 } 2304 2305 2306 function unescape_string(s) { 2307 // You think that a regex would work for this 2308 // return s.replace(/\\x([0-9a-f]{2})/gi, function(match, val) { 2309 // return String.fromCharCode(parseInt(val, 16)); 2310 // }) 2311 // However, dealing with '\xff', '\\xff', '\\\xff' makes this more fun. 2312 var out = '', 2313 escaped = 0; 2314 2315 var input_scan = new InputScanner(s); 2316 var matched = null; 2317 2318 while (input_scan.hasNext()) { 2319 // Keep any whitespace, non-slash characters 2320 // also keep slash pairs. 2321 matched = input_scan.match(/([\s]|[^\\]|\\\\)+/g); 2322 2323 if (matched) { 2324 out += matched[0]; 2325 } 2326 2327 if (input_scan.peek() === '\\') { 2328 input_scan.next(); 2329 if (input_scan.peek() === 'x') { 2330 matched = input_scan.match(/x([0-9A-Fa-f]{2})/g); 2331 } else if (input_scan.peek() === 'u') { 2332 matched = input_scan.match(/u([0-9A-Fa-f]{4})/g); 2333 } else { 2334 out += '\\'; 2335 if (input_scan.hasNext()) { 2336 out += input_scan.next(); 2337 } 2338 continue; 2339 } 2340 2341 // If there's some error decoding, return the original string 2342 if (!matched) { 2343 return s; 2344 } 2345 2346 escaped = parseInt(matched[1], 16); 2347 2348 if (escaped > 0x7e && escaped <= 0xff && matched[0].indexOf('x') === 0) { 2349 // we bail out on \x7f..\xff, 2350 // leaving whole string escaped, 2351 // as it's probably completely binary 2352 return s; 2353 } else if (escaped >= 0x00 && escaped < 0x20) { 2354 // leave 0x00...0x1f escaped 2355 out += '\\' + matched[0]; 2356 continue; 2357 } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) { 2358 // single-quote, apostrophe, backslash - escape these 2359 out += '\\' + String.fromCharCode(escaped); 2360 } else { 2361 out += String.fromCharCode(escaped); 2362 } 2363 } 2364 } 2365 2366 return out; 2367 } 2368 } 2369 2370 var beautifier = new Beautifier(js_source_text, options); 2371 return beautifier.beautify(); 2372 2373 } 2374 2375 if (typeof define === "function" && define.amd) { 2376 // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- ) 2377 define([], function() { 2378 return { js_beautify: js_beautify }; 2379 }); 2380 } else if (typeof exports !== "undefined") { 2381 // Add support for CommonJS. Just put this file somewhere on your require.paths 2382 // and you will be able to `var js_beautify = require("beautify").js_beautify`. 2383 exports.js_beautify = js_beautify; 2384 } else if (typeof window !== "undefined") { 2385 // If we're running a web page and don't have either of the above, add our one global 2386 window.js_beautify = js_beautify; 2387 } else if (typeof global !== "undefined") { 2388 // If we don't even have window, try global. 2389 global.js_beautify = js_beautify; 2390 } 2391 2392}());
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.