PluginProbe
Smart Grid-Layout Design for Contact Form 7 / 3.3.6
Smart Grid-Layout Design for Contact Form 7 v3.3.6
4.18.0 4.17.0 3.2.0 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 4.0.0 4.0.1 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10 4.11 4.12 4.13 4.14 All 119 releases
cf7-grid-layout / assets / beautify / beautify.js

beautify.js in Smart Grid-Layout Design for Contact Form 7 3.3.6, at assets/beautify/beautify.js

2,481 lines 113.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
2 /*
3
4 The MIT License (MIT)
5
6 Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, 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, <einar@jsbeautifier.org>
33 http://jsbeautifier.org/
34
35 Originally converted to javascript by Vital, <vital76@gmail.com>
36 "End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com>
37 Parsing improvements for brace-less statements by Liam Newman <bitwiseman@gmail.com>
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" | "expand" | "end-expand" | "none" | any of the former + ",preserve-inline"
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 preserve-inline will try to preserve inline blocks of curly braces
67
68 space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)",
69
70 unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65"
71
72 wrap_line_length (default unlimited) - lines should wrap at next opportunity after this number of characters.
73 NOTE: This is not a hard limit. Lines will continue until a point where a newline would
74 be preserved if it were present.
75
76 end_with_newline (default false) - end output with a newline
77
78
79 e.g
80
81 js_beautify(js_source_text, {
82 'indent_size': 1,
83 'indent_char': '\t'
84 });
85
86 */
87
88 // Object.values polyfill found here:
89 // http://tokenposts.blogspot.com.au/2012/04/javascript-objectkeys-browser.html
90 if (!Object.values) {
91 Object.values = function(o) {
92 if (o !== Object(o)) {
93 throw new TypeError('Object.values called on a non-object');
94 }
95 var k = [],
96 p;
97 for (p in o) {
98 if (Object.prototype.hasOwnProperty.call(o, p)) {
99 k.push(o[p]);
100 }
101 }
102 return k;
103 };
104 }
105
106 (function() {
107
108 function mergeOpts(allOptions, targetType) {
109 var finalOpts = {};
110 var name;
111
112 for (name in allOptions) {
113 if (name !== targetType) {
114 finalOpts[name] = allOptions[name];
115 }
116 }
117
118 //merge in the per type settings for the targetType
119 if (targetType in allOptions) {
120 for (name in allOptions[targetType]) {
121 finalOpts[name] = allOptions[targetType][name];
122 }
123 }
124 return finalOpts;
125 }
126
127 function js_beautify(js_source_text, options) {
128
129 var acorn = {};
130 (function(exports) {
131 /* jshint curly: false */
132 // This section of code is taken from acorn.
133 //
134 // Acorn was written by Marijn Haverbeke and released under an MIT
135 // license. The Unicode regexps (for identifiers and whitespace) were
136 // taken from [Esprima](http://esprima.org) by Ariya Hidayat.
137 //
138 // Git repositories for Acorn are available at
139 //
140 // http://marijnhaverbeke.nl/git/acorn
141 // https://github.com/marijnh/acorn.git
142
143 // ## Character categories
144
145 // Big ugly regular expressions that match characters in the
146 // whitespace, identifier, and identifier-start categories. These
147 // are only applied when a character is found to actually have a
148 // code point above 128.
149
150 var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line
151 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\u19c1-\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";
152 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";
153 var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
154 var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
155
156 // Whether a single character denotes a newline.
157
158 exports.newline = /[\n\r\u2028\u2029]/;
159
160 // Matches a whole line break (where CRLF is considered a single
161 // line break). Used to count lines.
162
163 // in javascript, these two differ
164 // in python they are the same, different methods are called on them
165 exports.lineBreak = new RegExp('\r\n|' + exports.newline.source);
166 exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g');
167
168
169 // Test whether a given character code starts an identifier.
170
171 exports.isIdentifierStart = function(code) {
172 // permit $ (36) and @ (64). @ is used in ES7 decorators.
173 if (code < 65) return code === 36 || code === 64;
174 // 65 through 91 are uppercase letters.
175 if (code < 91) return true;
176 // permit _ (95).
177 if (code < 97) return code === 95;
178 // 97 through 123 are lowercase letters.
179 if (code < 123) return true;
180 return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
181 };
182
183 // Test whether a given character is part of an identifier.
184
185 exports.isIdentifierChar = function(code) {
186 if (code < 48) return code === 36;
187 if (code < 58) return true;
188 if (code < 65) return false;
189 if (code < 91) return true;
190 if (code < 97) return code === 95;
191 if (code < 123) return true;
192 return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
193 };
194 })(acorn);
195 /* jshint curly: true */
196
197 function in_array(what, arr) {
198 for (var i = 0; i < arr.length; i += 1) {
199 if (arr[i] === what) {
200 return true;
201 }
202 }
203 return false;
204 }
205
206 function trim(s) {
207 return s.replace(/^\s+|\s+$/g, '');
208 }
209
210 function ltrim(s) {
211 return s.replace(/^\s+/g, '');
212 }
213
214 // function rtrim(s) {
215 // return s.replace(/\s+$/g, '');
216 // }
217
218 function sanitizeOperatorPosition(opPosition) {
219 opPosition = opPosition || OPERATOR_POSITION.before_newline;
220
221 var validPositionValues = Object.values(OPERATOR_POSITION);
222
223 if (!in_array(opPosition, validPositionValues)) {
224 throw new Error("Invalid Option Value: The option 'operator_position' must be one of the following values\n" +
225 validPositionValues +
226 "\nYou passed in: '" + opPosition + "'");
227 }
228
229 return opPosition;
230 }
231
232 var OPERATOR_POSITION = {
233 before_newline: 'before-newline',
234 after_newline: 'after-newline',
235 preserve_newline: 'preserve-newline',
236 };
237
238 var OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION.before_newline, OPERATOR_POSITION.preserve_newline];
239
240 var MODE = {
241 BlockStatement: 'BlockStatement', // 'BLOCK'
242 Statement: 'Statement', // 'STATEMENT'
243 ObjectLiteral: 'ObjectLiteral', // 'OBJECT',
244 ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]',
245 ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)',
246 Conditional: 'Conditional', //'(COND-EXPRESSION)',
247 Expression: 'Expression' //'(EXPRESSION)'
248 };
249
250 function Beautifier(js_source_text, options) {
251 "use strict";
252 var output;
253 var tokens = [],
254 token_pos;
255 var Tokenizer;
256 var current_token;
257 var last_type, last_last_text, indent_string;
258 var flags, previous_flags, flag_store;
259 var prefix;
260
261 var handlers, opt;
262 var baseIndentString = '';
263
264 handlers = {
265 'TK_START_EXPR': handle_start_expr,
266 'TK_END_EXPR': handle_end_expr,
267 'TK_START_BLOCK': handle_start_block,
268 'TK_END_BLOCK': handle_end_block,
269 'TK_WORD': handle_word,
270 'TK_RESERVED': handle_word,
271 'TK_SEMICOLON': handle_semicolon,
272 'TK_STRING': handle_string,
273 'TK_EQUALS': handle_equals,
274 'TK_OPERATOR': handle_operator,
275 'TK_COMMA': handle_comma,
276 'TK_BLOCK_COMMENT': handle_block_comment,
277 'TK_COMMENT': handle_comment,
278 'TK_DOT': handle_dot,
279 'TK_UNKNOWN': handle_unknown,
280 'TK_EOF': handle_eof
281 };
282
283 function create_flags(flags_base, mode) {
284 var next_indent_level = 0;
285 if (flags_base) {
286 next_indent_level = flags_base.indentation_level;
287 if (!output.just_added_newline() &&
288 flags_base.line_indent_level > next_indent_level) {
289 next_indent_level = flags_base.line_indent_level;
290 }
291 }
292
293 var next_flags = {
294 mode: mode,
295 parent: flags_base,
296 last_text: flags_base ? flags_base.last_text : '', // last token text
297 last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed
298 declaration_statement: false,
299 declaration_assignment: false,
300 multiline_frame: false,
301 inline_frame: false,
302 if_block: false,
303 else_block: false,
304 do_block: false,
305 do_while: false,
306 import_block: false,
307 in_case_statement: false, // switch(..){ INSIDE HERE }
308 in_case: false, // we're on the exact line with "case 0:"
309 case_body: false, // the indented case-action block
310 indentation_level: next_indent_level,
311 line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level,
312 start_line_index: output.get_line_number(),
313 ternary_depth: 0
314 };
315 return next_flags;
316 }
317
318 // Some interpreters have unexpected results with foo = baz || bar;
319 options = options ? options : {};
320
321 // Allow the setting of language/file-type specific options
322 // with inheritance of overall settings
323 options = mergeOpts(options, 'js');
324
325 opt = {};
326
327 // compatibility, re
328 if (options.brace_style === "expand-strict") { //graceful handling of deprecated option
329 options.brace_style = "expand";
330 } else if (options.brace_style === "collapse-preserve-inline") { //graceful handling of deprecated option
331 options.brace_style = "collapse,preserve-inline";
332 } else if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
333 options.brace_style = options.braces_on_own_line ? "expand" : "collapse";
334 } else if (!options.brace_style) //Nothing exists to set it
335 {
336 options.brace_style = "collapse";
337 }
338
339
340 var brace_style_split = options.brace_style.split(/[^a-zA-Z0-9_\-]+/);
341 opt.brace_style = brace_style_split[0];
342 opt.brace_preserve_inline = brace_style_split[1] ? brace_style_split[1] : false;
343
344 opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4;
345 opt.indent_char = options.indent_char ? options.indent_char : ' ';
346 opt.eol = options.eol ? options.eol : 'auto';
347 opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
348 opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods;
349 opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10);
350 opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren;
351 opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren;
352 opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy;
353 opt.space_after_anon_function = (options.space_after_anon_function === undefined) ? false : options.space_after_anon_function;
354 opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation;
355 opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional;
356 opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings;
357 opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10);
358 opt.e4x = (options.e4x === undefined) ? false : options.e4x;
359 opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
360 opt.comma_first = (options.comma_first === undefined) ? false : options.comma_first;
361 opt.operator_position = sanitizeOperatorPosition(options.operator_position);
362
363 // For testing of beautify ignore:start directive
364 opt.test_output_raw = (options.test_output_raw === undefined) ? false : options.test_output_raw;
365
366 // force opt.space_after_anon_function to true if opt.jslint_happy
367 if (opt.jslint_happy) {
368 opt.space_after_anon_function = true;
369 }
370
371 if (options.indent_with_tabs) {
372 opt.indent_char = '\t';
373 opt.indent_size = 1;
374 }
375
376 if (opt.eol === 'auto') {
377 opt.eol = '\n';
378 if (js_source_text && acorn.lineBreak.test(js_source_text || '')) {
379 opt.eol = js_source_text.match(acorn.lineBreak)[0];
380 }
381 }
382
383 opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n');
384
385 //----------------------------------
386 indent_string = '';
387 while (opt.indent_size > 0) {
388 indent_string += opt.indent_char;
389 opt.indent_size -= 1;
390 }
391
392 var preindent_index = 0;
393 if (js_source_text && js_source_text.length) {
394 while ((js_source_text.charAt(preindent_index) === ' ' ||
395 js_source_text.charAt(preindent_index) === '\t')) {
396 baseIndentString += js_source_text.charAt(preindent_index);
397 preindent_index += 1;
398 }
399 js_source_text = js_source_text.substring(preindent_index);
400 }
401
402 last_type = 'TK_START_BLOCK'; // last token type
403 last_last_text = ''; // pre-last token text
404 output = new Output(indent_string, baseIndentString);
405
406 // If testing the ignore directive, start with output disable set to true
407 output.raw = opt.test_output_raw;
408
409
410 // Stack of parsing/formatting states, including MODE.
411 // We tokenize, parse, and output in an almost purely a forward-only stream of token input
412 // and formatted output. This makes the beautifier less accurate than full parsers
413 // but also far more tolerant of syntax errors.
414 //
415 // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type
416 // MODE.BlockStatement on the the stack, even though it could be object literal. If we later
417 // encounter a ":", we'll switch to to MODE.ObjectLiteral. If we then see a ";",
418 // most full parsers would die, but the beautifier gracefully falls back to
419 // MODE.BlockStatement and continues on.
420 flag_store = [];
421 set_mode(MODE.BlockStatement);
422
423 this.beautify = function() {
424
425 /*jshint onevar:true */
426 var sweet_code;
427 Tokenizer = new tokenizer(js_source_text, opt, indent_string);
428 tokens = Tokenizer.tokenize();
429 token_pos = 0;
430
431 current_token = get_token();
432 while (current_token) {
433 handlers[current_token.type]();
434
435 last_last_text = flags.last_text;
436 last_type = current_token.type;
437 flags.last_text = current_token.text;
438
439 token_pos += 1;
440 current_token = get_token();
441 }
442
443 sweet_code = output.get_code();
444 if (opt.end_with_newline) {
445 sweet_code += '\n';
446 }
447
448 if (opt.eol !== '\n') {
449 sweet_code = sweet_code.replace(/[\n]/g, opt.eol);
450 }
451
452 return sweet_code;
453 };
454
455 function handle_whitespace_and_comments(local_token, preserve_statement_flags) {
456 var newlines = local_token.newlines;
457 var keep_whitespace = opt.keep_array_indentation && is_array(flags.mode);
458 var temp_token = current_token;
459
460 for (var h = 0; h < local_token.comments_before.length; h++) {
461 // The cleanest handling of inline comments is to treat them as though they aren't there.
462 // Just continue formatting and the behavior should be logical.
463 // Also ignore unknown tokens. Again, this should result in better behavior.
464 current_token = local_token.comments_before[h];
465 handle_whitespace_and_comments(current_token, preserve_statement_flags);
466 handlers[current_token.type](preserve_statement_flags);
467 }
468 current_token = temp_token;
469
470 if (keep_whitespace) {
471 for (var i = 0; i < newlines; i += 1) {
472 print_newline(i > 0, preserve_statement_flags);
473 }
474 } else {
475 if (opt.max_preserve_newlines && newlines > opt.max_preserve_newlines) {
476 newlines = opt.max_preserve_newlines;
477 }
478
479 if (opt.preserve_newlines) {
480 if (local_token.newlines > 1) {
481 print_newline(false, preserve_statement_flags);
482 for (var j = 1; j < newlines; j += 1) {
483 print_newline(true, preserve_statement_flags);
484 }
485 }
486 }
487 }
488
489 }
490
491 // we could use just string.split, but
492 // IE doesn't like returning empty strings
493 function split_linebreaks(s) {
494 //return s.split(/\x0d\x0a|\x0a/);
495
496 s = s.replace(acorn.allLineBreaks, '\n');
497 var out = [],
498 idx = s.indexOf("\n");
499 while (idx !== -1) {
500 out.push(s.substring(0, idx));
501 s = s.substring(idx + 1);
502 idx = s.indexOf("\n");
503 }
504 if (s.length) {
505 out.push(s);
506 }
507 return out;
508 }
509
510 var newline_restricted_tokens = ['break', 'continue', 'return', 'throw'];
511
512 function allow_wrap_or_preserved_newline(force_linewrap) {
513 force_linewrap = (force_linewrap === undefined) ? false : force_linewrap;
514
515 // Never wrap the first token on a line
516 if (output.just_added_newline()) {
517 return;
518 }
519
520 var shouldPreserveOrForce = (opt.preserve_newlines && current_token.wanted_newline) || force_linewrap;
521 var operatorLogicApplies = in_array(flags.last_text, Tokenizer.positionable_operators) || in_array(current_token.text, Tokenizer.positionable_operators);
522
523 if (operatorLogicApplies) {
524 var shouldPrintOperatorNewline = (
525 in_array(flags.last_text, Tokenizer.positionable_operators) &&
526 in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)
527 ) ||
528 in_array(current_token.text, Tokenizer.positionable_operators);
529 shouldPreserveOrForce = shouldPreserveOrForce && shouldPrintOperatorNewline;
530 }
531
532 if (shouldPreserveOrForce) {
533 print_newline(false, true);
534 } else if (opt.wrap_line_length) {
535 if (last_type === 'TK_RESERVED' && in_array(flags.last_text, newline_restricted_tokens)) {
536 // These tokens should never have a newline inserted
537 // between them and the following expression.
538 return;
539 }
540 var proposed_line_length = output.current_line.get_character_count() + current_token.text.length +
541 (output.space_before_token ? 1 : 0);
542 if (proposed_line_length >= opt.wrap_line_length) {
543 print_newline(false, true);
544 }
545 }
546 }
547
548 function print_newline(force_newline, preserve_statement_flags) {
549 if (!preserve_statement_flags) {
550 if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') {
551 var next_token = get_token(1);
552 while (flags.mode === MODE.Statement &&
553 !(flags.if_block && next_token && next_token.type === 'TK_RESERVED' && next_token.text === 'else') &&
554 !flags.do_block) {
555 restore_mode();
556 }
557 }
558 }
559
560 if (output.add_new_line(force_newline)) {
561 flags.multiline_frame = true;
562 }
563 }
564
565 function print_token_line_indentation() {
566 if (output.just_added_newline()) {
567 if (opt.keep_array_indentation && is_array(flags.mode) && current_token.wanted_newline) {
568 output.current_line.push(current_token.whitespace_before);
569 output.space_before_token = false;
570 } else if (output.set_indent(flags.indentation_level)) {
571 flags.line_indent_level = flags.indentation_level;
572 }
573 }
574 }
575
576 function print_token(printable_token) {
577 if (output.raw) {
578 output.add_raw_token(current_token);
579 return;
580 }
581
582 if (opt.comma_first && last_type === 'TK_COMMA' &&
583 output.just_added_newline()) {
584 if (output.previous_line.last() === ',') {
585 var popped = output.previous_line.pop();
586 // if the comma was already at the start of the line,
587 // pull back onto that line and reprint the indentation
588 if (output.previous_line.is_empty()) {
589 output.previous_line.push(popped);
590 output.trim(true);
591 output.current_line.pop();
592 output.trim();
593 }
594
595 // add the comma in front of the next token
596 print_token_line_indentation();
597 output.add_token(',');
598 output.space_before_token = true;
599 }
600 }
601
602 printable_token = printable_token || current_token.text;
603 print_token_line_indentation();
604 output.add_token(printable_token);
605 }
606
607 function indent() {
608 flags.indentation_level += 1;
609 }
610
611 function deindent() {
612 if (flags.indentation_level > 0 &&
613 ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level)) {
614 flags.indentation_level -= 1;
615
616 }
617 }
618
619 function set_mode(mode) {
620 if (flags) {
621 flag_store.push(flags);
622 previous_flags = flags;
623 } else {
624 previous_flags = create_flags(null, mode);
625 }
626
627 flags = create_flags(previous_flags, mode);
628 }
629
630 function is_array(mode) {
631 return mode === MODE.ArrayLiteral;
632 }
633
634 function is_expression(mode) {
635 return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]);
636 }
637
638 function restore_mode() {
639 if (flag_store.length > 0) {
640 previous_flags = flags;
641 flags = flag_store.pop();
642 if (previous_flags.mode === MODE.Statement) {
643 output.remove_redundant_indentation(previous_flags);
644 }
645 }
646 }
647
648 function start_of_object_property() {
649 return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && (
650 (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set'])));
651 }
652
653 function start_of_statement() {
654 if (
655 (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') ||
656 (last_type === 'TK_RESERVED' && flags.last_text === 'do') ||
657 (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw']) && !current_token.wanted_newline) ||
658 (last_type === 'TK_RESERVED' && flags.last_text === 'else' &&
659 !(current_token.type === 'TK_RESERVED' && current_token.text === 'if' && !current_token.comments_before.length)) ||
660 (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) ||
661 (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement &&
662 !flags.in_case &&
663 !(current_token.text === '--' || current_token.text === '++') &&
664 last_last_text !== 'function' &&
665 current_token.type !== 'TK_WORD' && current_token.type !== 'TK_RESERVED') ||
666 (flags.mode === MODE.ObjectLiteral && (
667 (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set']))))
668 ) {
669
670 set_mode(MODE.Statement);
671 indent();
672
673 handle_whitespace_and_comments(current_token, true);
674
675 // Issue #276:
676 // If starting a new statement with [if, for, while, do], push to a new line.
677 // if (a) if (b) if(c) d(); else e(); else f();
678 if (!start_of_object_property()) {
679 allow_wrap_or_preserved_newline(
680 current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['do', 'for', 'if', 'while']));
681 }
682
683 return true;
684 }
685 return false;
686 }
687
688 function all_lines_start_with(lines, c) {
689 for (var i = 0; i < lines.length; i++) {
690 var line = trim(lines[i]);
691 if (line.charAt(0) !== c) {
692 return false;
693 }
694 }
695 return true;
696 }
697
698 function each_line_matches_indent(lines, indent) {
699 var i = 0,
700 len = lines.length,
701 line;
702 for (; i < len; i++) {
703 line = lines[i];
704 // allow empty lines to pass through
705 if (line && line.indexOf(indent) !== 0) {
706 return false;
707 }
708 }
709 return true;
710 }
711
712 function is_special_word(word) {
713 return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
714 }
715
716 function get_token(offset) {
717 var index = token_pos + (offset || 0);
718 return (index < 0 || index >= tokens.length) ? null : tokens[index];
719 }
720
721 function handle_start_expr() {
722 // The conditional starts the statement if appropriate.
723 if (!start_of_statement()) {
724 handle_whitespace_and_comments(current_token);
725 }
726
727 var next_mode = MODE.Expression;
728 if (current_token.text === '[') {
729
730 if (last_type === 'TK_WORD' || flags.last_text === ')') {
731 // this is array index specifier, break immediately
732 // a[x], fn()[x]
733 if (last_type === 'TK_RESERVED' && in_array(flags.last_text, Tokenizer.line_starters)) {
734 output.space_before_token = true;
735 }
736 set_mode(next_mode);
737 print_token();
738 indent();
739 if (opt.space_in_paren) {
740 output.space_before_token = true;
741 }
742 return;
743 }
744
745 next_mode = MODE.ArrayLiteral;
746 if (is_array(flags.mode)) {
747 if (flags.last_text === '[' ||
748 (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) {
749 // ], [ goes to new line
750 // }, [ goes to new line
751 if (!opt.keep_array_indentation) {
752 print_newline();
753 }
754 }
755 }
756
757 } else {
758 if (last_type === 'TK_RESERVED' && flags.last_text === 'for') {
759 next_mode = MODE.ForInitializer;
760 } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) {
761 next_mode = MODE.Conditional;
762 } else {
763 // next_mode = MODE.Expression;
764 }
765 }
766
767 if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') {
768 print_newline();
769 } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') {
770 // TODO: Consider whether forcing this is required. Review failing tests when removed.
771 allow_wrap_or_preserved_newline(current_token.wanted_newline);
772 // do nothing on (( and )( and ][ and ]( and .(
773 } else if (!(last_type === 'TK_RESERVED' && current_token.text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
774 output.space_before_token = true;
775 } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) ||
776 (flags.last_text === '*' &&
777 (in_array(last_last_text, ['function', 'yield']) ||
778 (flags.mode === MODE.ObjectLiteral && in_array(last_last_text, ['{', ',']))))) {
779 // function() vs function ()
780 // yield*() vs yield* ()
781 // function*() vs function* ()
782 if (opt.space_after_anon_function) {
783 output.space_before_token = true;
784 }
785 } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, Tokenizer.line_starters) || flags.last_text === 'catch')) {
786 if (opt.space_before_conditional) {
787 output.space_before_token = true;
788 }
789 }
790
791 // Should be a space between await and an IIFE
792 if (current_token.text === '(' && last_type === 'TK_RESERVED' && flags.last_word === 'await') {
793 output.space_before_token = true;
794 }
795
796 // Support of this kind of newline preservation.
797 // a = (b &&
798 // (c || d));
799 if (current_token.text === '(') {
800 if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
801 if (!start_of_object_property()) {
802 allow_wrap_or_preserved_newline();
803 }
804 }
805 }
806
807 // Support preserving wrapped arrow function expressions
808 // a.b('c',
809 // () => d.e
810 // )
811 if (current_token.text === '(' && last_type !== 'TK_WORD' && last_type !== 'TK_RESERVED') {
812 allow_wrap_or_preserved_newline();
813 }
814
815 set_mode(next_mode);
816 print_token();
817 if (opt.space_in_paren) {
818 output.space_before_token = true;
819 }
820
821 // In all cases, if we newline while inside an expression it should be indented.
822 indent();
823 }
824
825 function handle_end_expr() {
826 // statements inside expressions are not valid syntax, but...
827 // statements must all be closed when their container closes
828 while (flags.mode === MODE.Statement) {
829 restore_mode();
830 }
831
832 handle_whitespace_and_comments(current_token);
833
834 if (flags.multiline_frame) {
835 allow_wrap_or_preserved_newline(current_token.text === ']' && is_array(flags.mode) && !opt.keep_array_indentation);
836 }
837
838 if (opt.space_in_paren) {
839 if (last_type === 'TK_START_EXPR' && !opt.space_in_empty_paren) {
840 // () [] no inner space in empty parens like these, ever, ref #320
841 output.trim();
842 output.space_before_token = false;
843 } else {
844 output.space_before_token = true;
845 }
846 }
847 if (current_token.text === ']' && opt.keep_array_indentation) {
848 print_token();
849 restore_mode();
850 } else {
851 restore_mode();
852 print_token();
853 }
854 output.remove_redundant_indentation(previous_flags);
855
856 // do {} while () // no statement required after
857 if (flags.do_while && previous_flags.mode === MODE.Conditional) {
858 previous_flags.mode = MODE.Expression;
859 flags.do_block = false;
860 flags.do_while = false;
861
862 }
863 }
864
865 function handle_start_block() {
866 handle_whitespace_and_comments(current_token);
867
868 // Check if this is should be treated as a ObjectLiteral
869 var next_token = get_token(1);
870 var second_token = get_token(2);
871 if (second_token && (
872 (in_array(second_token.text, [':', ',']) && in_array(next_token.type, ['TK_STRING', 'TK_WORD', 'TK_RESERVED'])) ||
873 (in_array(next_token.text, ['get', 'set', '...']) && in_array(second_token.type, ['TK_WORD', 'TK_RESERVED']))
874 )) {
875 // We don't support TypeScript,but we didn't break it for a very long time.
876 // We'll try to keep not breaking it.
877 if (!in_array(last_last_text, ['class', 'interface'])) {
878 set_mode(MODE.ObjectLiteral);
879 } else {
880 set_mode(MODE.BlockStatement);
881 }
882 } else if (last_type === 'TK_OPERATOR' && flags.last_text === '=>') {
883 // arrow function: (param1, paramN) => { statements }
884 set_mode(MODE.BlockStatement);
885 } else if (in_array(last_type, ['TK_EQUALS', 'TK_START_EXPR', 'TK_COMMA', 'TK_OPERATOR']) ||
886 (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw', 'import', 'default']))
887 ) {
888 // Detecting shorthand function syntax is difficult by scanning forward,
889 // so check the surrounding context.
890 // If the block is being returned, imported, export default, passed as arg,
891 // assigned with = or assigned in a nested object, treat as an ObjectLiteral.
892 set_mode(MODE.ObjectLiteral);
893 } else {
894 set_mode(MODE.BlockStatement);
895 }
896
897 var empty_braces = !next_token.comments_before.length && next_token.text === '}';
898 var empty_anonymous_function = empty_braces && flags.last_word === 'function' &&
899 last_type === 'TK_END_EXPR';
900
901 if (opt.brace_preserve_inline) // check for inline, set inline_frame if so
902 {
903 // search forward for a newline wanted inside this block
904 var index = 0;
905 var check_token = null;
906 flags.inline_frame = true;
907 do {
908 index += 1;
909 check_token = get_token(index);
910 if (check_token.wanted_newline) {
911 flags.inline_frame = false;
912 break;
913 }
914 } while (check_token.type !== 'TK_EOF' &&
915 !(check_token.type === 'TK_END_BLOCK' && check_token.opened === current_token));
916 }
917
918 if ((opt.brace_style === "expand" ||
919 (opt.brace_style === "none" && current_token.wanted_newline)) &&
920 !flags.inline_frame) {
921 if (last_type !== 'TK_OPERATOR' &&
922 (empty_anonymous_function ||
923 last_type === 'TK_EQUALS' ||
924 (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) {
925 output.space_before_token = true;
926 } else {
927 print_newline(false, true);
928 }
929 } else { // collapse || inline_frame
930 if (is_array(previous_flags.mode) && (last_type === 'TK_START_EXPR' || last_type === 'TK_COMMA')) {
931 if (last_type === 'TK_COMMA' || opt.space_in_paren) {
932 output.space_before_token = true;
933 }
934
935 if (last_type === 'TK_COMMA' || (last_type === 'TK_START_EXPR' && flags.inline_frame)) {
936 allow_wrap_or_preserved_newline();
937 previous_flags.multiline_frame = previous_flags.multiline_frame || flags.multiline_frame;
938 flags.multiline_frame = false;
939 }
940 }
941 if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
942 if (last_type === 'TK_START_BLOCK' && !flags.inline_frame) {
943 print_newline();
944 } else {
945 output.space_before_token = true;
946 }
947 }
948 }
949 print_token();
950 indent();
951 }
952
953 function handle_end_block() {
954 // statements must all be closed when their container closes
955 handle_whitespace_and_comments(current_token);
956
957 while (flags.mode === MODE.Statement) {
958 restore_mode();
959 }
960
961 var empty_braces = last_type === 'TK_START_BLOCK';
962
963 if (flags.inline_frame && !empty_braces) { // try inline_frame (only set if opt.braces-preserve-inline) first
964 output.space_before_token = true;
965 } else if (opt.brace_style === "expand") {
966 if (!empty_braces) {
967 print_newline();
968 }
969 } else {
970 // skip {}
971 if (!empty_braces) {
972 if (is_array(flags.mode) && opt.keep_array_indentation) {
973 // we REALLY need a newline here, but newliner would skip that
974 opt.keep_array_indentation = false;
975 print_newline();
976 opt.keep_array_indentation = true;
977
978 } else {
979 print_newline();
980 }
981 }
982 }
983 restore_mode();
984 print_token();
985 }
986
987 function handle_word() {
988 if (current_token.type === 'TK_RESERVED') {
989 if (in_array(current_token.text, ['set', 'get']) && flags.mode !== MODE.ObjectLiteral) {
990 current_token.type = 'TK_WORD';
991 } else if (in_array(current_token.text, ['as', 'from']) && !flags.import_block) {
992 current_token.type = 'TK_WORD';
993 } else if (flags.mode === MODE.ObjectLiteral) {
994 var next_token = get_token(1);
995 if (next_token.text === ':') {
996 current_token.type = 'TK_WORD';
997 }
998 }
999 }
1000
1001 if (start_of_statement()) {
1002 // The conditional starts the statement if appropriate.
1003 if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') {
1004 flags.declaration_statement = true;
1005 }
1006 } else if (current_token.wanted_newline && !is_expression(flags.mode) &&
1007 (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) &&
1008 last_type !== 'TK_EQUALS' &&
1009 (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const', 'set', 'get'])))) {
1010 handle_whitespace_and_comments(current_token);
1011 print_newline();
1012 } else {
1013 handle_whitespace_and_comments(current_token);
1014 }
1015
1016 if (flags.do_block && !flags.do_while) {
1017 if (current_token.type === 'TK_RESERVED' && current_token.text === 'while') {
1018 // do {} ## while ()
1019 output.space_before_token = true;
1020 print_token();
1021 output.space_before_token = true;
1022 flags.do_while = true;
1023 return;
1024 } else {
1025 // do {} should always have while as the next word.
1026 // if we don't see the expected while, recover
1027 print_newline();
1028 flags.do_block = false;
1029 }
1030 }
1031
1032 // if may be followed by else, or not
1033 // Bare/inline ifs are tricky
1034 // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
1035 if (flags.if_block) {
1036 if (!flags.else_block && (current_token.type === 'TK_RESERVED' && current_token.text === 'else')) {
1037 flags.else_block = true;
1038 } else {
1039 while (flags.mode === MODE.Statement) {
1040 restore_mode();
1041 }
1042 flags.if_block = false;
1043 flags.else_block = false;
1044 }
1045 }
1046
1047 if (current_token.type === 'TK_RESERVED' && (current_token.text === 'case' || (current_token.text === 'default' && flags.in_case_statement))) {
1048 print_newline();
1049 if (flags.case_body || opt.jslint_happy) {
1050 // switch cases following one another
1051 deindent();
1052 flags.case_body = false;
1053 }
1054 print_token();
1055 flags.in_case = true;
1056 flags.in_case_statement = true;
1057 return;
1058 }
1059
1060 if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
1061 if (!start_of_object_property()) {
1062 allow_wrap_or_preserved_newline();
1063 }
1064 }
1065
1066 if (current_token.type === 'TK_RESERVED' && current_token.text === 'function') {
1067 if (in_array(flags.last_text, ['}', ';']) ||
1068 (output.just_added_newline() && !(in_array(flags.last_text, ['(', '[', '{', ':', '=', ',']) || last_type === 'TK_OPERATOR'))) {
1069 // make sure there is a nice clean space of at least one blank line
1070 // before a new function definition
1071 if (!output.just_added_blankline() && !current_token.comments_before.length) {
1072 print_newline();
1073 print_newline(true);
1074 }
1075 }
1076 if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
1077 if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return', 'export', 'async'])) {
1078 output.space_before_token = true;
1079 } else if (last_type === 'TK_RESERVED' && flags.last_text === 'default' && last_last_text === 'export') {
1080 output.space_before_token = true;
1081 } else {
1082 print_newline();
1083 }
1084 } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') {
1085 // foo = function
1086 output.space_before_token = true;
1087 } else if (!flags.multiline_frame && (is_expression(flags.mode) || is_array(flags.mode))) {
1088 // (function
1089 } else {
1090 print_newline();
1091 }
1092
1093 print_token();
1094 flags.last_word = current_token.text;
1095 return;
1096 }
1097
1098 prefix = 'NONE';
1099
1100 if (last_type === 'TK_END_BLOCK') {
1101
1102 if (previous_flags.inline_frame) {
1103 prefix = 'SPACE';
1104 } else if (!(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally', 'from']))) {
1105 prefix = 'NEWLINE';
1106 } else {
1107 if (opt.brace_style === "expand" ||
1108 opt.brace_style === "end-expand" ||
1109 (opt.brace_style === "none" && current_token.wanted_newline)) {
1110 prefix = 'NEWLINE';
1111 } else {
1112 prefix = 'SPACE';
1113 output.space_before_token = true;
1114 }
1115 }
1116 } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) {
1117 // TODO: Should this be for STATEMENT as well?
1118 prefix = 'NEWLINE';
1119 } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
1120 prefix = 'SPACE';
1121 } else if (last_type === 'TK_STRING') {
1122 prefix = 'NEWLINE';
1123 } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' ||
1124 (flags.last_text === '*' &&
1125 (in_array(last_last_text, ['function', 'yield']) ||
1126 (flags.mode === MODE.ObjectLiteral && in_array(last_last_text, ['{', ',']))))) {
1127 prefix = 'SPACE';
1128 } else if (last_type === 'TK_START_BLOCK') {
1129 if (flags.inline_frame) {
1130 prefix = 'SPACE';
1131 } else {
1132 prefix = 'NEWLINE';
1133 }
1134 } else if (last_type === 'TK_END_EXPR') {
1135 output.space_before_token = true;
1136 prefix = 'NEWLINE';
1137 }
1138
1139 if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
1140 if (flags.inline_frame || flags.last_text === 'else' || flags.last_text === 'export') {
1141 prefix = 'SPACE';
1142 } else {
1143 prefix = 'NEWLINE';
1144 }
1145
1146 }
1147
1148 if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally'])) {
1149 if ((!(last_type === 'TK_END_BLOCK' && previous_flags.mode === MODE.BlockStatement) ||
1150 opt.brace_style === "expand" ||
1151 opt.brace_style === "end-expand" ||
1152 (opt.brace_style === "none" && current_token.wanted_newline)) &&
1153 !flags.inline_frame) {
1154 print_newline();
1155 } else {
1156 output.trim(true);
1157 var line = output.current_line;
1158 // If we trimmed and there's something other than a close block before us
1159 // put a newline back in. Handles '} // comment' scenario.
1160 if (line.last() !== '}') {
1161 print_newline();
1162 }
1163 output.space_before_token = true;
1164 }
1165 } else if (prefix === 'NEWLINE') {
1166 if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
1167 // no newline between 'return nnn'
1168 output.space_before_token = true;
1169 } else if (last_type !== 'TK_END_EXPR') {
1170 if ((last_type !== 'TK_START_EXPR' || !(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['var', 'let', 'const']))) && flags.last_text !== ':') {
1171 // no need to force newline on 'var': for (var x = 0...)
1172 if (current_token.type === 'TK_RESERVED' && current_token.text === 'if' && flags.last_text === 'else') {
1173 // no newline for } else if {
1174 output.space_before_token = true;
1175 } else {
1176 print_newline();
1177 }
1178 }
1179 } else if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
1180 print_newline();
1181 }
1182 } else if (flags.multiline_frame && is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') {
1183 print_newline(); // }, in lists get a newline treatment
1184 } else if (prefix === 'SPACE') {
1185 output.space_before_token = true;
1186 }
1187 print_token();
1188 flags.last_word = current_token.text;
1189
1190 if (current_token.type === 'TK_RESERVED') {
1191 if (current_token.text === 'do') {
1192 flags.do_block = true;
1193 } else if (current_token.text === 'if') {
1194 flags.if_block = true;
1195 } else if (current_token.text === 'import') {
1196 flags.import_block = true;
1197 } else if (flags.import_block && current_token.type === 'TK_RESERVED' && current_token.text === 'from') {
1198 flags.import_block = false;
1199 }
1200 }
1201 }
1202
1203 function handle_semicolon() {
1204 if (start_of_statement()) {
1205 // The conditional starts the statement if appropriate.
1206 // Semicolon can be the start (and end) of a statement
1207 output.space_before_token = false;
1208 } else {
1209 handle_whitespace_and_comments(current_token);
1210 }
1211
1212 var next_token = get_token(1);
1213 while (flags.mode === MODE.Statement &&
1214 !(flags.if_block && next_token && next_token.type === 'TK_RESERVED' && next_token.text === 'else') &&
1215 !flags.do_block) {
1216 restore_mode();
1217 }
1218
1219 // hacky but effective for the moment
1220 if (flags.import_block) {
1221 flags.import_block = false;
1222 }
1223 print_token();
1224 }
1225
1226 function handle_string() {
1227 if (start_of_statement()) {
1228 // The conditional starts the statement if appropriate.
1229 // One difference - strings want at least a space before
1230 output.space_before_token = true;
1231 } else {
1232 handle_whitespace_and_comments(current_token);
1233 if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' || flags.inline_frame) {
1234 output.space_before_token = true;
1235 } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
1236 if (!start_of_object_property()) {
1237 allow_wrap_or_preserved_newline();
1238 }
1239 } else {
1240 print_newline();
1241 }
1242 }
1243 print_token();
1244 }
1245
1246 function handle_equals() {
1247 if (start_of_statement()) {
1248 // The conditional starts the statement if appropriate.
1249 } else {
1250 handle_whitespace_and_comments(current_token);
1251 }
1252
1253 if (flags.declaration_statement) {
1254 // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
1255 flags.declaration_assignment = true;
1256 }
1257 output.space_before_token = true;
1258 print_token();
1259 output.space_before_token = true;
1260 }
1261
1262 function handle_comma() {
1263 handle_whitespace_and_comments(current_token, true);
1264
1265 print_token();
1266 output.space_before_token = true;
1267 if (flags.declaration_statement) {
1268 if (is_expression(flags.parent.mode)) {
1269 // do not break on comma, for(var a = 1, b = 2)
1270 flags.declaration_assignment = false;
1271 }
1272
1273 if (flags.declaration_assignment) {
1274 flags.declaration_assignment = false;
1275 print_newline(false, true);
1276 } else if (opt.comma_first) {
1277 // for comma-first, we want to allow a newline before the comma
1278 // to turn into a newline after the comma, which we will fixup later
1279 allow_wrap_or_preserved_newline();
1280 }
1281 } else if (flags.mode === MODE.ObjectLiteral ||
1282 (flags.mode === MODE.Statement && flags.parent.mode === MODE.ObjectLiteral)) {
1283 if (flags.mode === MODE.Statement) {
1284 restore_mode();
1285 }
1286
1287 if (!flags.inline_frame) {
1288 print_newline();
1289 }
1290 } else if (opt.comma_first) {
1291 // EXPR or DO_BLOCK
1292 // for comma-first, we want to allow a newline before the comma
1293 // to turn into a newline after the comma, which we will fixup later
1294 allow_wrap_or_preserved_newline();
1295 }
1296 }
1297
1298 function handle_operator() {
1299 var isGeneratorAsterisk = current_token.text === '*' &&
1300 ((last_type === 'TK_RESERVED' && in_array(flags.last_text, ['function', 'yield'])) ||
1301 (in_array(last_type, ['TK_START_BLOCK', 'TK_COMMA', 'TK_END_BLOCK', 'TK_SEMICOLON']))
1302 );
1303 var isUnary = in_array(current_token.text, ['-', '+']) && (
1304 in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) ||
1305 in_array(flags.last_text, Tokenizer.line_starters) ||
1306 flags.last_text === ','
1307 );
1308
1309 if (start_of_statement()) {
1310 // The conditional starts the statement if appropriate.
1311 } else {
1312 var preserve_statement_flags = !isGeneratorAsterisk;
1313 handle_whitespace_and_comments(current_token, preserve_statement_flags);
1314 }
1315
1316 if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
1317 // "return" had a special handling in TK_WORD. Now we need to return the favor
1318 output.space_before_token = true;
1319 print_token();
1320 return;
1321 }
1322
1323 // hack for actionscript's import .*;
1324 if (current_token.text === '*' && last_type === 'TK_DOT') {
1325 print_token();
1326 return;
1327 }
1328
1329 if (current_token.text === '::') {
1330 // no spaces around exotic namespacing syntax operator
1331 print_token();
1332 return;
1333 }
1334
1335 // Allow line wrapping between operators when operator_position is
1336 // set to before or preserve
1337 if (last_type === 'TK_OPERATOR' && in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)) {
1338 allow_wrap_or_preserved_newline();
1339 }
1340
1341 if (current_token.text === ':' && flags.in_case) {
1342 flags.case_body = true;
1343 indent();
1344 print_token();
1345 print_newline();
1346 flags.in_case = false;
1347 return;
1348 }
1349
1350 var space_before = true;
1351 var space_after = true;
1352 var in_ternary = false;
1353 if (current_token.text === ':') {
1354 if (flags.ternary_depth === 0) {
1355 // Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
1356 space_before = false;
1357 } else {
1358 flags.ternary_depth -= 1;
1359 in_ternary = true;
1360 }
1361 } else if (current_token.text === '?') {
1362 flags.ternary_depth += 1;
1363 }
1364
1365 // let's handle the operator_position option prior to any conflicting logic
1366 if (!isUnary && !isGeneratorAsterisk && opt.preserve_newlines && in_array(current_token.text, Tokenizer.positionable_operators)) {
1367 var isColon = current_token.text === ':';
1368 var isTernaryColon = (isColon && in_ternary);
1369 var isOtherColon = (isColon && !in_ternary);
1370
1371 switch (opt.operator_position) {
1372 case OPERATOR_POSITION.before_newline:
1373 // if the current token is : and it's not a ternary statement then we set space_before to false
1374 output.space_before_token = !isOtherColon;
1375
1376 print_token();
1377
1378 if (!isColon || isTernaryColon) {
1379 allow_wrap_or_preserved_newline();
1380 }
1381
1382 output.space_before_token = true;
1383 return;
1384
1385 case OPERATOR_POSITION.after_newline:
1386 // if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement,
1387 // then print a newline.
1388
1389 output.space_before_token = true;
1390
1391 if (!isColon || isTernaryColon) {
1392 if (get_token(1).wanted_newline) {
1393 print_newline(false, true);
1394 } else {
1395 allow_wrap_or_preserved_newline();
1396 }
1397 } else {
1398 output.space_before_token = false;
1399 }
1400
1401 print_token();
1402
1403 output.space_before_token = true;
1404 return;
1405
1406 case OPERATOR_POSITION.preserve_newline:
1407 if (!isOtherColon) {
1408 allow_wrap_or_preserved_newline();
1409 }
1410
1411 // if we just added a newline, or the current token is : and it's not a ternary statement,
1412 // then we set space_before to false
1413 space_before = !(output.just_added_newline() || isOtherColon);
1414
1415 output.space_before_token = space_before;
1416 print_token();
1417 output.space_before_token = true;
1418 return;
1419 }
1420 }
1421
1422 if (isGeneratorAsterisk) {
1423 allow_wrap_or_preserved_newline();
1424 space_before = false;
1425 var next_token = get_token(1);
1426 space_after = next_token && in_array(next_token.type, ['TK_WORD', 'TK_RESERVED']);
1427 } else if (current_token.text === '...') {
1428 allow_wrap_or_preserved_newline();
1429 space_before = last_type === 'TK_START_BLOCK';
1430 space_after = false;
1431 } else if (in_array(current_token.text, ['--', '++', '!', '~']) || isUnary) {
1432 // unary operators (and binary +/- pretending to be unary) special cases
1433
1434 space_before = false;
1435 space_after = false;
1436
1437 // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
1438 // if there is a newline between -- or ++ and anything else we should preserve it.
1439 if (current_token.wanted_newline && (current_token.text === '--' || current_token.text === '++')) {
1440 print_newline(false, true);
1441 }
1442
1443 if (flags.last_text === ';' && is_expression(flags.mode)) {
1444 // for (;; ++i)
1445 // ^^^
1446 space_before = true;
1447 }
1448
1449 if (last_type === 'TK_RESERVED') {
1450 space_before = true;
1451 } else if (last_type === 'TK_END_EXPR') {
1452 space_before = !(flags.last_text === ']' && (current_token.text === '--' || current_token.text === '++'));
1453 } else if (last_type === 'TK_OPERATOR') {
1454 // a++ + ++b;
1455 // a - -b
1456 space_before = in_array(current_token.text, ['--', '-', '++', '+']) && in_array(flags.last_text, ['--', '-', '++', '+']);
1457 // + and - are not unary when preceeded by -- or ++ operator
1458 // a-- + b
1459 // a * +b
1460 // a - -b
1461 if (in_array(current_token.text, ['+', '-']) && in_array(flags.last_text, ['--', '++'])) {
1462 space_after = true;
1463 }
1464 }
1465
1466
1467 if (((flags.mode === MODE.BlockStatement && !flags.inline_frame) || flags.mode === MODE.Statement) &&
1468 (flags.last_text === '{' || flags.last_text === ';')) {
1469 // { foo; --i }
1470 // foo(); --bar;
1471 print_newline();
1472 }
1473 }
1474
1475 output.space_before_token = output.space_before_token || space_before;
1476 print_token();
1477 output.space_before_token = space_after;
1478 }
1479
1480 function handle_block_comment(preserve_statement_flags) {
1481 if (output.raw) {
1482 output.add_raw_token(current_token);
1483 if (current_token.directives && current_token.directives.preserve === 'end') {
1484 // If we're testing the raw output behavior, do not allow a directive to turn it off.
1485 output.raw = opt.test_output_raw;
1486 }
1487 return;
1488 }
1489
1490 if (current_token.directives) {
1491 print_newline(false, preserve_statement_flags);
1492 print_token();
1493 if (current_token.directives.preserve === 'start') {
1494 output.raw = true;
1495 }
1496 print_newline(false, true);
1497 return;
1498 }
1499
1500 // inline block
1501 if (!acorn.newline.test(current_token.text) && !current_token.wanted_newline) {
1502 output.space_before_token = true;
1503 print_token();
1504 output.space_before_token = true;
1505 return;
1506 }
1507
1508 var lines = split_linebreaks(current_token.text);
1509 var j; // iterator for this case
1510 var javadoc = false;
1511 var starless = false;
1512 var lastIndent = current_token.whitespace_before;
1513 var lastIndentLength = lastIndent.length;
1514
1515 // block comment starts with a new line
1516 print_newline(false, preserve_statement_flags);
1517 if (lines.length > 1) {
1518 javadoc = all_lines_start_with(lines.slice(1), '*');
1519 starless = each_line_matches_indent(lines.slice(1), lastIndent);
1520 }
1521
1522 // first line always indented
1523 print_token(lines[0]);
1524 for (j = 1; j < lines.length; j++) {
1525 print_newline(false, true);
1526 if (javadoc) {
1527 // javadoc: reformat and re-indent
1528 print_token(' ' + ltrim(lines[j]));
1529 } else if (starless && lines[j].length > lastIndentLength) {
1530 // starless: re-indent non-empty content, avoiding trim
1531 print_token(lines[j].substring(lastIndentLength));
1532 } else {
1533 // normal comments output raw
1534 output.add_token(lines[j]);
1535 }
1536 }
1537
1538 // for comments of more than one line, make sure there's a new line after
1539 print_newline(false, preserve_statement_flags);
1540 }
1541
1542 function handle_comment(preserve_statement_flags) {
1543 if (current_token.wanted_newline) {
1544 print_newline(false, preserve_statement_flags);
1545 } else {
1546 output.trim(true);
1547 }
1548
1549 output.space_before_token = true;
1550 print_token();
1551 print_newline(false, preserve_statement_flags);
1552 }
1553
1554 function handle_dot() {
1555 if (start_of_statement()) {
1556 // The conditional starts the statement if appropriate.
1557 } else {
1558 handle_whitespace_and_comments(current_token, true);
1559 }
1560
1561 if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
1562 output.space_before_token = true;
1563 } else {
1564 // allow preserved newlines before dots in general
1565 // force newlines on dots after close paren when break_chained - for bar().baz()
1566 allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods);
1567 }
1568
1569 print_token();
1570 }
1571
1572 function handle_unknown(preserve_statement_flags) {
1573 print_token();
1574
1575 if (current_token.text[current_token.text.length - 1] === '\n') {
1576 print_newline(false, preserve_statement_flags);
1577 }
1578 }
1579
1580 function handle_eof() {
1581 // Unwind any open statements
1582 while (flags.mode === MODE.Statement) {
1583 restore_mode();
1584 }
1585 handle_whitespace_and_comments(current_token);
1586 }
1587 }
1588
1589
1590 function OutputLine(parent) {
1591 var _character_count = 0;
1592 // use indent_count as a marker for lines that have preserved indentation
1593 var _indent_count = -1;
1594
1595 var _items = [];
1596 var _empty = true;
1597
1598 this.set_indent = function(level) {
1599 _character_count = parent.baseIndentLength + level * parent.indent_length;
1600 _indent_count = level;
1601 };
1602
1603 this.get_character_count = function() {
1604 return _character_count;
1605 };
1606
1607 this.is_empty = function() {
1608 return _empty;
1609 };
1610
1611 this.last = function() {
1612 if (!this._empty) {
1613 return _items[_items.length - 1];
1614 } else {
1615 return null;
1616 }
1617 };
1618
1619 this.push = function(input) {
1620 _items.push(input);
1621 _character_count += input.length;
1622 _empty = false;
1623 };
1624
1625 this.pop = function() {
1626 var item = null;
1627 if (!_empty) {
1628 item = _items.pop();
1629 _character_count -= item.length;
1630 _empty = _items.length === 0;
1631 }
1632 return item;
1633 };
1634
1635 this.remove_indent = function() {
1636 if (_indent_count > 0) {
1637 _indent_count -= 1;
1638 _character_count -= parent.indent_length;
1639 }
1640 };
1641
1642 this.trim = function() {
1643 while (this.last() === ' ') {
1644 _items.pop();
1645 _character_count -= 1;
1646 }
1647 _empty = _items.length === 0;
1648 };
1649
1650 this.toString = function() {
1651 var result = '';
1652 if (!this._empty) {
1653 if (_indent_count >= 0) {
1654 result = parent.indent_cache[_indent_count];
1655 }
1656 result += _items.join('');
1657 }
1658 return result;
1659 };
1660 }
1661
1662 function Output(indent_string, baseIndentString) {
1663 baseIndentString = baseIndentString || '';
1664 this.indent_cache = [baseIndentString];
1665 this.baseIndentLength = baseIndentString.length;
1666 this.indent_length = indent_string.length;
1667 this.raw = false;
1668
1669 var lines = [];
1670 this.baseIndentString = baseIndentString;
1671 this.indent_string = indent_string;
1672 this.previous_line = null;
1673 this.current_line = null;
1674 this.space_before_token = false;
1675
1676 this.add_outputline = function() {
1677 this.previous_line = this.current_line;
1678 this.current_line = new OutputLine(this);
1679 lines.push(this.current_line);
1680 };
1681
1682 // initialize
1683 this.add_outputline();
1684
1685
1686 this.get_line_number = function() {
1687 return lines.length;
1688 };
1689
1690 // Using object instead of string to allow for later expansion of info about each line
1691 this.add_new_line = function(force_newline) {
1692 if (this.get_line_number() === 1 && this.just_added_newline()) {
1693 return false; // no newline on start of file
1694 }
1695
1696 if (force_newline || !this.just_added_newline()) {
1697 if (!this.raw) {
1698 this.add_outputline();
1699 }
1700 return true;
1701 }
1702
1703 return false;
1704 };
1705
1706 this.get_code = function() {
1707 var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, '');
1708 return sweet_code;
1709 };
1710
1711 this.set_indent = function(level) {
1712 // Never indent your first output indent at the start of the file
1713 if (lines.length > 1) {
1714 while (level >= this.indent_cache.length) {
1715 this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string);
1716 }
1717
1718 this.current_line.set_indent(level);
1719 return true;
1720 }
1721 this.current_line.set_indent(0);
1722 return false;
1723 };
1724
1725 this.add_raw_token = function(token) {
1726 for (var x = 0; x < token.newlines; x++) {
1727 this.add_outputline();
1728 }
1729 this.current_line.push(token.whitespace_before);
1730 this.current_line.push(token.text);
1731 this.space_before_token = false;
1732 };
1733
1734 this.add_token = function(printable_token) {
1735 this.add_space_before_token();
1736 this.current_line.push(printable_token);
1737 };
1738
1739 this.add_space_before_token = function() {
1740 if (this.space_before_token && !this.just_added_newline()) {
1741 this.current_line.push(' ');
1742 }
1743 this.space_before_token = false;
1744 };
1745
1746 this.remove_redundant_indentation = function(frame) {
1747 // This implementation is effective but has some issues:
1748 // - can cause line wrap to happen too soon due to indent removal
1749 // after wrap points are calculated
1750 // These issues are minor compared to ugly indentation.
1751
1752 if (frame.multiline_frame ||
1753 frame.mode === MODE.ForInitializer ||
1754 frame.mode === MODE.Conditional) {
1755 return;
1756 }
1757
1758 // remove one indent from each line inside this section
1759 var index = frame.start_line_index;
1760
1761 var output_length = lines.length;
1762 while (index < output_length) {
1763 lines[index].remove_indent();
1764 index++;
1765 }
1766 };
1767
1768 this.trim = function(eat_newlines) {
1769 eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;
1770
1771 this.current_line.trim(indent_string, baseIndentString);
1772
1773 while (eat_newlines && lines.length > 1 &&
1774 this.current_line.is_empty()) {
1775 lines.pop();
1776 this.current_line = lines[lines.length - 1];
1777 this.current_line.trim();
1778 }
1779
1780 this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null;
1781 };
1782
1783 this.just_added_newline = function() {
1784 return this.current_line.is_empty();
1785 };
1786
1787 this.just_added_blankline = function() {
1788 if (this.just_added_newline()) {
1789 if (lines.length === 1) {
1790 return true; // start of the file and newline = blank
1791 }
1792
1793 var line = lines[lines.length - 2];
1794 return line.is_empty();
1795 }
1796 return false;
1797 };
1798 }
1799
1800 var InputScanner = function(input) {
1801 var _input = input;
1802 var _input_length = _input.length;
1803 var _position = 0;
1804
1805 this.back = function() {
1806 _position -= 1;
1807 };
1808
1809 this.hasNext = function() {
1810 return _position < _input_length;
1811 };
1812
1813 this.next = function() {
1814 var val = null;
1815 if (this.hasNext()) {
1816 val = _input.charAt(_position);
1817 _position += 1;
1818 }
1819 return val;
1820 };
1821
1822 this.peek = function(index) {
1823 var val = null;
1824 index = index || 0;
1825 index += _position;
1826 if (index >= 0 && index < _input_length) {
1827 val = _input.charAt(index);
1828 }
1829 return val;
1830 };
1831
1832 this.peekCharCode = function(index) {
1833 var val = 0;
1834 index = index || 0;
1835 index += _position;
1836 if (index >= 0 && index < _input_length) {
1837 val = _input.charCodeAt(index);
1838 }
1839 return val;
1840 };
1841
1842 this.test = function(pattern, index) {
1843 index = index || 0;
1844 pattern.lastIndex = _position + index;
1845 return pattern.test(_input);
1846 };
1847
1848 this.testChar = function(pattern, index) {
1849 var val = this.peek(index);
1850 return val !== null && pattern.test(val);
1851 };
1852
1853 this.match = function(pattern) {
1854 pattern.lastIndex = _position;
1855 var pattern_match = pattern.exec(_input);
1856 if (pattern_match && pattern_match.index === _position) {
1857 _position += pattern_match[0].length;
1858 } else {
1859 pattern_match = null;
1860 }
1861 return pattern_match;
1862 };
1863 };
1864
1865 var Token = function(type, text, newlines, whitespace_before, parent) {
1866 this.type = type;
1867 this.text = text;
1868
1869 // comments_before are
1870 // comments that have a new line before them
1871 // and may or may not have a newline after
1872 // this is a set of comments before
1873 this.comments_before = /* inline comment*/ [];
1874
1875
1876 this.comments_after = []; // no new line before and newline after
1877 this.newlines = newlines || 0;
1878 this.wanted_newline = newlines > 0;
1879 this.whitespace_before = whitespace_before || '';
1880 this.parent = parent || null;
1881 this.opened = null;
1882 this.directives = null;
1883 };
1884
1885 function tokenizer(input_string, opts) {
1886
1887 var whitespace = "\n\r\t ".split('');
1888 var digit = /[0-9]/;
1889 var digit_bin = /[01]/;
1890 var digit_oct = /[01234567]/;
1891 var digit_hex = /[0123456789abcdefABCDEF]/;
1892
1893 this.positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ');
1894 var punct = this.positionable_operators.concat(
1895 // non-positionable operators - these do not follow operator position settings
1896 '! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~ ...'.split(' '));
1897
1898 // words which should always start on new line.
1899 this.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',');
1900 var reserved_words = this.line_starters.concat(['do', 'in', 'of', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as']);
1901
1902 // /* ... */ comment ends with nearest */ or end of file
1903 var block_comment_pattern = /([\s\S]*?)((?:\*\/)|$)/g;
1904
1905 // comment ends just before nearest linefeed or end of file
1906 var comment_pattern = /([^\n\r\u2028\u2029]*)/g;
1907
1908 var directives_block_pattern = /\/\* beautify( \w+[:]\w+)+ \*\//g;
1909 var directive_pattern = / (\w+)[:](\w+)/g;
1910 var directives_end_ignore_pattern = /([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)/g;
1911
1912 var template_pattern = /((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)/g;
1913
1914 var n_newlines, whitespace_before_token, in_html_comment, tokens;
1915 var input;
1916
1917 this.tokenize = function() {
1918 input = new InputScanner(input_string);
1919 in_html_comment = false;
1920 tokens = [];
1921
1922 var next, last;
1923 var token_values;
1924 var open = null;
1925 var open_stack = [];
1926 var comments = [];
1927
1928 while (!(last && last.type === 'TK_EOF')) {
1929 token_values = tokenize_next();
1930 next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
1931 while (next.type === 'TK_COMMENT' || next.type === 'TK_BLOCK_COMMENT' || next.type === 'TK_UNKNOWN') {
1932 if (next.type === 'TK_BLOCK_COMMENT') {
1933 next.directives = token_values[2];
1934 }
1935 comments.push(next);
1936 token_values = tokenize_next();
1937 next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
1938 }
1939
1940 if (comments.length) {
1941 next.comments_before = comments;
1942 comments = [];
1943 }
1944
1945 if (next.type === 'TK_START_BLOCK' || next.type === 'TK_START_EXPR') {
1946 next.parent = last;
1947 open_stack.push(open);
1948 open = next;
1949 } else if ((next.type === 'TK_END_BLOCK' || next.type === 'TK_END_EXPR') &&
1950 (open && (
1951 (next.text === ']' && open.text === '[') ||
1952 (next.text === ')' && open.text === '(') ||
1953 (next.text === '}' && open.text === '{')))) {
1954 next.parent = open.parent;
1955 next.opened = open;
1956
1957 open = open_stack.pop();
1958 }
1959
1960 tokens.push(next);
1961 last = next;
1962 }
1963
1964 return tokens;
1965 };
1966
1967 function get_directives(text) {
1968 if (!text.match(directives_block_pattern)) {
1969 return null;
1970 }
1971
1972 var directives = {};
1973 directive_pattern.lastIndex = 0;
1974 var directive_match = directive_pattern.exec(text);
1975
1976 while (directive_match) {
1977 directives[directive_match[1]] = directive_match[2];
1978 directive_match = directive_pattern.exec(text);
1979 }
1980
1981 return directives;
1982 }
1983
1984 function tokenize_next() {
1985 var resulting_string;
1986 var whitespace_on_this_line = [];
1987
1988 n_newlines = 0;
1989 whitespace_before_token = '';
1990
1991 var c = input.next();
1992
1993 if (c === null) {
1994 return ['', 'TK_EOF'];
1995 }
1996
1997 var last_token;
1998 if (tokens.length) {
1999 last_token = tokens[tokens.length - 1];
2000 } else {
2001 // For the sake of tokenizing we can pretend that there was on open brace to start
2002 last_token = new Token('TK_START_BLOCK', '{');
2003 }
2004
2005 while (in_array(c, whitespace)) {
2006
2007 if (acorn.newline.test(c)) {
2008 if (!(c === '\n' && input.peek(-2) === '\r')) {
2009 n_newlines += 1;
2010 whitespace_on_this_line = [];
2011 }
2012 } else {
2013 whitespace_on_this_line.push(c);
2014 }
2015
2016 c = input.next();
2017
2018 if (c === null) {
2019 return ['', 'TK_EOF'];
2020 }
2021 }
2022
2023 if (whitespace_on_this_line.length) {
2024 whitespace_before_token = whitespace_on_this_line.join('');
2025 }
2026
2027 if (digit.test(c) || (c === '.' && input.testChar(digit))) {
2028 var allow_decimal = true;
2029 var allow_e = true;
2030 var local_digit = digit;
2031
2032 if (c === '0' && input.testChar(/[XxOoBb]/)) {
2033 // switch to hex/oct/bin number, no decimal or e, just hex/oct/bin digits
2034 allow_decimal = false;
2035 allow_e = false;
2036 if (input.testChar(/[Bb]/)) {
2037 local_digit = digit_bin;
2038 } else if (input.testChar(/[Oo]/)) {
2039 local_digit = digit_oct;
2040 } else {
2041 local_digit = digit_hex;
2042 }
2043 c += input.next();
2044 } else if (c === '.') {
2045 // Already have a decimal for this literal, don't allow another
2046 allow_decimal = false;
2047 } else {
2048 // we know this first loop will run. It keeps the logic simpler.
2049 c = '';
2050 input.back();
2051 }
2052
2053 // Add the digits
2054 while (input.testChar(local_digit)) {
2055 c += input.next();
2056
2057 if (allow_decimal && input.peek() === '.') {
2058 c += input.next();
2059 allow_decimal = false;
2060 }
2061
2062 // a = 1.e-7 is valid, so we test for . then e in one loop
2063 if (allow_e && input.testChar(/[Ee]/)) {
2064 c += input.next();
2065
2066 if (input.testChar(/[+-]/)) {
2067 c += input.next();
2068 }
2069
2070 allow_e = false;
2071 allow_decimal = false;
2072 }
2073 }
2074
2075 return [c, 'TK_WORD'];
2076 }
2077
2078 if (acorn.isIdentifierStart(input.peekCharCode(-1))) {
2079 if (input.hasNext()) {
2080 while (acorn.isIdentifierChar(input.peekCharCode())) {
2081 c += input.next();
2082 if (!input.hasNext()) {
2083 break;
2084 }
2085 }
2086 }
2087
2088 if (!(last_token.type === 'TK_DOT' ||
2089 (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get']))) &&
2090 in_array(c, reserved_words)) {
2091 if (c === 'in' || c === 'of') { // hack for 'in' and 'of' operators
2092 return [c, 'TK_OPERATOR'];
2093 }
2094 return [c, 'TK_RESERVED'];
2095 }
2096
2097 return [c, 'TK_WORD'];
2098 }
2099
2100 if (c === '(' || c === '[') {
2101 return [c, 'TK_START_EXPR'];
2102 }
2103
2104 if (c === ')' || c === ']') {
2105 return [c, 'TK_END_EXPR'];
2106 }
2107
2108 if (c === '{') {
2109 return [c, 'TK_START_BLOCK'];
2110 }
2111
2112 if (c === '}') {
2113 return [c, 'TK_END_BLOCK'];
2114 }
2115
2116 if (c === ';') {
2117 return [c, 'TK_SEMICOLON'];
2118 }
2119
2120 if (c === '/') {
2121 var comment = '';
2122 var comment_match;
2123 // peek for comment /* ... */
2124 if (input.peek() === '*') {
2125 input.next();
2126 comment_match = input.match(block_comment_pattern);
2127 comment = '/*' + comment_match[0];
2128 var directives = get_directives(comment);
2129 if (directives && directives.ignore === 'start') {
2130 comment_match = input.match(directives_end_ignore_pattern);
2131 comment += comment_match[0];
2132 }
2133 comment = comment.replace(acorn.allLineBreaks, '\n');
2134 return [comment, 'TK_BLOCK_COMMENT', directives];
2135 }
2136 // peek for comment // ...
2137 if (input.peek() === '/') {
2138 input.next();
2139 comment_match = input.match(comment_pattern);
2140 comment = '//' + comment_match[0];
2141 return [comment, 'TK_COMMENT'];
2142 }
2143
2144 }
2145
2146 var startXmlRegExp = /<()([-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;
2147
2148 if (c === '`' || c === "'" || c === '"' || // string
2149 (
2150 (c === '/') || // regexp
2151 (opts.e4x && c === "<" && input.test(startXmlRegExp, -1)) // xml
2152 ) && ( // regex and xml can only appear in specific locations during parsing
2153 (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) ||
2154 (last_token.type === 'TK_END_EXPR' && last_token.text === ')' &&
2155 last_token.parent && last_token.parent.type === 'TK_RESERVED' && in_array(last_token.parent.text, ['if', 'while', 'for'])) ||
2156 (in_array(last_token.type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK',
2157 'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'
2158 ]))
2159 )) {
2160
2161 var sep = c,
2162 esc = false,
2163 has_char_escapes = false;
2164
2165 resulting_string = c;
2166
2167 if (sep === '/') {
2168 //
2169 // handle regexp
2170 //
2171 var in_char_class = false;
2172 while (input.hasNext() &&
2173 ((esc || in_char_class || input.peek() !== sep) &&
2174 !input.testChar(acorn.newline))) {
2175 resulting_string += input.peek();
2176 if (!esc) {
2177 esc = input.peek() === '\\';
2178 if (input.peek() === '[') {
2179 in_char_class = true;
2180 } else if (input.peek() === ']') {
2181 in_char_class = false;
2182 }
2183 } else {
2184 esc = false;
2185 }
2186 input.next();
2187 }
2188 } else if (opts.e4x && sep === '<') {
2189 //
2190 // handle e4x xml literals
2191 //
2192
2193 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;
2194 input.back();
2195 var xmlStr = '';
2196 var match = input.match(startXmlRegExp);
2197 if (match) {
2198 // Trim root tag to attempt to
2199 var rootTag = match[2].replace(/^{\s+/, '{').replace(/\s+}$/, '}');
2200 var isCurlyRoot = rootTag.indexOf('{') === 0;
2201 var depth = 0;
2202 while (match) {
2203 var isEndTag = !!match[1];
2204 var tagName = match[2];
2205 var isSingletonTag = (!!match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA[");
2206 if (!isSingletonTag &&
2207 (tagName === rootTag || (isCurlyRoot && tagName.replace(/^{\s+/, '{').replace(/\s+}$/, '}')))) {
2208 if (isEndTag) {
2209 --depth;
2210 } else {
2211 ++depth;
2212 }
2213 }
2214 xmlStr += match[0];
2215 if (depth <= 0) {
2216 break;
2217 }
2218 match = input.match(xmlRegExp);
2219 }
2220 // if we didn't close correctly, keep unformatted.
2221 if (!match) {
2222 xmlStr += input.match(/[\s\S]*/g)[0];
2223 }
2224 xmlStr = xmlStr.replace(acorn.allLineBreaks, '\n');
2225 return [xmlStr, "TK_STRING"];
2226 }
2227 } else {
2228 //
2229 // handle string
2230 //
2231 var parse_string = function(delimiter, allow_unescaped_newlines, start_sub) {
2232 // Template strings can travers lines without escape characters.
2233 // Other strings cannot
2234 var current_char;
2235 while (input.hasNext()) {
2236 current_char = input.peek();
2237 if (!(esc || (current_char !== delimiter &&
2238 (allow_unescaped_newlines || !acorn.newline.test(current_char))))) {
2239 break;
2240 }
2241
2242 // Handle \r\n linebreaks after escapes or in template strings
2243 if ((esc || allow_unescaped_newlines) && acorn.newline.test(current_char)) {
2244 if (current_char === '\r' && input.peek(1) === '\n') {
2245 input.next();
2246 current_char = input.peek();
2247 }
2248 resulting_string += '\n';
2249 } else {
2250 resulting_string += current_char;
2251 }
2252
2253 if (esc) {
2254 if (current_char === 'x' || current_char === 'u') {
2255 has_char_escapes = true;
2256 }
2257 esc = false;
2258 } else {
2259 esc = current_char === '\\';
2260 }
2261
2262 input.next();
2263
2264 if (start_sub && resulting_string.indexOf(start_sub, resulting_string.length - start_sub.length) !== -1) {
2265 if (delimiter === '`') {
2266 parse_string('}', allow_unescaped_newlines, '`');
2267 } else {
2268 parse_string('`', allow_unescaped_newlines, '${');
2269 }
2270
2271 if (input.hasNext()) {
2272 resulting_string += input.next();
2273 }
2274 }
2275 }
2276 };
2277
2278 if (sep === '`') {
2279 parse_string('`', true, '${');
2280 } else {
2281 parse_string(sep);
2282 }
2283 }
2284
2285 if (has_char_escapes && opts.unescape_strings) {
2286 resulting_string = unescape_string(resulting_string);
2287 }
2288
2289 if (input.peek() === sep) {
2290 resulting_string += sep;
2291 input.next();
2292
2293 if (sep === '/') {
2294 // regexps may have modifiers /regexp/MOD , so fetch those, too
2295 // Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
2296 while (input.hasNext() && acorn.isIdentifierStart(input.peekCharCode())) {
2297 resulting_string += input.next();
2298 }
2299 }
2300 }
2301 return [resulting_string, 'TK_STRING'];
2302 }
2303
2304 if (c === '#') {
2305
2306 if (tokens.length === 0 && input.peek() === '!') {
2307 // shebang
2308 resulting_string = c;
2309 while (input.hasNext() && c !== '\n') {
2310 c = input.next();
2311 resulting_string += c;
2312 }
2313 return [trim(resulting_string) + '\n', 'TK_UNKNOWN'];
2314 }
2315
2316
2317
2318 // Spidermonkey-specific sharp variables for circular references
2319 // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
2320 // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
2321 var sharp = '#';
2322 if (input.hasNext() && input.testChar(digit)) {
2323 do {
2324 c = input.next();
2325 sharp += c;
2326 } while (input.hasNext() && c !== '#' && c !== '=');
2327 if (c === '#') {
2328 //
2329 } else if (input.peek() === '[' && input.peek(1) === ']') {
2330 sharp += '[]';
2331 input.next();
2332 input.next();
2333 } else if (input.peek() === '{' && input.peek(1) === '}') {
2334 sharp += '{}';
2335 input.next();
2336 input.next();
2337 }
2338 return [sharp, 'TK_WORD'];
2339 }
2340 }
2341
2342 if (c === '<' && (input.peek() === '?' || input.peek() === '%')) {
2343 input.back();
2344 var template_match = input.match(template_pattern);
2345 if (template_match) {
2346 c = template_match[0];
2347 c = c.replace(acorn.allLineBreaks, '\n');
2348 return [c, 'TK_STRING'];
2349 }
2350 }
2351
2352 if (c === '<' && input.match(/\!--/g)) {
2353 c = '<!--';
2354 while (input.hasNext() && !input.testChar(acorn.newline)) {
2355 c += input.next();
2356 }
2357 in_html_comment = true;
2358 return [c, 'TK_COMMENT'];
2359 }
2360
2361 if (c === '-' && in_html_comment && input.match(/->/g)) {
2362 in_html_comment = false;
2363 return ['-->', 'TK_COMMENT'];
2364 }
2365
2366 if (c === '.') {
2367 if (input.peek() === '.' && input.peek(1) === '.') {
2368 c += input.next() + input.next();
2369 return [c, 'TK_OPERATOR'];
2370 }
2371 return [c, 'TK_DOT'];
2372 }
2373
2374 if (in_array(c, punct)) {
2375 while (input.hasNext() && in_array(c + input.peek(), punct)) {
2376 c += input.next();
2377 if (!input.hasNext()) {
2378 break;
2379 }
2380 }
2381
2382 if (c === ',') {
2383 return [c, 'TK_COMMA'];
2384 } else if (c === '=') {
2385 return [c, 'TK_EQUALS'];
2386 } else {
2387 return [c, 'TK_OPERATOR'];
2388 }
2389 }
2390
2391 return [c, 'TK_UNKNOWN'];
2392 }
2393
2394
2395 function unescape_string(s) {
2396 // You think that a regex would work for this
2397 // return s.replace(/\\x([0-9a-f]{2})/gi, function(match, val) {
2398 // return String.fromCharCode(parseInt(val, 16));
2399 // })
2400 // However, dealing with '\xff', '\\xff', '\\\xff' makes this more fun.
2401 var out = '',
2402 escaped = 0;
2403
2404 var input_scan = new InputScanner(s);
2405 var matched = null;
2406
2407 while (input_scan.hasNext()) {
2408 // Keep any whitespace, non-slash characters
2409 // also keep slash pairs.
2410 matched = input_scan.match(/([\s]|[^\\]|\\\\)+/g);
2411
2412 if (matched) {
2413 out += matched[0];
2414 }
2415
2416 if (input_scan.peek() === '\\') {
2417 input_scan.next();
2418 if (input_scan.peek() === 'x') {
2419 matched = input_scan.match(/x([0-9A-Fa-f]{2})/g);
2420 } else if (input_scan.peek() === 'u') {
2421 matched = input_scan.match(/u([0-9A-Fa-f]{4})/g);
2422 } else {
2423 out += '\\';
2424 if (input_scan.hasNext()) {
2425 out += input_scan.next();
2426 }
2427 continue;
2428 }
2429
2430 // If there's some error decoding, return the original string
2431 if (!matched) {
2432 return s;
2433 }
2434
2435 escaped = parseInt(matched[1], 16);
2436
2437 if (escaped > 0x7e && escaped <= 0xff && matched[0].indexOf('x') === 0) {
2438 // we bail out on \x7f..\xff,
2439 // leaving whole string escaped,
2440 // as it's probably completely binary
2441 return s;
2442 } else if (escaped >= 0x00 && escaped < 0x20) {
2443 // leave 0x00...0x1f escaped
2444 out += '\\' + matched[0];
2445 continue;
2446 } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) {
2447 // single-quote, apostrophe, backslash - escape these
2448 out += '\\' + String.fromCharCode(escaped);
2449 } else {
2450 out += String.fromCharCode(escaped);
2451 }
2452 }
2453 }
2454
2455 return out;
2456 }
2457 }
2458
2459 var beautifier = new Beautifier(js_source_text, options);
2460 return beautifier.beautify();
2461
2462 }
2463
2464 if (typeof define === "function" && define.amd) {
2465 // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
2466 define([], function() {
2467 return { js_beautify: js_beautify };
2468 });
2469 } else if (typeof exports !== "undefined") {
2470 // Add support for CommonJS. Just put this file somewhere on your require.paths
2471 // and you will be able to `var js_beautify = require("beautify").js_beautify`.
2472 exports.js_beautify = js_beautify;
2473 } else if (typeof window !== "undefined") {
2474 // If we're running a web page and don't have either of the above, add our one global
2475 window.js_beautify = js_beautify;
2476 } else if (typeof global !== "undefined") {
2477 // If we don't even have window, try global.
2478 global.js_beautify = js_beautify;
2479 }
2480
2481 }());