PageSourceSearch

https://wtools.io/js/check/beautify.js?v=1561625770

js wtools.io collected 2026-10-02 03:28:56 UTC 111,892 bytes, 2,392 lines download raw bytes

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.