├── LICENSE.txt ├── README.md ├── js-beautifier.py └── lib └── jsbeautifier ├── __init__.py ├── __init__.pyc ├── __version__.py ├── __version__.pyc ├── tests ├── __init__.py ├── shell-smoke-test.sh ├── testindentation.py └── testjsbeautifier.py └── unpackers ├── README.specs.mkd ├── __init__.py ├── __init__.pyc ├── evalbased.py ├── evalbased.pyc ├── javascriptobfuscator.py ├── javascriptobfuscator.pyc ├── myobfuscate.py ├── myobfuscate.pyc ├── packer.py ├── packer.pyc ├── tests ├── __init__.py ├── test-myobfuscate-input.js ├── test-myobfuscate-output.js ├── test-packer-62-input.js ├── test-packer-non62-input.js ├── testjavascriptobfuscator.py ├── testmyobfuscate.py ├── testpacker.py └── testurlencode.py ├── urlencode.py └── urlencode.pyc /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | js-beautifier Copyright (c) 2007-2013 Einar Lielmanis and contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | burp_jsbeautifier 2 | ================= 3 | 4 | js-beautifier extension for Burp Suite 5 | 6 | Place lib/ folder in Burpsuite directory. 7 | 8 | Tested against Burpsuite Pro v1.5.11 and js-beautifier v1.3.2 9 | 10 | Warning: Enabling the automatic modification of all JavaScript may cause significant slowdown where large js files are encountered. 11 | 12 | Before: 13 | 14 | ![ScreenShot](https://a248.e.akamai.net/camo.github.com/acaebeb576347dc25e336eda946cdd4de09dbae9/687474703a2f2f7332332e706f7374696d672e6f72672f6265786d686a6c6c372f6265666f72652e6a7067) 15 | 16 | 17 | After: 18 | 19 | ![ScreenShot](https://a248.e.akamai.net/camo.github.com/4eca352737778e5c5db3a708127e8bfcd72a6fa8/687474703a2f2f7331372e706f7374696d672e6f72672f38676c6578613462332f61667465722e706e67) 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /js-beautifier.py: -------------------------------------------------------------------------------- 1 | ## 2 | # js-beautifier BurpSuite Extension 3 | # Ben Campbell 4 | # http://rewtdance.blogspot.co.uk 5 | # http://github.com/Meatballs1/burp_jsbeautifier 6 | # 7 | # Place the jsbeautifier python folder in the burpsuite/lib/ folder. 8 | # Load extension in the Extender tab. 9 | # 10 | # Tested in Burpsuite Pro v1.5.11 with js-beautify v1.3.2 11 | # http://jsbeautifier.org/ 12 | ## 13 | 14 | from burp import IBurpExtender 15 | from burp import IMessageEditorTabFactory 16 | from burp import IMessageEditorTab 17 | from burp import IParameter 18 | from burp import IHttpListener 19 | from burp import IBurpExtenderCallbacks 20 | from burp import ITab 21 | from javax import swing 22 | try: 23 | import jsbeautifier 24 | except ImportError: 25 | print "ERROR: jsbeautifier missing from burpsuite lib/ folder." 26 | 27 | 28 | def getHeadersContaining(findValue, headers): 29 | if (findValue != None and headers != None and len(headers)>0): 30 | return [s for s in headers if findValue in s] 31 | return None 32 | 33 | def parseContent(helper, content): 34 | javascript = "" 35 | 36 | if content == None: 37 | return javascript 38 | 39 | info = helper.analyzeResponse(content) 40 | 41 | js = helper.bytesToString(content[info.getBodyOffset():]) 42 | 43 | if (js != None and len(js) > 0): 44 | try: 45 | bjs = jsbeautifier.beautify(js) 46 | if (bjs != None and len(bjs) > 0): 47 | javascript = bjs 48 | else: 49 | print "ERROR: jsbeautifier returned an empty string or None." 50 | javascript = js 51 | except: 52 | print "ERROR: jsbeautifier threw an exception: %s" % sys.exc_info()[0] 53 | javascript = js 54 | 55 | return javascript 56 | 57 | class BurpExtender(IBurpExtender, IMessageEditorTabFactory, IHttpListener, ITab): 58 | 59 | # 60 | # implement IBurpExtender 61 | # 62 | def registerExtenderCallbacks(self, callbacks): 63 | print "js-beautifier BurpSuite Extension" 64 | print "Ben Campbell " 65 | print "http://rewtdance.blogspot.co.uk" 66 | print "http://github.com/Meatballs1/burp_jsbeautifier" 67 | 68 | 69 | # keep a reference to our callbacks object 70 | self._callbacks = callbacks 71 | 72 | # obtain an extension helpers object 73 | self._helpers = callbacks.getHelpers() 74 | 75 | # set our extension name 76 | callbacks.setExtensionName("Javascript Beautifier") 77 | 78 | # Don't Auto modify requests by default 79 | self._replaceAll = False 80 | 81 | # Create Tab 82 | self._jPanel = swing.JPanel() 83 | self._toggleButton = swing.JToggleButton('Enable Automatic JavaScript Beautifying', actionPerformed=self.toggleOnOff) 84 | self._jPanel.add(self._toggleButton) 85 | callbacks.customizeUiComponent(self._jPanel) 86 | 87 | # register ourselves as a message editor tab factory 88 | callbacks.registerMessageEditorTabFactory(self) 89 | callbacks.registerHttpListener(self) 90 | callbacks.addSuiteTab(self) 91 | return 92 | 93 | # 94 | # implement IMessageEditorTabFactory 95 | # 96 | def createNewInstance(self, controller, editable): 97 | # create a new instance of our custom editor tab 98 | return JavaScriptTab(self, controller, editable) 99 | 100 | # 101 | # implement IHttpListener 102 | # 103 | def processHttpMessage(self, toolFlag, messageIsRequest, messageInfo): 104 | if messageIsRequest: 105 | return 106 | 107 | if (self._replaceAll and toolFlag == IBurpExtenderCallbacks.TOOL_PROXY): 108 | response_info = self._helpers.analyzeResponse(messageInfo.getResponse()) 109 | headers = response_info.getHeaders() 110 | if (headers != None and len(headers) > 0): 111 | content_type_headers = getHeadersContaining('Content-Type', headers) 112 | if (content_type_headers != None): 113 | for content_type_header in content_type_headers: 114 | if ('javascript' in content_type_header): 115 | javascript = parseContent(self._helpers, messageInfo.getResponse()) 116 | messageInfo.setResponse(self._helpers.buildHttpMessage(headers, javascript)) 117 | 118 | return 119 | 120 | # 121 | # implement ITab 122 | # 123 | def getTabCaption(self): 124 | return "JSBeautifier" 125 | 126 | # 127 | # implement ITab 128 | # 129 | def getUiComponent(self): 130 | return self._jPanel 131 | 132 | def toggleOnOff(self, button): 133 | self._replaceAll = not self._replaceAll 134 | if self._replaceAll: 135 | start = 'Disable' 136 | else: 137 | start = 'Enable' 138 | self._toggleButton.setText('%s Automatic JavaScript Beautifying' % start) 139 | 140 | # 141 | # class implementing IMessageEditorTab 142 | # 143 | class JavaScriptTab(IMessageEditorTab): 144 | 145 | def __init__(self, extender, controller, editable): 146 | self._extender = extender 147 | self._editable = editable 148 | 149 | # create an instance of Burp's text editor, to display the javascript 150 | self._txtInput = extender._callbacks.createTextEditor() 151 | self._txtInput.setEditable(editable) 152 | 153 | # Set JS Beautifier opts 154 | opts = jsbeautifier.default_options() 155 | opts.indent_size = 2 156 | 157 | # Store httpHeaders incase request is modified 158 | self._httpHeaders = None 159 | return 160 | 161 | # 162 | # implement IMessageEditorTab 163 | # 164 | def getTabCaption(self): 165 | return "JavaScript" 166 | 167 | def getUiComponent(self): 168 | return self._txtInput.getComponent() 169 | 170 | def isEnabled(self, content, isRequest): 171 | # enable this tab only for responses containing javascript Content-Types 172 | if isRequest: 173 | return False 174 | 175 | response_info = self._extender._helpers.analyzeResponse(content) 176 | 177 | if response_info != None: 178 | headers = response_info.getHeaders() 179 | # Store HTTP Headers incase we edit the response. 180 | self._httpHeaders = headers 181 | if (headers != None and len(headers) > 0): 182 | content_type_headers = getHeadersContaining('Content-Type', headers) 183 | if (content_type_headers != None): 184 | for content_type_header in content_type_headers: 185 | if ('javascript' in content_type_header): 186 | return True 187 | 188 | return False 189 | 190 | 191 | 192 | def setMessage(self, content, isRequest): 193 | if (content is None): 194 | # clear our display 195 | self._txtInput.setText(None) 196 | self._txtInput.setEditable(False) 197 | 198 | else: 199 | javascript = parseContent(self._extender._helpers, content) 200 | 201 | self._txtInput.setText(javascript) 202 | self._txtInput.setEditable(self._editable) 203 | 204 | # remember the displayed content 205 | self._currentMessage = content 206 | return 207 | 208 | def getMessage(self): 209 | if (self._txtInput.isTextModified()): 210 | # reserialize the data 211 | text = self._txtInput.getText() 212 | 213 | # update the request with the new edited js 214 | return self._extender._helpers.buildHttpMessage(self._httpHeaders,text) 215 | else: 216 | return self._currentMessage 217 | 218 | def isModified(self): 219 | return self._txtInput.isTextModified() 220 | 221 | def getSelectedData(self): 222 | return self._txtInput.getSelectedText() 223 | 224 | -------------------------------------------------------------------------------- /lib/jsbeautifier/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import sys 3 | import getopt 4 | import re 5 | import string 6 | from jsbeautifier.__version__ import __version__ 7 | 8 | # 9 | # The MIT License (MIT) 10 | 11 | # Copyright (c) 2007-2013 Einar Lielmanis and contributors. 12 | 13 | # Permission is hereby granted, free of charge, to any person 14 | # obtaining a copy of this software and associated documentation files 15 | # (the "Software"), to deal in the Software without restriction, 16 | # including without limitation the rights to use, copy, modify, merge, 17 | # publish, distribute, sublicense, and/or sell copies of the Software, 18 | # and to permit persons to whom the Software is furnished to do so, 19 | # subject to the following conditions: 20 | 21 | # The above copyright notice and this permission notice shall be 22 | # included in all copies or substantial portions of the Software. 23 | 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 28 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 29 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 30 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | # SOFTWARE. 32 | # 33 | # Originally written by Einar Lielmanis et al., 34 | # Conversion to python by Einar Lielmanis, einar@jsbeautifier.org, 35 | # Parsing improvement for brace-less and semicolon-less statements 36 | # by Liam Newman 37 | # Python is not my native language, feel free to push things around. 38 | # 39 | # Use either from command line (script displays its usage when run 40 | # without any parameters), 41 | # 42 | # 43 | # or, alternatively, use it as a module: 44 | # 45 | # import jsbeautifier 46 | # res = jsbeautifier.beautify('your javascript string') 47 | # res = jsbeautifier.beautify_file('some_file.js') 48 | # 49 | # you may specify some options: 50 | # 51 | # opts = jsbeautifier.default_options() 52 | # opts.indent_size = 2 53 | # res = jsbeautifier.beautify('some javascript', opts) 54 | # 55 | # 56 | # Here are the available options: (read source) 57 | 58 | 59 | class BeautifierOptions: 60 | def __init__(self): 61 | self.indent_size = 4 62 | self.indent_char = ' ' 63 | self.indent_with_tabs = False 64 | self.preserve_newlines = True 65 | self.max_preserve_newlines = 10 66 | self.space_in_paren = False 67 | self.e4x = False 68 | self.jslint_happy = False 69 | self.brace_style = 'collapse' 70 | self.keep_array_indentation = False 71 | self.keep_function_indentation = False 72 | self.eval_code = False 73 | self.unescape_strings = False 74 | self.wrap_line_length = 0 75 | self.break_chained_methods = False 76 | 77 | 78 | 79 | def __repr__(self): 80 | return \ 81 | """indent_size = %d 82 | indent_char = [%s] 83 | preserve_newlines = %s 84 | max_preserve_newlines = %d 85 | space_in_paren = %s 86 | jslint_happy = %s 87 | indent_with_tabs = %s 88 | brace_style = %s 89 | keep_array_indentation = %s 90 | eval_code = %s 91 | wrap_line_length = %s 92 | unescape_strings = %s 93 | """ % ( self.indent_size, 94 | self.indent_char, 95 | self.preserve_newlines, 96 | self.max_preserve_newlines, 97 | self.space_in_paren, 98 | self.jslint_happy, 99 | self.indent_with_tabs, 100 | self.brace_style, 101 | self.keep_array_indentation, 102 | self.eval_code, 103 | self.wrap_line_length, 104 | self.unescape_strings, 105 | ) 106 | 107 | 108 | class BeautifierFlags: 109 | def __init__(self, mode): 110 | self.mode = mode 111 | self.last_text = '' 112 | self.last_word = '' 113 | self.var_line = False 114 | self.var_line_tainted = False 115 | self.var_line_reindented = False 116 | self.in_html_comment = False 117 | self.multiline_array = False 118 | self.if_block = False 119 | self.do_block = False 120 | self.do_while = False 121 | self.in_case = False 122 | self.in_case_statement = False 123 | self.case_body = False 124 | self.indentation_level = 0 125 | self.ternary_depth = 0 126 | 127 | def apply_base(self, flags_base): 128 | self.last_text = flags_base.last_text 129 | self.last_word = flags_base.last_word 130 | self.indentation_level = flags_base.indentation_level 131 | 132 | if flags_base.var_line and flags_base.var_line_reindented: 133 | self.indentation_level += 1 134 | 135 | 136 | def default_options(): 137 | return BeautifierOptions() 138 | 139 | 140 | def beautify(string, opts = default_options() ): 141 | b = Beautifier() 142 | return b.beautify(string, opts) 143 | 144 | def beautify_file(file_name, opts = default_options() ): 145 | if file_name == '-': # stdin 146 | stream = sys.stdin 147 | else: 148 | stream = open(file_name) 149 | 150 | return beautify(''.join(stream.readlines()), opts); 151 | 152 | 153 | def usage(stream=sys.stdout): 154 | 155 | print("jsbeautifier.py@" + __version__ + """ 156 | 157 | Javascript beautifier (http://jsbeautifier.org/) 158 | 159 | Usage: jsbeautifier.py [options] 160 | 161 | can be "-", which means stdin. 162 | defaults to stdout 163 | 164 | Input options: 165 | 166 | -i, --stdin read input from stdin 167 | 168 | Output options: 169 | 170 | -s, --indent-size=NUMBER indentation size. (default 4). 171 | -c, --indent-char=CHAR character to indent with. (default space). 172 | -t, --indent-with-tabs Indent with tabs, overrides -s and -c 173 | -d, --disable-preserve-newlines do not preserve existing line breaks. 174 | -P, --space-in-paren add padding spaces within paren, ie. f( a, b ) 175 | -j, --jslint-happy more jslint-compatible output 176 | -b, --brace-style=collapse brace style (collapse, expand, end-expand) 177 | -k, --keep-array-indentation keep array indentation. 178 | -o, --outfile=FILE specify a file to output to (default stdout) 179 | -f, --keep-function-indentation Do not re-indent function bodies defined in var lines. 180 | -x, --unescape-strings Decode printable chars encoded in \\xNN notation. 181 | -X, --e4x Pass E4X xml literals through untouched 182 | -w, --wrap-line-length Attempt to wrap line when it exceeds this length. 183 | NOTE: Line continues until next wrap point is found. 184 | 185 | Rarely needed options: 186 | 187 | --eval-code evaluate code if a JS interpreter is 188 | installed. May be useful with some obfuscated 189 | script but poses a potential security issue. 190 | 191 | -l, --indent-level=NUMBER initial indentation level. (default 0). 192 | 193 | -h, --help, --usage prints this help statement. 194 | -v, --version Show the version 195 | 196 | """, file=stream) 197 | if stream == sys.stderr: 198 | return 1 199 | else: 200 | return 0 201 | 202 | 203 | 204 | class MODE: 205 | BlockStatement, Statement, ObjectLiteral, ArrayLiteral, \ 206 | ForInitializer, Conditional, Expression = range(7) 207 | 208 | class Beautifier: 209 | 210 | def __init__(self, opts = default_options() ): 211 | 212 | self.opts = opts 213 | self.blank_state() 214 | 215 | def blank_state(self): 216 | 217 | # internal flags 218 | self.flags = None 219 | self.previous_flags = None 220 | self.flag_store = [] 221 | self.input_wanted_newline = False 222 | 223 | if self.opts.indent_with_tabs: 224 | self.indent_string = "\t" 225 | else: 226 | self.indent_string = self.opts.indent_char * self.opts.indent_size 227 | 228 | self.preindent_string = '' 229 | self.last_type = 'TK_START_EXPR' # last token type 230 | self.last_last_text = '' # pre-last token text 231 | 232 | self.input = None 233 | self.dot_after_newline = False 234 | self.output = [] # formatted javascript gets built here 235 | self.output_wrapped = False 236 | self.output_space_before_token = False 237 | self.whitespace_before_token = [] 238 | 239 | self.whitespace = ["\n", "\r", "\t", " "] 240 | self.wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$' 241 | self.digits = '0123456789' 242 | self.punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::' 243 | self.punct += ' <%= <% %>' 244 | self.punct = self.punct.split(' ') 245 | 246 | 247 | # Words which always should start on a new line 248 | self.line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(',') 249 | 250 | self.set_mode(MODE.BlockStatement) 251 | 252 | self.parser_pos = 0 253 | 254 | 255 | def beautify(self, s, opts = None ): 256 | 257 | if opts != None: 258 | self.opts = opts 259 | 260 | if self.opts.brace_style not in ['expand', 'collapse', 'end-expand']: 261 | raise(Exception('opts.brace_style must be "expand", "collapse" or "end-expand".')) 262 | 263 | self.blank_state() 264 | 265 | while s and s[0] in [' ', '\t']: 266 | self.preindent_string += s[0] 267 | s = s[1:] 268 | 269 | self.input = self.unpack(s, self.opts.eval_code) 270 | 271 | self.parser_pos = 0 272 | handlers = { 273 | 'TK_START_EXPR': self.handle_start_expr, 274 | 'TK_END_EXPR': self.handle_end_expr, 275 | 'TK_START_BLOCK': self.handle_start_block, 276 | 'TK_END_BLOCK': self.handle_end_block, 277 | 'TK_WORD': self.handle_word, 278 | 'TK_SEMICOLON': self.handle_semicolon, 279 | 'TK_STRING': self.handle_string, 280 | 'TK_EQUALS': self.handle_equals, 281 | 'TK_OPERATOR': self.handle_operator, 282 | 'TK_COMMA': self.handle_comma, 283 | 'TK_BLOCK_COMMENT': self.handle_block_comment, 284 | 'TK_INLINE_COMMENT': self.handle_inline_comment, 285 | 'TK_COMMENT': self.handle_comment, 286 | 'TK_DOT': self.handle_dot, 287 | 'TK_UNKNOWN': self.handle_unknown, 288 | } 289 | 290 | while True: 291 | self.token_text, token_type = self.get_next_token() 292 | 293 | #print (token_text, token_type, self.flags.mode) 294 | if token_type == 'TK_EOF': 295 | break 296 | 297 | keep_whitespace = self.opts.keep_array_indentation and self.is_array(self.flags.mode) 298 | 299 | if keep_whitespace: 300 | for i in range(self.n_newlines): 301 | self.append_newline(force_newline = True) 302 | else: # not keep_whitespace 303 | self.input_wanted_newline = self.n_newlines > 0 304 | if self.opts.max_preserve_newlines != 0 and self.n_newlines > self.opts.max_preserve_newlines: 305 | self.n_newlines = self.opts.max_preserve_newlines 306 | 307 | if self.opts.preserve_newlines and self.n_newlines > 1: 308 | for i in range(self.n_newlines): 309 | self.append_newline(i != 0) 310 | 311 | handlers[token_type](self.token_text) 312 | 313 | # The cleanest handling of inline comments is to treat them as though they aren't there. 314 | # Just continue formatting and the behavior should be logical. 315 | if token_type != 'TK_INLINE_COMMENT' and token_type != 'TK_COMMENT' and token_type != 'TK_UNKNOWN': 316 | self.last_last_text = self.flags.last_text 317 | self.last_type = token_type 318 | self.flags.last_text = self.token_text 319 | 320 | sweet_code = self.preindent_string + re.sub('[\n ]+$', '', ''.join(self.output)) 321 | return sweet_code 322 | 323 | def unpack(self, source, evalcode=False): 324 | import jsbeautifier.unpackers as unpackers 325 | try: 326 | return unpackers.run(source, evalcode) 327 | except unpackers.UnpackingError as error: 328 | print('error:', error) 329 | return '' 330 | 331 | def trim_output(self, eat_newlines = False): 332 | while len(self.output) \ 333 | and ( 334 | self.output[-1] == ' '\ 335 | or self.output[-1] == self.indent_string \ 336 | or self.output[-1] == self.preindent_string \ 337 | or (eat_newlines and self.output[-1] in ['\n', '\r'])): 338 | self.output.pop() 339 | 340 | def is_special_word(self, s): 341 | return s in ['case', 'return', 'do', 'if', 'throw', 'else'] 342 | 343 | def is_array(self, mode): 344 | return mode == MODE.ArrayLiteral 345 | 346 | 347 | def is_expression(self, mode): 348 | return mode in [MODE.Expression, MODE.ForInitializer, MODE.Conditional] 349 | 350 | def just_added_newline(self): 351 | return len(self.output) and self.output[-1] == '\n' 352 | 353 | def just_added_blankline(self): 354 | return self.just_added_newline() and len(self.output) - 1 > 0 and self.output[-2] == '\n' 355 | 356 | def last_index(self, arr, find): 357 | last_index = len(arr) - 1 358 | while last_index >= 0: 359 | if arr[last_index] == find: 360 | break 361 | else: 362 | last_index -= 1 363 | 364 | return last_index 365 | 366 | 367 | def allow_wrap_or_preserved_newline(self, token_text, force_linewrap = False): 368 | if self.opts.wrap_line_length > 0 and not force_linewrap: 369 | start_line = self.last_index(self.output, '\n') + 1 370 | 371 | # never wrap the first token of a line. 372 | if start_line < len(self.output): 373 | current_line = ''.join(self.output[start_line:]) 374 | proposed_line_length = len(current_line) + len(token_text) 375 | if self.output_space_before_token: 376 | proposed_line_length += 1 377 | 378 | if proposed_line_length >= self.opts.wrap_line_length: 379 | force_linewrap = True 380 | 381 | if ((self.opts.preserve_newlines and self.input_wanted_newline) or force_linewrap) and not self.just_added_newline(): 382 | self.append_newline(preserve_statement_flags = True) 383 | self.output_wrapped = True 384 | self.input_wanted_newline = False 385 | 386 | 387 | def append_newline(self, force_newline = False, preserve_statement_flags = False): 388 | self.output_wrapped = False 389 | self.output_space_before_token = False 390 | 391 | if not preserve_statement_flags: 392 | if self.flags.last_text != ';': 393 | while self.flags.mode == MODE.Statement and not self.flags.if_block: 394 | self.restore_mode(); 395 | 396 | if self.flags.mode == MODE.ArrayLiteral: 397 | self.flags.multiline_array = True 398 | 399 | 400 | if len(self.output) == 0: 401 | # no newline on start of file 402 | return 403 | 404 | if force_newline or not self.just_added_newline(): 405 | self.output.append('\n') 406 | 407 | 408 | def append_token_line_indentation(self): 409 | if self.just_added_newline(): 410 | if self.opts.keep_array_indentation and self.is_array(self.flags.mode) and len(self.whitespace_before_token) > 0: 411 | self.output.append(''.join(self.whitespace_before_token)) 412 | else: 413 | if self.preindent_string: 414 | self.output.append(self.preindent_string) 415 | 416 | self.append_indent_string(self.flags.indentation_level) 417 | self.append_indent_string(self.flags.var_line and self.flags.var_line_reindented) 418 | self.append_indent_string(self.output_wrapped) 419 | 420 | 421 | def append_indent_string(self, level = 1): 422 | if not isinstance(level, int): 423 | if level: 424 | level = 1 425 | else: 426 | level = 0 427 | 428 | if self.flags.last_text != '': 429 | for i in range(level): 430 | self.output.append(self.indent_string) 431 | 432 | 433 | def append_token_space_before(self): 434 | # make sure only single space gets drawn 435 | if self.output_space_before_token and len(self.output) and self.output[-1] not in [' ', '\n', self.indent_string]: 436 | self.output.append(' ') 437 | 438 | 439 | def append_token(self, s): 440 | self.append_token_line_indentation() 441 | self.output_wrapped = False 442 | self.append_token_space_before() 443 | self.output_space_before_token = False 444 | self.output.append(s) 445 | 446 | 447 | def indent(self): 448 | self.flags.indentation_level += 1 449 | 450 | 451 | def set_mode(self, mode): 452 | if self.flags: 453 | self.flag_store.append(self.flags) 454 | self.previous_flags = self.flags 455 | else: 456 | self.previous_flags = BeautifierFlags(mode) 457 | 458 | self.flags = BeautifierFlags(mode) 459 | self.flags.apply_base(self.previous_flags); 460 | 461 | def restore_mode(self): 462 | if len(self.flag_store) > 0: 463 | self.previous_flags = self.flags 464 | self.flags = self.flag_store.pop() 465 | 466 | def start_of_statement(self): 467 | if (self.flags.last_text == 'do' \ 468 | or (self.flags.last_text == 'else' and self.token_text != 'if' ) \ 469 | or (self.last_type == 'TK_END_EXPR' and (self.previous_flags.mode == MODE.ForInitializer or self.previous_flags.mode == MODE.Conditional))): 470 | self.allow_wrap_or_preserved_newline(self.token_text) 471 | self.set_mode(MODE.Statement) 472 | self.indent() 473 | self.output_wrapped = False 474 | return True 475 | else: 476 | return False 477 | 478 | def is_next(self, find): 479 | local_pos = self.parser_pos 480 | if local_pos >= len(self.input): 481 | return False 482 | c = self.input[local_pos] 483 | 484 | while (c in self.whitespace) and c != find: 485 | local_pos+= 1 486 | if local_pos >= len(self.input): 487 | return False 488 | c = self.input[local_pos] 489 | 490 | return c == find 491 | 492 | def get_next_token(self): 493 | 494 | self.n_newlines = 0 495 | 496 | if self.parser_pos >= len(self.input): 497 | return '', 'TK_EOF' 498 | 499 | self.input_wanted_newline = False 500 | self.whitespace_before_token = [] 501 | 502 | c = self.input[self.parser_pos] 503 | self.parser_pos += 1 504 | 505 | while c in self.whitespace: 506 | if c == '\n': 507 | self.n_newlines += 1 508 | self.whitespace_before_token = [] 509 | elif c == self.indent_string: 510 | self.whitespace_before_token.append(self.indent_string) 511 | elif c != '\r': 512 | self.whitespace_before_token.append(' ') 513 | 514 | if self.parser_pos >= len(self.input): 515 | return '', 'TK_EOF' 516 | 517 | c = self.input[self.parser_pos] 518 | self.parser_pos += 1 519 | 520 | 521 | if c in self.wordchar: 522 | if self.parser_pos < len(self.input): 523 | while self.input[self.parser_pos] in self.wordchar: 524 | c = c + self.input[self.parser_pos] 525 | self.parser_pos += 1 526 | if self.parser_pos == len(self.input): 527 | break 528 | 529 | # small and surprisingly unugly hack for IE-10 representation 530 | if self.parser_pos != len(self.input) and self.input[self.parser_pos] in '+-' \ 531 | and re.match('^[0-9]+[Ee]$', c): 532 | 533 | sign = self.input[self.parser_pos] 534 | self.parser_pos += 1 535 | t = self.get_next_token() 536 | c += sign + t[0] 537 | return c, 'TK_WORD' 538 | 539 | if c == 'in': # in is an operator, need to hack 540 | return c, 'TK_OPERATOR' 541 | 542 | return c, 'TK_WORD' 543 | 544 | if c in '([': 545 | return c, 'TK_START_EXPR' 546 | 547 | if c in ')]': 548 | return c, 'TK_END_EXPR' 549 | 550 | if c == '{': 551 | return c, 'TK_START_BLOCK' 552 | 553 | if c == '}': 554 | return c, 'TK_END_BLOCK' 555 | 556 | if c == ';': 557 | return c, 'TK_SEMICOLON' 558 | 559 | if c == '/': 560 | comment = '' 561 | inline_comment = True 562 | if self.input[self.parser_pos] == '*': # peek /* .. */ comment 563 | self.parser_pos += 1 564 | if self.parser_pos < len(self.input): 565 | while not (self.input[self.parser_pos] == '*' and \ 566 | self.parser_pos + 1 < len(self.input) and \ 567 | self.input[self.parser_pos + 1] == '/')\ 568 | and self.parser_pos < len(self.input): 569 | c = self.input[self.parser_pos] 570 | comment += c 571 | if c in '\r\n': 572 | inline_comment = False 573 | self.parser_pos += 1 574 | if self.parser_pos >= len(self.input): 575 | break 576 | self.parser_pos += 2 577 | if inline_comment and self.n_newlines == 0: 578 | return '/*' + comment + '*/', 'TK_INLINE_COMMENT' 579 | else: 580 | return '/*' + comment + '*/', 'TK_BLOCK_COMMENT' 581 | 582 | if self.input[self.parser_pos] == '/': # peek // comment 583 | comment = c 584 | while self.input[self.parser_pos] not in '\r\n': 585 | comment += self.input[self.parser_pos] 586 | self.parser_pos += 1 587 | if self.parser_pos >= len(self.input): 588 | break 589 | 590 | return comment, 'TK_COMMENT' 591 | 592 | if c == "'" or c == '"' or \ 593 | ( \ 594 | (c == '/') or \ 595 | (self.opts.e4x and c == "<" and re.match('^<[a-zA-Z:0-9]+\s*([a-zA-Z:0-9]+="[^"]*"\s*)*\/?\s*>', self.input[self.parser_pos - 1:])) \ 596 | ) and ( \ 597 | (self.last_type == 'TK_WORD' and self.is_special_word(self.flags.last_text)) or \ 598 | (self.last_type == 'TK_END_EXPR' and self.previous_flags.mode in [MODE.Conditional, MODE.ForInitializer]) or \ 599 | (self.last_type in ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_OPERATOR', \ 600 | 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'])): 601 | sep = c 602 | esc = False 603 | esc1 = 0 604 | esc2 = 0 605 | resulting_string = c 606 | in_char_class = False 607 | 608 | if self.parser_pos < len(self.input): 609 | if sep == '/': 610 | # handle regexp 611 | in_char_class = False 612 | while esc or in_char_class or self.input[self.parser_pos] != sep: 613 | resulting_string += self.input[self.parser_pos] 614 | if not esc: 615 | esc = self.input[self.parser_pos] == '\\' 616 | if self.input[self.parser_pos] == '[': 617 | in_char_class = True 618 | elif self.input[self.parser_pos] == ']': 619 | in_char_class = False 620 | else: 621 | esc = False 622 | self.parser_pos += 1 623 | if self.parser_pos >= len(self.input): 624 | # incomplete regex when end-of-file reached 625 | # bail out with what has received so far 626 | return resulting_string, 'TK_STRING' 627 | 628 | elif self.opts.e4x and sep == '<': 629 | # handle e4x xml literals 630 | xmlRegExp = re.compile('<(\/?)([a-zA-Z:0-9]+)\s*([a-zA-Z:0-9]+="[^"]*"\s*)*(\/?)\s*>') 631 | xmlStr = self.input[self.parser_pos - 1:] 632 | match = xmlRegExp.match(xmlStr) 633 | if match: 634 | rootTag = match.group(2) 635 | depth = 0 636 | while (match): 637 | isEndTag = match.group(1) 638 | tagName = match.group(2) 639 | isSingletonTag = match.group(4) 640 | if tagName == rootTag and not isSingletonTag: 641 | if isEndTag: 642 | depth -= 1 643 | else: 644 | depth += 1 645 | 646 | if depth <= 0: 647 | break 648 | 649 | match = xmlRegExp.search(xmlStr, match.end()) 650 | 651 | if match: 652 | xmlLength = match.end() # + len(match.group()) 653 | else: 654 | xmlLength = len(xmlStr) 655 | 656 | self.parser_pos += xmlLength - 1 657 | return xmlStr[:xmlLength], 'TK_STRING' 658 | 659 | else: 660 | # handle string 661 | while esc or self.input[self.parser_pos] != sep: 662 | resulting_string += self.input[self.parser_pos] 663 | if esc1 and esc1 >= esc2: 664 | try: 665 | esc1 = int(resulting_string[-esc2:], 16) 666 | except Exception: 667 | esc1 = False 668 | if esc1 and esc1 >= 0x20 and esc1 <= 0x7e: 669 | esc1 = chr(esc1) 670 | resulting_string = resulting_string[:-2 - esc2] 671 | if esc1 == sep or esc1 == '\\': 672 | resulting_string += '\\' 673 | resulting_string += esc1 674 | esc1 = 0 675 | if esc1: 676 | esc1 += 1 677 | elif not esc: 678 | esc = self.input[self.parser_pos] == '\\' 679 | else: 680 | esc = False 681 | if self.opts.unescape_strings: 682 | if self.input[self.parser_pos] == 'x': 683 | esc1 += 1 684 | esc2 = 2 685 | elif self.input[self.parser_pos] == 'u': 686 | esc1 += 1 687 | esc2 = 4 688 | self.parser_pos += 1 689 | if self.parser_pos >= len(self.input): 690 | # incomplete string when end-of-file reached 691 | # bail out with what has received so far 692 | return resulting_string, 'TK_STRING' 693 | 694 | 695 | self.parser_pos += 1 696 | resulting_string += sep 697 | if sep == '/': 698 | # regexps may have modifiers /regexp/MOD, so fetch those too 699 | while self.parser_pos < len(self.input) and self.input[self.parser_pos] in self.wordchar: 700 | resulting_string += self.input[self.parser_pos] 701 | self.parser_pos += 1 702 | return resulting_string, 'TK_STRING' 703 | 704 | if c == '#': 705 | 706 | # she-bang 707 | if len(self.output) == 0 and len(self.input) > 1 and self.input[self.parser_pos] == '!': 708 | resulting_string = c 709 | while self.parser_pos < len(self.input) and c != '\n': 710 | c = self.input[self.parser_pos] 711 | resulting_string += c 712 | self.parser_pos += 1 713 | return resulting_string.strip() + '\n', 'TK_UNKNOWN' 714 | 715 | 716 | # Spidermonkey-specific sharp variables for circular references 717 | # https://developer.mozilla.org/En/Sharp_variables_in_JavaScript 718 | # http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935 719 | sharp = '#' 720 | if self.parser_pos < len(self.input) and self.input[self.parser_pos] in self.digits: 721 | while True: 722 | c = self.input[self.parser_pos] 723 | sharp += c 724 | self.parser_pos += 1 725 | if self.parser_pos >= len(self.input) or c == '#' or c == '=': 726 | break 727 | if c == '#' or self.parser_pos >= len(self.input): 728 | pass 729 | elif self.input[self.parser_pos] == '[' and self.input[self.parser_pos + 1] == ']': 730 | sharp += '[]' 731 | self.parser_pos += 2 732 | elif self.input[self.parser_pos] == '{' and self.input[self.parser_pos + 1] == '}': 733 | sharp += '{}' 734 | self.parser_pos += 2 735 | return sharp, 'TK_WORD' 736 | 737 | if c == '<' and self.input[self.parser_pos - 1 : self.parser_pos + 3] == '': 747 | self.flags.in_html_comment = False 748 | self.parser_pos += 2 749 | return '-->', 'TK_COMMENT' 750 | 751 | if c == '.': 752 | return c, 'TK_DOT' 753 | 754 | if c in self.punct: 755 | while self.parser_pos < len(self.input) and c + self.input[self.parser_pos] in self.punct: 756 | c += self.input[self.parser_pos] 757 | self.parser_pos += 1 758 | if self.parser_pos >= len(self.input): 759 | break 760 | 761 | if c == ',': 762 | return c, 'TK_COMMA' 763 | if c == '=': 764 | return c, 'TK_EQUALS' 765 | 766 | return c, 'TK_OPERATOR' 767 | 768 | return c, 'TK_UNKNOWN' 769 | 770 | 771 | def handle_start_expr(self, token_text): 772 | if self.start_of_statement(): 773 | # The conditional starts the statement if appropriate. 774 | pass 775 | 776 | if token_text == '[': 777 | if self.last_type == 'TK_WORD' or self.flags.last_text == ')': 778 | if self.flags.last_text in self.line_starters: 779 | self.output_space_before_token = True 780 | self.set_mode(MODE.Expression) 781 | self.append_token(token_text) 782 | if self.opts.space_in_paren: 783 | self.output_space_before_token = True 784 | return 785 | 786 | if self.is_array(self.flags.mode): 787 | if self.flags.last_text == '[' or ( 788 | self.flags.last_text == ',' and (self.last_last_text == ']' or self.last_last_text == '}')): 789 | # ], [ goes to a new line 790 | # }, [ goes to a new line 791 | if not self.opts.keep_array_indentation: 792 | self.append_newline() 793 | 794 | else: 795 | if self.flags.last_text == 'for': 796 | self.set_mode(MODE.ForInitializer) 797 | elif self.flags.last_text in ['if', 'while']: 798 | self.set_mode(MODE.Conditional) 799 | else: 800 | self.set_mode(MODE.Expression) 801 | 802 | 803 | if self.flags.last_text == ';' or self.last_type == 'TK_START_BLOCK': 804 | self.append_newline() 805 | elif self.last_type in ['TK_END_EXPR', 'TK_START_EXPR', 'TK_END_BLOCK'] or self.flags.last_text == '.': 806 | # do nothing on (( and )( and ][ and ]( and .( 807 | if self.input_wanted_newline: 808 | self.append_newline() 809 | elif self.last_type not in ['TK_WORD', 'TK_OPERATOR']: 810 | self.output_space_before_token = True 811 | elif self.flags.last_word == 'function' or self.flags.last_word == 'typeof': 812 | # function() vs function (), typeof() vs typeof () 813 | if self.opts.jslint_happy: 814 | self.output_space_before_token = True 815 | elif self.flags.last_text in self.line_starters or self.flags.last_text == 'catch': 816 | # TODO: option space_before_conditional 817 | self.output_space_before_token = True 818 | 819 | # Support of this kind of newline preservation: 820 | # a = (b && 821 | # (c || d)); 822 | if self.last_type in ['TK_EQUALS', 'TK_OPERATOR']: 823 | if self.flags.mode != 'OBJECT': 824 | self.allow_wrap_or_preserved_newline(token_text) 825 | 826 | self.append_token(token_text) 827 | if self.opts.space_in_paren: 828 | self.output_space_before_token = True 829 | if self.token_text == '[': 830 | self.set_mode(MODE.ArrayLiteral) 831 | self.indent() 832 | if self.dot_after_newline: 833 | self.dot_after_newline = False 834 | self.indent() 835 | 836 | 837 | 838 | def handle_end_expr(self, token_text): 839 | # statements inside expressions are not valid syntax, but... 840 | # statements must all be closed when their container closes 841 | while self.flags.mode == MODE.Statement: 842 | self.restore_mode() 843 | 844 | if self.token_text == ']' and self.is_array(self.flags.mode) and self.flags.multiline_array and not self.opts.keep_array_indentation: 845 | self.append_newline() 846 | 847 | self.restore_mode() 848 | if self.opts.space_in_paren: 849 | self.output_space_before_token = True 850 | self.append_token(token_text) 851 | 852 | # do {} while () // no statement required after 853 | if self.flags.do_while and self.previous_flags.mode == MODE.Conditional: 854 | self.previous_flags.mode = MODE.Expression 855 | self.flags.do_block = False 856 | self.flags.do_while = False 857 | 858 | def handle_start_block(self, token_text): 859 | self.set_mode(MODE.BlockStatement) 860 | 861 | empty_braces = self.is_next('}') 862 | empty_anonymous_function = empty_braces and self.flags.last_word == 'function' and \ 863 | self.last_type == 'TK_END_EXPR' 864 | 865 | if self.opts.brace_style == 'expand': 866 | if self.last_type != 'TK_OPERATOR' and \ 867 | (empty_anonymous_function or 868 | self.last_type == 'TK_EQUALS' or 869 | (self.is_special_word(self.flags.last_text) and self.flags.last_text != 'else')): 870 | self.output_space_before_token = True 871 | else: 872 | self.append_newline() 873 | else: # collapse 874 | if self.last_type not in ['TK_OPERATOR', 'TK_START_EXPR']: 875 | if self.last_type == 'TK_START_BLOCK': 876 | self.append_newline() 877 | else: 878 | self.output_space_before_token = True 879 | else: 880 | # if TK_OPERATOR or TK_START_EXPR 881 | if self.is_array(self.previous_flags.mode) and self.flags.last_text == ',': 882 | if self.last_last_text == '}': 883 | self.output_space_before_token = True 884 | else: 885 | self.append_newline() 886 | 887 | self.append_token(token_text) 888 | self.indent() 889 | 890 | 891 | def handle_end_block(self, token_text): 892 | # statements must all be closed when their container closes 893 | while self.flags.mode == MODE.Statement: 894 | self.restore_mode() 895 | 896 | self.restore_mode() 897 | 898 | empty_braces = self.last_type == 'TK_START_BLOCK'; 899 | if self.opts.brace_style == 'expand': 900 | if not empty_braces: 901 | self.append_newline() 902 | else: 903 | # skip {} 904 | if not empty_braces: 905 | if self.is_array(self.flags.mode) and self.opts.keep_array_indentation: 906 | self.opts.keep_array_indentation = False 907 | self.append_newline() 908 | self.opts.keep_array_indentation = True 909 | else: 910 | self.append_newline() 911 | 912 | self.append_token(token_text) 913 | 914 | 915 | def handle_word(self, token_text): 916 | if self.start_of_statement(): 917 | # The conditional starts the statement if appropriate. 918 | pass 919 | elif self.input_wanted_newline and \ 920 | not self.is_expression(self.flags.mode) and \ 921 | (self.last_type != 'TK_OPERATOR' or (self.flags.last_text == '--' or self.flags.last_text == '++')) and \ 922 | self.last_type != 'TK_EQUALS' and \ 923 | (self.opts.preserve_newlines or self.flags.last_text != 'var'): 924 | self.append_newline() 925 | 926 | if self.flags.do_block and not self.flags.do_while: 927 | if token_text == 'while': 928 | # do {} ## while () 929 | self.output_space_before_token = True 930 | self.append_token(token_text) 931 | self.output_space_before_token = True 932 | self.flags.do_while = True 933 | return 934 | else: 935 | # do {} should always have while as the next word. 936 | # if we don't see the expected while, recover 937 | self.append_newline() 938 | self.flags.do_block = False 939 | 940 | if self.dot_after_newline and self.is_special_word(token_text): 941 | self.dot_after_newline = False 942 | 943 | # if may be followed by else, or not 944 | # Bare/inline ifs are tricky 945 | # Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e(); 946 | if self.flags.if_block: 947 | if token_text != 'else': 948 | while self.flags.mode == MODE.Statement: 949 | self.restore_mode() 950 | 951 | self.flags.if_block = False; 952 | 953 | 954 | if token_text == 'function': 955 | if self.flags.var_line and self.flags.last_text != '=': 956 | self.flags.var_line_reindented = not self.opts.keep_function_indentation 957 | if (self.just_added_newline() or self.flags.last_text == ';' or self.flags.last_text == '}') and \ 958 | self.flags.last_text != '{' and not self.is_array(self.flags.mode): 959 | # make sure there is a nice clean space of at least one blank line 960 | # before a new function definition, except in arrays 961 | if not self.just_added_newline(): 962 | self.append_newline(True) 963 | if not self.just_added_blankline(): 964 | self.append_newline(True) 965 | 966 | if self.last_type == 'TK_WORD': 967 | if self.flags.last_text in ['get', 'set', 'new', 'return']: 968 | self.output_space_before_token = True 969 | else: 970 | self.append_newline() 971 | elif self.last_type == 'TK_OPERATOR' or self.flags.last_text == '=': 972 | # foo = function 973 | self.output_space_before_token = True 974 | elif self.is_expression(self.flags.mode): 975 | # (function 976 | pass 977 | else: 978 | self.append_newline() 979 | 980 | self.append_token(token_text) 981 | self.flags.last_word = token_text 982 | return 983 | 984 | if token_text == 'case' or (token_text == 'default' and self.flags.in_case_statement): 985 | self.append_newline() 986 | if self.flags.case_body or self.opts.jslint_happy: 987 | self.flags.case_body = False 988 | self.flags.indentation_level -= 1 989 | self.append_token(token_text) 990 | self.flags.in_case = True 991 | self.flags.in_case_statement = True 992 | return 993 | 994 | prefix = 'NONE' 995 | 996 | if self.last_type == 'TK_END_BLOCK': 997 | if token_text not in ['else', 'catch', 'finally']: 998 | prefix = 'NEWLINE' 999 | else: 1000 | if self.opts.brace_style in ['expand', 'end-expand']: 1001 | prefix = 'NEWLINE' 1002 | else: 1003 | prefix = 'SPACE' 1004 | self.output_space_before_token = True 1005 | elif self.last_type == 'TK_SEMICOLON' and self.flags.mode == MODE.BlockStatement: 1006 | # TODO: Should this be for STATEMENT as well? 1007 | prefix = 'NEWLINE' 1008 | elif self.last_type == 'TK_SEMICOLON' and self.is_expression(self.flags.mode): 1009 | prefix = 'SPACE' 1010 | elif self.last_type == 'TK_STRING': 1011 | prefix = 'NEWLINE' 1012 | elif self.last_type == 'TK_WORD': 1013 | prefix = 'SPACE' 1014 | elif self.last_type == 'TK_START_BLOCK': 1015 | prefix = 'NEWLINE' 1016 | elif self.last_type == 'TK_END_EXPR': 1017 | self.output_space_before_token = True 1018 | prefix = 'NEWLINE' 1019 | 1020 | if token_text in self.line_starters: 1021 | if self.flags.last_text == 'else': 1022 | prefix = 'SPACE' 1023 | else: 1024 | prefix = 'NEWLINE' 1025 | 1026 | if self.last_type in ['TK_COMMA', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']: 1027 | if self.flags.mode != 'OBJECT': 1028 | self.allow_wrap_or_preserved_newline(token_text) 1029 | 1030 | if token_text in ['else', 'catch', 'finally']: 1031 | if self.last_type != 'TK_END_BLOCK' \ 1032 | or self.opts.brace_style == 'expand' \ 1033 | or self.opts.brace_style == 'end-expand': 1034 | self.append_newline() 1035 | else: 1036 | self.trim_output(True) 1037 | # If we trimmed and there's something other than a close block before us 1038 | # put a newline back in. Handles '} // comment' scenario. 1039 | if self.output[-1] != '}': 1040 | self.append_newline() 1041 | 1042 | self.output_space_before_token = True 1043 | 1044 | elif prefix == 'NEWLINE': 1045 | if self.is_special_word(self.flags.last_text): 1046 | # no newline between return nnn 1047 | self.output_space_before_token = True 1048 | elif self.last_type != 'TK_END_EXPR': 1049 | if (self.last_type != 'TK_START_EXPR' or token_text != 'var') and self.flags.last_text != ':': 1050 | # no need to force newline on VAR - 1051 | # for (var x = 0... 1052 | if token_text == 'if' and self.flags.last_word == 'else' and self.flags.last_text != '{': 1053 | self.output_space_before_token = True 1054 | else: 1055 | self.flags.var_line = False 1056 | self.flags.var_line_reindented = False 1057 | self.append_newline() 1058 | elif token_text in self.line_starters and self.flags.last_text != ')': 1059 | self.flags.var_line = False 1060 | self.flags.var_line_reindented = False 1061 | self.append_newline() 1062 | elif self.is_array(self.flags.mode) and self.flags.last_text == ',' and self.last_last_text == '}': 1063 | self.append_newline() # }, in lists get a newline 1064 | elif prefix == 'SPACE': 1065 | self.output_space_before_token = True 1066 | 1067 | 1068 | self.append_token(token_text) 1069 | self.flags.last_word = token_text 1070 | 1071 | if token_text == 'var': 1072 | self.flags.var_line = True 1073 | self.flags.var_line_reindented = False 1074 | self.flags.var_line_tainted = False 1075 | 1076 | 1077 | if token_text == 'do': 1078 | self.flags.do_block = True 1079 | 1080 | if token_text == 'if': 1081 | self.flags.if_block = True 1082 | 1083 | 1084 | def handle_semicolon(self, token_text): 1085 | while self.flags.mode == MODE.Statement and not self.flags.if_block: 1086 | self.restore_mode() 1087 | 1088 | self.append_token(token_text) 1089 | self.flags.var_line = False 1090 | self.flags.var_line_reindented = False 1091 | if self.flags.mode == 'OBJECT': 1092 | # OBJECT mode is weird and doesn't get reset too well. 1093 | self.flags.mode = MODE.BlockStatement 1094 | 1095 | 1096 | def handle_string(self, token_text): 1097 | if self.start_of_statement(): 1098 | # The conditional starts the statement if appropriate. 1099 | # One difference - strings want at least a space before 1100 | self.output_space_before_token = True 1101 | elif self.last_type == 'TK_WORD': 1102 | self.output_space_before_token = True 1103 | elif self.last_type == 'TK_END_EXPR' and self.previous_flags.mode in [MODE.Conditional, MODE.ForInitializer]: 1104 | self.output_space_before_token = True 1105 | elif self.last_type in ['TK_COMMA', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']: 1106 | if self.flags.mode != 'OBJECT': 1107 | self.allow_wrap_or_preserved_newline(token_text) 1108 | else: 1109 | self.append_newline() 1110 | 1111 | self.append_token(token_text) 1112 | 1113 | 1114 | def handle_equals(self, token_text): 1115 | if self.flags.var_line: 1116 | # just got an '=' in a var-line, different line breaking rules will apply 1117 | self.flags.var_line_tainted = True 1118 | 1119 | self.output_space_before_token = True 1120 | self.append_token(token_text) 1121 | self.output_space_before_token = True 1122 | 1123 | 1124 | def handle_comma(self, token_text): 1125 | if self.flags.var_line: 1126 | if self.is_expression(self.flags.mode) or self.last_type == 'TK_END_BLOCK': 1127 | # do not break on comma, for ( var a = 1, b = 2 1128 | self.flags.var_line_tainted = False 1129 | 1130 | if self.flags.var_line: 1131 | self.flags.var_line_reindented = True 1132 | 1133 | self.append_token(token_text) 1134 | 1135 | if self.flags.var_line_tainted: 1136 | self.flags.var_line_tainted = False 1137 | self.append_newline() 1138 | else: 1139 | self.output_space_before_token = True 1140 | 1141 | return 1142 | 1143 | if self.last_type == 'TK_END_BLOCK' and self.flags.mode != MODE.Expression: 1144 | self.append_token(token_text) 1145 | if self.flags.mode == 'OBJECT' and self.flags.last_text == '}': 1146 | self.append_newline() 1147 | else: 1148 | self.output_space_before_token = True 1149 | else: 1150 | if self.flags.mode == 'OBJECT': 1151 | self.append_token(token_text) 1152 | self.append_newline() 1153 | else: 1154 | # EXPR or DO_BLOCK 1155 | self.append_token(token_text) 1156 | self.output_space_before_token = True 1157 | 1158 | 1159 | def handle_operator(self, token_text): 1160 | space_before = True 1161 | space_after = True 1162 | 1163 | if self.is_special_word(self.flags.last_text): 1164 | # return had a special handling in TK_WORD 1165 | self.output_space_before_token = True 1166 | self.append_token(token_text) 1167 | return 1168 | 1169 | # hack for actionscript's import .*; 1170 | if token_text == '*' and self.last_type == 'TK_DOT' and not self.last_last_text.isdigit(): 1171 | self.append_token(token_text) 1172 | return 1173 | 1174 | 1175 | if token_text == ':' and self.flags.in_case: 1176 | self.flags.case_body = True 1177 | self.indent() 1178 | self.append_token(token_text) 1179 | self.append_newline() 1180 | self.flags.in_case = False 1181 | return 1182 | 1183 | if token_text == '::': 1184 | # no spaces around the exotic namespacing syntax operator 1185 | self.append_token(token_text) 1186 | return 1187 | 1188 | # http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1 1189 | # if there is a newline between -- or ++ and anything else we should preserve it. 1190 | if self.input_wanted_newline and (token_text == '--' or token_text == '++'): 1191 | self.append_newline() 1192 | 1193 | 1194 | if token_text in ['--', '++', '!'] \ 1195 | or (token_text in ['+', '-'] \ 1196 | and (self.last_type in ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR'] \ 1197 | or self.flags.last_text in self.line_starters or self.flags.last_text == ',')): 1198 | 1199 | space_before = False 1200 | space_after = False 1201 | 1202 | if self.flags.last_text == ';' and self.is_expression(self.flags.mode): 1203 | # for (;; ++i) 1204 | # ^^ 1205 | space_before = True 1206 | 1207 | if self.last_type == 'TK_WORD' and self.flags.last_text in self.line_starters: 1208 | space_before = True 1209 | 1210 | if self.flags.mode == MODE.BlockStatement and self.flags.last_text in ['{', ';']: 1211 | # { foo: --i } 1212 | # foo(): --bar 1213 | self.append_newline() 1214 | 1215 | elif token_text == ':': 1216 | if self.flags.ternary_depth == 0: 1217 | if self.flags.mode == MODE.BlockStatement: 1218 | self.flags.mode = 'OBJECT' 1219 | space_before = False 1220 | else: 1221 | self.flags.ternary_depth -= 1 1222 | elif token_text == '?': 1223 | self.flags.ternary_depth += 1 1224 | 1225 | if space_before: 1226 | self.output_space_before_token = True 1227 | 1228 | self.append_token(token_text) 1229 | 1230 | if space_after: 1231 | self.output_space_before_token = True 1232 | 1233 | 1234 | 1235 | def handle_block_comment(self, token_text): 1236 | 1237 | lines = token_text.replace('\x0d', '').split('\x0a') 1238 | # all lines start with an asterisk? that's a proper box comment 1239 | if not any(l for l in lines[1:] if ( l.strip() == '' or (l.lstrip())[0] != '*')): 1240 | self.append_newline(preserve_statement_flags = True) 1241 | self.append_token(lines[0]) 1242 | for line in lines[1:]: 1243 | self.append_newline(preserve_statement_flags = True) 1244 | self.append_token(' ' + line.strip()) 1245 | else: 1246 | # simple block comment: leave intact 1247 | if len(lines) > 1: 1248 | # multiline comment starts on a new line 1249 | self.append_newline(preserve_statement_flags = True) 1250 | else: 1251 | # single line /* ... */ comment stays on the same line 1252 | self.output_space_before_token = True 1253 | 1254 | self.append_token(lines[0]) 1255 | self.output.append('\n') 1256 | for line in lines[1:]: 1257 | self.output.append(line) 1258 | self.output.append('\n') 1259 | 1260 | if not self.is_next('\n'): 1261 | self.append_newline(preserve_statement_flags = True) 1262 | 1263 | 1264 | def handle_inline_comment(self, token_text): 1265 | self.output_space_before_token = True 1266 | self.append_token(token_text) 1267 | self.output_space_before_token = True 1268 | 1269 | 1270 | def handle_comment(self, token_text): 1271 | if self.input_wanted_newline: 1272 | self.append_newline(preserve_statement_flags = True) 1273 | 1274 | if not self.input_wanted_newline: 1275 | self.trim_output(True) 1276 | 1277 | self.output_space_before_token = True 1278 | self.append_token(token_text) 1279 | self.append_newline(preserve_statement_flags = True) 1280 | 1281 | 1282 | def handle_dot(self, token_text): 1283 | if self.is_special_word(self.flags.last_text): 1284 | self.output_space_before_token = True 1285 | else: 1286 | # allow preserved newlines before dots in general 1287 | # force newlines on dots after close paren when break_chained - for bar().baz() 1288 | self.allow_wrap_or_preserved_newline(token_text, 1289 | self.flags.last_text == ')' and self.opts.break_chained_methods) 1290 | 1291 | if self.just_added_newline(): 1292 | self.dot_after_newline = True 1293 | 1294 | self.append_token(token_text) 1295 | 1296 | def handle_unknown(self, token_text): 1297 | self.append_token(token_text) 1298 | if token_text[len(token_text) - 1] == '\n': 1299 | self.append_newline() 1300 | 1301 | 1302 | 1303 | 1304 | 1305 | def main(): 1306 | 1307 | argv = sys.argv[1:] 1308 | 1309 | try: 1310 | opts, args = getopt.getopt(argv, "s:c:o:dPjbkil:xhtfvXw:", 1311 | ['indent-size=','indent-char=','outfile=', 'disable-preserve-newlines', 1312 | 'space-in-paren', 'jslint-happy', 'brace-style=', 'keep-array-indentation', 1313 | 'indent-level=', 'unescape-strings', 'help', 'usage', 'stdin', 'eval-code', 1314 | 'indent-with-tabs', 'keep-function-indentation', 'version', 'e4x', 'wrap-line-length']) 1315 | except getopt.GetoptError as ex: 1316 | print(ex, file=sys.stderr) 1317 | return usage(sys.stderr) 1318 | 1319 | js_options = default_options() 1320 | 1321 | file = None 1322 | outfile = 'stdout' 1323 | if len(args) == 1: 1324 | file = args[0] 1325 | 1326 | for opt, arg in opts: 1327 | if opt in ('--keep-array-indentation', '-k'): 1328 | js_options.keep_array_indentation = True 1329 | if opt in ('--keep-function-indentation','-f'): 1330 | js_options.keep_function_indentation = True 1331 | elif opt in ('--outfile', '-o'): 1332 | outfile = arg 1333 | elif opt in ('--indent-size', '-s'): 1334 | js_options.indent_size = int(arg) 1335 | elif opt in ('--indent-char', '-c'): 1336 | js_options.indent_char = arg 1337 | elif opt in ('--indent-with-tabs', '-t'): 1338 | js_options.indent_with_tabs = True 1339 | elif opt in ('--disable-preserve_newlines', '-d'): 1340 | js_options.preserve_newlines = False 1341 | elif opt in ('--space-in-paren', '-P'): 1342 | js_options.space_in_paren = True 1343 | elif opt in ('--jslint-happy', '-j'): 1344 | js_options.jslint_happy = True 1345 | elif opt in ('--eval-code'): 1346 | js_options.eval_code = True 1347 | elif opt in ('--brace-style', '-b'): 1348 | js_options.brace_style = arg 1349 | elif opt in ('--unescape-strings', '-x'): 1350 | js_options.unescape_strings = True 1351 | elif opt in ('--e4x', '-X'): 1352 | js_options.e4x = True 1353 | elif opt in ('--wrap-line-length ', '-w'): 1354 | js_options.wrap_line_length = int(arg) 1355 | elif opt in ('--stdin', '-i'): 1356 | file = '-' 1357 | elif opt in ('--version', '-v'): 1358 | return print(__version__) 1359 | elif opt in ('--help', '--usage', '-h'): 1360 | return usage() 1361 | 1362 | 1363 | if not file: 1364 | print("Must define at least one file.", file=sys.stderr) 1365 | return usage(sys.stderr) 1366 | else: 1367 | try: 1368 | if outfile == 'stdout': 1369 | print(beautify_file(file, js_options)) 1370 | else: 1371 | with open(outfile, 'w') as f: 1372 | f.write(beautify_file(file, js_options) + '\n') 1373 | except Exception as ex: 1374 | print(ex, file=sys.stderr) 1375 | return 1 1376 | 1377 | # Success 1378 | return 0 1379 | -------------------------------------------------------------------------------- /lib/jsbeautifier/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meatballs1/burp_jsbeautifier/28fb29e685dd034913899c83af3cea16ea4b56d8/lib/jsbeautifier/__init__.pyc -------------------------------------------------------------------------------- /lib/jsbeautifier/__version__.py: -------------------------------------------------------------------------------- 1 | __version__ = '1.3.2' 2 | -------------------------------------------------------------------------------- /lib/jsbeautifier/__version__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Meatballs1/burp_jsbeautifier/28fb29e685dd034913899c83af3cea16ea4b56d8/lib/jsbeautifier/__version__.pyc -------------------------------------------------------------------------------- /lib/jsbeautifier/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Empty file :) 2 | -------------------------------------------------------------------------------- /lib/jsbeautifier/tests/shell-smoke-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | REL_SCRIPT_DIR="`dirname \"$0\"`" 4 | SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`" 5 | 6 | test_cli_common() 7 | { 8 | echo ---------------------------------------- 9 | echo Testing common cli behavior... 10 | CLI_SCRIPT_NAME=${1:?missing_param} 11 | CLI_SCRIPT=$SCRIPT_DIR/../../$CLI_SCRIPT_NAME 12 | echo Script: $CLI_SCRIPT 13 | 14 | # should find the minimal help output 15 | $CLI_SCRIPT 2>&1 | grep -q "Must define at least one file\." || { 16 | echo "[$CLI_SCRIPT_NAME] Output should be help message." 17 | exit 1 18 | } 19 | 20 | $CLI_SCRIPT 2> /dev/null && { 21 | echo "[$CLI_SCRIPT_NAME (with no parameters)] Return code should be error." 22 | exit 1 23 | } 24 | 25 | $CLI_SCRIPT -invalidParameter 2> /dev/null && { 26 | echo "[$CLI_SCRIPT_NAME -invalidParameter] Return code should be error." 27 | exit 1 28 | } 29 | 30 | $CLI_SCRIPT -h > /dev/null || { 31 | echo "[$CLI_SCRIPT_NAME -h] Return code should be success." 32 | exit 1 33 | } 34 | 35 | $CLI_SCRIPT -v > /dev/null || { 36 | echo "[$CLI_SCRIPT_NAME -v] Return code should be success." 37 | exit 1 38 | } 39 | 40 | MISSING_FILE="$SCRIPT_DIR/../../../js/bin/missing_file" 41 | MISSING_FILE_MESSAGE="No such file or directory" 42 | $CLI_SCRIPT $MISSING_FILE 2> /dev/null && { 43 | echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Return code should be error." 44 | exit 1 45 | } 46 | 47 | $CLI_SCRIPT $MISSING_FILE 2>&1 | grep -q "$MISSING_FILE_MESSAGE" || { 48 | echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Stderr should have useful message." 49 | exit 1 50 | } 51 | 52 | if [ "`$CLI_SCRIPT $MISSING_FILE 2> /dev/null`" != "" ]; then 53 | echo "[$CLI_SCRIPT_NAME $MISSING_FILE] Stdout should have no text." 54 | exit 1 55 | fi 56 | 57 | } 58 | 59 | test_cli_js_beautify() 60 | { 61 | echo ---------------------------------------- 62 | echo Testing js-beautify cli behavior... 63 | CLI_SCRIPT=$SCRIPT_DIR/../../js-beautify 64 | 65 | $CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js > /dev/null || { 66 | echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected succeed." 67 | exit 1 68 | } 69 | 70 | $CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/css-beautify.js > /dev/null || { 71 | echo "js-beautify output for $SCRIPT_DIR/../bin/css-beautify.js was expected succeed." 72 | exit 1 73 | } 74 | 75 | $CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/js-beautify.js | diff $SCRIPT_DIR/../../../js/bin/js-beautify.js - || { 76 | echo "js-beautify output for $SCRIPT_DIR/../bin/js-beautify.js was expected to be unchanged." 77 | exit 1 78 | } 79 | 80 | $CLI_SCRIPT $SCRIPT_DIR/../../../js/bin/css-beautify.js | diff -q $SCRIPT_DIR/../../../js/bin/css-beautify.js - && { 81 | echo "js-beautify output for $SCRIPT_DIR/../bin/css-beautify.js was expected to be different." 82 | exit 1 83 | } 84 | 85 | } 86 | 87 | #test_cli_common css-beautify 88 | #test_cli_common html-beautify 89 | test_cli_common js-beautify 90 | 91 | test_cli_js_beautify 92 | 93 | echo ---------------------------------------- 94 | echo $0 - PASSED. 95 | echo ---------------------------------------- 96 | -------------------------------------------------------------------------------- /lib/jsbeautifier/tests/testindentation.py: -------------------------------------------------------------------------------- 1 | import re 2 | import unittest 3 | import jsbeautifier 4 | 5 | class TestJSBeautifierIndentation(unittest.TestCase): 6 | def test_tabs(self): 7 | test_fragment = self.decodesto 8 | 9 | self.options.indent_with_tabs = 1; 10 | test_fragment('{tabs()}', "{\n\ttabs()\n}"); 11 | 12 | def test_function_indent(self): 13 | test_fragment = self.decodesto 14 | 15 | self.options.indent_with_tabs = 1; 16 | self.options.keep_function_indentation = 1; 17 | test_fragment('var foo = function(){ bar() }();', "var foo = function() {\n\tbar()\n}();"); 18 | 19 | self.options.tabs = 1; 20 | self.options.keep_function_indentation = 0; 21 | test_fragment('var foo = function(){ baz() }();', "var foo = function() {\n\tbaz()\n}();"); 22 | 23 | def decodesto(self, input, expectation=None): 24 | self.assertEqual( 25 | jsbeautifier.beautify(input, self.options), expectation or input) 26 | 27 | @classmethod 28 | def setUpClass(cls): 29 | options = jsbeautifier.default_options() 30 | options.indent_size = 4 31 | options.indent_char = ' ' 32 | options.preserve_newlines = True 33 | options.jslint_happy = False 34 | options.keep_array_indentation = False 35 | options.brace_style = 'collapse' 36 | options.indent_level = 0 37 | 38 | cls.options = options 39 | cls.wrapregex = re.compile('^(.+)$', re.MULTILINE) 40 | 41 | 42 | if __name__ == '__main__': 43 | unittest.main() 44 | -------------------------------------------------------------------------------- /lib/jsbeautifier/tests/testjsbeautifier.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import re 5 | import unittest 6 | import jsbeautifier 7 | 8 | class TestJSBeautifier(unittest.TestCase): 9 | def test_unescape(self): 10 | # Test cases contributed by 11 | test_fragment = self.decodesto 12 | bt = self.bt 13 | 14 | bt('"\\\\s"'); # == "\\s" in the js source 15 | bt("'\\\\s'"); # == '\\s' in the js source 16 | bt("'\\\\\\s'"); # == '\\\s' in the js source 17 | bt("'\\s'"); # == '\s' in the js source 18 | bt('"•"'); 19 | bt('"—"'); 20 | bt('"\\x41\\x42\\x43\\x01"', '"\\x41\\x42\\x43\\x01"'); 21 | bt('"\\u2022"', '"\\u2022"'); 22 | bt('a = /\s+/') 23 | #bt('a = /\\x41/','a = /A/') 24 | bt('"\\u2022";a = /\s+/;"\\x41\\x42\\x43\\x01".match(/\\x41/);','"\\u2022";\na = /\s+/;\n"\\x41\\x42\\x43\\x01".match(/\\x41/);') 25 | bt('"\\x22\\x27",\'\\x22\\x27\',"\\x5c",\'\\x5c\',"\\xff and \\xzz","unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff \\uzzzz"', '"\\x22\\x27", \'\\x22\\x27\', "\\x5c", \'\\x5c\', "\\xff and \\xzz", "unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff \\uzzzz"'); 26 | 27 | self.options.unescape_strings = True 28 | 29 | bt('"\\x41\\x42\\x43\\x01"', '"ABC\\x01"'); 30 | bt('"\\u2022"', '"\\u2022"'); 31 | bt('a = /\s+/') 32 | bt('"\\u2022";a = /\s+/;"\\x41\\x42\\x43\\x01".match(/\\x41/);','"\\u2022";\na = /\s+/;\n"ABC\\x01".match(/\\x41/);') 33 | bt('"\\x22\\x27",\'\\x22\\x27\',"\\x5c",\'\\x5c\',"\\xff and \\xzz","unicode \\u0000 \\u0022 \\u0027 \\u005c \\uffff \\uzzzz"', '"\\"\'", \'"\\\'\', "\\\\", \'\\\\\', "\\xff and \\xzz", "unicode \\u0000 \\" \' \\\\ \\uffff \\uzzzz"'); 34 | 35 | self.options.unescape_strings = False 36 | 37 | def test_beautifier(self): 38 | test_fragment = self.decodesto 39 | bt = self.bt 40 | 41 | bt(''); 42 | bt('return .5'); 43 | test_fragment(' return .5'); 44 | bt('a = 1', 'a = 1'); 45 | bt('a=1', 'a = 1'); 46 | bt("a();\n\nb();", "a();\n\nb();"); 47 | bt('var a = 1 var b = 2', "var a = 1\nvar b = 2"); 48 | bt('var a=1, b=c[d], e=6;', 'var a = 1,\n b = c[d],\n e = 6;'); 49 | bt('a = " 12345 "'); 50 | bt("a = ' 12345 '"); 51 | bt('if (a == 1) b = 2;', "if (a == 1) b = 2;"); 52 | bt('if(1){2}else{3}', "if (1) {\n 2\n} else {\n 3\n}"); 53 | bt('if(1||2);', 'if (1 || 2);'); 54 | bt('(a==1)||(b==2)', '(a == 1) || (b == 2)'); 55 | bt('var a = 1 if (2) 3;', "var a = 1\nif (2) 3;"); 56 | bt('a = a + 1'); 57 | bt('a = a == 1'); 58 | bt('/12345[^678]*9+/.match(a)'); 59 | bt('a /= 5'); 60 | bt('a = 0.5 * 3'); 61 | bt('a *= 10.55'); 62 | bt('a < .5'); 63 | bt('a <= .5'); 64 | bt('a<.5', 'a < .5'); 65 | bt('a<=.5', 'a <= .5'); 66 | bt('a = 0xff;'); 67 | bt('a=0xff+4', 'a = 0xff + 4'); 68 | bt('a = [1, 2, 3, 4]'); 69 | bt('F*(g/=f)*g+b', 'F * (g /= f) * g + b'); 70 | bt('a.b({c:d})', "a.b({\n c: d\n})"); 71 | bt('a.b\n(\n{\nc:\nd\n}\n)', "a.b({\n c: d\n})"); 72 | bt('a=!b', 'a = !b'); 73 | bt('a?b:c', 'a ? b : c'); 74 | bt('a?1:2', 'a ? 1 : 2'); 75 | bt('a?(b):c', 'a ? (b) : c'); 76 | bt('x={a:1,b:w=="foo"?x:y,c:z}', 'x = {\n a: 1,\n b: w == "foo" ? x : y,\n c: z\n}'); 77 | bt('x=a?b?c?d:e:f:g;', 'x = a ? b ? c ? d : e : f : g;'); 78 | bt('x=a?b?c?d:{e1:1,e2:2}:f:g;', 'x = a ? b ? c ? d : {\n e1: 1,\n e2: 2\n} : f : g;'); 79 | bt('function void(void) {}'); 80 | bt('if(!a)foo();', 'if (!a) foo();'); 81 | bt('a=~a', 'a = ~a'); 82 | bt('a;/*comment*/b;', "a; /*comment*/\nb;"); 83 | bt('a;/* comment */b;', "a; /* comment */\nb;"); 84 | test_fragment('a;/*\ncomment\n*/b;', "a;\n/*\ncomment\n*/\nb;"); # simple comments don't get touched at all 85 | bt('a;/**\n* javadoc\n*/b;', "a;\n/**\n * javadoc\n */\nb;"); 86 | test_fragment('a;/**\n\nno javadoc\n*/b;', "a;\n/**\n\nno javadoc\n*/\nb;"); 87 | bt('a;/*\n* javadoc\n*/b;', "a;\n/*\n * javadoc\n */\nb;"); # comment blocks detected and reindented even w/o javadoc starter 88 | 89 | bt('if(a)break;', "if (a) break;"); 90 | bt('if(a){break}', "if (a) {\n break\n}"); 91 | bt('if((a))foo();', 'if ((a)) foo();'); 92 | bt('for(var i=0;;) a', 'for (var i = 0;;) a'); 93 | bt('for(var i=0;;)\na', 'for (var i = 0;;)\n a'); 94 | bt('a++;', 'a++;'); 95 | bt('for(;;i++)a()', 'for (;; i++) a()'); 96 | bt('for(;;i++)\na()', 'for (;; i++)\n a()'); 97 | bt('for(;;++i)a', 'for (;; ++i) a'); 98 | bt('return(1)', 'return (1)'); 99 | bt('try{a();}catch(b){c();}finally{d();}', "try {\n a();\n} catch (b) {\n c();\n} finally {\n d();\n}"); 100 | bt('(xx)()'); # magic function call 101 | bt('a[1]()'); # another magic function call 102 | bt('if(a){b();}else if(c) foo();', "if (a) {\n b();\n} else if (c) foo();"); 103 | bt('switch(x) {case 0: case 1: a(); break; default: break}', "switch (x) {\n case 0:\n case 1:\n a();\n break;\n default:\n break\n}"); 104 | bt('switch(x){case -1:break;case !y:break;}', 'switch (x) {\n case -1:\n break;\n case !y:\n break;\n}'); 105 | bt('a !== b'); 106 | bt('if (a) b(); else c();', "if (a) b();\nelse c();"); 107 | bt("// comment\n(function something() {})"); # typical greasemonkey start 108 | bt("{\n\n x();\n\n}"); # was: duplicating newlines 109 | bt('if (a in b) foo();'); 110 | bt('var a, b;'); 111 | # bt('var a, b'); 112 | bt('{a:1, b:2}', "{\n a: 1,\n b: 2\n}"); 113 | bt('a={1:[-1],2:[+1]}', 'a = {\n 1: [-1],\n 2: [+1]\n}'); 114 | bt('var l = {\'a\':\'1\', \'b\':\'2\'}', "var l = {\n 'a': '1',\n 'b': '2'\n}"); 115 | bt('if (template.user[n] in bk) foo();'); 116 | bt('{{}/z/}', "{\n {}\n /z/\n}"); 117 | bt('return 45', "return 45"); 118 | bt('If[1]', "If[1]"); 119 | bt('Then[1]', "Then[1]"); 120 | bt('a = 1e10', "a = 1e10"); 121 | bt('a = 1.3e10', "a = 1.3e10"); 122 | bt('a = 1.3e-10', "a = 1.3e-10"); 123 | bt('a = -1.3e-10', "a = -1.3e-10"); 124 | bt('a = 1e-10', "a = 1e-10"); 125 | bt('a = e - 10', "a = e - 10"); 126 | bt('a = 11-10', "a = 11 - 10"); 127 | bt("a = 1;// comment", "a = 1; // comment"); 128 | bt("a = 1; // comment", "a = 1; // comment"); 129 | bt("a = 1;\n // comment", "a = 1;\n// comment"); 130 | bt('a = [-1, -1, -1]'); 131 | 132 | # The exact formatting these should have is open for discussion, but they are at least reasonable 133 | bt('a = [ // comment\n -1, -1, -1\n]'); 134 | bt('var a = [ // comment\n -1, -1, -1\n]'); 135 | bt('a = [ // comment\n -1, // comment\n -1, -1\n]'); 136 | bt('var a = [ // comment\n -1, // comment\n -1, -1\n]'); 137 | 138 | bt('o = [{a:b},{c:d}]', 'o = [{\n a: b\n }, {\n c: d\n }\n]'); 139 | 140 | bt("if (a) {\n do();\n}"); # was: extra space appended 141 | 142 | bt("if (a) {\n// comment\n}else{\n// comment\n}", "if (a) {\n // comment\n} else {\n // comment\n}"); # if/else statement with empty body 143 | bt("if (a) {\n// comment\n// comment\n}", "if (a) {\n // comment\n // comment\n}"); # multiple comments indentation 144 | bt("if (a) b() else c();", "if (a) b()\nelse c();"); 145 | bt("if (a) b() else if c() d();", "if (a) b()\nelse if c() d();"); 146 | 147 | bt("{}"); 148 | bt("{\n\n}"); 149 | bt("do { a(); } while ( 1 );", "do {\n a();\n} while (1);"); 150 | bt("do {} while (1);"); 151 | bt("do {\n} while (1);", "do {} while (1);"); 152 | bt("do {\n\n} while (1);"); 153 | bt("var a = x(a, b, c)"); 154 | bt("delete x if (a) b();", "delete x\nif (a) b();"); 155 | bt("delete x[x] if (a) b();", "delete x[x]\nif (a) b();"); 156 | bt("for(var a=1,b=2)d", "for (var a = 1, b = 2) d"); 157 | bt("for(var a=1,b=2,c=3) d", "for (var a = 1, b = 2, c = 3) d"); 158 | bt("for(var a=1,b=2,c=3;d<3;d++)\ne", "for (var a = 1, b = 2, c = 3; d < 3; d++)\n e"); 159 | bt("function x(){(a||b).c()}", "function x() {\n (a || b).c()\n}"); 160 | bt("function x(){return - 1}", "function x() {\n return -1\n}"); 161 | bt("function x(){return ! a}", "function x() {\n return !a\n}"); 162 | 163 | # a common snippet in jQuery plugins 164 | bt("settings = $.extend({},defaults,settings);", "settings = $.extend({}, defaults, settings);"); 165 | 166 | bt('{xxx;}()', '{\n xxx;\n}()'); 167 | 168 | bt("a = 'a'\nb = 'b'"); 169 | bt("a = /reg/exp"); 170 | bt("a = /reg/"); 171 | bt('/abc/.test()'); 172 | bt('/abc/i.test()'); 173 | bt("{/abc/i.test()}", "{\n /abc/i.test()\n}"); 174 | bt('var x=(a)/a;', 'var x = (a) / a;'); 175 | 176 | bt('x != -1', 'x != -1'); 177 | 178 | bt('for (; s-->0;)t', 'for (; s-- > 0;) t'); 179 | bt('for (; s++>0;)u', 'for (; s++ > 0;) u'); 180 | bt('a = s++>s--;', 'a = s++ > s--;'); 181 | bt('a = s++>--s;', 'a = s++ > --s;'); 182 | 183 | bt('{x=#1=[]}', '{\n x = #1=[]\n}'); 184 | bt('{a:#1={}}', '{\n a: #1={}\n}'); 185 | bt('{a:#1#}', '{\n a: #1#\n}'); 186 | 187 | test_fragment('"incomplete-string'); 188 | test_fragment("'incomplete-string"); 189 | test_fragment('/incomplete-regex'); 190 | 191 | test_fragment('{a:1},{a:2}', '{\n a: 1\n}, {\n a: 2\n}'); 192 | test_fragment('var ary=[{a:1}, {a:2}];', 'var ary = [{\n a: 1\n }, {\n a: 2\n }\n];'); 193 | 194 | test_fragment('{a:#1', '{\n a: #1'); # incomplete 195 | test_fragment('{a:#', '{\n a: #'); # incomplete 196 | 197 | test_fragment('}}}', '}\n}\n}'); # incomplete 198 | 199 | test_fragment('', ''); 200 | 201 | test_fragment('a=/regexp', 'a = /regexp'); # incomplete regexp 202 | 203 | bt('{a:#1=[],b:#1#,c:#999999#}', '{\n a: #1=[],\n b: #1#,\n c: #999999#\n}'); 204 | 205 | bt("a = 1e+2"); 206 | bt("a = 1e-2"); 207 | bt("do{x()}while(a>1)", "do {\n x()\n} while (a > 1)"); 208 | 209 | bt("x(); /reg/exp.match(something)", "x();\n/reg/exp.match(something)"); 210 | 211 | test_fragment("something();(", "something();\n("); 212 | test_fragment("#!she/bangs, she bangs\nf=1", "#!she/bangs, she bangs\n\nf = 1"); 213 | test_fragment("#!she/bangs, she bangs\n\nf=1", "#!she/bangs, she bangs\n\nf = 1"); 214 | test_fragment("#!she/bangs, she bangs\n\n/* comment */", "#!she/bangs, she bangs\n\n/* comment */"); 215 | test_fragment("#!she/bangs, she bangs\n\n\n/* comment */", "#!she/bangs, she bangs\n\n\n/* comment */"); 216 | test_fragment("#", "#"); 217 | test_fragment("#!", "#!"); 218 | 219 | bt("function namespace::something()"); 220 | 221 | test_fragment("", ""); 222 | test_fragment("", ""); 223 | 224 | bt('{foo();--bar;}', '{\n foo();\n --bar;\n}'); 225 | bt('{foo();++bar;}', '{\n foo();\n ++bar;\n}'); 226 | bt('{--bar;}', '{\n --bar;\n}'); 227 | bt('{++bar;}', '{\n ++bar;\n}'); 228 | 229 | # Handling of newlines around unary ++ and -- operators 230 | bt('{foo\n++bar;}', '{\n foo\n ++bar;\n}'); 231 | bt('{foo++\nbar;}', '{\n foo++\n bar;\n}'); 232 | 233 | # This is invalid, but harder to guard against. Issue #203. 234 | bt('{foo\n++\nbar;}', '{\n foo\n ++\n bar;\n}'); 235 | 236 | 237 | # regexps 238 | bt('a(/abc\\/\\/def/);b()', "a(/abc\\/\\/def/);\nb()"); 239 | bt('a(/a[b\\[\\]c]d/);b()', "a(/a[b\\[\\]c]d/);\nb()"); 240 | test_fragment('a(/a[b\\[', "a(/a[b\\["); # incomplete char class 241 | # allow unescaped / in char classes 242 | bt('a(/[a/b]/);b()', "a(/[a/b]/);\nb()"); 243 | 244 | bt('a=[[1,2],[4,5],[7,8]]', "a = [\n [1, 2],\n [4, 5],\n [7, 8]\n]"); 245 | bt('a=[[1,2],[4,5],function(){},[7,8]]', 246 | "a = [\n [1, 2],\n [4, 5],\n function() {},\n [7, 8]\n]"); 247 | bt('a=[[1,2],[4,5],function(){},function(){},[7,8]]', 248 | "a = [\n [1, 2],\n [4, 5],\n function() {},\n function() {},\n [7, 8]\n]"); 249 | bt('a=[[1,2],[4,5],function(){},[7,8]]', 250 | "a = [\n [1, 2],\n [4, 5],\n function() {},\n [7, 8]\n]"); 251 | bt('a=[b,c,function(){},function(){},d]', 252 | "a = [b, c,\n function() {},\n function() {},\n d\n]"); 253 | bt('a=[a[1],b[4],c[d[7]]]', "a = [a[1], b[4], c[d[7]]]"); 254 | bt('[1,2,[3,4,[5,6],7],8]', "[1, 2, [3, 4, [5, 6], 7], 8]"); 255 | 256 | bt('[[["1","2"],["3","4"]],[["5","6","7"],["8","9","0"]],[["1","2","3"],["4","5","6","7"],["8","9","0"]]]', 257 | '[\n [\n ["1", "2"],\n ["3", "4"]\n ],\n [\n ["5", "6", "7"],\n ["8", "9", "0"]\n ],\n [\n ["1", "2", "3"],\n ["4", "5", "6", "7"],\n ["8", "9", "0"]\n ]\n]'); 258 | 259 | bt('{[x()[0]];indent;}', '{\n [x()[0]];\n indent;\n}'); 260 | 261 | bt('return ++i', 'return ++i'); 262 | bt('return !!x', 'return !!x'); 263 | bt('return !x', 'return !x'); 264 | bt('return [1,2]', 'return [1, 2]'); 265 | bt('return;', 'return;'); 266 | bt('return\nfunc', 'return\nfunc'); 267 | bt('catch(e)', 'catch (e)'); 268 | 269 | bt('var a=1,b={foo:2,bar:3},{baz:4,wham:5},c=4;', 'var a = 1,\n b = {\n foo: 2,\n bar: 3\n }, {\n baz: 4,\n wham: 5\n }, c = 4;'); 270 | bt('var a=1,b={foo:2,bar:3},{baz:4,wham:5},\nc=4;', 'var a = 1,\n b = {\n foo: 2,\n bar: 3\n }, {\n baz: 4,\n wham: 5\n },\n c = 4;'); 271 | 272 | # inline comment 273 | bt('function x(/*int*/ start, /*string*/ foo)', 'function x( /*int*/ start, /*string*/ foo)'); 274 | 275 | # javadoc comment 276 | bt('/**\n* foo\n*/', '/**\n * foo\n */'); 277 | bt('{\n/**\n* foo\n*/\n}', '{\n /**\n * foo\n */\n}'); 278 | 279 | bt('var a,b,c=1,d,e,f=2;', 'var a, b, c = 1,\n d, e, f = 2;'); 280 | bt('var a,b,c=[],d,e,f=2;', 'var a, b, c = [],\n d, e, f = 2;'); 281 | bt('function() {\n var a, b, c, d, e = [],\n f;\n}'); 282 | 283 | bt('do/regexp/;\nwhile(1);', 'do /regexp/;\nwhile (1);'); # hmmm 284 | 285 | bt('var a = a,\na;\nb = {\nb\n}', 'var a = a,\n a;\nb = {\n b\n}'); 286 | 287 | bt('var a = a,\n /* c */\n b;'); 288 | bt('var a = a,\n // c\n b;'); 289 | 290 | bt('foo.("bar");'); # weird element referencing 291 | 292 | 293 | bt('if (a) a()\nelse b()\nnewline()'); 294 | bt('if (a) a()\nnewline()'); 295 | bt('a=typeof(x)', 'a = typeof(x)'); 296 | 297 | bt('var a = function() {\n return null;\n},\n b = false;'); 298 | 299 | bt('var a = function() {\n func1()\n}'); 300 | bt('var a = function() {\n func1()\n}\nvar b = function() {\n func2()\n}'); 301 | 302 | 303 | 304 | 305 | self.options.jslint_happy = True 306 | 307 | bt('x();\n\nfunction(){}', 'x();\n\nfunction () {}'); 308 | bt('function () {\n var a, b, c, d, e = [],\n f;\n}'); 309 | bt('switch(x) {case 0: case 1: a(); break; default: break}', 310 | "switch (x) {\ncase 0:\ncase 1:\n a();\n break;\ndefault:\n break\n}"); 311 | bt('switch(x){case -1:break;case !y:break;}', 312 | 'switch (x) {\ncase -1:\n break;\ncase !y:\n break;\n}'); 313 | test_fragment("// comment 1\n(function()", "// comment 1\n(function ()"); # typical greasemonkey start 314 | bt('var o1=$.extend(a);function(){alert(x);}', 'var o1 = $.extend(a);\n\nfunction () {\n alert(x);\n}'); 315 | bt('a=typeof(x)', 'a = typeof (x)'); 316 | 317 | self.options.jslint_happy = False 318 | 319 | bt('switch(x) {case 0: case 1: a(); break; default: break}', 320 | "switch (x) {\n case 0:\n case 1:\n a();\n break;\n default:\n break\n}"); 321 | bt('switch(x){case -1:break;case !y:break;}', 322 | 'switch (x) {\n case -1:\n break;\n case !y:\n break;\n}'); 323 | test_fragment("// comment 2\n(function()", "// comment 2\n(function()"); # typical greasemonkey start 324 | bt("var a2, b2, c2, d2 = 0, c = function() {}, d = '';", "var a2, b2, c2, d2 = 0,\n c = function() {}, d = '';"); 325 | bt("var a2, b2, c2, d2 = 0, c = function() {},\nd = '';", "var a2, b2, c2, d2 = 0,\n c = function() {},\n d = '';"); 326 | bt('var o2=$.extend(a);function(){alert(x);}', 'var o2 = $.extend(a);\n\nfunction() {\n alert(x);\n}'); 327 | 328 | bt('{"x":[{"a":1,"b":3},7,8,8,8,8,{"b":99},{"a":11}]}', '{\n "x": [{\n "a": 1,\n "b": 3\n },\n 7, 8, 8, 8, 8, {\n "b": 99\n }, {\n "a": 11\n }\n ]\n}'); 329 | 330 | bt('{"1":{"1a":"1b"},"2"}', '{\n "1": {\n "1a": "1b"\n },\n "2"\n}'); 331 | bt('{a:{a:b},c}', '{\n a: {\n a: b\n },\n c\n}'); 332 | 333 | bt('{[y[a]];keep_indent;}', '{\n [y[a]];\n keep_indent;\n}'); 334 | 335 | bt('if (x) {y} else { if (x) {y}}', 'if (x) {\n y\n} else {\n if (x) {\n y\n }\n}'); 336 | 337 | bt('if (foo) one()\ntwo()\nthree()'); 338 | bt('if (1 + foo() && bar(baz()) / 2) one()\ntwo()\nthree()'); 339 | bt('if (1 + foo() && bar(baz()) / 2) one();\ntwo();\nthree();'); 340 | 341 | self.options.indent_size = 1; 342 | self.options.indent_char = ' '; 343 | bt('{ one_char() }', "{\n one_char()\n}"); 344 | 345 | bt('var a,b=1,c=2', 'var a, b = 1,\n c = 2'); 346 | 347 | self.options.indent_size = 4; 348 | self.options.indent_char = ' '; 349 | bt('{ one_char() }', "{\n one_char()\n}"); 350 | 351 | self.options.indent_size = 1; 352 | self.options.indent_char = "\t"; 353 | bt('{ one_char() }', "{\n\tone_char()\n}"); 354 | bt('x = a ? b : c; x;', 'x = a ? b : c;\nx;'); 355 | 356 | self.options.indent_size = 4; 357 | self.options.indent_char = ' '; 358 | 359 | self.options.preserve_newlines = False; 360 | bt('var\na=dont_preserve_newlines;', 'var a = dont_preserve_newlines;'); 361 | 362 | # make sure the blank line between function definitions stays 363 | # even when preserve_newlines = False 364 | bt('function foo() {\n return 1;\n}\n\nfunction foo() {\n return 1;\n}'); 365 | bt('function foo() {\n return 1;\n}\nfunction foo() {\n return 1;\n}', 366 | 'function foo() {\n return 1;\n}\n\nfunction foo() {\n return 1;\n}' 367 | ); 368 | bt('function foo() {\n return 1;\n}\n\n\nfunction foo() {\n return 1;\n}', 369 | 'function foo() {\n return 1;\n}\n\nfunction foo() {\n return 1;\n}' 370 | ); 371 | 372 | 373 | self.options.preserve_newlines = True; 374 | bt('var\na=do_preserve_newlines;', 'var\na = do_preserve_newlines;') 375 | bt('// a\n// b\n\n// c\n// d') 376 | bt('if (foo) // comment\n{\n bar();\n}') 377 | 378 | self.options.keep_array_indentation = True; 379 | 380 | bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f']"); 381 | bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f',\n 'g', 'h', 'i']"); 382 | bt("a = ['a', 'b', 'c',\n 'd', 'e', 'f',\n 'g', 'h', 'i']"); 383 | 384 | 385 | bt('var x = [{}\n]', 'var x = [{}\n]'); 386 | bt('var x = [{foo:bar}\n]', 'var x = [{\n foo: bar\n }\n]'); 387 | bt("a = ['something',\n 'completely',\n 'different'];\nif (x);"); 388 | bt("a = ['a','b','c']", "a = ['a', 'b', 'c']"); 389 | bt("a = ['a', 'b','c']", "a = ['a', 'b', 'c']"); 390 | 391 | bt("x = [{'a':0}]", "x = [{\n 'a': 0\n }]"); 392 | 393 | bt('{a([[a1]], {b;});}', '{\n a([[a1]], {\n b;\n });\n}'); 394 | 395 | bt('a = //comment\n/regex/;'); 396 | 397 | test_fragment('/*\n * X\n */'); 398 | test_fragment('/*\r\n * X\r\n */', '/*\n * X\n */'); 399 | 400 | bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a) {\n b;\n} else {\n c;\n}'); 401 | 402 | bt('var a = new function();'); 403 | test_fragment('new function'); 404 | 405 | self.options.brace_style = 'expand'; 406 | 407 | bt('//case 1\nif (a == 1)\n{}\n//case 2\nelse if (a == 2)\n{}'); 408 | bt('if(1){2}else{3}', "if (1)\n{\n 2\n}\nelse\n{\n 3\n}"); 409 | bt('try{a();}catch(b){c();}catch(d){}finally{e();}', 410 | "try\n{\n a();\n}\ncatch (b)\n{\n c();\n}\ncatch (d)\n{}\nfinally\n{\n e();\n}"); 411 | bt('if(a){b();}else if(c) foo();', 412 | "if (a)\n{\n b();\n}\nelse if (c) foo();"); 413 | bt("if (a) {\n// comment\n}else{\n// comment\n}", 414 | "if (a)\n{\n // comment\n}\nelse\n{\n // comment\n}"); # if/else statement with empty body 415 | bt('if (x) {y} else { if (x) {y}}', 416 | 'if (x)\n{\n y\n}\nelse\n{\n if (x)\n {\n y\n }\n}'); 417 | bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 418 | 'if (a)\n{\n b;\n}\nelse\n{\n c;\n}'); 419 | test_fragment(' /*\n* xx\n*/\n// xx\nif (foo) {\n bar();\n}', 420 | ' /*\n * xx\n */\n // xx\n if (foo)\n {\n bar();\n }'); 421 | bt('if (foo)\n{}\nelse /regex/.test();'); 422 | bt('if (foo) /regex/.test();'); 423 | bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a)\n{\n b;\n}\nelse\n{\n c;\n}'); 424 | test_fragment('if (foo) {', 'if (foo)\n{'); 425 | test_fragment('foo {', 'foo\n{'); 426 | test_fragment('return {', 'return {'); # return needs the brace. 427 | test_fragment('return /* inline */ {', 'return /* inline */ {'); 428 | # test_fragment('return\n{', 'return\n{'); # can't support this?, but that's an improbable and extreme case anyway. 429 | test_fragment('return;\n{', 'return;\n{'); 430 | bt("throw {}"); 431 | bt("throw {\n foo;\n}"); 432 | bt('var foo = {}'); 433 | bt('if (foo) bar();\nelse break'); 434 | bt('function x() {\n foo();\n}zzz', 'function x()\n{\n foo();\n}\nzzz'); 435 | bt('a: do {} while (); xxx', 'a: do {} while ();\nxxx'); 436 | bt('var a = new function();'); 437 | bt('var a = new function() {};'); 438 | bt('var a = new function a()\n {};'); 439 | test_fragment('new function'); 440 | 441 | 442 | self.options.brace_style = 'collapse'; 443 | 444 | bt('//case 1\nif (a == 1) {}\n//case 2\nelse if (a == 2) {}'); 445 | bt('if(1){2}else{3}', "if (1) {\n 2\n} else {\n 3\n}"); 446 | bt('try{a();}catch(b){c();}catch(d){}finally{e();}', 447 | "try {\n a();\n} catch (b) {\n c();\n} catch (d) {} finally {\n e();\n}"); 448 | bt('if(a){b();}else if(c) foo();', 449 | "if (a) {\n b();\n} else if (c) foo();"); 450 | bt("if (a) {\n// comment\n}else{\n// comment\n}", 451 | "if (a) {\n // comment\n} else {\n // comment\n}"); # if/else statement with empty body 452 | bt('if (x) {y} else { if (x) {y}}', 453 | 'if (x) {\n y\n} else {\n if (x) {\n y\n }\n}'); 454 | bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 455 | 'if (a) {\n b;\n} else {\n c;\n}'); 456 | test_fragment(' /*\n* xx\n*/\n// xx\nif (foo) {\n bar();\n}', 457 | ' /*\n * xx\n */\n // xx\n if (foo) {\n bar();\n }'); 458 | bt('if (foo) {} else /regex/.test();'); 459 | bt('if (foo) /regex/.test();'); 460 | bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a) {\n b;\n} else {\n c;\n}'); 461 | test_fragment('if (foo) {', 'if (foo) {'); 462 | test_fragment('foo {', 'foo {'); 463 | test_fragment('return {', 'return {'); # return needs the brace. 464 | test_fragment('return /* inline */ {', 'return /* inline */ {'); 465 | # test_fragment('return\n{', 'return\n{'); # can't support this?, but that's an improbable and extreme case anyway. 466 | test_fragment('return;\n{', 'return; {'); 467 | bt("throw {}"); 468 | bt("throw {\n foo;\n}"); 469 | bt('var foo = {}'); 470 | bt('if (foo) bar();\nelse break'); 471 | bt('function x() {\n foo();\n}zzz', 'function x() {\n foo();\n}\nzzz'); 472 | bt('a: do {} while (); xxx', 'a: do {} while ();\nxxx'); 473 | bt('var a = new function();'); 474 | bt('var a = new function() {};'); 475 | bt('var a = new function a() {};'); 476 | test_fragment('new function'); 477 | 478 | self.options.brace_style = "end-expand"; 479 | 480 | bt('//case 1\nif (a == 1) {}\n//case 2\nelse if (a == 2) {}'); 481 | bt('if(1){2}else{3}', "if (1) {\n 2\n}\nelse {\n 3\n}"); 482 | bt('try{a();}catch(b){c();}catch(d){}finally{e();}', 483 | "try {\n a();\n}\ncatch (b) {\n c();\n}\ncatch (d) {}\nfinally {\n e();\n}"); 484 | bt('if(a){b();}else if(c) foo();', 485 | "if (a) {\n b();\n}\nelse if (c) foo();"); 486 | bt("if (a) {\n// comment\n}else{\n// comment\n}", 487 | "if (a) {\n // comment\n}\nelse {\n // comment\n}"); # if/else statement with empty body 488 | bt('if (x) {y} else { if (x) {y}}', 489 | 'if (x) {\n y\n}\nelse {\n if (x) {\n y\n }\n}'); 490 | bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 491 | 'if (a) {\n b;\n}\nelse {\n c;\n}'); 492 | test_fragment(' /*\n* xx\n*/\n// xx\nif (foo) {\n bar();\n}', 493 | ' /*\n * xx\n */\n // xx\n if (foo) {\n bar();\n }'); 494 | bt('if (foo) {}\nelse /regex/.test();'); 495 | bt('if (foo) /regex/.test();'); 496 | bt('if (a)\n{\nb;\n}\nelse\n{\nc;\n}', 'if (a) {\n b;\n}\nelse {\n c;\n}'); 497 | test_fragment('if (foo) {', 'if (foo) {'); 498 | test_fragment('foo {', 'foo {'); 499 | test_fragment('return {', 'return {'); # return needs the brace. 500 | test_fragment('return /* inline */ {', 'return /* inline */ {'); 501 | # test_fragment('return\n{', 'return\n{'); # can't support this?, but that's an improbable and extreme case anyway. 502 | test_fragment('return;\n{', 'return; {'); 503 | bt("throw {}"); 504 | bt("throw {\n foo;\n}"); 505 | bt('var foo = {}'); 506 | bt('if (foo) bar();\nelse break'); 507 | bt('function x() {\n foo();\n}zzz', 'function x() {\n foo();\n}\nzzz'); 508 | bt('a: do {} while (); xxx', 'a: do {} while ();\nxxx'); 509 | bt('var a = new function();'); 510 | bt('var a = new function() {};'); 511 | bt('var a = new function a() {};'); 512 | test_fragment('new function'); 513 | 514 | self.options.brace_style = 'collapse'; 515 | 516 | bt('a = ;'); # not the most perfect thing in the world, but you're the weirdo beaufifying php mix-ins with javascript beautifier 517 | bt('a = <%= external() %> ;'); 518 | 519 | test_fragment('roo = {\n /*\n ****\n FOO\n ****\n */\n BAR: 0\n};'); 520 | test_fragment("if (zz) {\n // ....\n}\n(function"); 521 | 522 | self.options.preserve_newlines = True; 523 | bt('var a = 42; // foo\n\nvar b;') 524 | bt('var a = 42; // foo\n\n\nvar b;') 525 | bt("var a = 'foo' +\n 'bar';"); 526 | bt("var a = \"foo\" +\n \"bar\";"); 527 | 528 | bt('"foo""bar""baz"', '"foo"\n"bar"\n"baz"') 529 | bt("'foo''bar''baz'", "'foo'\n'bar'\n'baz'") 530 | bt("{\n get foo() {}\n}") 531 | bt("{\n var a = get\n foo();\n}") 532 | bt("{\n set foo() {}\n}") 533 | bt("{\n var a = set\n foo();\n}") 534 | bt("var x = {\n get function()\n}") 535 | bt("var x = {\n set function()\n}") 536 | bt("var x = set\n\nfunction() {}", "var x = set\n\n function() {}") 537 | 538 | bt('') 539 | bt('