From 2009d250914ba865e81b20b264aa40736e38bf86 Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Wed, 3 May 2017 23:45:00 -0700 Subject: [PATCH] Move to Webpack and independent modules --- .codeclimate.yml | 4 + .gitignore | 3 + .travis.yml | 9 +- appveyor.yml | 2 +- js/lib/beautify-css.js | 1061 +++-- js/lib/beautify-html.js | 1773 ++++--- js/lib/beautify.js | 4404 ++++++++++-------- js/src/core/acorn.js | 63 + js/src/core/inputscanner.js | 95 + js/src/core/options.js | 48 + js/src/core/output.js | 234 + js/src/core/token.js | 49 + js/src/css/beautifier.js | 477 ++ js/src/css/index.js | 36 + js/src/html/beautifier.js | 1035 ++++ js/src/html/index.js | 36 + js/src/index.js | 27 + js/src/javascript/beautifier.js | 1437 ++++++ js/src/javascript/index.js | 36 + js/src/javascript/tokenizer.js | 620 +++ package.json | 7 +- python/cssbeautifier/__init__.py | 489 +- python/cssbeautifier/__version__.py | 1 - python/cssbeautifier/css/__init__.py | 1 + python/cssbeautifier/css/beautifier.py | 452 ++ python/cssbeautifier/css/options.py | 57 + python/jsbeautifier/__init__.py | 2009 +------- python/jsbeautifier/core/__init__.py | 1 + python/jsbeautifier/core/acorn.py | 70 + python/jsbeautifier/core/inputscanner.py | 77 + python/jsbeautifier/core/options.py | 36 + python/jsbeautifier/core/output.py | 186 + python/jsbeautifier/core/token.py | 35 + python/jsbeautifier/javascript/__init__.py | 1 + python/jsbeautifier/javascript/beautifier.py | 1185 +++++ python/jsbeautifier/javascript/options.py | 86 + python/jsbeautifier/javascript/tokenizer.py | 520 +++ tools/build.sh | 31 +- tools/generate-changelog.sh | 10 + tools/template/beautify-css.begin.js | 66 + tools/template/beautify-css.end.js | 23 + tools/template/beautify-html.begin.js | 75 + tools/template/beautify-html.end.js | 37 + tools/template/beautify.begin.js | 109 + tools/template/beautify.end.js | 21 + webpack.config.js | 52 + 46 files changed, 11506 insertions(+), 5580 deletions(-) create mode 100644 js/src/core/acorn.js create mode 100644 js/src/core/inputscanner.js create mode 100644 js/src/core/options.js create mode 100644 js/src/core/output.js create mode 100644 js/src/core/token.js create mode 100644 js/src/css/beautifier.js create mode 100644 js/src/css/index.js create mode 100644 js/src/html/beautifier.js create mode 100644 js/src/html/index.js create mode 100644 js/src/index.js create mode 100644 js/src/javascript/beautifier.js create mode 100644 js/src/javascript/index.js create mode 100644 js/src/javascript/tokenizer.js delete mode 100644 python/cssbeautifier/__version__.py create mode 100644 python/cssbeautifier/css/__init__.py create mode 100644 python/cssbeautifier/css/beautifier.py create mode 100644 python/cssbeautifier/css/options.py create mode 100644 python/jsbeautifier/core/__init__.py create mode 100644 python/jsbeautifier/core/acorn.py create mode 100644 python/jsbeautifier/core/inputscanner.py create mode 100644 python/jsbeautifier/core/options.py create mode 100644 python/jsbeautifier/core/output.py create mode 100644 python/jsbeautifier/core/token.py create mode 100644 python/jsbeautifier/javascript/__init__.py create mode 100644 python/jsbeautifier/javascript/beautifier.py create mode 100644 python/jsbeautifier/javascript/options.py create mode 100644 python/jsbeautifier/javascript/tokenizer.py create mode 100644 tools/template/beautify-css.begin.js create mode 100644 tools/template/beautify-css.end.js create mode 100644 tools/template/beautify-html.begin.js create mode 100644 tools/template/beautify-html.end.js create mode 100644 tools/template/beautify.begin.js create mode 100644 tools/template/beautify.end.js create mode 100644 webpack.config.js diff --git a/.codeclimate.yml b/.codeclimate.yml index fcf2f5a19..ec7df6d56 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -10,4 +10,8 @@ ratings: exclude_paths: - test/resources/** - web/third-party/** +- js/lib/beautify.js +- js/lib/beautify-css.js +- js/lib/beautify-html.js +- tools/template/* - "**/generated/*" diff --git a/.gitignore b/.gitignore index b2b2e83d7..8e956ecb6 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ python/MANIFEST python/build python/dist python/jsbeautifier.egg-info +.nvmrc +.nvm/ target +dist/ .idea/ diff --git a/.travis.yml b/.travis.yml index fe291502e..4c4ee94f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,19 @@ language: python python: - "2.7" - - "3.2" - "3.3" - "3.4" - "3.5" + - "3.6" node_js: - - "0.10" + - 'node' +env: + - TRAVIS_NODE_VERSION="6" +install: + - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION + - npm install script: "./build ci" diff --git a/appveyor.yml b/appveyor.yml index 0cc47ea65..125f7b442 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,7 +8,7 @@ deploy: off # Test against this version of Node.js environment: global: - nodejs_version: "0.12" + nodejs_version: "6" matrix: - PYTHON: "C:\\Python27" diff --git a/js/lib/beautify-css.js b/js/lib/beautify-css.js index 5bfd98b3c..aab3f9e96 100644 --- a/js/lib/beautify-css.js +++ b/js/lib/beautify-css.js @@ -1,4 +1,5 @@ -/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ +/*jshint curly:false, eqeqeq:true, laxbreak:true, noempty:false */ +/* AUTO-GENERATED. DO NOT MODIFY. */ /* The MIT License (MIT) @@ -41,7 +42,6 @@ The options are (default in brackets): indent_size (4) — indentation size, indent_char (space) — character to indent with, - preserve_newlines (default false) - whether existing line breaks should be preserved, selector_separator_newline (true) - separate selectors with newline or not (e.g. "a,\nbr" or "a, br") end_with_newline (false) - end with a newline @@ -64,256 +64,320 @@ // http://www.w3.org/TR/css3-syntax/ (function() { +var legacy_beautify_css = +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // identity function for calling harmony imports with the correct context +/******/ __webpack_require__.i = function(value) { return value; }; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 4); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { - function mergeOpts(allOptions, targetType) { - var finalOpts = {}; - var name; +/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ +/* - for (name in allOptions) { - if (name !== targetType) { - finalOpts[name] = allOptions[name]; - } - } + The MIT License (MIT) + Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors. - //merge in the per type settings for the targetType - if (targetType in allOptions) { - for (name in allOptions[targetType]) { - finalOpts[name] = allOptions[targetType][name]; - } - } - return finalOpts; - } + 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: - var lineBreak = /\r\n|[\n\r\u2028\u2029]/; - var allLineBreaks = new RegExp(lineBreak.source, 'g'); + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. - function css_beautify(source_text, options) { - options = options || {}; + 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. +*/ - // Allow the setting of language/file-type specific options - // with inheritance of overall settings - options = mergeOpts(options, 'css'); +var mergeOpts = __webpack_require__(2).mergeOpts; +var acorn = __webpack_require__(1); +var Output = __webpack_require__(3).Output; - source_text = source_text || ''; - var newlinesFromLastWSEat = 0; - var indentSize = options.indent_size ? parseInt(options.indent_size, 10) : 4; - var indentCharacter = options.indent_char || ' '; - var preserve_newlines = (options.preserve_newlines === undefined) ? false : options.preserve_newlines; - var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline; - var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; - var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules; - var space_around_combinator = (options.space_around_combinator === undefined) ? false : options.space_around_combinator; - space_around_combinator = space_around_combinator || ((options.space_around_selector_separator === undefined) ? false : options.space_around_selector_separator); - var eol = options.eol ? options.eol : 'auto'; +var lineBreak = acorn.lineBreak; +var allLineBreaks = acorn.allLineBreaks; - if (options.indent_with_tabs) { - indentCharacter = '\t'; - indentSize = 1; - } +function Beautifier(source_text, options) { + options = options || {}; - if (eol === 'auto') { - eol = '\n'; - if (source_text && lineBreak.test(source_text || '')) { - eol = source_text.match(lineBreak)[0]; - } + // Allow the setting of language/file-type specific options + // with inheritance of overall settings + options = mergeOpts(options, 'css'); + + source_text = source_text || ''; + + var newlinesFromLastWSEat = 0; + var indentSize = options.indent_size ? parseInt(options.indent_size, 10) : 4; + var indentCharacter = options.indent_char || ' '; + var preserve_newlines = (options.preserve_newlines === undefined) ? false : options.preserve_newlines; + var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline; + var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; + var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules; + var space_around_combinator = (options.space_around_combinator === undefined) ? false : options.space_around_combinator; + space_around_combinator = space_around_combinator || ((options.space_around_selector_separator === undefined) ? false : options.space_around_selector_separator); + var eol = options.eol ? options.eol : 'auto'; + + if (options.indent_with_tabs) { + indentCharacter = '\t'; + indentSize = 1; + } + + if (eol === 'auto') { + eol = '\n'; + if (source_text && lineBreak.test(source_text || '')) { + eol = source_text.match(lineBreak)[0]; } + } - eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n'); + eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n'); - // HACK: newline parsing inconsistent. This brute force normalizes the input. - source_text = source_text.replace(allLineBreaks, '\n'); + // HACK: newline parsing inconsistent. This brute force normalizes the input. + source_text = source_text.replace(allLineBreaks, '\n'); - // tokenizer - var whiteRe = /^\s+$/; + // tokenizer + var whiteRe = /^\s+$/; - var pos = -1, - ch; - var parenLevel = 0; + var pos = -1, + ch; + var parenLevel = 0; - function next() { - ch = source_text.charAt(++pos); - return ch || ''; - } + function next() { + ch = source_text.charAt(++pos); + return ch || ''; + } - function peek(skipWhitespace) { - var result = ''; - var prev_pos = pos; - if (skipWhitespace) { - eatWhitespace(); - } - result = source_text.charAt(pos + 1) || ''; - pos = prev_pos - 1; - next(); - return result; + function peek(skipWhitespace) { + var result = ''; + var prev_pos = pos; + if (skipWhitespace) { + eatWhitespace(); } + result = source_text.charAt(pos + 1) || ''; + pos = prev_pos - 1; + next(); + return result; + } - function eatString(endChars) { - var start = pos; - while (next()) { - if (ch === "\\") { - next(); - } else if (endChars.indexOf(ch) !== -1) { - break; - } else if (ch === "\n") { - break; - } + function eatString(endChars) { + var start = pos; + while (next()) { + if (ch === "\\") { + next(); + } else if (endChars.indexOf(ch) !== -1) { + break; + } else if (ch === "\n") { + break; } - return source_text.substring(start, pos + 1); } + return source_text.substring(start, pos + 1); + } - function peekString(endChar) { - var prev_pos = pos; - var str = eatString(endChar); - pos = prev_pos - 1; - next(); - return str; - } + function peekString(endChar) { + var prev_pos = pos; + var str = eatString(endChar); + pos = prev_pos - 1; + next(); + return str; + } - function eatWhitespace(preserve_newlines_local) { - var result = 0; - while (whiteRe.test(peek())) { - next(); - if (ch === '\n' && preserve_newlines_local && preserve_newlines) { - print.newLine(true); - result++; - } + function eatWhitespace(preserve_newlines_local) { + var result = 0; + while (whiteRe.test(peek())) { + next(); + if (ch === '\n' && preserve_newlines_local && preserve_newlines) { + output.add_new_line(true); + result++; } - newlinesFromLastWSEat = result; - return result; } + newlinesFromLastWSEat = result; + return result; + } - function skipWhitespace() { - var result = ''; - if (ch && whiteRe.test(ch)) { - result = ch; - } - while (whiteRe.test(next())) { - result += ch; - } - return result; + function skipWhitespace() { + var result = ''; + if (ch && whiteRe.test(ch)) { + result = ch; } + while (whiteRe.test(next())) { + result += ch; + } + return result; + } - function eatComment(singleLine) { - var start = pos; - singleLine = peek() === "/"; - next(); - while (next()) { - if (!singleLine && ch === "*" && peek() === "/") { - next(); - break; - } else if (singleLine && ch === "\n") { - return source_text.substring(start, pos); - } + function eatComment() { + var start = pos; + var singleLine = peek() === "/"; + next(); + while (next()) { + if (!singleLine && ch === "*" && peek() === "/") { + next(); + break; + } else if (singleLine && ch === "\n") { + return source_text.substring(start, pos); } - - return source_text.substring(start, pos) + ch; } + return source_text.substring(start, pos) + ch; + } - function lookBack(str) { - return source_text.substring(pos - str.length, pos).toLowerCase() === - str; - } - // Nested pseudo-class if we are insideRule - // and the next special character found opens - // a new block - function foundNestedPseudoClass() { - var openParen = 0; - for (var i = pos + 1; i < source_text.length; i++) { - var ch = source_text.charAt(i); - if (ch === "{") { - return true; - } else if (ch === '(') { - // pseudoclasses can contain () - openParen += 1; - } else if (ch === ')') { - if (openParen === 0) { - return false; - } - openParen -= 1; - } else if (ch === ";" || ch === "}") { + function lookBack(str) { + return source_text.substring(pos - str.length, pos).toLowerCase() === + str; + } + + // Nested pseudo-class if we are insideRule + // and the next special character found opens + // a new block + function foundNestedPseudoClass() { + var openParen = 0; + for (var i = pos + 1; i < source_text.length; i++) { + var ch = source_text.charAt(i); + if (ch === "{") { + return true; + } else if (ch === '(') { + // pseudoclasses can contain () + openParen += 1; + } else if (ch === ')') { + if (openParen === 0) { return false; } + openParen -= 1; + } else if (ch === ";" || ch === "}") { + return false; } - return false; } + return false; + } - // printer - var basebaseIndentString = source_text.match(/^[\t ]*/)[0]; - var singleIndent = new Array(indentSize + 1).join(indentCharacter); - var indentLevel = 0; - var nestedLevel = 0; - - function indent() { - indentLevel++; - basebaseIndentString += singleIndent; + // printer + var baseIndentString = ''; + var preindent_index = 0; + if (source_text && source_text.length) { + while ((source_text.charAt(preindent_index) === ' ' || + source_text.charAt(preindent_index) === '\t')) { + preindent_index += 1; } + baseIndentString = source_text.substring(0, preindent_index); + js_source_text = source_text.substring(preindent_index); + } - function outdent() { - indentLevel--; - basebaseIndentString = basebaseIndentString.slice(0, -indentSize); - } - var print = {}; - print["{"] = function(ch) { - print.singleSpace(); - output.push(ch); - if (!eatWhitespace(true)) { - print.newLine(); - } - }; - print["}"] = function(newline) { - if (newline) { - print.newLine(); - } - output.push('}'); - if (!eatWhitespace(true)) { - print.newLine(); - } - }; + var singleIndent = new Array(indentSize + 1).join(indentCharacter); + var indentLevel; + var nestedLevel; + var output; - print._lastCharWhitespace = function() { - return whiteRe.test(output[output.length - 1]); - }; + function print_string(output_string) { + if (output.just_added_newline()) { + output.set_indent(indentLevel); + } + output.add_token(output_string); + } - print.newLine = function(keepWhitespace) { - if (output.length) { - if (!keepWhitespace && output[output.length - 1] !== '\n') { - print.trim(); - } else if (output[output.length - 1] === basebaseIndentString) { - output.pop(); - } - output.push('\n'); + function preserveSingleSpace(isAfterSpace) { + if (isAfterSpace) { + output.space_before_token = true; + } + } - if (basebaseIndentString) { - output.push(basebaseIndentString); - } - } - }; - print.singleSpace = function() { - if (output.length && !print._lastCharWhitespace()) { - output.push(' '); - } - }; + function indent() { + indentLevel++; + } - print.preserveSingleSpace = function() { - if (isAfterSpace) { - print.singleSpace(); - } - }; + function outdent() { + if (indentLevel > 0) { + indentLevel--; + } + } - print.trim = function() { - while (print._lastCharWhitespace()) { - output.pop(); - } - }; + /*_____________________--------------------_____________________*/ + this.beautify = function() { + // reset + output = new Output(singleIndent, baseIndentString); + indentLevel = 0; + nestedLevel = 0; - var output = []; - /*_____________________--------------------_____________________*/ + pos = -1; + ch = null; + parenLevel = 0; var insideRule = false; var insidePropertyValue = false; @@ -334,29 +398,29 @@ var header = indentLevel === 0; if (isAfterNewline || header) { - print.newLine(); + output.add_new_line(); } - output.push(eatComment()); - print.newLine(); + print_string(eatComment()); + output.add_new_line(); if (header) { - print.newLine(true); + output.add_new_line(true); } } else if (ch === '/' && peek() === '/') { // single line comment if (!isAfterNewline && last_top_ch !== '{') { - print.trim(); + output.trim(true); } - print.singleSpace(); - output.push(eatComment()); - print.newLine(); + output.space_before_token = true; + print_string(eatComment()); + output.add_new_line(); } else if (ch === '@') { - print.preserveSingleSpace(); + preserveSingleSpace(isAfterSpace); // deal with less propery mixins @{...} if (peek() === '{') { - output.push(eatString('}')); + print_string(eatString('}')); } else { - output.push(ch); + print_string(ch); // strip trailing space, if present, for hash property checks var variableOrRule = peekString(": ,;{}()[]/='\""); @@ -365,36 +429,44 @@ // we have a variable or pseudo-class, add it and insert one space before continuing next(); variableOrRule = eatString(": ").replace(/\s$/, ''); - output.push(variableOrRule); - print.singleSpace(); + print_string(variableOrRule); + output.space_before_token = true; } variableOrRule = variableOrRule.replace(/\s$/, ''); // might be a nesting at-rule - if (variableOrRule in css_beautify.NESTED_AT_RULE) { + if (variableOrRule in this.NESTED_AT_RULE) { nestedLevel += 1; - if (variableOrRule in css_beautify.CONDITIONAL_GROUP_RULE) { + if (variableOrRule in this.CONDITIONAL_GROUP_RULE) { enteringConditionalGroup = true; } } } } else if (ch === '#' && peek() === '{') { - print.preserveSingleSpace(); - output.push(eatString('}')); + preserveSingleSpace(isAfterSpace); + print_string(eatString('}')); } else if (ch === '{') { if (peek(true) === '}') { eatWhitespace(); next(); - print.singleSpace(); - output.push("{"); - print['}'](false); + output.space_before_token = true; + print_string("{}"); + if (!eatWhitespace(true)) { + output.add_new_line(); + } + if (newlinesFromLastWSEat < 2 && newline_between_rules && indentLevel === 0) { - print.newLine(true); + output.add_new_line(true); } } else { indent(); - print["{"](ch); + output.space_before_token = true; + print_string(ch); + if (!eatWhitespace(true)) { + output.add_new_line(); + } + // when entering conditional groups, only rulesets are allowed if (enteringConditionalGroup) { enteringConditionalGroup = false; @@ -406,14 +478,20 @@ } } else if (ch === '}') { outdent(); - print["}"](true); + output.add_new_line(); + print_string(ch); insideRule = false; insidePropertyValue = false; if (nestedLevel) { nestedLevel--; } + + if (!eatWhitespace(true)) { + output.add_new_line(); + } + if (newlinesFromLastWSEat < 2 && newline_between_rules && indentLevel === 0) { - print.newLine(true); + output.add_new_line(true); } } else if (ch === ":") { eatWhitespace(); @@ -422,73 +500,73 @@ !lookBack("(")) { // 'property: value' delimiter // which could be in a conditional group query - output.push(':'); + print_string(':'); if (!insidePropertyValue) { insidePropertyValue = true; - print.singleSpace(); + output.space_before_token = true; } } else { // sass/less parent reference don't use a space // sass nested pseudo-class don't use a space // preserve space before pseudoclasses/pseudoelements, as it means "in any child" - if (lookBack(" ") && output[output.length - 1] !== " ") { - output.push(" "); + if (lookBack(" ")) { + output.space_before_token = true; } if (peek() === ":") { // pseudo-element next(); - output.push("::"); + print_string("::"); } else { // pseudo-class - output.push(':'); + print_string(':'); } } } else if (ch === '"' || ch === '\'') { - print.preserveSingleSpace(); - output.push(eatString(ch)); + preserveSingleSpace(isAfterSpace); + print_string(eatString(ch)); } else if (ch === ';') { insidePropertyValue = false; - output.push(ch); + print_string(ch); if (!eatWhitespace(true)) { - print.newLine(); + output.add_new_line(); } } else if (ch === '(') { // may be a url if (lookBack("url")) { - output.push(ch); + print_string(ch); eatWhitespace(); if (next()) { if (ch !== ')' && ch !== '"' && ch !== '\'') { - output.push(eatString(')')); + print_string(eatString(')')); } else { pos--; } } } else { parenLevel++; - print.preserveSingleSpace(); - output.push(ch); + preserveSingleSpace(isAfterSpace); + print_string(ch); eatWhitespace(); } } else if (ch === ')') { - output.push(ch); + print_string(ch); parenLevel--; } else if (ch === ',') { - output.push(ch); + print_string(ch); if (!eatWhitespace(true) && selectorSeparatorNewline && !insidePropertyValue && parenLevel < 1) { - print.newLine(); + output.add_new_line(); } else { - print.singleSpace(); + output.space_before_token = true; } } else if ((ch === '>' || ch === '+' || ch === '~') && !insidePropertyValue && parenLevel < 1) { //handle combinator spacing if (space_around_combinator) { - print.singleSpace(); - output.push(ch); - print.singleSpace(); + output.space_before_token = true; + print_string(ch); + output.space_before_token = true; } else { - output.push(ch); + print_string(ch); eatWhitespace(); // squash extra whitespace if (ch && whiteRe.test(ch)) { @@ -496,44 +574,30 @@ } } } else if (ch === ']') { - output.push(ch); + print_string(ch); } else if (ch === '[') { - print.preserveSingleSpace(); - output.push(ch); + preserveSingleSpace(isAfterSpace); + print_string(ch); } else if (ch === '=') { // no whitespace before or after eatWhitespace(); - output.push('='); + print_string('='); if (whiteRe.test(ch)) { ch = ''; } + } else { - print.preserveSingleSpace(); - output.push(ch); + preserveSingleSpace(isAfterSpace); + print_string(ch); } } - - var sweetCode = ''; - if (basebaseIndentString) { - sweetCode += basebaseIndentString; - } - - sweetCode += output.join('').replace(/[\r\n\t ]+$/, ''); - - // establish end_with_newline - if (end_with_newline) { - sweetCode += '\n'; - } - - if (eol !== '\n') { - sweetCode = sweetCode.replace(/[\n]/g, eol); - } + var sweetCode = output.get_code(end_with_newline, eol); return sweetCode; - } + }; // https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule - css_beautify.NESTED_AT_RULE = { + this.NESTED_AT_RULE = { "@page": true, "@font-face": true, "@keyframes": true, @@ -542,30 +606,441 @@ "@supports": true, "@document": true }; - css_beautify.CONDITIONAL_GROUP_RULE = { + this.CONDITIONAL_GROUP_RULE = { "@media": true, "@supports": true, "@document": true }; +} + +module.exports.Beautifier = Beautifier; + + +/***/ }), +/* 1 */ +/***/ (function(module, exports) { + +/* jshint curly: false */ +// This section of code is taken from acorn. +// +// Acorn was written by Marijn Haverbeke and released under an MIT +// license. The Unicode regexps (for identifiers and whitespace) were +// taken from [Esprima](http://esprima.org) by Ariya Hidayat. +// +// Git repositories for Acorn are available at +// +// http://marijnhaverbeke.nl/git/acorn +// https://github.com/marijnh/acorn.git + +// ## Character categories + +// Big ugly regular expressions that match characters in the +// whitespace, identifier, and identifier-start categories. These +// are only applied when a character is found to actually have a +// code point above 128. + +var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line +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"; +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"; +var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); +var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); + +// Whether a single character denotes a newline. + +exports.newline = /[\n\r\u2028\u2029]/; + +// Matches a whole line break (where CRLF is considered a single +// line break). Used to count lines. + +// in javascript, these two differ +// in python they are the same, different methods are called on them +exports.lineBreak = new RegExp('\r\n|' + exports.newline.source); +exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g'); + + +// Test whether a given character code starts an identifier. + +exports.isIdentifierStart = function(code) { + // permit $ (36) and @ (64). @ is used in ES7 decorators. + if (code < 65) return code === 36 || code === 64; + // 65 through 91 are uppercase letters. + if (code < 91) return true; + // permit _ (95). + if (code < 97) return code === 95; + // 97 through 123 are lowercase letters. + if (code < 123) return true; + return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)); +}; - /*global define */ - if (typeof define === "function" && define.amd) { - // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- ) - define([], function() { - return { - css_beautify: css_beautify - }; - }); - } else if (typeof exports !== "undefined") { - // Add support for CommonJS. Just put this file somewhere on your require.paths - // and you will be able to `var html_beautify = require("beautify").html_beautify`. - exports.css_beautify = css_beautify; - } else if (typeof window !== "undefined") { - // If we're running a web page and don't have either of the above, add our one global - window.css_beautify = css_beautify; - } else if (typeof global !== "undefined") { - // If we don't even have window, try global. - global.css_beautify = css_beautify; +// Test whether a given character is part of an identifier. + +exports.isIdentifierChar = function(code) { + if (code < 48) return code === 36; + if (code < 58) return true; + if (code < 65) return false; + if (code < 91) return true; + if (code < 97) return code === 95; + if (code < 123) return true; + return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)); +}; + + +/***/ }), +/* 2 */ +/***/ (function(module, exports) { + +/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ +/* + + The MIT License (MIT) + + Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +function mergeOpts(allOptions, targetType) { + var finalOpts = {}; + var name; + + for (name in allOptions) { + if (name !== targetType) { + finalOpts[name] = allOptions[name]; + } + } + + //merge in the per type settings for the targetType + if (targetType in allOptions) { + for (name in allOptions[targetType]) { + finalOpts[name] = allOptions[targetType][name]; + } } + return finalOpts; +} + +module.exports.mergeOpts = mergeOpts; + + +/***/ }), +/* 3 */ +/***/ (function(module, exports) { + +/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ +/* + + The MIT License (MIT) + + Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +function OutputLine(parent) { + var _character_count = 0; + // use indent_count as a marker for lines that have preserved indentation + var _indent_count = -1; + + var _items = []; + var _empty = true; + + this.set_indent = function(level) { + _character_count = parent.baseIndentLength + level * parent.indent_length; + _indent_count = level; + }; + + this.get_character_count = function() { + return _character_count; + }; + + this.is_empty = function() { + return _empty; + }; + + this.last = function() { + if (!this._empty) { + return _items[_items.length - 1]; + } else { + return null; + } + }; + + this.push = function(input) { + _items.push(input); + _character_count += input.length; + _empty = false; + }; + + this.pop = function() { + var item = null; + if (!_empty) { + item = _items.pop(); + _character_count -= item.length; + _empty = _items.length === 0; + } + return item; + }; + + this.remove_indent = function() { + if (_indent_count > 0) { + _indent_count -= 1; + _character_count -= parent.indent_length; + } + }; + + this.trim = function() { + while (this.last() === ' ') { + _items.pop(); + _character_count -= 1; + } + _empty = _items.length === 0; + }; -}()); \ No newline at end of file + this.toString = function() { + var result = ''; + if (!this._empty) { + if (_indent_count >= 0) { + result = parent.indent_cache[_indent_count]; + } + result += _items.join(''); + } + return result; + }; +} + +function Output(indent_string, baseIndentString) { + baseIndentString = baseIndentString || ''; + this.indent_cache = [baseIndentString]; + this.baseIndentLength = baseIndentString.length; + this.indent_length = indent_string.length; + this.raw = false; + + var lines = []; + this.baseIndentString = baseIndentString; + this.indent_string = indent_string; + this.previous_line = null; + this.current_line = null; + this.space_before_token = false; + + this.add_outputline = function() { + this.previous_line = this.current_line; + this.current_line = new OutputLine(this); + lines.push(this.current_line); + }; + + // initialize + this.add_outputline(); + + + this.get_line_number = function() { + return lines.length; + }; + + // Using object instead of string to allow for later expansion of info about each line + this.add_new_line = function(force_newline) { + if (this.get_line_number() === 1 && this.just_added_newline()) { + return false; // no newline on start of file + } + + if (force_newline || !this.just_added_newline()) { + if (!this.raw) { + this.add_outputline(); + } + return true; + } + + return false; + }; + + this.get_code = function(end_with_newline, eol) { + var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, ''); + + if (end_with_newline) { + sweet_code += '\n'; + } + + if (eol !== '\n') { + sweet_code = sweet_code.replace(/[\n]/g, eol); + } + + return sweet_code; + }; + + this.set_indent = function(level) { + // Never indent your first output indent at the start of the file + if (lines.length > 1) { + while (level >= this.indent_cache.length) { + this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string); + } + + this.current_line.set_indent(level); + return true; + } + this.current_line.set_indent(0); + return false; + }; + + this.add_raw_token = function(token) { + for (var x = 0; x < token.newlines; x++) { + this.add_outputline(); + } + this.current_line.push(token.whitespace_before); + this.current_line.push(token.text); + this.space_before_token = false; + }; + + this.add_token = function(printable_token) { + this.add_space_before_token(); + this.current_line.push(printable_token); + }; + + this.add_space_before_token = function() { + if (this.space_before_token && !this.just_added_newline()) { + this.current_line.push(' '); + } + this.space_before_token = false; + }; + + this.remove_indent = function(index) { + var output_length = lines.length; + while (index < output_length) { + lines[index].remove_indent(); + index++; + } + }; + + this.trim = function(eat_newlines) { + eat_newlines = (eat_newlines === undefined) ? false : eat_newlines; + + this.current_line.trim(indent_string, baseIndentString); + + while (eat_newlines && lines.length > 1 && + this.current_line.is_empty()) { + lines.pop(); + this.current_line = lines[lines.length - 1]; + this.current_line.trim(); + } + + this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null; + }; + + this.just_added_newline = function() { + return this.current_line.is_empty(); + }; + + this.just_added_blankline = function() { + if (this.just_added_newline()) { + if (lines.length === 1) { + return true; // start of the file and newline = blank + } + + var line = lines[lines.length - 2]; + return line.is_empty(); + } + return false; + }; +} + +module.exports.Output = Output; + + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ +/* + + The MIT License (MIT) + + Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +var Beautifier = __webpack_require__(0).Beautifier; + +function css_beautify(source_text, options) { + var beautifier = new Beautifier(source_text, options); + return beautifier.beautify(); +} + +module.exports = css_beautify; + +/***/ }) +/******/ ]); +var css_beautify = legacy_beautify_css; +/* Footer */ +if (typeof define === "function" && define.amd) { + // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- ) + define([], function() { + return { + css_beautify: css_beautify + }; + }); +} else if (typeof exports !== "undefined") { + // Add support for CommonJS. Just put this file somewhere on your require.paths + // and you will be able to `var html_beautify = require("beautify").html_beautify`. + exports.css_beautify = css_beautify; +} else if (typeof window !== "undefined") { + // If we're running a web page and don't have either of the above, add our one global + window.css_beautify = css_beautify; +} else if (typeof global !== "undefined") { + // If we don't even have window, try global. + global.css_beautify = css_beautify; +} + +}()); diff --git a/js/lib/beautify-html.js b/js/lib/beautify-html.js index a1c3f8bf9..ea5a3b62f 100644 --- a/js/lib/beautify-html.js +++ b/js/lib/beautify-html.js @@ -1,4 +1,5 @@ -/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ +/*jshint curly:false, eqeqeq:true, laxbreak:true, noempty:false */ +/* AUTO-GENERATED. DO NOT MODIFY. */ /* The MIT License (MIT) @@ -72,870 +73,956 @@ */ (function() { +var legacy_beautify_html = +/******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // identity function for calling harmony imports with the correct context +/******/ __webpack_require__.i = function(value) { return value; }; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 3); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { - // function trim(s) { - // return s.replace(/^\s+|\s+$/g, ''); - // } +/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ +/* - function ltrim(s) { - return s.replace(/^\s+/g, ''); - } + The MIT License (MIT) - function rtrim(s) { - return s.replace(/\s+$/g, ''); - } + Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors. - function mergeOpts(allOptions, targetType) { - var finalOpts = {}; - var name; + 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: - for (name in allOptions) { - if (name !== targetType) { - finalOpts[name] = allOptions[name]; - } - } + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. - //merge in the per type settings for the targetType - if (targetType in allOptions) { - for (name in allOptions[targetType]) { - finalOpts[name] = allOptions[targetType][name]; - } - } - return finalOpts; - } + 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. +*/ - var lineBreak = /\r\n|[\n\r\u2028\u2029]/; - var allLineBreaks = new RegExp(lineBreak.source, 'g'); - - function style_html(html_source, options, js_beautify, css_beautify) { - //Wrapper function to invoke all the necessary constructors and deal with the output. - - var multi_parser, - indent_inner_html, - indent_body_inner_html, - indent_head_inner_html, - indent_size, - indent_character, - wrap_line_length, - brace_style, - unformatted, - content_unformatted, - preserve_newlines, - max_preserve_newlines, - indent_handlebars, - wrap_attributes, - wrap_attributes_indent_size, - is_wrap_attributes_force, - is_wrap_attributes_force_expand_multiline, - is_wrap_attributes_force_aligned, - end_with_newline, - extra_liners, - eol; - - options = options || {}; - - // Allow the setting of language/file-type specific options - // with inheritance of overall settings - options = mergeOpts(options, 'html'); - - // backwards compatibility to 1.3.4 - if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) && - (options.max_char !== undefined && parseInt(options.max_char, 10) !== 0)) { - options.wrap_line_length = options.max_char; - } +var mergeOpts = __webpack_require__(2).mergeOpts; +var acorn = __webpack_require__(1); + + +var lineBreak = acorn.lineBreak; +var allLineBreaks = acorn.allLineBreaks; + +// function trim(s) { +// return s.replace(/^\s+|\s+$/g, ''); +// } + +function ltrim(s) { + return s.replace(/^\s+/g, ''); +} + +function rtrim(s) { + return s.replace(/\s+$/g, ''); +} + +function Beautifier(html_source, options, js_beautify, css_beautify) { + //Wrapper function to invoke all the necessary constructors and deal with the output. + html_source = html_source || ''; + + var multi_parser, + indent_inner_html, + indent_body_inner_html, + indent_head_inner_html, + indent_size, + indent_character, + wrap_line_length, + brace_style, + unformatted, + content_unformatted, + preserve_newlines, + max_preserve_newlines, + indent_handlebars, + wrap_attributes, + wrap_attributes_indent_size, + is_wrap_attributes_force, + is_wrap_attributes_force_expand_multiline, + is_wrap_attributes_force_aligned, + end_with_newline, + extra_liners, + eol; + + options = options || {}; + + // Allow the setting of language/file-type specific options + // with inheritance of overall settings + options = mergeOpts(options, 'html'); + + // backwards compatibility to 1.3.4 + if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) && + (options.max_char !== undefined && parseInt(options.max_char, 10) !== 0)) { + options.wrap_line_length = options.max_char; + } - indent_inner_html = (options.indent_inner_html === undefined) ? false : options.indent_inner_html; - indent_body_inner_html = (options.indent_body_inner_html === undefined) ? true : options.indent_body_inner_html; - indent_head_inner_html = (options.indent_head_inner_html === undefined) ? true : options.indent_head_inner_html; - indent_size = (options.indent_size === undefined) ? 4 : parseInt(options.indent_size, 10); - indent_character = (options.indent_char === undefined) ? ' ' : options.indent_char; - brace_style = (options.brace_style === undefined) ? 'collapse' : options.brace_style; - wrap_line_length = parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10); - unformatted = options.unformatted || [ - // https://www.w3.org/TR/html5/dom.html#phrasing-content - 'a', 'abbr', 'area', 'audio', 'b', 'bdi', 'bdo', 'br', 'button', 'canvas', 'cite', - 'code', 'data', 'datalist', 'del', 'dfn', 'em', 'embed', 'i', 'iframe', 'img', - 'input', 'ins', 'kbd', 'keygen', 'label', 'map', 'mark', 'math', 'meter', 'noscript', - 'object', 'output', 'progress', 'q', 'ruby', 's', 'samp', /* 'script', */ 'select', 'small', - 'span', 'strong', 'sub', 'sup', 'svg', 'template', 'textarea', 'time', 'u', 'var', - 'video', 'wbr', 'text', - // prexisting - not sure of full effect of removing, leaving in - 'acronym', 'address', 'big', 'dt', 'ins', 'strike', 'tt', - ]; - content_unformatted = options.content_unformatted || [ - 'pre', - ]; - preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines; - max_preserve_newlines = preserve_newlines ? - (isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10)) : - 0; - indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars; - wrap_attributes = (options.wrap_attributes === undefined) ? 'auto' : options.wrap_attributes; - wrap_attributes_indent_size = (isNaN(parseInt(options.wrap_attributes_indent_size, 10))) ? indent_size : parseInt(options.wrap_attributes_indent_size, 10); - is_wrap_attributes_force = wrap_attributes.substr(0, 'force'.length) === 'force'; - is_wrap_attributes_force_expand_multiline = (wrap_attributes === 'force-expand-multiline'); - is_wrap_attributes_force_aligned = (wrap_attributes === 'force-aligned'); - end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; - extra_liners = (typeof options.extra_liners === 'object') && options.extra_liners ? - options.extra_liners.concat() : (typeof options.extra_liners === 'string') ? - options.extra_liners.split(',') : 'head,body,/html'.split(','); - eol = options.eol ? options.eol : 'auto'; - - if (options.indent_with_tabs) { - indent_character = '\t'; - indent_size = 1; - } + indent_inner_html = (options.indent_inner_html === undefined) ? false : options.indent_inner_html; + indent_body_inner_html = (options.indent_body_inner_html === undefined) ? true : options.indent_body_inner_html; + indent_head_inner_html = (options.indent_head_inner_html === undefined) ? true : options.indent_head_inner_html; + indent_size = (options.indent_size === undefined) ? 4 : parseInt(options.indent_size, 10); + indent_character = (options.indent_char === undefined) ? ' ' : options.indent_char; + brace_style = (options.brace_style === undefined) ? 'collapse' : options.brace_style; + wrap_line_length = parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10); + unformatted = options.unformatted || [ + // https://www.w3.org/TR/html5/dom.html#phrasing-content + 'a', 'abbr', 'area', 'audio', 'b', 'bdi', 'bdo', 'br', 'button', 'canvas', 'cite', + 'code', 'data', 'datalist', 'del', 'dfn', 'em', 'embed', 'i', 'iframe', 'img', + 'input', 'ins', 'kbd', 'keygen', 'label', 'map', 'mark', 'math', 'meter', 'noscript', + 'object', 'output', 'progress', 'q', 'ruby', 's', 'samp', /* 'script', */ 'select', 'small', + 'span', 'strong', 'sub', 'sup', 'svg', 'template', 'textarea', 'time', 'u', 'var', + 'video', 'wbr', 'text', + // prexisting - not sure of full effect of removing, leaving in + 'acronym', 'address', 'big', 'dt', 'ins', 'strike', 'tt', + ]; + content_unformatted = options.content_unformatted || [ + 'pre', + ]; + preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines; + max_preserve_newlines = preserve_newlines ? + (isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10)) : + 0; + indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars; + wrap_attributes = (options.wrap_attributes === undefined) ? 'auto' : options.wrap_attributes; + wrap_attributes_indent_size = (isNaN(parseInt(options.wrap_attributes_indent_size, 10))) ? indent_size : parseInt(options.wrap_attributes_indent_size, 10); + is_wrap_attributes_force = wrap_attributes.substr(0, 'force'.length) === 'force'; + is_wrap_attributes_force_expand_multiline = (wrap_attributes === 'force-expand-multiline'); + is_wrap_attributes_force_aligned = (wrap_attributes === 'force-aligned'); + end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline; + extra_liners = (typeof options.extra_liners === 'object') && options.extra_liners ? + options.extra_liners.concat() : (typeof options.extra_liners === 'string') ? + options.extra_liners.split(',') : 'head,body,/html'.split(','); + eol = options.eol ? options.eol : 'auto'; + + if (options.indent_with_tabs) { + indent_character = '\t'; + indent_size = 1; + } - if (eol === 'auto') { - eol = '\n'; - if (html_source && lineBreak.test(html_source || '')) { - eol = html_source.match(lineBreak)[0]; - } + if (eol === 'auto') { + eol = '\n'; + if (html_source && lineBreak.test(html_source || '')) { + eol = html_source.match(lineBreak)[0]; } + } - eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n'); + eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n'); - // HACK: newline parsing inconsistent. This brute force normalizes the input. - html_source = html_source.replace(allLineBreaks, '\n'); + // HACK: newline parsing inconsistent. This brute force normalizes the input. + html_source = html_source.replace(allLineBreaks, '\n'); - function Parser() { + function Parser() { - this.pos = 0; //Parser position - this.token = ''; - this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT - this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values - parent: 'parent1', - parentcount: 1, - parent1: '' - }; - this.tag_type = ''; - this.token_text = this.last_token = this.last_text = this.token_type = ''; - this.newlines = 0; - this.indent_content = indent_inner_html; - this.indent_body_inner_html = indent_body_inner_html; - this.indent_head_inner_html = indent_head_inner_html; - - this.Utils = { //Uilities made available to the various functions - whitespace: "\n\r\t ".split(''), - - single_token: options.void_elements || [ - // HTLM void elements - aka self-closing tags - aka singletons - // https://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements - 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', - 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr', - // NOTE: Optional tags - are not understood. - // https://www.w3.org/TR/html5/syntax.html#optional-tags - // The rules for optional tags are too complex for a simple list - // Also, the content of these tags should still be indented in many cases. - // 'li' is a good exmple. - - // Doctype and xml elements - '!doctype', '?xml', - // ?php tag - '?php', - // other tags that were in this list, keeping just in case - 'basefont', 'isindex' - ], - extra_liners: extra_liners, //for tags that need a line of whitespace before them - in_array: function(what, arr) { - for (var i = 0; i < arr.length; i++) { - if (what === arr[i]) { - return true; - } + this.pos = 0; //Parser position + this.token = ''; + this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT + this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values + parent: 'parent1', + parentcount: 1, + parent1: '' + }; + this.tag_type = ''; + this.token_text = this.last_token = this.last_text = this.token_type = ''; + this.newlines = 0; + this.indent_content = indent_inner_html; + this.indent_body_inner_html = indent_body_inner_html; + this.indent_head_inner_html = indent_head_inner_html; + + this.Utils = { //Uilities made available to the various functions + whitespace: "\n\r\t ".split(''), + + single_token: options.void_elements || [ + // HTLM void elements - aka self-closing tags - aka singletons + // https://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements + 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', + 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr', + // NOTE: Optional tags - are not understood. + // https://www.w3.org/TR/html5/syntax.html#optional-tags + // The rules for optional tags are too complex for a simple list + // Also, the content of these tags should still be indented in many cases. + // 'li' is a good exmple. + + // Doctype and xml elements + '!doctype', '?xml', + // ?php tag + '?php', + // other tags that were in this list, keeping just in case + 'basefont', 'isindex' + ], + extra_liners: extra_liners, //for tags that need a line of whitespace before them + in_array: function(what, arr) { + for (var i = 0; i < arr.length; i++) { + if (what === arr[i]) { + return true; } + } + return false; + } + }; + + // Return true if the given text is composed entirely of whitespace. + this.is_whitespace = function(text) { + for (var n = 0; n < text.length; n++) { + if (!this.Utils.in_array(text.charAt(n), this.Utils.whitespace)) { return false; } - }; + } + return true; + }; + + this.traverse_whitespace = function() { + var input_char = ''; - // Return true if the given text is composed entirely of whitespace. - this.is_whitespace = function(text) { - for (var n = 0; n < text.length; n++) { - if (!this.Utils.in_array(text.charAt(n), this.Utils.whitespace)) { - return false; + input_char = this.input.charAt(this.pos); + if (this.Utils.in_array(input_char, this.Utils.whitespace)) { + this.newlines = 0; + while (this.Utils.in_array(input_char, this.Utils.whitespace)) { + if (preserve_newlines && input_char === '\n' && this.newlines <= max_preserve_newlines) { + this.newlines += 1; } + + this.pos++; + input_char = this.input.charAt(this.pos); } return true; - }; + } + return false; + }; - this.traverse_whitespace = function() { - var input_char = ''; + // Append a space to the given content (string array) or, if we are + // at the wrap_line_length, append a newline/indentation. + // return true if a newline was added, false if a space was added + this.space_or_wrap = function(content) { + if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached + this.print_newline(false, content); + this.print_indentation(content); + return true; + } else { + this.line_char_count++; + content.push(' '); + return false; + } + }; - input_char = this.input.charAt(this.pos); - if (this.Utils.in_array(input_char, this.Utils.whitespace)) { - this.newlines = 0; - while (this.Utils.in_array(input_char, this.Utils.whitespace)) { - if (preserve_newlines && input_char === '\n' && this.newlines <= max_preserve_newlines) { - this.newlines += 1; - } + this.get_content = function() { //function to capture regular content between tags + var input_char = '', + content = [], + handlebarsStarted = 0; - this.pos++; - input_char = this.input.charAt(this.pos); - } - return true; + while (this.input.charAt(this.pos) !== '<' || handlebarsStarted === 2) { + if (this.pos >= this.input.length) { + return content.length ? content.join('') : ['', 'TK_EOF']; } - return false; - }; - // Append a space to the given content (string array) or, if we are - // at the wrap_line_length, append a newline/indentation. - // return true if a newline was added, false if a space was added - this.space_or_wrap = function(content) { - if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached - this.print_newline(false, content); - this.print_indentation(content); - return true; - } else { - this.line_char_count++; - content.push(' '); - return false; + if (handlebarsStarted < 2 && this.traverse_whitespace()) { + this.space_or_wrap(content); + continue; } - }; - this.get_content = function() { //function to capture regular content between tags - var input_char = '', - content = [], - handlebarsStarted = 0; - - while (this.input.charAt(this.pos) !== '<' || handlebarsStarted === 2) { - if (this.pos >= this.input.length) { - return content.length ? content.join('') : ['', 'TK_EOF']; - } + input_char = this.input.charAt(this.pos); - if (handlebarsStarted < 2 && this.traverse_whitespace()) { - this.space_or_wrap(content); - continue; + if (indent_handlebars) { + if (input_char === '{') { + handlebarsStarted += 1; + } else if (handlebarsStarted < 2) { + handlebarsStarted = 0; } - input_char = this.input.charAt(this.pos); - - if (indent_handlebars) { - if (input_char === '{') { - handlebarsStarted += 1; - } else if (handlebarsStarted < 2) { - handlebarsStarted = 0; - } - - if (input_char === '}' && handlebarsStarted > 0) { - if (handlebarsStarted-- === 0) { - break; - } + if (input_char === '}' && handlebarsStarted > 0) { + if (handlebarsStarted-- === 0) { + break; } - // Handlebars parsing is complicated. - // {{#foo}} and {{/foo}} are formatted tags. - // {{something}} should get treated as content, except: - // {{else}} specifically behaves like {{#if}} and {{/if}} - var peek3 = this.input.substr(this.pos, 3); - if (peek3 === '{{#' || peek3 === '{{/') { - // These are tags and not content. + } + // Handlebars parsing is complicated. + // {{#foo}} and {{/foo}} are formatted tags. + // {{something}} should get treated as content, except: + // {{else}} specifically behaves like {{#if}} and {{/if}} + var peek3 = this.input.substr(this.pos, 3); + if (peek3 === '{{#' || peek3 === '{{/') { + // These are tags and not content. + break; + } else if (peek3 === '{{!') { + return [this.get_tag(), 'TK_TAG_HANDLEBARS_COMMENT']; + } else if (this.input.substr(this.pos, 2) === '{{') { + if (this.get_tag(true) === '{{else}}') { break; - } else if (peek3 === '{{!') { - return [this.get_tag(), 'TK_TAG_HANDLEBARS_COMMENT']; - } else if (this.input.substr(this.pos, 2) === '{{') { - if (this.get_tag(true) === '{{else}}') { - break; - } } } - - this.pos++; - this.line_char_count++; - content.push(input_char); //letter at-a-time (or string) inserted to an array } - return content.length ? content.join('') : ''; - }; - this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify - if (this.pos === this.input.length) { - return ['', 'TK_EOF']; - } - var content = ''; - var reg_match = new RegExp('', 'igm'); - reg_match.lastIndex = this.pos; - var reg_array = reg_match.exec(this.input); - var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script - if (this.pos < end_script) { //get everything in between the script tags - content = this.input.substring(this.pos, end_script); - this.pos = end_script; - } - return content; - }; + this.pos++; + this.line_char_count++; + content.push(input_char); //letter at-a-time (or string) inserted to an array + } + return content.length ? content.join('') : ''; + }; - this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object - if (this.tags[tag + 'count']) { //check for the existence of this tag type - this.tags[tag + 'count']++; - this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level - } else { //otherwise initialize this tag type - this.tags[tag + 'count'] = 1; - this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level - } - this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent) - this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1') - }; + this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify + if (this.pos === this.input.length) { + return ['', 'TK_EOF']; + } + var content = ''; + var reg_match = new RegExp('', 'igm'); + reg_match.lastIndex = this.pos; + var reg_array = reg_match.exec(this.input); + var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script + if (this.pos < end_script) { //get everything in between the script tags + content = this.input.substring(this.pos, end_script); + this.pos = end_script; + } + return content; + }; - this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer - if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it - var temp_parent = this.tags.parent; //check to see if it's a closable tag. - while (temp_parent) { //till we reach '' (the initial value); - if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it - break; - } - temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree - } - if (temp_parent) { //if we caught something - this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly - this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent - } - delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference... - delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself - if (this.tags[tag + 'count'] === 1) { - delete this.tags[tag + 'count']; - } else { - this.tags[tag + 'count']--; - } - } - }; + this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object + if (this.tags[tag + 'count']) { //check for the existence of this tag type + this.tags[tag + 'count']++; + this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level + } else { //otherwise initialize this tag type + this.tags[tag + 'count'] = 1; + this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level + } + this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent) + this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1') + }; - this.indent_to_tag = function(tag) { - // Match the indentation level to the last use of this tag, but don't remove it. - if (!this.tags[tag + 'count']) { - return; - } - var temp_parent = this.tags.parent; - while (temp_parent) { - if (tag + this.tags[tag + 'count'] === temp_parent) { + this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer + if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it + var temp_parent = this.tags.parent; //check to see if it's a closable tag. + while (temp_parent) { //till we reach '' (the initial value); + if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it break; } - temp_parent = this.tags[temp_parent + 'parent']; + temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree } - if (temp_parent) { - this.indent_level = this.tags[tag + this.tags[tag + 'count']]; + if (temp_parent) { //if we caught something + this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly + this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent } - }; + delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference... + delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself + if (this.tags[tag + 'count'] === 1) { + delete this.tags[tag + 'count']; + } else { + this.tags[tag + 'count']--; + } + } + }; - this.get_tag = function(peek) { //function to get a full tag and parse its type - var input_char = '', - content = [], - comment = '', - space = false, - first_attr = true, - has_wrapped_attrs = false, - tag_start, tag_end, - tag_start_char, - orig_pos = this.pos, - orig_line_char_count = this.line_char_count, - is_tag_closed = false, - tail; - - peek = peek !== undefined ? peek : false; - - do { - if (this.pos >= this.input.length) { - if (peek) { - this.pos = orig_pos; - this.line_char_count = orig_line_char_count; - } - return content.length ? content.join('') : ['', 'TK_EOF']; + this.indent_to_tag = function(tag) { + // Match the indentation level to the last use of this tag, but don't remove it. + if (!this.tags[tag + 'count']) { + return; + } + var temp_parent = this.tags.parent; + while (temp_parent) { + if (tag + this.tags[tag + 'count'] === temp_parent) { + break; + } + temp_parent = this.tags[temp_parent + 'parent']; + } + if (temp_parent) { + this.indent_level = this.tags[tag + this.tags[tag + 'count']]; + } + }; + + this.get_tag = function(peek) { //function to get a full tag and parse its type + var input_char = '', + content = [], + comment = '', + space = false, + first_attr = true, + has_wrapped_attrs = false, + tag_start, tag_end, + tag_start_char, + orig_pos = this.pos, + orig_line_char_count = this.line_char_count, + is_tag_closed = false, + tail; + + peek = peek !== undefined ? peek : false; + + do { + if (this.pos >= this.input.length) { + if (peek) { + this.pos = orig_pos; + this.line_char_count = orig_line_char_count; } + return content.length ? content.join('') : ['', 'TK_EOF']; + } - input_char = this.input.charAt(this.pos); - this.pos++; + input_char = this.input.charAt(this.pos); + this.pos++; - if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space - space = true; - continue; - } + if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space + space = true; + continue; + } - if (input_char === "'" || input_char === '"') { - input_char += this.get_unformatted(input_char); - space = true; - } + if (input_char === "'" || input_char === '"') { + input_char += this.get_unformatted(input_char); + space = true; + } - if (input_char === '=') { //no space before = + if (input_char === '=') { //no space before = + space = false; + } + tail = this.input.substr(this.pos - 1); + if (is_wrap_attributes_force_expand_multiline && has_wrapped_attrs && !is_tag_closed && (input_char === '>' || input_char === '/')) { + if (tail.match(/^\/?\s*>/)) { space = false; + is_tag_closed = true; + this.print_newline(false, content); + this.print_indentation(content); } - tail = this.input.substr(this.pos - 1); - if (is_wrap_attributes_force_expand_multiline && has_wrapped_attrs && !is_tag_closed && (input_char === '>' || input_char === '/')) { - if (tail.match(/^\/?\s*>/)) { - space = false; - is_tag_closed = true; + } + if (content.length && content[content.length - 1] !== '=' && input_char !== '>' && space) { + //no space after = or before > + var wrapped = this.space_or_wrap(content); + var indentAttrs = wrapped && input_char !== '/' && !is_wrap_attributes_force; + space = false; + + if (is_wrap_attributes_force && input_char !== '/') { + var force_first_attr_wrap = false; + if (is_wrap_attributes_force_expand_multiline && first_attr) { + var is_only_attribute = tail.match(/^\S*(="([^"]|\\")*")?\s*\/?\s*>/) !== null; + force_first_attr_wrap = !is_only_attribute; + } + if (!first_attr || force_first_attr_wrap) { this.print_newline(false, content); this.print_indentation(content); + indentAttrs = true; } } - if (content.length && content[content.length - 1] !== '=' && input_char !== '>' && space) { - //no space after = or before > - var wrapped = this.space_or_wrap(content); - var indentAttrs = wrapped && input_char !== '/' && !is_wrap_attributes_force; - space = false; + if (indentAttrs) { + has_wrapped_attrs = true; - if (is_wrap_attributes_force && input_char !== '/') { - var force_first_attr_wrap = false; - if (is_wrap_attributes_force_expand_multiline && first_attr) { - var is_only_attribute = tail.match(/^\S*(="([^"]|\\")*")?\s*\/?\s*>/) !== null; - force_first_attr_wrap = !is_only_attribute; - } - if (!first_attr || force_first_attr_wrap) { - this.print_newline(false, content); - this.print_indentation(content); - indentAttrs = true; - } + //indent attributes an auto, forced, or forced-align line-wrap + var alignment_size = wrap_attributes_indent_size; + if (is_wrap_attributes_force_aligned) { + alignment_size = content.indexOf(' ') + 1; } - if (indentAttrs) { - has_wrapped_attrs = true; - - //indent attributes an auto, forced, or forced-align line-wrap - var alignment_size = wrap_attributes_indent_size; - if (is_wrap_attributes_force_aligned) { - alignment_size = content.indexOf(' ') + 1; - } - for (var count = 0; count < alignment_size; count++) { - // only ever further indent with spaces since we're trying to align characters - content.push(' '); - } + for (var count = 0; count < alignment_size; count++) { + // only ever further indent with spaces since we're trying to align characters + content.push(' '); } - if (first_attr) { - for (var i = 0; i < content.length; i++) { - if (content[i] === ' ') { - first_attr = false; - break; - } + } + if (first_attr) { + for (var i = 0; i < content.length; i++) { + if (content[i] === ' ') { + first_attr = false; + break; } } } + } - if (indent_handlebars && tag_start_char === '<') { - // When inside an angle-bracket tag, put spaces around - // handlebars not inside of strings. - if ((input_char + this.input.charAt(this.pos)) === '{{') { - input_char += this.get_unformatted('}}'); - if (content.length && content[content.length - 1] !== ' ' && content[content.length - 1] !== '<') { - input_char = ' ' + input_char; - } - space = true; + if (indent_handlebars && tag_start_char === '<') { + // When inside an angle-bracket tag, put spaces around + // handlebars not inside of strings. + if ((input_char + this.input.charAt(this.pos)) === '{{') { + input_char += this.get_unformatted('}}'); + if (content.length && content[content.length - 1] !== ' ' && content[content.length - 1] !== '<') { + input_char = ' ' + input_char; } + space = true; } + } - if (input_char === '<' && !tag_start_char) { - tag_start = this.pos - 1; - tag_start_char = '<'; - } + if (input_char === '<' && !tag_start_char) { + tag_start = this.pos - 1; + tag_start_char = '<'; + } - if (indent_handlebars && !tag_start_char) { - if (content.length >= 2 && content[content.length - 1] === '{' && content[content.length - 2] === '{') { - if (input_char === '#' || input_char === '/' || input_char === '!') { - tag_start = this.pos - 3; - } else { - tag_start = this.pos - 2; - } - tag_start_char = '{'; + if (indent_handlebars && !tag_start_char) { + if (content.length >= 2 && content[content.length - 1] === '{' && content[content.length - 2] === '{') { + if (input_char === '#' || input_char === '/' || input_char === '!') { + tag_start = this.pos - 3; + } else { + tag_start = this.pos - 2; } + tag_start_char = '{'; } + } - this.line_char_count++; - content.push(input_char); //inserts character at-a-time (or string) + this.line_char_count++; + content.push(input_char); //inserts character at-a-time (or string) - if (content[1] && (content[1] === '!' || content[1] === '?' || content[1] === '%')) { //if we're in a comment, do something special - // We treat all comments as literals, even more than preformatted tags - // we just look for the appropriate close tag - content = [this.get_comment(tag_start)]; - break; - } + if (content[1] && (content[1] === '!' || content[1] === '?' || content[1] === '%')) { //if we're in a comment, do something special + // We treat all comments as literals, even more than preformatted tags + // we just look for the appropriate close tag + content = [this.get_comment(tag_start)]; + break; + } - if (indent_handlebars && content[1] && content[1] === '{' && content[2] && content[2] === '!') { //if we're in a comment, do something special - // We treat all comments as literals, even more than preformatted tags - // we just look for the appropriate close tag - content = [this.get_comment(tag_start)]; - break; - } + if (indent_handlebars && content[1] && content[1] === '{' && content[2] && content[2] === '!') { //if we're in a comment, do something special + // We treat all comments as literals, even more than preformatted tags + // we just look for the appropriate close tag + content = [this.get_comment(tag_start)]; + break; + } - if (indent_handlebars && tag_start_char === '{' && content.length > 2 && content[content.length - 2] === '}' && content[content.length - 1] === '}') { - break; - } - } while (input_char !== '>'); - - var tag_complete = content.join(''); - var tag_index; - var tag_offset; - - // must check for space first otherwise the tag could have the first attribute included, and - // then not un-indent correctly - if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends - tag_index = tag_complete.indexOf(' '); - } else if (tag_complete.indexOf('\n') !== -1) { //if there's a line break, thats where the tag name ends - tag_index = tag_complete.indexOf('\n'); - } else if (tag_complete.charAt(0) === '{') { - tag_index = tag_complete.indexOf('}'); - } else { //otherwise go with the tag ending - tag_index = tag_complete.indexOf('>'); + if (indent_handlebars && tag_start_char === '{' && content.length > 2 && content[content.length - 2] === '}' && content[content.length - 1] === '}') { + break; } - if (tag_complete.charAt(0) === '<' || !indent_handlebars) { - tag_offset = 1; - } else { - tag_offset = tag_complete.charAt(2) === '#' ? 3 : 2; + } while (input_char !== '>'); + + var tag_complete = content.join(''); + var tag_index; + var tag_offset; + + // must check for space first otherwise the tag could have the first attribute included, and + // then not un-indent correctly + if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends + tag_index = tag_complete.indexOf(' '); + } else if (tag_complete.indexOf('\n') !== -1) { //if there's a line break, thats where the tag name ends + tag_index = tag_complete.indexOf('\n'); + } else if (tag_complete.charAt(0) === '{') { + tag_index = tag_complete.indexOf('}'); + } else { //otherwise go with the tag ending + tag_index = tag_complete.indexOf('>'); + } + if (tag_complete.charAt(0) === '<' || !indent_handlebars) { + tag_offset = 1; + } else { + tag_offset = tag_complete.charAt(2) === '#' ? 3 : 2; + } + var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase(); + if (tag_complete.charAt(tag_complete.length - 2) === '/' || + this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /) + if (!peek) { + this.tag_type = 'SINGLE'; } - var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase(); - if (tag_complete.charAt(tag_complete.length - 2) === '/' || - this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /) - if (!peek) { - this.tag_type = 'SINGLE'; - } - } else if (indent_handlebars && tag_complete.charAt(0) === '{' && tag_check === 'else') { - if (!peek) { - this.indent_to_tag('if'); - this.tag_type = 'HANDLEBARS_ELSE'; - this.indent_content = true; - this.traverse_whitespace(); - } - } else if (this.is_unformatted(tag_check, unformatted) || - this.is_unformatted(tag_check, content_unformatted)) { - // do not reformat the "unformatted" or "content_unformatted" tags - comment = this.get_unformatted('', tag_complete); //...delegate to get_unformatted function - content.push(comment); - tag_end = this.pos - 1; + } else if (indent_handlebars && tag_complete.charAt(0) === '{' && tag_check === 'else') { + if (!peek) { + this.indent_to_tag('if'); + this.tag_type = 'HANDLEBARS_ELSE'; + this.indent_content = true; + this.traverse_whitespace(); + } + } else if (this.is_unformatted(tag_check, unformatted) || + this.is_unformatted(tag_check, content_unformatted)) { + // do not reformat the "unformatted" or "content_unformatted" tags + comment = this.get_unformatted('', tag_complete); //...delegate to get_unformatted function + content.push(comment); + tag_end = this.pos - 1; + this.tag_type = 'SINGLE'; + } else if (tag_check === 'script' && + (tag_complete.search('type') === -1 || + (tag_complete.search('type') > -1 && + tag_complete.search(/\b(text|application|dojo)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json|method|aspect)/) > -1))) { + if (!peek) { + this.record_tag(tag_check); + this.tag_type = 'SCRIPT'; + } + } else if (tag_check === 'style' && + (tag_complete.search('type') === -1 || + (tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) { + if (!peek) { + this.record_tag(tag_check); + this.tag_type = 'STYLE'; + } + } else if (tag_check.charAt(0) === '!') { //peek for -1 && - tag_complete.search(/\b(text|application|dojo)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json|method|aspect)/) > -1))) { - if (!peek) { - this.record_tag(tag_check); - this.tag_type = 'SCRIPT'; - } - } else if (tag_check === 'style' && - (tag_complete.search('type') === -1 || - (tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) { - if (!peek) { - this.record_tag(tag_check); - this.tag_type = 'STYLE'; - } - } else if (tag_check.charAt(0) === '!') { //peek for ', - matched = false; + this.get_comment = function(start_pos) { //function to return comment content in its entirety + // this is will have very poor perf, but will work for now. + var comment = '', + delimiter = '>', + matched = false; - this.pos = start_pos; - var input_char = this.input.charAt(this.pos); - this.pos++; + this.pos = start_pos; + var input_char = this.input.charAt(this.pos); + this.pos++; - while (this.pos <= this.input.length) { - comment += input_char; + while (this.pos <= this.input.length) { + comment += input_char; - // only need to check for the delimiter if the last chars match - if (comment.charAt(comment.length - 1) === delimiter.charAt(delimiter.length - 1) && - comment.indexOf(delimiter) !== -1) { - break; - } + // only need to check for the delimiter if the last chars match + if (comment.charAt(comment.length - 1) === delimiter.charAt(delimiter.length - 1) && + comment.indexOf(delimiter) !== -1) { + break; + } - // only need to search for custom delimiter for the first few characters - if (!matched && comment.length < 10) { - if (comment.indexOf(''; - matched = true; - } else if (comment.indexOf(''; - matched = true; - } else if (comment.indexOf(''; - matched = true; - } else if (comment.indexOf(''; - matched = true; - } else if (comment.indexOf('{{!--') === 0) { // {{!-- handlebars comment - delimiter = '--}}'; - matched = true; - } else if (comment.indexOf('{{!') === 0) { // {{! handlebars comment - if (comment.length === 5 && comment.indexOf('{{!--') === -1) { - delimiter = '}}'; - matched = true; - } - } else if (comment.indexOf(''; - matched = true; - } else if (comment.indexOf('<%') === 0) { // {{! handlebars comment - delimiter = '%>'; + // only need to search for custom delimiter for the first few characters + if (!matched && comment.length < 10) { + if (comment.indexOf(''; + matched = true; + } else if (comment.indexOf(''; + matched = true; + } else if (comment.indexOf(''; + matched = true; + } else if (comment.indexOf(''; + matched = true; + } else if (comment.indexOf('{{!--') === 0) { // {{!-- handlebars comment + delimiter = '--}}'; + matched = true; + } else if (comment.indexOf('{{!') === 0) { // {{! handlebars comment + if (comment.length === 5 && comment.indexOf('{{!--') === -1) { + delimiter = '}}'; matched = true; } + } else if (comment.indexOf(''; + matched = true; + } else if (comment.indexOf('<%') === 0) { // {{! handlebars comment + delimiter = '%>'; + matched = true; } - - input_char = this.input.charAt(this.pos); - this.pos++; } - return comment; - }; + input_char = this.input.charAt(this.pos); + this.pos++; + } - function tokenMatcher(delimiter) { - var token = ''; + return comment; + }; - var add = function(str) { - var newToken = token + str.toLowerCase(); - token = newToken.length <= delimiter.length ? newToken : newToken.substr(newToken.length - delimiter.length, delimiter.length); - }; + function tokenMatcher(delimiter) { + var token = ''; - var doesNotMatch = function() { - return token.indexOf(delimiter) === -1; - }; + var add = function(str) { + var newToken = token + str.toLowerCase(); + token = newToken.length <= delimiter.length ? newToken : newToken.substr(newToken.length - delimiter.length, delimiter.length); + }; - return { - add: add, - doesNotMatch: doesNotMatch - }; - } + var doesNotMatch = function() { + return token.indexOf(delimiter) === -1; + }; - this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety - if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) !== -1) { - return ''; - } - var input_char = ''; - var content = ''; - var space = true; + return { + add: add, + doesNotMatch: doesNotMatch + }; + } - var delimiterMatcher = tokenMatcher(delimiter); + this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety + if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) !== -1) { + return ''; + } + var input_char = ''; + var content = ''; + var space = true; - do { + var delimiterMatcher = tokenMatcher(delimiter); - if (this.pos >= this.input.length) { - return content; - } + do { - input_char = this.input.charAt(this.pos); - this.pos++; - - if (this.Utils.in_array(input_char, this.Utils.whitespace)) { - if (!space) { - this.line_char_count--; - continue; - } - if (input_char === '\n' || input_char === '\r') { - content += '\n'; - /* Don't change tab indention for unformatted blocks. If using code for html editing, this will greatly affect
 tags if they are specified in the 'unformatted array'
-                for (var i=0; i= this.input.length) {
+                    return content;
                 }
-                space = false; //...and make sure other indentation is erased
-                */
-                            this.line_char_count = 0;
-                            continue;
-                        }
-                    }
-                    content += input_char;
-                    delimiterMatcher.add(input_char);
-                    this.line_char_count++;
-                    space = true;
 
-                    if (indent_handlebars && input_char === '{' && content.length && content.charAt(content.length - 2) === '{') {
-                        // Handlebars expressions in strings should also be unformatted.
-                        content += this.get_unformatted('}}');
-                        // Don't consider when stopping for delimiters.
+                input_char = this.input.charAt(this.pos);
+                this.pos++;
+
+                if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
+                    if (!space) {
+                        this.line_char_count--;
+                        continue;
+                    }
+                    if (input_char === '\n' || input_char === '\r') {
+                        content += '\n';
+                        /*  Don't change tab indention for unformatted blocks.  If using code for html editing, this will greatly affect 
 tags if they are specified in the 'unformatted array'
+            for (var i=0; i]*>\s*$/);
+            // test next_tag to see if it is just html tag (no external content)
+            var tag = (next_tag || "").match(/^\s*<\s*\/?([a-z]*)\s*[^>]*>\s*$/);
 
-                // if next_tag comes back but is not an isolated tag, then
-                // let's treat the 'a' tag as having content
-                // and respect the unformatted option
-                if (!tag || this.Utils.in_array(tag[1], unformatted)) {
-                    return true;
-                } else {
-                    return false;
-                }
-            };
+            // if next_tag comes back but is not an isolated tag, then
+            // let's treat the 'a' tag as having content
+            // and respect the unformatted option
+            if (!tag || this.Utils.in_array(tag[1], unformatted)) {
+                return true;
+            } else {
+                return false;
+            }
+        };
 
-            this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions
+        this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions
 
-                this.input = js_source || ''; //gets the input for the Parser
+            this.input = js_source || ''; //gets the input for the Parser
 
-                // HACK: newline parsing inconsistent. This brute force normalizes the input.
-                this.input = this.input.replace(/\r\n|[\r\u2028\u2029]/g, '\n');
+            // HACK: newline parsing inconsistent. This brute force normalizes the input.
+            this.input = this.input.replace(/\r\n|[\r\u2028\u2029]/g, '\n');
 
-                this.output = [];
-                this.indent_character = indent_character;
-                this.indent_string = '';
-                this.indent_size = indent_size;
-                this.brace_style = brace_style;
-                this.indent_level = 0;
-                this.wrap_line_length = wrap_line_length;
-                this.line_char_count = 0; //count to see if wrap_line_length was exceeded
+            this.output = [];
+            this.indent_character = indent_character;
+            this.indent_string = '';
+            this.indent_size = indent_size;
+            this.brace_style = brace_style;
+            this.indent_level = 0;
+            this.wrap_line_length = wrap_line_length;
+            this.line_char_count = 0; //count to see if wrap_line_length was exceeded
 
-                for (var i = 0; i < this.indent_size; i++) {
-                    this.indent_string += this.indent_character;
-                }
+            for (var i = 0; i < this.indent_size; i++) {
+                this.indent_string += this.indent_character;
+            }
 
-                this.print_newline = function(force, arr) {
-                    this.line_char_count = 0;
-                    if (!arr || !arr.length) {
-                        return;
-                    }
-                    if (force || (arr[arr.length - 1] !== '\n')) { //we might want the extra line
-                        if ((arr[arr.length - 1] !== '\n')) {
-                            arr[arr.length - 1] = rtrim(arr[arr.length - 1]);
-                        }
-                        arr.push('\n');
+            this.print_newline = function(force, arr) {
+                this.line_char_count = 0;
+                if (!arr || !arr.length) {
+                    return;
+                }
+                if (force || (arr[arr.length - 1] !== '\n')) { //we might want the extra line
+                    if ((arr[arr.length - 1] !== '\n')) {
+                        arr[arr.length - 1] = rtrim(arr[arr.length - 1]);
                     }
-                };
+                    arr.push('\n');
+                }
+            };
 
-                this.print_indentation = function(arr) {
-                    for (var i = 0; i < this.indent_level; i++) {
-                        arr.push(this.indent_string);
-                        this.line_char_count += this.indent_string.length;
-                    }
-                };
+            this.print_indentation = function(arr) {
+                for (var i = 0; i < this.indent_level; i++) {
+                    arr.push(this.indent_string);
+                    this.line_char_count += this.indent_string.length;
+                }
+            };
 
-                this.print_token = function(text) {
-                    // Avoid printing initial whitespace.
-                    if (this.is_whitespace(text) && !this.output.length) {
-                        return;
-                    }
-                    if (text || text !== '') {
-                        if (this.output.length && this.output[this.output.length - 1] === '\n') {
-                            this.print_indentation(this.output);
-                            text = ltrim(text);
-                        }
-                    }
-                    this.print_token_raw(text);
-                };
-
-                this.print_token_raw = function(text) {
-                    // If we are going to print newlines, truncate trailing
-                    // whitespace, as the newlines will represent the space.
-                    if (this.newlines > 0) {
-                        text = rtrim(text);
+            this.print_token = function(text) {
+                // Avoid printing initial whitespace.
+                if (this.is_whitespace(text) && !this.output.length) {
+                    return;
+                }
+                if (text || text !== '') {
+                    if (this.output.length && this.output[this.output.length - 1] === '\n') {
+                        this.print_indentation(this.output);
+                        text = ltrim(text);
                     }
+                }
+                this.print_token_raw(text);
+            };
 
-                    if (text && text !== '') {
-                        if (text.length > 1 && text.charAt(text.length - 1) === '\n') {
-                            // unformatted tags can grab newlines as their last character
-                            this.output.push(text.slice(0, -1));
-                            this.print_newline(false, this.output);
-                        } else {
-                            this.output.push(text);
-                        }
-                    }
+            this.print_token_raw = function(text) {
+                // If we are going to print newlines, truncate trailing
+                // whitespace, as the newlines will represent the space.
+                if (this.newlines > 0) {
+                    text = rtrim(text);
+                }
 
-                    for (var n = 0; n < this.newlines; n++) {
-                        this.print_newline(n > 0, this.output);
+                if (text && text !== '') {
+                    if (text.length > 1 && text.charAt(text.length - 1) === '\n') {
+                        // unformatted tags can grab newlines as their last character
+                        this.output.push(text.slice(0, -1));
+                        this.print_newline(false, this.output);
+                    } else {
+                        this.output.push(text);
                     }
-                    this.newlines = 0;
-                };
+                }
 
-                this.indent = function() {
-                    this.indent_level++;
-                };
+                for (var n = 0; n < this.newlines; n++) {
+                    this.print_newline(n > 0, this.output);
+                }
+                this.newlines = 0;
+            };
 
-                this.unindent = function() {
-                    if (this.indent_level > 0) {
-                        this.indent_level--;
-                    }
-                };
+            this.indent = function() {
+                this.indent_level++;
             };
-            return this;
-        }
 
-        /*_____________________--------------------_____________________*/
+            this.unindent = function() {
+                if (this.indent_level > 0) {
+                    this.indent_level--;
+                }
+            };
+        };
+        return this;
+    }
+
+    /*_____________________--------------------_____________________*/
 
+    this.beautify = function() {
         multi_parser = new Parser(); //wrapping functions Parser
         multi_parser.printer(html_source, indent_character, indent_size, wrap_line_length, brace_style); //initialize starting values
-
         while (true) {
             var t = multi_parser.get_token();
             multi_parser.token_text = t[0];
@@ -1090,39 +1177,211 @@
         }
 
         return sweet_code;
-    }
+    };
+}
 
-    if (typeof define === "function" && define.amd) {
-        // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
-        define(["require", "./beautify", "./beautify-css"], function(requireamd) {
-            var js_beautify = requireamd("./beautify");
-            var css_beautify = requireamd("./beautify-css");
+module.exports.Beautifier = Beautifier;
 
-            return {
-                html_beautify: function(html_source, options) {
-                    return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
-                }
-            };
-        });
-    } else if (typeof exports !== "undefined") {
-        // Add support for CommonJS. Just put this file somewhere on your require.paths
-        // and you will be able to `var html_beautify = require("beautify").html_beautify`.
-        var js_beautify = require('./beautify.js');
-        var css_beautify = require('./beautify-css.js');
-
-        exports.html_beautify = function(html_source, options) {
-            return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
-        };
-    } else if (typeof window !== "undefined") {
-        // If we're running a web page and don't have either of the above, add our one global
-        window.html_beautify = function(html_source, options) {
-            return style_html(html_source, options, window.js_beautify, window.css_beautify);
-        };
-    } else if (typeof global !== "undefined") {
-        // If we don't even have window, try global.
-        global.html_beautify = function(html_source, options) {
-            return style_html(html_source, options, global.js_beautify, global.css_beautify);
-        };
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports) {
+
+/* jshint curly: false */
+// This section of code is taken from acorn.
+//
+// Acorn was written by Marijn Haverbeke and released under an MIT
+// license. The Unicode regexps (for identifiers and whitespace) were
+// taken from [Esprima](http://esprima.org) by Ariya Hidayat.
+//
+// Git repositories for Acorn are available at
+//
+//     http://marijnhaverbeke.nl/git/acorn
+//     https://github.com/marijnh/acorn.git
+
+// ## Character categories
+
+// Big ugly regular expressions that match characters in the
+// whitespace, identifier, and identifier-start categories. These
+// are only applied when a character is found to actually have a
+// code point above 128.
+
+var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line
+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";
+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";
+var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
+var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
+
+// Whether a single character denotes a newline.
+
+exports.newline = /[\n\r\u2028\u2029]/;
+
+// Matches a whole line break (where CRLF is considered a single
+// line break). Used to count lines.
+
+// in javascript, these two differ
+// in python they are the same, different methods are called on them
+exports.lineBreak = new RegExp('\r\n|' + exports.newline.source);
+exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g');
+
+
+// Test whether a given character code starts an identifier.
+
+exports.isIdentifierStart = function(code) {
+    // permit $ (36) and @ (64). @ is used in ES7 decorators.
+    if (code < 65) return code === 36 || code === 64;
+    // 65 through 91 are uppercase letters.
+    if (code < 91) return true;
+    // permit _ (95).
+    if (code < 97) return code === 95;
+    // 97 through 123 are lowercase letters.
+    if (code < 123) return true;
+    return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
+};
+
+// Test whether a given character is part of an identifier.
+
+exports.isIdentifierChar = function(code) {
+    if (code < 48) return code === 36;
+    if (code < 58) return true;
+    if (code < 65) return false;
+    if (code < 91) return true;
+    if (code < 97) return code === 95;
+    if (code < 123) return true;
+    return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
+};
+
+
+/***/ }),
+/* 2 */
+/***/ (function(module, exports) {
+
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+    The MIT License (MIT)
+
+    Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+*/
+
+function mergeOpts(allOptions, targetType) {
+    var finalOpts = {};
+    var name;
+
+    for (name in allOptions) {
+        if (name !== targetType) {
+            finalOpts[name] = allOptions[name];
+        }
     }
 
-}());
\ No newline at end of file
+    //merge in the per type settings for the targetType
+    if (targetType in allOptions) {
+        for (name in allOptions[targetType]) {
+            finalOpts[name] = allOptions[targetType][name];
+        }
+    }
+    return finalOpts;
+}
+
+module.exports.mergeOpts = mergeOpts;
+
+
+/***/ }),
+/* 3 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+    The MIT License (MIT)
+
+    Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+*/
+
+var Beautifier = __webpack_require__(0).Beautifier;
+
+function style_html(html_source, options, js_beautify, css_beautify) {
+    var beautifier = new Beautifier(html_source, options, js_beautify, css_beautify);
+    return beautifier.beautify();
+}
+
+module.exports = style_html;
+
+/***/ })
+/******/ ]);
+var style_html = legacy_beautify_html;
+/* Footer */
+if (typeof define === "function" && define.amd) {
+    // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
+    define(["require", "./beautify", "./beautify-css"], function(requireamd) {
+        var js_beautify = requireamd("./beautify");
+        var css_beautify = requireamd("./beautify-css");
+
+        return {
+            html_beautify: function(html_source, options) {
+                return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
+            }
+        };
+    });
+} else if (typeof exports !== "undefined") {
+    // Add support for CommonJS. Just put this file somewhere on your require.paths
+    // and you will be able to `var html_beautify = require("beautify").html_beautify`.
+    var js_beautify = require('./beautify.js');
+    var css_beautify = require('./beautify-css.js');
+
+    exports.html_beautify = function(html_source, options) {
+        return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
+    };
+} else if (typeof window !== "undefined") {
+    // If we're running a web page and don't have either of the above, add our one global
+    window.html_beautify = function(html_source, options) {
+        return style_html(html_source, options, window.js_beautify, window.css_beautify);
+    };
+} else if (typeof global !== "undefined") {
+    // If we don't even have window, try global.
+    global.html_beautify = function(html_source, options) {
+        return style_html(html_source, options, global.js_beautify, global.css_beautify);
+    };
+}
+
+}());
diff --git a/js/lib/beautify.js b/js/lib/beautify.js
index 7710fa38a..4dc7c7f81 100644
--- a/js/lib/beautify.js
+++ b/js/lib/beautify.js
@@ -1,4 +1,6 @@
-/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*jshint curly:false, eqeqeq:true, laxbreak:true, noempty:false */
+/* AUTO-GENERATED. DO NOT MODIFY. */
+/* see js/src/javascript/index.js */
 /*
 
   The MIT License (MIT)
@@ -87,6 +89,7 @@
 
 // Object.values polyfill found here:
 // http://tokenposts.blogspot.com.au/2012/04/javascript-objectkeys-browser.html
+// This is required for early versions of IE.
 if (!Object.values) {
     Object.values = function(o) {
         if (o !== Object(o)) {
@@ -104,2378 +107,2721 @@ if (!Object.values) {
 }
 
 (function() {
+var legacy_beautify_js =
+/******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+/******/
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId]) {
+/******/ 			return installedModules[moduleId].exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = true;
+/******/
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/
+/******/
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+/******/
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+/******/
+/******/ 	// identity function for calling harmony imports with the correct context
+/******/ 	__webpack_require__.i = function(value) { return value; };
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, {
+/******/ 				configurable: false,
+/******/ 				enumerable: true,
+/******/ 				get: getter
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 6);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports) {
+
+/* jshint curly: false */
+// This section of code is taken from acorn.
+//
+// Acorn was written by Marijn Haverbeke and released under an MIT
+// license. The Unicode regexps (for identifiers and whitespace) were
+// taken from [Esprima](http://esprima.org) by Ariya Hidayat.
+//
+// Git repositories for Acorn are available at
+//
+//     http://marijnhaverbeke.nl/git/acorn
+//     https://github.com/marijnh/acorn.git
+
+// ## Character categories
+
+// Big ugly regular expressions that match characters in the
+// whitespace, identifier, and identifier-start categories. These
+// are only applied when a character is found to actually have a
+// code point above 128.
+
+var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line
+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";
+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";
+var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
+var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
+
+// Whether a single character denotes a newline.
+
+exports.newline = /[\n\r\u2028\u2029]/;
+
+// Matches a whole line break (where CRLF is considered a single
+// line break). Used to count lines.
+
+// in javascript, these two differ
+// in python they are the same, different methods are called on them
+exports.lineBreak = new RegExp('\r\n|' + exports.newline.source);
+exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g');
+
+
+// Test whether a given character code starts an identifier.
+
+exports.isIdentifierStart = function(code) {
+    // permit $ (36) and @ (64). @ is used in ES7 decorators.
+    if (code < 65) return code === 36 || code === 64;
+    // 65 through 91 are uppercase letters.
+    if (code < 91) return true;
+    // permit _ (95).
+    if (code < 97) return code === 95;
+    // 97 through 123 are lowercase letters.
+    if (code < 123) return true;
+    return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
+};
+
+// Test whether a given character is part of an identifier.
+
+exports.isIdentifierChar = function(code) {
+    if (code < 48) return code === 36;
+    if (code < 58) return true;
+    if (code < 65) return false;
+    if (code < 91) return true;
+    if (code < 97) return code === 95;
+    if (code < 123) return true;
+    return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
+};
+
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports, __webpack_require__) {
 
-    function mergeOpts(allOptions, targetType) {
-        var finalOpts = {};
-        var name;
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
 
-        for (name in allOptions) {
-            if (name !== targetType) {
-                finalOpts[name] = allOptions[name];
-            }
-        }
+    The MIT License (MIT)
+
+    Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+*/
 
-        //merge in the per type settings for the targetType
-        if (targetType in allOptions) {
-            for (name in allOptions[targetType]) {
-                finalOpts[name] = allOptions[targetType][name];
-            }
-        }
-        return finalOpts;
+var mergeOpts = __webpack_require__(3).mergeOpts;
+var acorn = __webpack_require__(0);
+var Output = __webpack_require__(4).Output;
+var Tokenizer = __webpack_require__(7).Tokenizer;
+
+function remove_redundant_indentation(output, frame) {
+    // This implementation is effective but has some issues:
+    //     - can cause line wrap to happen too soon due to indent removal
+    //           after wrap points are calculated
+    // These issues are minor compared to ugly indentation.
+
+    if (frame.multiline_frame ||
+        frame.mode === MODE.ForInitializer ||
+        frame.mode === MODE.Conditional) {
+        return;
     }
 
-    function js_beautify(js_source_text, options) {
-
-        var acorn = {};
-        (function(exports) {
-            /* jshint curly: false */
-            // This section of code is taken from acorn.
-            //
-            // Acorn was written by Marijn Haverbeke and released under an MIT
-            // license. The Unicode regexps (for identifiers and whitespace) were
-            // taken from [Esprima](http://esprima.org) by Ariya Hidayat.
-            //
-            // Git repositories for Acorn are available at
-            //
-            //     http://marijnhaverbeke.nl/git/acorn
-            //     https://github.com/marijnh/acorn.git
-
-            // ## Character categories
-
-            // Big ugly regular expressions that match characters in the
-            // whitespace, identifier, and identifier-start categories. These
-            // are only applied when a character is found to actually have a
-            // code point above 128.
-
-            var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line
-            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";
-            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";
-            var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
-            var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
-
-            // Whether a single character denotes a newline.
-
-            exports.newline = /[\n\r\u2028\u2029]/;
-
-            // Matches a whole line break (where CRLF is considered a single
-            // line break). Used to count lines.
-
-            // in javascript, these two differ
-            // in python they are the same, different methods are called on them
-            exports.lineBreak = new RegExp('\r\n|' + exports.newline.source);
-            exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g');
-
-
-            // Test whether a given character code starts an identifier.
-
-            exports.isIdentifierStart = function(code) {
-                // permit $ (36) and @ (64). @ is used in ES7 decorators.
-                if (code < 65) return code === 36 || code === 64;
-                // 65 through 91 are uppercase letters.
-                if (code < 91) return true;
-                // permit _ (95).
-                if (code < 97) return code === 95;
-                // 97 through 123 are lowercase letters.
-                if (code < 123) return true;
-                return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
-            };
-
-            // Test whether a given character is part of an identifier.
-
-            exports.isIdentifierChar = function(code) {
-                if (code < 48) return code === 36;
-                if (code < 58) return true;
-                if (code < 65) return false;
-                if (code < 91) return true;
-                if (code < 97) return code === 95;
-                if (code < 123) return true;
-                return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
-            };
-        })(acorn);
-        /* jshint curly: true */
-
-        function in_array(what, arr) {
-            for (var i = 0; i < arr.length; i += 1) {
-                if (arr[i] === what) {
-                    return true;
-                }
-            }
-            return false;
-        }
+    // remove one indent from each line inside this section
+    var start_index = frame.start_line_index;
 
-        function trim(s) {
-            return s.replace(/^\s+|\s+$/g, '');
-        }
+    output.remove_indent(start_index);
+}
 
-        function ltrim(s) {
-            return s.replace(/^\s+/g, '');
+function in_array(what, arr) {
+    for (var i = 0; i < arr.length; i += 1) {
+        if (arr[i] === what) {
+            return true;
         }
+    }
+    return false;
+}
 
-        // function rtrim(s) {
-        //     return s.replace(/\s+$/g, '');
-        // }
-
-        function sanitizeOperatorPosition(opPosition) {
-            opPosition = opPosition || OPERATOR_POSITION.before_newline;
-
-            var validPositionValues = Object.values(OPERATOR_POSITION);
-
-            if (!in_array(opPosition, validPositionValues)) {
-                throw new Error("Invalid Option Value: The option 'operator_position' must be one of the following values\n" +
-                    validPositionValues +
-                    "\nYou passed in: '" + opPosition + "'");
-            }
+function trim(s) {
+    return s.replace(/^\s+|\s+$/g, '');
+}
 
-            return opPosition;
-        }
+function ltrim(s) {
+    return s.replace(/^\s+/g, '');
+}
 
-        var OPERATOR_POSITION = {
-            before_newline: 'before-newline',
-            after_newline: 'after-newline',
-            preserve_newline: 'preserve-newline',
-        };
+// function rtrim(s) {
+//     return s.replace(/\s+$/g, '');
+// }
 
-        var OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION.before_newline, OPERATOR_POSITION.preserve_newline];
+function sanitizeOperatorPosition(opPosition) {
+    opPosition = opPosition || OPERATOR_POSITION.before_newline;
 
-        var MODE = {
-            BlockStatement: 'BlockStatement', // 'BLOCK'
-            Statement: 'Statement', // 'STATEMENT'
-            ObjectLiteral: 'ObjectLiteral', // 'OBJECT',
-            ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]',
-            ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)',
-            Conditional: 'Conditional', //'(COND-EXPRESSION)',
-            Expression: 'Expression' //'(EXPRESSION)'
-        };
+    var validPositionValues = Object.values(OPERATOR_POSITION);
 
-        function Beautifier(js_source_text, options) {
-            "use strict";
-            var output;
-            var tokens = [],
-                token_pos;
-            var Tokenizer;
-            var current_token;
-            var last_type, last_last_text, indent_string;
-            var flags, previous_flags, flag_store;
-            var prefix;
-
-            var handlers, opt;
-            var baseIndentString = '';
-
-            handlers = {
-                'TK_START_EXPR': handle_start_expr,
-                'TK_END_EXPR': handle_end_expr,
-                'TK_START_BLOCK': handle_start_block,
-                'TK_END_BLOCK': handle_end_block,
-                'TK_WORD': handle_word,
-                'TK_RESERVED': handle_word,
-                'TK_SEMICOLON': handle_semicolon,
-                'TK_STRING': handle_string,
-                'TK_EQUALS': handle_equals,
-                'TK_OPERATOR': handle_operator,
-                'TK_COMMA': handle_comma,
-                'TK_BLOCK_COMMENT': handle_block_comment,
-                'TK_COMMENT': handle_comment,
-                'TK_DOT': handle_dot,
-                'TK_UNKNOWN': handle_unknown,
-                'TK_EOF': handle_eof
-            };
-
-            function create_flags(flags_base, mode) {
-                var next_indent_level = 0;
-                if (flags_base) {
-                    next_indent_level = flags_base.indentation_level;
-                    if (!output.just_added_newline() &&
-                        flags_base.line_indent_level > next_indent_level) {
-                        next_indent_level = flags_base.line_indent_level;
-                    }
-                }
+    if (!in_array(opPosition, validPositionValues)) {
+        throw new Error("Invalid Option Value: The option 'operator_position' must be one of the following values\n" +
+            validPositionValues +
+            "\nYou passed in: '" + opPosition + "'");
+    }
 
-                var next_flags = {
-                    mode: mode,
-                    parent: flags_base,
-                    last_text: flags_base ? flags_base.last_text : '', // last token text
-                    last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed
-                    declaration_statement: false,
-                    declaration_assignment: false,
-                    multiline_frame: false,
-                    inline_frame: false,
-                    if_block: false,
-                    else_block: false,
-                    do_block: false,
-                    do_while: false,
-                    import_block: false,
-                    in_case_statement: false, // switch(..){ INSIDE HERE }
-                    in_case: false, // we're on the exact line with "case 0:"
-                    case_body: false, // the indented case-action block
-                    indentation_level: next_indent_level,
-                    line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level,
-                    start_line_index: output.get_line_number(),
-                    ternary_depth: 0
-                };
-                return next_flags;
-            }
-
-            // Some interpreters have unexpected results with foo = baz || bar;
-            options = options ? options : {};
-
-            // Allow the setting of language/file-type specific options
-            // with inheritance of overall settings
-            options = mergeOpts(options, 'js');
-
-            opt = {};
-
-            // compatibility, re
-            if (options.brace_style === "expand-strict") { //graceful handling of deprecated option
-                options.brace_style = "expand";
-            } else if (options.brace_style === "collapse-preserve-inline") { //graceful handling of deprecated option
-                options.brace_style = "collapse,preserve-inline";
-            } else if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
-                options.brace_style = options.braces_on_own_line ? "expand" : "collapse";
-            } else if (!options.brace_style) //Nothing exists to set it
-            {
-                options.brace_style = "collapse";
-            }
-
-
-            var brace_style_split = options.brace_style.split(/[^a-zA-Z0-9_\-]+/);
-            opt.brace_style = brace_style_split[0];
-            opt.brace_preserve_inline = brace_style_split[1] ? brace_style_split[1] : false;
-
-            opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4;
-            opt.indent_char = options.indent_char ? options.indent_char : ' ';
-            opt.eol = options.eol ? options.eol : 'auto';
-            opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
-            opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods;
-            opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10);
-            opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren;
-            opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren;
-            opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy;
-            opt.space_after_anon_function = (options.space_after_anon_function === undefined) ? false : options.space_after_anon_function;
-            opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation;
-            opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional;
-            opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings;
-            opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10);
-            opt.e4x = (options.e4x === undefined) ? false : options.e4x;
-            opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
-            opt.comma_first = (options.comma_first === undefined) ? false : options.comma_first;
-            opt.operator_position = sanitizeOperatorPosition(options.operator_position);
-
-            // For testing of beautify ignore:start directive
-            opt.test_output_raw = (options.test_output_raw === undefined) ? false : options.test_output_raw;
-
-            // force opt.space_after_anon_function to true if opt.jslint_happy
-            if (opt.jslint_happy) {
-                opt.space_after_anon_function = true;
-            }
-
-            if (options.indent_with_tabs) {
-                opt.indent_char = '\t';
-                opt.indent_size = 1;
-            }
-
-            if (opt.eol === 'auto') {
-                opt.eol = '\n';
-                if (js_source_text && acorn.lineBreak.test(js_source_text || '')) {
-                    opt.eol = js_source_text.match(acorn.lineBreak)[0];
-                }
-            }
+    return opPosition;
+}
 
-            opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n');
+var OPERATOR_POSITION = {
+    before_newline: 'before-newline',
+    after_newline: 'after-newline',
+    preserve_newline: 'preserve-newline',
+};
+
+var OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION.before_newline, OPERATOR_POSITION.preserve_newline];
+
+var MODE = {
+    BlockStatement: 'BlockStatement', // 'BLOCK'
+    Statement: 'Statement', // 'STATEMENT'
+    ObjectLiteral: 'ObjectLiteral', // 'OBJECT',
+    ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]',
+    ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)',
+    Conditional: 'Conditional', //'(COND-EXPRESSION)',
+    Expression: 'Expression' //'(EXPRESSION)'
+};
+
+function Beautifier(js_source_text, options) {
+    "use strict";
+    var output;
+    var tokens = [],
+        token_pos;
+    var tokenizer;
+    var current_token;
+    var last_type, last_last_text, indent_string;
+    var flags, previous_flags, flag_store;
+    var prefix;
+
+    var handlers, opt;
+    var baseIndentString = '';
+
+    handlers = {
+        'TK_START_EXPR': handle_start_expr,
+        'TK_END_EXPR': handle_end_expr,
+        'TK_START_BLOCK': handle_start_block,
+        'TK_END_BLOCK': handle_end_block,
+        'TK_WORD': handle_word,
+        'TK_RESERVED': handle_word,
+        'TK_SEMICOLON': handle_semicolon,
+        'TK_STRING': handle_string,
+        'TK_EQUALS': handle_equals,
+        'TK_OPERATOR': handle_operator,
+        'TK_COMMA': handle_comma,
+        'TK_BLOCK_COMMENT': handle_block_comment,
+        'TK_COMMENT': handle_comment,
+        'TK_DOT': handle_dot,
+        'TK_UNKNOWN': handle_unknown,
+        'TK_EOF': handle_eof
+    };
 
-            //----------------------------------
-            indent_string = '';
-            while (opt.indent_size > 0) {
-                indent_string += opt.indent_char;
-                opt.indent_size -= 1;
+    function create_flags(flags_base, mode) {
+        var next_indent_level = 0;
+        if (flags_base) {
+            next_indent_level = flags_base.indentation_level;
+            if (!output.just_added_newline() &&
+                flags_base.line_indent_level > next_indent_level) {
+                next_indent_level = flags_base.line_indent_level;
             }
+        }
 
-            var preindent_index = 0;
-            if (js_source_text && js_source_text.length) {
-                while ((js_source_text.charAt(preindent_index) === ' ' ||
-                        js_source_text.charAt(preindent_index) === '\t')) {
-                    baseIndentString += js_source_text.charAt(preindent_index);
-                    preindent_index += 1;
-                }
-                js_source_text = js_source_text.substring(preindent_index);
-            }
+        var next_flags = {
+            mode: mode,
+            parent: flags_base,
+            last_text: flags_base ? flags_base.last_text : '', // last token text
+            last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed
+            declaration_statement: false,
+            declaration_assignment: false,
+            multiline_frame: false,
+            inline_frame: false,
+            if_block: false,
+            else_block: false,
+            do_block: false,
+            do_while: false,
+            import_block: false,
+            in_case_statement: false, // switch(..){ INSIDE HERE }
+            in_case: false, // we're on the exact line with "case 0:"
+            case_body: false, // the indented case-action block
+            indentation_level: next_indent_level,
+            line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level,
+            start_line_index: output.get_line_number(),
+            ternary_depth: 0
+        };
+        return next_flags;
+    }
 
-            last_type = 'TK_START_BLOCK'; // last token type
-            last_last_text = ''; // pre-last token text
-            output = new Output(indent_string, baseIndentString);
+    // Some interpreters have unexpected results with foo = baz || bar;
+    options = options ? options : {};
+
+    // Allow the setting of language/file-type specific options
+    // with inheritance of overall settings
+    options = mergeOpts(options, 'js');
+
+    opt = {};
+
+    // compatibility, re
+    if (options.brace_style === "expand-strict") { //graceful handling of deprecated option
+        options.brace_style = "expand";
+    } else if (options.brace_style === "collapse-preserve-inline") { //graceful handling of deprecated option
+        options.brace_style = "collapse,preserve-inline";
+    } else if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
+        options.brace_style = options.braces_on_own_line ? "expand" : "collapse";
+    } else if (!options.brace_style) //Nothing exists to set it
+    {
+        options.brace_style = "collapse";
+    }
 
-            // If testing the ignore directive, start with output disable set to true
-            output.raw = opt.test_output_raw;
 
+    var brace_style_split = options.brace_style.split(/[^a-zA-Z0-9_\-]+/);
+    opt.brace_style = brace_style_split[0];
+    opt.brace_preserve_inline = brace_style_split[1] ? brace_style_split[1] : false;
+
+    opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4;
+    opt.indent_char = options.indent_char ? options.indent_char : ' ';
+    opt.eol = options.eol ? options.eol : 'auto';
+    opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
+    opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods;
+    opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10);
+    opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren;
+    opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren;
+    opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy;
+    opt.space_after_anon_function = (options.space_after_anon_function === undefined) ? false : options.space_after_anon_function;
+    opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation;
+    opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional;
+    opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings;
+    opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10);
+    opt.e4x = (options.e4x === undefined) ? false : options.e4x;
+    opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
+    opt.comma_first = (options.comma_first === undefined) ? false : options.comma_first;
+    opt.operator_position = sanitizeOperatorPosition(options.operator_position);
+
+    // For testing of beautify ignore:start directive
+    opt.test_output_raw = (options.test_output_raw === undefined) ? false : options.test_output_raw;
+
+    // force opt.space_after_anon_function to true if opt.jslint_happy
+    if (opt.jslint_happy) {
+        opt.space_after_anon_function = true;
+    }
 
-            // Stack of parsing/formatting states, including MODE.
-            // We tokenize, parse, and output in an almost purely a forward-only stream of token input
-            // and formatted output.  This makes the beautifier less accurate than full parsers
-            // but also far more tolerant of syntax errors.
-            //
-            // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type
-            // MODE.BlockStatement on the the stack, even though it could be object literal.  If we later
-            // encounter a ":", we'll switch to to MODE.ObjectLiteral.  If we then see a ";",
-            // most full parsers would die, but the beautifier gracefully falls back to
-            // MODE.BlockStatement and continues on.
-            flag_store = [];
-            set_mode(MODE.BlockStatement);
+    if (options.indent_with_tabs) {
+        opt.indent_char = '\t';
+        opt.indent_size = 1;
+    }
 
-            this.beautify = function() {
+    if (opt.eol === 'auto') {
+        opt.eol = '\n';
+        if (js_source_text && acorn.lineBreak.test(js_source_text || '')) {
+            opt.eol = js_source_text.match(acorn.lineBreak)[0];
+        }
+    }
 
-                /*jshint onevar:true */
-                var sweet_code;
-                Tokenizer = new tokenizer(js_source_text, opt, indent_string);
-                tokens = Tokenizer.tokenize();
-                token_pos = 0;
+    opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n');
 
-                current_token = get_token();
-                while (current_token) {
-                    handlers[current_token.type]();
+    //----------------------------------
+    indent_string = '';
+    while (opt.indent_size > 0) {
+        indent_string += opt.indent_char;
+        opt.indent_size -= 1;
+    }
 
-                    last_last_text = flags.last_text;
-                    last_type = current_token.type;
-                    flags.last_text = current_token.text;
+    var preindent_index = 0;
+    if (js_source_text && js_source_text.length) {
+        while ((js_source_text.charAt(preindent_index) === ' ' ||
+                js_source_text.charAt(preindent_index) === '\t')) {
+            preindent_index += 1;
+        }
+        baseIndentString = js_source_text.substring(0, preindent_index);
+        js_source_text = js_source_text.substring(preindent_index);
+    }
 
-                    token_pos += 1;
-                    current_token = get_token();
-                }
+    last_type = 'TK_START_BLOCK'; // last token type
+    last_last_text = ''; // pre-last token text
+    output = new Output(indent_string, baseIndentString);
+
+    // If testing the ignore directive, start with output disable set to true
+    output.raw = opt.test_output_raw;
+
+
+    // Stack of parsing/formatting states, including MODE.
+    // We tokenize, parse, and output in an almost purely a forward-only stream of token input
+    // and formatted output.  This makes the beautifier less accurate than full parsers
+    // but also far more tolerant of syntax errors.
+    //
+    // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type
+    // MODE.BlockStatement on the the stack, even though it could be object literal.  If we later
+    // encounter a ":", we'll switch to to MODE.ObjectLiteral.  If we then see a ";",
+    // most full parsers would die, but the beautifier gracefully falls back to
+    // MODE.BlockStatement and continues on.
+    flag_store = [];
+    set_mode(MODE.BlockStatement);
+
+    this.beautify = function() {
+
+        /*jshint onevar:true */
+        var sweet_code;
+        tokenizer = new Tokenizer(js_source_text, opt, indent_string);
+        tokens = tokenizer.tokenize();
+        token_pos = 0;
+
+        current_token = get_token();
+        while (current_token) {
+            handlers[current_token.type]();
+
+            last_last_text = flags.last_text;
+            last_type = current_token.type;
+            flags.last_text = current_token.text;
+
+            token_pos += 1;
+            current_token = get_token();
+        }
 
-                sweet_code = output.get_code();
-                if (opt.end_with_newline) {
-                    sweet_code += '\n';
-                }
+        sweet_code = output.get_code(opt.end_with_newline, opt.eol);
 
-                if (opt.eol !== '\n') {
-                    sweet_code = sweet_code.replace(/[\n]/g, opt.eol);
-                }
+        return sweet_code;
+    };
 
-                return sweet_code;
-            };
-
-            function handle_whitespace_and_comments(local_token, preserve_statement_flags) {
-                var newlines = local_token.newlines;
-                var keep_whitespace = opt.keep_array_indentation && is_array(flags.mode);
-                var temp_token = current_token;
-
-                for (var h = 0; h < local_token.comments_before.length; h++) {
-                    // The cleanest handling of inline comments is to treat them as though they aren't there.
-                    // Just continue formatting and the behavior should be logical.
-                    // Also ignore unknown tokens.  Again, this should result in better behavior.
-                    current_token = local_token.comments_before[h];
-                    handle_whitespace_and_comments(current_token, preserve_statement_flags);
-                    handlers[current_token.type](preserve_statement_flags);
-                }
-                current_token = temp_token;
+    function handle_whitespace_and_comments(local_token, preserve_statement_flags) {
+        var newlines = local_token.newlines;
+        var keep_whitespace = opt.keep_array_indentation && is_array(flags.mode);
+        var temp_token = current_token;
+
+        for (var h = 0; h < local_token.comments_before.length; h++) {
+            // The cleanest handling of inline comments is to treat them as though they aren't there.
+            // Just continue formatting and the behavior should be logical.
+            // Also ignore unknown tokens.  Again, this should result in better behavior.
+            current_token = local_token.comments_before[h];
+            handle_whitespace_and_comments(current_token, preserve_statement_flags);
+            handlers[current_token.type](preserve_statement_flags);
+        }
+        current_token = temp_token;
 
-                if (keep_whitespace) {
-                    for (var i = 0; i < newlines; i += 1) {
-                        print_newline(i > 0, preserve_statement_flags);
-                    }
-                } else {
-                    if (opt.max_preserve_newlines && newlines > opt.max_preserve_newlines) {
-                        newlines = opt.max_preserve_newlines;
-                    }
+        if (keep_whitespace) {
+            for (var i = 0; i < newlines; i += 1) {
+                print_newline(i > 0, preserve_statement_flags);
+            }
+        } else {
+            if (opt.max_preserve_newlines && newlines > opt.max_preserve_newlines) {
+                newlines = opt.max_preserve_newlines;
+            }
 
-                    if (opt.preserve_newlines) {
-                        if (local_token.newlines > 1) {
-                            print_newline(false, preserve_statement_flags);
-                            for (var j = 1; j < newlines; j += 1) {
-                                print_newline(true, preserve_statement_flags);
-                            }
-                        }
+            if (opt.preserve_newlines) {
+                if (local_token.newlines > 1) {
+                    print_newline(false, preserve_statement_flags);
+                    for (var j = 1; j < newlines; j += 1) {
+                        print_newline(true, preserve_statement_flags);
                     }
                 }
-
             }
+        }
+
+    }
 
-            // we could use just string.split, but
-            // IE doesn't like returning empty strings
-            function split_linebreaks(s) {
-                //return s.split(/\x0d\x0a|\x0a/);
+    // we could use just string.split, but
+    // IE doesn't like returning empty strings
+    function split_linebreaks(s) {
+        //return s.split(/\x0d\x0a|\x0a/);
+
+        s = s.replace(acorn.allLineBreaks, '\n');
+        var out = [],
+            idx = s.indexOf("\n");
+        while (idx !== -1) {
+            out.push(s.substring(0, idx));
+            s = s.substring(idx + 1);
+            idx = s.indexOf("\n");
+        }
+        if (s.length) {
+            out.push(s);
+        }
+        return out;
+    }
 
-                s = s.replace(acorn.allLineBreaks, '\n');
-                var out = [],
-                    idx = s.indexOf("\n");
-                while (idx !== -1) {
-                    out.push(s.substring(0, idx));
-                    s = s.substring(idx + 1);
-                    idx = s.indexOf("\n");
-                }
-                if (s.length) {
-                    out.push(s);
-                }
-                return out;
-            }
+    var newline_restricted_tokens = ['break', 'continue', 'return', 'throw'];
 
-            var newline_restricted_tokens = ['break', 'continue', 'return', 'throw'];
+    function allow_wrap_or_preserved_newline(force_linewrap) {
+        force_linewrap = (force_linewrap === undefined) ? false : force_linewrap;
 
-            function allow_wrap_or_preserved_newline(force_linewrap) {
-                force_linewrap = (force_linewrap === undefined) ? false : force_linewrap;
+        // Never wrap the first token on a line
+        if (output.just_added_newline()) {
+            return;
+        }
 
-                // Never wrap the first token on a line
-                if (output.just_added_newline()) {
-                    return;
-                }
+        var shouldPreserveOrForce = (opt.preserve_newlines && current_token.wanted_newline) || force_linewrap;
+        var operatorLogicApplies = in_array(flags.last_text, tokenizer.positionable_operators) || in_array(current_token.text, tokenizer.positionable_operators);
 
-                var shouldPreserveOrForce = (opt.preserve_newlines && current_token.wanted_newline) || force_linewrap;
-                var operatorLogicApplies = in_array(flags.last_text, Tokenizer.positionable_operators) || in_array(current_token.text, Tokenizer.positionable_operators);
+        if (operatorLogicApplies) {
+            var shouldPrintOperatorNewline = (
+                    in_array(flags.last_text, tokenizer.positionable_operators) &&
+                    in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)
+                ) ||
+                in_array(current_token.text, tokenizer.positionable_operators);
+            shouldPreserveOrForce = shouldPreserveOrForce && shouldPrintOperatorNewline;
+        }
 
-                if (operatorLogicApplies) {
-                    var shouldPrintOperatorNewline = (
-                            in_array(flags.last_text, Tokenizer.positionable_operators) &&
-                            in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)
-                        ) ||
-                        in_array(current_token.text, Tokenizer.positionable_operators);
-                    shouldPreserveOrForce = shouldPreserveOrForce && shouldPrintOperatorNewline;
-                }
+        if (shouldPreserveOrForce) {
+            print_newline(false, true);
+        } else if (opt.wrap_line_length) {
+            if (last_type === 'TK_RESERVED' && in_array(flags.last_text, newline_restricted_tokens)) {
+                // These tokens should never have a newline inserted
+                // between them and the following expression.
+                return;
+            }
+            var proposed_line_length = output.current_line.get_character_count() + current_token.text.length +
+                (output.space_before_token ? 1 : 0);
+            if (proposed_line_length >= opt.wrap_line_length) {
+                print_newline(false, true);
+            }
+        }
+    }
 
-                if (shouldPreserveOrForce) {
-                    print_newline(false, true);
-                } else if (opt.wrap_line_length) {
-                    if (last_type === 'TK_RESERVED' && in_array(flags.last_text, newline_restricted_tokens)) {
-                        // These tokens should never have a newline inserted
-                        // between them and the following expression.
-                        return;
-                    }
-                    var proposed_line_length = output.current_line.get_character_count() + current_token.text.length +
-                        (output.space_before_token ? 1 : 0);
-                    if (proposed_line_length >= opt.wrap_line_length) {
-                        print_newline(false, true);
-                    }
+    function print_newline(force_newline, preserve_statement_flags) {
+        if (!preserve_statement_flags) {
+            if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') {
+                var next_token = get_token(1);
+                while (flags.mode === MODE.Statement &&
+                    !(flags.if_block && next_token && next_token.type === 'TK_RESERVED' && next_token.text === 'else') &&
+                    !flags.do_block) {
+                    restore_mode();
                 }
             }
+        }
 
-            function print_newline(force_newline, preserve_statement_flags) {
-                if (!preserve_statement_flags) {
-                    if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') {
-                        var next_token = get_token(1);
-                        while (flags.mode === MODE.Statement &&
-                            !(flags.if_block && next_token && next_token.type === 'TK_RESERVED' && next_token.text === 'else') &&
-                            !flags.do_block) {
-                            restore_mode();
-                        }
-                    }
-                }
+        if (output.add_new_line(force_newline)) {
+            flags.multiline_frame = true;
+        }
+    }
 
-                if (output.add_new_line(force_newline)) {
-                    flags.multiline_frame = true;
-                }
+    function print_token_line_indentation() {
+        if (output.just_added_newline()) {
+            if (opt.keep_array_indentation && is_array(flags.mode) && current_token.wanted_newline) {
+                output.current_line.push(current_token.whitespace_before);
+                output.space_before_token = false;
+            } else if (output.set_indent(flags.indentation_level)) {
+                flags.line_indent_level = flags.indentation_level;
             }
+        }
+    }
 
-            function print_token_line_indentation() {
-                if (output.just_added_newline()) {
-                    if (opt.keep_array_indentation && is_array(flags.mode) && current_token.wanted_newline) {
-                        output.current_line.push(current_token.whitespace_before);
-                        output.space_before_token = false;
-                    } else if (output.set_indent(flags.indentation_level)) {
-                        flags.line_indent_level = flags.indentation_level;
-                    }
+    function print_token(printable_token) {
+        if (output.raw) {
+            output.add_raw_token(current_token);
+            return;
+        }
+
+        if (opt.comma_first && last_type === 'TK_COMMA' &&
+            output.just_added_newline()) {
+            if (output.previous_line.last() === ',') {
+                var popped = output.previous_line.pop();
+                // if the comma was already at the start of the line,
+                // pull back onto that line and reprint the indentation
+                if (output.previous_line.is_empty()) {
+                    output.previous_line.push(popped);
+                    output.trim(true);
+                    output.current_line.pop();
+                    output.trim();
                 }
+
+                // add the comma in front of the next token
+                print_token_line_indentation();
+                output.add_token(',');
+                output.space_before_token = true;
             }
+        }
 
-            function print_token(printable_token) {
-                if (output.raw) {
-                    output.add_raw_token(current_token);
-                    return;
-                }
+        printable_token = printable_token || current_token.text;
+        print_token_line_indentation();
+        output.add_token(printable_token);
+    }
 
-                if (opt.comma_first && last_type === 'TK_COMMA' &&
-                    output.just_added_newline()) {
-                    if (output.previous_line.last() === ',') {
-                        var popped = output.previous_line.pop();
-                        // if the comma was already at the start of the line,
-                        // pull back onto that line and reprint the indentation
-                        if (output.previous_line.is_empty()) {
-                            output.previous_line.push(popped);
-                            output.trim(true);
-                            output.current_line.pop();
-                            output.trim();
-                        }
+    function indent() {
+        flags.indentation_level += 1;
+    }
 
-                        // add the comma in front of the next token
-                        print_token_line_indentation();
-                        output.add_token(',');
-                        output.space_before_token = true;
-                    }
-                }
+    function deindent() {
+        if (flags.indentation_level > 0 &&
+            ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level)) {
+            flags.indentation_level -= 1;
 
-                printable_token = printable_token || current_token.text;
-                print_token_line_indentation();
-                output.add_token(printable_token);
-            }
+        }
+    }
 
-            function indent() {
-                flags.indentation_level += 1;
-            }
+    function set_mode(mode) {
+        if (flags) {
+            flag_store.push(flags);
+            previous_flags = flags;
+        } else {
+            previous_flags = create_flags(null, mode);
+        }
 
-            function deindent() {
-                if (flags.indentation_level > 0 &&
-                    ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level)) {
-                    flags.indentation_level -= 1;
+        flags = create_flags(previous_flags, mode);
+    }
 
-                }
-            }
+    function is_array(mode) {
+        return mode === MODE.ArrayLiteral;
+    }
 
-            function set_mode(mode) {
-                if (flags) {
-                    flag_store.push(flags);
-                    previous_flags = flags;
-                } else {
-                    previous_flags = create_flags(null, mode);
-                }
+    function is_expression(mode) {
+        return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]);
+    }
 
-                flags = create_flags(previous_flags, mode);
+    function restore_mode() {
+        if (flag_store.length > 0) {
+            previous_flags = flags;
+            flags = flag_store.pop();
+            if (previous_flags.mode === MODE.Statement) {
+                remove_redundant_indentation(output, previous_flags);
             }
+        }
+    }
 
-            function is_array(mode) {
-                return mode === MODE.ArrayLiteral;
+    function start_of_object_property() {
+        return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && (
+            (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set'])));
+    }
+
+    function start_of_statement() {
+        if (
+            (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') ||
+            (last_type === 'TK_RESERVED' && flags.last_text === 'do') ||
+            (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw']) && !current_token.wanted_newline) ||
+            (last_type === 'TK_RESERVED' && flags.last_text === 'else' &&
+                !(current_token.type === 'TK_RESERVED' && current_token.text === 'if' && !current_token.comments_before.length)) ||
+            (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) ||
+            (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement &&
+                !flags.in_case &&
+                !(current_token.text === '--' || current_token.text === '++') &&
+                last_last_text !== 'function' &&
+                current_token.type !== 'TK_WORD' && current_token.type !== 'TK_RESERVED') ||
+            (flags.mode === MODE.ObjectLiteral && (
+                (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set']))))
+        ) {
+
+            set_mode(MODE.Statement);
+            indent();
+
+            handle_whitespace_and_comments(current_token, true);
+
+            // Issue #276:
+            // If starting a new statement with [if, for, while, do], push to a new line.
+            // if (a) if (b) if(c) d(); else e(); else f();
+            if (!start_of_object_property()) {
+                allow_wrap_or_preserved_newline(
+                    current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['do', 'for', 'if', 'while']));
             }
 
-            function is_expression(mode) {
-                return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]);
+            return true;
+        }
+        return false;
+    }
+
+    function all_lines_start_with(lines, c) {
+        for (var i = 0; i < lines.length; i++) {
+            var line = trim(lines[i]);
+            if (line.charAt(0) !== c) {
+                return false;
             }
+        }
+        return true;
+    }
 
-            function restore_mode() {
-                if (flag_store.length > 0) {
-                    previous_flags = flags;
-                    flags = flag_store.pop();
-                    if (previous_flags.mode === MODE.Statement) {
-                        output.remove_redundant_indentation(previous_flags);
-                    }
-                }
+    function each_line_matches_indent(lines, indent) {
+        var i = 0,
+            len = lines.length,
+            line;
+        for (; i < len; i++) {
+            line = lines[i];
+            // allow empty lines to pass through
+            if (line && line.indexOf(indent) !== 0) {
+                return false;
             }
+        }
+        return true;
+    }
 
-            function start_of_object_property() {
-                return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && (
-                    (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set'])));
-            }
-
-            function start_of_statement() {
-                if (
-                    (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') ||
-                    (last_type === 'TK_RESERVED' && flags.last_text === 'do') ||
-                    (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw']) && !current_token.wanted_newline) ||
-                    (last_type === 'TK_RESERVED' && flags.last_text === 'else' &&
-                        !(current_token.type === 'TK_RESERVED' && current_token.text === 'if' && !current_token.comments_before.length)) ||
-                    (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) ||
-                    (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement &&
-                        !flags.in_case &&
-                        !(current_token.text === '--' || current_token.text === '++') &&
-                        last_last_text !== 'function' &&
-                        current_token.type !== 'TK_WORD' && current_token.type !== 'TK_RESERVED') ||
-                    (flags.mode === MODE.ObjectLiteral && (
-                        (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set']))))
-                ) {
-
-                    set_mode(MODE.Statement);
-                    indent();
-
-                    handle_whitespace_and_comments(current_token, true);
-
-                    // Issue #276:
-                    // If starting a new statement with [if, for, while, do], push to a new line.
-                    // if (a) if (b) if(c) d(); else e(); else f();
-                    if (!start_of_object_property()) {
-                        allow_wrap_or_preserved_newline(
-                            current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['do', 'for', 'if', 'while']));
-                    }
+    function is_special_word(word) {
+        return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
+    }
+
+    function get_token(offset) {
+        var index = token_pos + (offset || 0);
+        return (index < 0 || index >= tokens.length) ? null : tokens[index];
+    }
+
+    function handle_start_expr() {
+        // The conditional starts the statement if appropriate.
+        if (!start_of_statement()) {
+            handle_whitespace_and_comments(current_token);
+        }
+
+        var next_mode = MODE.Expression;
+        if (current_token.text === '[') {
 
-                    return true;
+            if (last_type === 'TK_WORD' || flags.last_text === ')') {
+                // this is array index specifier, break immediately
+                // a[x], fn()[x]
+                if (last_type === 'TK_RESERVED' && in_array(flags.last_text, tokenizer.line_starters)) {
+                    output.space_before_token = true;
                 }
-                return false;
+                set_mode(next_mode);
+                print_token();
+                indent();
+                if (opt.space_in_paren) {
+                    output.space_before_token = true;
+                }
+                return;
             }
 
-            function all_lines_start_with(lines, c) {
-                for (var i = 0; i < lines.length; i++) {
-                    var line = trim(lines[i]);
-                    if (line.charAt(0) !== c) {
-                        return false;
-                    }
-                }
-                return true;
-            }
-
-            function each_line_matches_indent(lines, indent) {
-                var i = 0,
-                    len = lines.length,
-                    line;
-                for (; i < len; i++) {
-                    line = lines[i];
-                    // allow empty lines to pass through
-                    if (line && line.indexOf(indent) !== 0) {
-                        return false;
+            next_mode = MODE.ArrayLiteral;
+            if (is_array(flags.mode)) {
+                if (flags.last_text === '[' ||
+                    (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) {
+                    // ], [ goes to new line
+                    // }, [ goes to new line
+                    if (!opt.keep_array_indentation) {
+                        print_newline();
                     }
                 }
-                return true;
             }
 
-            function is_special_word(word) {
-                return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
+        } else {
+            if (last_type === 'TK_RESERVED' && flags.last_text === 'for') {
+                next_mode = MODE.ForInitializer;
+            } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) {
+                next_mode = MODE.Conditional;
+            } else {
+                // next_mode = MODE.Expression;
             }
+        }
 
-            function get_token(offset) {
-                var index = token_pos + (offset || 0);
-                return (index < 0 || index >= tokens.length) ? null : tokens[index];
+        if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') {
+            print_newline();
+        } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') {
+            // TODO: Consider whether forcing this is required.  Review failing tests when removed.
+            allow_wrap_or_preserved_newline(current_token.wanted_newline);
+            // do nothing on (( and )( and ][ and ]( and .(
+        } else if (!(last_type === 'TK_RESERVED' && current_token.text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
+            output.space_before_token = true;
+        } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) ||
+            (flags.last_text === '*' &&
+                (in_array(last_last_text, ['function', 'yield']) ||
+                    (flags.mode === MODE.ObjectLiteral && in_array(last_last_text, ['{', ',']))))) {
+            // function() vs function ()
+            // yield*() vs yield* ()
+            // function*() vs function* ()
+            if (opt.space_after_anon_function) {
+                output.space_before_token = true;
+            }
+        } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, tokenizer.line_starters) || flags.last_text === 'catch')) {
+            if (opt.space_before_conditional) {
+                output.space_before_token = true;
             }
+        }
 
-            function handle_start_expr() {
-                // The conditional starts the statement if appropriate.
-                if (!start_of_statement()) {
-                    handle_whitespace_and_comments(current_token);
+        // Should be a space between await and an IIFE
+        if (current_token.text === '(' && last_type === 'TK_RESERVED' && flags.last_word === 'await') {
+            output.space_before_token = true;
+        }
+
+        // Support of this kind of newline preservation.
+        // a = (b &&
+        //     (c || d));
+        if (current_token.text === '(') {
+            if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
+                if (!start_of_object_property()) {
+                    allow_wrap_or_preserved_newline();
                 }
+            }
+        }
 
-                var next_mode = MODE.Expression;
-                if (current_token.text === '[') {
+        // Support preserving wrapped arrow function expressions
+        // a.b('c',
+        //     () => d.e
+        // )
+        if (current_token.text === '(' && last_type !== 'TK_WORD' && last_type !== 'TK_RESERVED') {
+            allow_wrap_or_preserved_newline();
+        }
 
-                    if (last_type === 'TK_WORD' || flags.last_text === ')') {
-                        // this is array index specifier, break immediately
-                        // a[x], fn()[x]
-                        if (last_type === 'TK_RESERVED' && in_array(flags.last_text, Tokenizer.line_starters)) {
-                            output.space_before_token = true;
-                        }
-                        set_mode(next_mode);
-                        print_token();
-                        indent();
-                        if (opt.space_in_paren) {
-                            output.space_before_token = true;
-                        }
-                        return;
-                    }
+        set_mode(next_mode);
+        print_token();
+        if (opt.space_in_paren) {
+            output.space_before_token = true;
+        }
 
-                    next_mode = MODE.ArrayLiteral;
-                    if (is_array(flags.mode)) {
-                        if (flags.last_text === '[' ||
-                            (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) {
-                            // ], [ goes to new line
-                            // }, [ goes to new line
-                            if (!opt.keep_array_indentation) {
-                                print_newline();
-                            }
-                        }
-                    }
+        // In all cases, if we newline while inside an expression it should be indented.
+        indent();
+    }
 
-                } else {
-                    if (last_type === 'TK_RESERVED' && flags.last_text === 'for') {
-                        next_mode = MODE.ForInitializer;
-                    } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) {
-                        next_mode = MODE.Conditional;
-                    } else {
-                        // next_mode = MODE.Expression;
-                    }
-                }
+    function handle_end_expr() {
+        // statements inside expressions are not valid syntax, but...
+        // statements must all be closed when their container closes
+        while (flags.mode === MODE.Statement) {
+            restore_mode();
+        }
 
-                if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') {
-                    print_newline();
-                } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') {
-                    // TODO: Consider whether forcing this is required.  Review failing tests when removed.
-                    allow_wrap_or_preserved_newline(current_token.wanted_newline);
-                    // do nothing on (( and )( and ][ and ]( and .(
-                } else if (!(last_type === 'TK_RESERVED' && current_token.text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
-                    output.space_before_token = true;
-                } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) ||
-                    (flags.last_text === '*' &&
-                        (in_array(last_last_text, ['function', 'yield']) ||
-                            (flags.mode === MODE.ObjectLiteral && in_array(last_last_text, ['{', ',']))))) {
-                    // function() vs function ()
-                    // yield*() vs yield* ()
-                    // function*() vs function* ()
-                    if (opt.space_after_anon_function) {
-                        output.space_before_token = true;
-                    }
-                } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, Tokenizer.line_starters) || flags.last_text === 'catch')) {
-                    if (opt.space_before_conditional) {
-                        output.space_before_token = true;
-                    }
-                }
+        handle_whitespace_and_comments(current_token);
 
-                // Should be a space between await and an IIFE
-                if (current_token.text === '(' && last_type === 'TK_RESERVED' && flags.last_word === 'await') {
-                    output.space_before_token = true;
-                }
+        if (flags.multiline_frame) {
+            allow_wrap_or_preserved_newline(current_token.text === ']' && is_array(flags.mode) && !opt.keep_array_indentation);
+        }
 
-                // Support of this kind of newline preservation.
-                // a = (b &&
-                //     (c || d));
-                if (current_token.text === '(') {
-                    if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
-                        if (!start_of_object_property()) {
-                            allow_wrap_or_preserved_newline();
-                        }
-                    }
-                }
+        if (opt.space_in_paren) {
+            if (last_type === 'TK_START_EXPR' && !opt.space_in_empty_paren) {
+                // () [] no inner space in empty parens like these, ever, ref #320
+                output.trim();
+                output.space_before_token = false;
+            } else {
+                output.space_before_token = true;
+            }
+        }
+        if (current_token.text === ']' && opt.keep_array_indentation) {
+            print_token();
+            restore_mode();
+        } else {
+            restore_mode();
+            print_token();
+        }
+        remove_redundant_indentation(output, previous_flags);
 
-                // Support preserving wrapped arrow function expressions
-                // a.b('c',
-                //     () => d.e
-                // )
-                if (current_token.text === '(' && last_type !== 'TK_WORD' && last_type !== 'TK_RESERVED') {
-                    allow_wrap_or_preserved_newline();
-                }
+        // do {} while () // no statement required after
+        if (flags.do_while && previous_flags.mode === MODE.Conditional) {
+            previous_flags.mode = MODE.Expression;
+            flags.do_block = false;
+            flags.do_while = false;
 
-                set_mode(next_mode);
-                print_token();
-                if (opt.space_in_paren) {
-                    output.space_before_token = true;
-                }
+        }
+    }
 
-                // In all cases, if we newline while inside an expression it should be indented.
-                indent();
+    function handle_start_block() {
+        handle_whitespace_and_comments(current_token);
+
+        // Check if this is should be treated as a ObjectLiteral
+        var next_token = get_token(1);
+        var second_token = get_token(2);
+        if (second_token && (
+                (in_array(second_token.text, [':', ',']) && in_array(next_token.type, ['TK_STRING', 'TK_WORD', 'TK_RESERVED'])) ||
+                (in_array(next_token.text, ['get', 'set', '...']) && in_array(second_token.type, ['TK_WORD', 'TK_RESERVED']))
+            )) {
+            // We don't support TypeScript,but we didn't break it for a very long time.
+            // We'll try to keep not breaking it.
+            if (!in_array(last_last_text, ['class', 'interface'])) {
+                set_mode(MODE.ObjectLiteral);
+            } else {
+                set_mode(MODE.BlockStatement);
             }
+        } else if (last_type === 'TK_OPERATOR' && flags.last_text === '=>') {
+            // arrow function: (param1, paramN) => { statements }
+            set_mode(MODE.BlockStatement);
+        } else if (in_array(last_type, ['TK_EQUALS', 'TK_START_EXPR', 'TK_COMMA', 'TK_OPERATOR']) ||
+            (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw', 'import', 'default']))
+        ) {
+            // Detecting shorthand function syntax is difficult by scanning forward,
+            //     so check the surrounding context.
+            // If the block is being returned, imported, export default, passed as arg,
+            //     assigned with = or assigned in a nested object, treat as an ObjectLiteral.
+            set_mode(MODE.ObjectLiteral);
+        } else {
+            set_mode(MODE.BlockStatement);
+        }
 
-            function handle_end_expr() {
-                // statements inside expressions are not valid syntax, but...
-                // statements must all be closed when their container closes
-                while (flags.mode === MODE.Statement) {
-                    restore_mode();
-                }
-
-                handle_whitespace_and_comments(current_token);
+        var empty_braces = !next_token.comments_before.length && next_token.text === '}';
+        var empty_anonymous_function = empty_braces && flags.last_word === 'function' &&
+            last_type === 'TK_END_EXPR';
+
+        if (opt.brace_preserve_inline) // check for inline, set inline_frame if so
+        {
+            // search forward for a newline wanted inside this block
+            var index = 0;
+            var check_token = null;
+            flags.inline_frame = true;
+            do {
+                index += 1;
+                check_token = get_token(index);
+                if (check_token.wanted_newline) {
+                    flags.inline_frame = false;
+                    break;
+                }
+            } while (check_token.type !== 'TK_EOF' &&
+                !(check_token.type === 'TK_END_BLOCK' && check_token.opened === current_token));
+        }
 
-                if (flags.multiline_frame) {
-                    allow_wrap_or_preserved_newline(current_token.text === ']' && is_array(flags.mode) && !opt.keep_array_indentation);
+        if ((opt.brace_style === "expand" ||
+                (opt.brace_style === "none" && current_token.wanted_newline)) &&
+            !flags.inline_frame) {
+            if (last_type !== 'TK_OPERATOR' &&
+                (empty_anonymous_function ||
+                    last_type === 'TK_EQUALS' ||
+                    (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) {
+                output.space_before_token = true;
+            } else {
+                print_newline(false, true);
+            }
+        } else { // collapse || inline_frame
+            if (is_array(previous_flags.mode) && (last_type === 'TK_START_EXPR' || last_type === 'TK_COMMA')) {
+                if (last_type === 'TK_COMMA' || opt.space_in_paren) {
+                    output.space_before_token = true;
                 }
 
-                if (opt.space_in_paren) {
-                    if (last_type === 'TK_START_EXPR' && !opt.space_in_empty_paren) {
-                        // () [] no inner space in empty parens like these, ever, ref #320
-                        output.trim();
-                        output.space_before_token = false;
-                    } else {
-                        output.space_before_token = true;
-                    }
+                if (last_type === 'TK_COMMA' || (last_type === 'TK_START_EXPR' && flags.inline_frame)) {
+                    allow_wrap_or_preserved_newline();
+                    previous_flags.multiline_frame = previous_flags.multiline_frame || flags.multiline_frame;
+                    flags.multiline_frame = false;
                 }
-                if (current_token.text === ']' && opt.keep_array_indentation) {
-                    print_token();
-                    restore_mode();
+            }
+            if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
+                if (last_type === 'TK_START_BLOCK' && !flags.inline_frame) {
+                    print_newline();
                 } else {
-                    restore_mode();
-                    print_token();
+                    output.space_before_token = true;
                 }
-                output.remove_redundant_indentation(previous_flags);
+            }
+        }
+        print_token();
+        indent();
+    }
 
-                // do {} while () // no statement required after
-                if (flags.do_while && previous_flags.mode === MODE.Conditional) {
-                    previous_flags.mode = MODE.Expression;
-                    flags.do_block = false;
-                    flags.do_while = false;
+    function handle_end_block() {
+        // statements must all be closed when their container closes
+        handle_whitespace_and_comments(current_token);
 
-                }
-            }
+        while (flags.mode === MODE.Statement) {
+            restore_mode();
+        }
 
-            function handle_start_block() {
-                handle_whitespace_and_comments(current_token);
+        var empty_braces = last_type === 'TK_START_BLOCK';
+
+        if (flags.inline_frame && !empty_braces) { // try inline_frame (only set if opt.braces-preserve-inline) first
+            output.space_before_token = true;
+        } else if (opt.brace_style === "expand") {
+            if (!empty_braces) {
+                print_newline();
+            }
+        } else {
+            // skip {}
+            if (!empty_braces) {
+                if (is_array(flags.mode) && opt.keep_array_indentation) {
+                    // we REALLY need a newline here, but newliner would skip that
+                    opt.keep_array_indentation = false;
+                    print_newline();
+                    opt.keep_array_indentation = true;
 
-                // Check if this is should be treated as a ObjectLiteral
-                var next_token = get_token(1);
-                var second_token = get_token(2);
-                if (second_token && (
-                        (in_array(second_token.text, [':', ',']) && in_array(next_token.type, ['TK_STRING', 'TK_WORD', 'TK_RESERVED'])) ||
-                        (in_array(next_token.text, ['get', 'set', '...']) && in_array(second_token.type, ['TK_WORD', 'TK_RESERVED']))
-                    )) {
-                    // We don't support TypeScript,but we didn't break it for a very long time.
-                    // We'll try to keep not breaking it.
-                    if (!in_array(last_last_text, ['class', 'interface'])) {
-                        set_mode(MODE.ObjectLiteral);
-                    } else {
-                        set_mode(MODE.BlockStatement);
-                    }
-                } else if (last_type === 'TK_OPERATOR' && flags.last_text === '=>') {
-                    // arrow function: (param1, paramN) => { statements }
-                    set_mode(MODE.BlockStatement);
-                } else if (in_array(last_type, ['TK_EQUALS', 'TK_START_EXPR', 'TK_COMMA', 'TK_OPERATOR']) ||
-                    (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw', 'import', 'default']))
-                ) {
-                    // Detecting shorthand function syntax is difficult by scanning forward,
-                    //     so check the surrounding context.
-                    // If the block is being returned, imported, export default, passed as arg,
-                    //     assigned with = or assigned in a nested object, treat as an ObjectLiteral.
-                    set_mode(MODE.ObjectLiteral);
                 } else {
-                    set_mode(MODE.BlockStatement);
+                    print_newline();
                 }
+            }
+        }
+        restore_mode();
+        print_token();
+    }
 
-                var empty_braces = !next_token.comments_before.length && next_token.text === '}';
-                var empty_anonymous_function = empty_braces && flags.last_word === 'function' &&
-                    last_type === 'TK_END_EXPR';
-
-                if (opt.brace_preserve_inline) // check for inline, set inline_frame if so
-                {
-                    // search forward for a newline wanted inside this block
-                    var index = 0;
-                    var check_token = null;
-                    flags.inline_frame = true;
-                    do {
-                        index += 1;
-                        check_token = get_token(index);
-                        if (check_token.wanted_newline) {
-                            flags.inline_frame = false;
-                            break;
-                        }
-                    } while (check_token.type !== 'TK_EOF' &&
-                        !(check_token.type === 'TK_END_BLOCK' && check_token.opened === current_token));
+    function handle_word() {
+        if (current_token.type === 'TK_RESERVED') {
+            if (in_array(current_token.text, ['set', 'get']) && flags.mode !== MODE.ObjectLiteral) {
+                current_token.type = 'TK_WORD';
+            } else if (in_array(current_token.text, ['as', 'from']) && !flags.import_block) {
+                current_token.type = 'TK_WORD';
+            } else if (flags.mode === MODE.ObjectLiteral) {
+                var next_token = get_token(1);
+                if (next_token.text === ':') {
+                    current_token.type = 'TK_WORD';
                 }
+            }
+        }
 
-                if ((opt.brace_style === "expand" ||
-                        (opt.brace_style === "none" && current_token.wanted_newline)) &&
-                    !flags.inline_frame) {
-                    if (last_type !== 'TK_OPERATOR' &&
-                        (empty_anonymous_function ||
-                            last_type === 'TK_EQUALS' ||
-                            (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) {
-                        output.space_before_token = true;
-                    } else {
-                        print_newline(false, true);
-                    }
-                } else { // collapse || inline_frame
-                    if (is_array(previous_flags.mode) && (last_type === 'TK_START_EXPR' || last_type === 'TK_COMMA')) {
-                        if (last_type === 'TK_COMMA' || opt.space_in_paren) {
-                            output.space_before_token = true;
-                        }
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+            if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') {
+                flags.declaration_statement = true;
+            }
+        } else if (current_token.wanted_newline && !is_expression(flags.mode) &&
+            (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) &&
+            last_type !== 'TK_EQUALS' &&
+            (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const', 'set', 'get'])))) {
+            handle_whitespace_and_comments(current_token);
+            print_newline();
+        } else {
+            handle_whitespace_and_comments(current_token);
+        }
 
-                        if (last_type === 'TK_COMMA' || (last_type === 'TK_START_EXPR' && flags.inline_frame)) {
-                            allow_wrap_or_preserved_newline();
-                            previous_flags.multiline_frame = previous_flags.multiline_frame || flags.multiline_frame;
-                            flags.multiline_frame = false;
-                        }
-                    }
-                    if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
-                        if (last_type === 'TK_START_BLOCK' && !flags.inline_frame) {
-                            print_newline();
-                        } else {
-                            output.space_before_token = true;
-                        }
-                    }
-                }
+        if (flags.do_block && !flags.do_while) {
+            if (current_token.type === 'TK_RESERVED' && current_token.text === 'while') {
+                // do {} ## while ()
+                output.space_before_token = true;
                 print_token();
-                indent();
+                output.space_before_token = true;
+                flags.do_while = true;
+                return;
+            } else {
+                // do {} should always have while as the next word.
+                // if we don't see the expected while, recover
+                print_newline();
+                flags.do_block = false;
             }
+        }
 
-            function handle_end_block() {
-                // statements must all be closed when their container closes
-                handle_whitespace_and_comments(current_token);
-
+        // if may be followed by else, or not
+        // Bare/inline ifs are tricky
+        // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
+        if (flags.if_block) {
+            if (!flags.else_block && (current_token.type === 'TK_RESERVED' && current_token.text === 'else')) {
+                flags.else_block = true;
+            } else {
                 while (flags.mode === MODE.Statement) {
                     restore_mode();
                 }
+                flags.if_block = false;
+                flags.else_block = false;
+            }
+        }
 
-                var empty_braces = last_type === 'TK_START_BLOCK';
+        if (current_token.type === 'TK_RESERVED' && (current_token.text === 'case' || (current_token.text === 'default' && flags.in_case_statement))) {
+            print_newline();
+            if (flags.case_body || opt.jslint_happy) {
+                // switch cases following one another
+                deindent();
+                flags.case_body = false;
+            }
+            print_token();
+            flags.in_case = true;
+            flags.in_case_statement = true;
+            return;
+        }
+
+        if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
+            if (!start_of_object_property()) {
+                allow_wrap_or_preserved_newline();
+            }
+        }
 
-                if (flags.inline_frame && !empty_braces) { // try inline_frame (only set if opt.braces-preserve-inline) first
+        if (current_token.type === 'TK_RESERVED' && current_token.text === 'function') {
+            if (in_array(flags.last_text, ['}', ';']) ||
+                (output.just_added_newline() && !(in_array(flags.last_text, ['(', '[', '{', ':', '=', ',']) || last_type === 'TK_OPERATOR'))) {
+                // make sure there is a nice clean space of at least one blank line
+                // before a new function definition
+                if (!output.just_added_blankline() && !current_token.comments_before.length) {
+                    print_newline();
+                    print_newline(true);
+                }
+            }
+            if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
+                if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return', 'export', 'async'])) {
+                    output.space_before_token = true;
+                } else if (last_type === 'TK_RESERVED' && flags.last_text === 'default' && last_last_text === 'export') {
                     output.space_before_token = true;
-                } else if (opt.brace_style === "expand") {
-                    if (!empty_braces) {
-                        print_newline();
-                    }
                 } else {
-                    // skip {}
-                    if (!empty_braces) {
-                        if (is_array(flags.mode) && opt.keep_array_indentation) {
-                            // we REALLY need a newline here, but newliner would skip that
-                            opt.keep_array_indentation = false;
-                            print_newline();
-                            opt.keep_array_indentation = true;
-
-                        } else {
-                            print_newline();
-                        }
-                    }
+                    print_newline();
                 }
-                restore_mode();
-                print_token();
+            } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') {
+                // foo = function
+                output.space_before_token = true;
+            } else if (!flags.multiline_frame && (is_expression(flags.mode) || is_array(flags.mode))) {
+                // (function
+            } else {
+                print_newline();
             }
 
-            function handle_word() {
-                if (current_token.type === 'TK_RESERVED') {
-                    if (in_array(current_token.text, ['set', 'get']) && flags.mode !== MODE.ObjectLiteral) {
-                        current_token.type = 'TK_WORD';
-                    } else if (in_array(current_token.text, ['as', 'from']) && !flags.import_block) {
-                        current_token.type = 'TK_WORD';
-                    } else if (flags.mode === MODE.ObjectLiteral) {
-                        var next_token = get_token(1);
-                        if (next_token.text === ':') {
-                            current_token.type = 'TK_WORD';
-                        }
-                    }
-                }
+            print_token();
+            flags.last_word = current_token.text;
+            return;
+        }
 
-                if (start_of_statement()) {
-                    // The conditional starts the statement if appropriate.
-                    if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') {
-                        flags.declaration_statement = true;
-                    }
-                } else if (current_token.wanted_newline && !is_expression(flags.mode) &&
-                    (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) &&
-                    last_type !== 'TK_EQUALS' &&
-                    (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const', 'set', 'get'])))) {
-                    handle_whitespace_and_comments(current_token);
-                    print_newline();
+        prefix = 'NONE';
+
+        if (last_type === 'TK_END_BLOCK') {
+
+            if (previous_flags.inline_frame) {
+                prefix = 'SPACE';
+            } else if (!(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally', 'from']))) {
+                prefix = 'NEWLINE';
+            } else {
+                if (opt.brace_style === "expand" ||
+                    opt.brace_style === "end-expand" ||
+                    (opt.brace_style === "none" && current_token.wanted_newline)) {
+                    prefix = 'NEWLINE';
                 } else {
-                    handle_whitespace_and_comments(current_token);
+                    prefix = 'SPACE';
+                    output.space_before_token = true;
                 }
+            }
+        } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) {
+            // TODO: Should this be for STATEMENT as well?
+            prefix = 'NEWLINE';
+        } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
+            prefix = 'SPACE';
+        } else if (last_type === 'TK_STRING') {
+            prefix = 'NEWLINE';
+        } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' ||
+            (flags.last_text === '*' &&
+                (in_array(last_last_text, ['function', 'yield']) ||
+                    (flags.mode === MODE.ObjectLiteral && in_array(last_last_text, ['{', ',']))))) {
+            prefix = 'SPACE';
+        } else if (last_type === 'TK_START_BLOCK') {
+            if (flags.inline_frame) {
+                prefix = 'SPACE';
+            } else {
+                prefix = 'NEWLINE';
+            }
+        } else if (last_type === 'TK_END_EXPR') {
+            output.space_before_token = true;
+            prefix = 'NEWLINE';
+        }
 
-                if (flags.do_block && !flags.do_while) {
-                    if (current_token.type === 'TK_RESERVED' && current_token.text === 'while') {
-                        // do {} ## while ()
-                        output.space_before_token = true;
-                        print_token();
+        if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, tokenizer.line_starters) && flags.last_text !== ')') {
+            if (flags.inline_frame || flags.last_text === 'else' || flags.last_text === 'export') {
+                prefix = 'SPACE';
+            } else {
+                prefix = 'NEWLINE';
+            }
+
+        }
+
+        if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally'])) {
+            if ((!(last_type === 'TK_END_BLOCK' && previous_flags.mode === MODE.BlockStatement) ||
+                    opt.brace_style === "expand" ||
+                    opt.brace_style === "end-expand" ||
+                    (opt.brace_style === "none" && current_token.wanted_newline)) &&
+                !flags.inline_frame) {
+                print_newline();
+            } else {
+                output.trim(true);
+                var line = output.current_line;
+                // If we trimmed and there's something other than a close block before us
+                // put a newline back in.  Handles '} // comment' scenario.
+                if (line.last() !== '}') {
+                    print_newline();
+                }
+                output.space_before_token = true;
+            }
+        } else if (prefix === 'NEWLINE') {
+            if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
+                // no newline between 'return nnn'
+                output.space_before_token = true;
+            } else if (last_type !== 'TK_END_EXPR') {
+                if ((last_type !== 'TK_START_EXPR' || !(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['var', 'let', 'const']))) && flags.last_text !== ':') {
+                    // no need to force newline on 'var': for (var x = 0...)
+                    if (current_token.type === 'TK_RESERVED' && current_token.text === 'if' && flags.last_text === 'else') {
+                        // no newline for } else if {
                         output.space_before_token = true;
-                        flags.do_while = true;
-                        return;
                     } else {
-                        // do {} should always have while as the next word.
-                        // if we don't see the expected while, recover
                         print_newline();
-                        flags.do_block = false;
                     }
                 }
+            } else if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, tokenizer.line_starters) && flags.last_text !== ')') {
+                print_newline();
+            }
+        } else if (flags.multiline_frame && is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') {
+            print_newline(); // }, in lists get a newline treatment
+        } else if (prefix === 'SPACE') {
+            output.space_before_token = true;
+        }
+        print_token();
+        flags.last_word = current_token.text;
+
+        if (current_token.type === 'TK_RESERVED') {
+            if (current_token.text === 'do') {
+                flags.do_block = true;
+            } else if (current_token.text === 'if') {
+                flags.if_block = true;
+            } else if (current_token.text === 'import') {
+                flags.import_block = true;
+            } else if (flags.import_block && current_token.type === 'TK_RESERVED' && current_token.text === 'from') {
+                flags.import_block = false;
+            }
+        }
+    }
 
-                // if may be followed by else, or not
-                // Bare/inline ifs are tricky
-                // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
-                if (flags.if_block) {
-                    if (!flags.else_block && (current_token.type === 'TK_RESERVED' && current_token.text === 'else')) {
-                        flags.else_block = true;
-                    } else {
-                        while (flags.mode === MODE.Statement) {
-                            restore_mode();
-                        }
-                        flags.if_block = false;
-                        flags.else_block = false;
-                    }
+    function handle_semicolon() {
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+            // Semicolon can be the start (and end) of a statement
+            output.space_before_token = false;
+        } else {
+            handle_whitespace_and_comments(current_token);
+        }
+
+        var next_token = get_token(1);
+        while (flags.mode === MODE.Statement &&
+            !(flags.if_block && next_token && next_token.type === 'TK_RESERVED' && next_token.text === 'else') &&
+            !flags.do_block) {
+            restore_mode();
+        }
+
+        // hacky but effective for the moment
+        if (flags.import_block) {
+            flags.import_block = false;
+        }
+        print_token();
+    }
+
+    function handle_string() {
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+            // One difference - strings want at least a space before
+            output.space_before_token = true;
+        } else {
+            handle_whitespace_and_comments(current_token);
+            if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' || flags.inline_frame) {
+                output.space_before_token = true;
+            } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
+                if (!start_of_object_property()) {
+                    allow_wrap_or_preserved_newline();
                 }
+            } else {
+                print_newline();
+            }
+        }
+        print_token();
+    }
+
+    function handle_equals() {
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+        } else {
+            handle_whitespace_and_comments(current_token);
+        }
+
+        if (flags.declaration_statement) {
+            // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
+            flags.declaration_assignment = true;
+        }
+        output.space_before_token = true;
+        print_token();
+        output.space_before_token = true;
+    }
+
+    function handle_comma() {
+        handle_whitespace_and_comments(current_token, true);
+
+        print_token();
+        output.space_before_token = true;
+        if (flags.declaration_statement) {
+            if (is_expression(flags.parent.mode)) {
+                // do not break on comma, for(var a = 1, b = 2)
+                flags.declaration_assignment = false;
+            }
+
+            if (flags.declaration_assignment) {
+                flags.declaration_assignment = false;
+                print_newline(false, true);
+            } else if (opt.comma_first) {
+                // for comma-first, we want to allow a newline before the comma
+                // to turn into a newline after the comma, which we will fixup later
+                allow_wrap_or_preserved_newline();
+            }
+        } else if (flags.mode === MODE.ObjectLiteral ||
+            (flags.mode === MODE.Statement && flags.parent.mode === MODE.ObjectLiteral)) {
+            if (flags.mode === MODE.Statement) {
+                restore_mode();
+            }
+
+            if (!flags.inline_frame) {
+                print_newline();
+            }
+        } else if (opt.comma_first) {
+            // EXPR or DO_BLOCK
+            // for comma-first, we want to allow a newline before the comma
+            // to turn into a newline after the comma, which we will fixup later
+            allow_wrap_or_preserved_newline();
+        }
+    }
+
+    function handle_operator() {
+        var isGeneratorAsterisk = current_token.text === '*' &&
+            ((last_type === 'TK_RESERVED' && in_array(flags.last_text, ['function', 'yield'])) ||
+                (in_array(last_type, ['TK_START_BLOCK', 'TK_COMMA', 'TK_END_BLOCK', 'TK_SEMICOLON']))
+            );
+        var isUnary = in_array(current_token.text, ['-', '+']) && (
+            in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) ||
+            in_array(flags.last_text, tokenizer.line_starters) ||
+            flags.last_text === ','
+        );
+
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+        } else {
+            var preserve_statement_flags = !isGeneratorAsterisk;
+            handle_whitespace_and_comments(current_token, preserve_statement_flags);
+        }
+
+        if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
+            // "return" had a special handling in TK_WORD. Now we need to return the favor
+            output.space_before_token = true;
+            print_token();
+            return;
+        }
+
+        // hack for actionscript's import .*;
+        if (current_token.text === '*' && last_type === 'TK_DOT') {
+            print_token();
+            return;
+        }
+
+        if (current_token.text === '::') {
+            // no spaces around exotic namespacing syntax operator
+            print_token();
+            return;
+        }
+
+        // Allow line wrapping between operators when operator_position is
+        //   set to before or preserve
+        if (last_type === 'TK_OPERATOR' && in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)) {
+            allow_wrap_or_preserved_newline();
+        }
+
+        if (current_token.text === ':' && flags.in_case) {
+            flags.case_body = true;
+            indent();
+            print_token();
+            print_newline();
+            flags.in_case = false;
+            return;
+        }
+
+        var space_before = true;
+        var space_after = true;
+        var in_ternary = false;
+        if (current_token.text === ':') {
+            if (flags.ternary_depth === 0) {
+                // Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
+                space_before = false;
+            } else {
+                flags.ternary_depth -= 1;
+                in_ternary = true;
+            }
+        } else if (current_token.text === '?') {
+            flags.ternary_depth += 1;
+        }
+
+        // let's handle the operator_position option prior to any conflicting logic
+        if (!isUnary && !isGeneratorAsterisk && opt.preserve_newlines && in_array(current_token.text, tokenizer.positionable_operators)) {
+            var isColon = current_token.text === ':';
+            var isTernaryColon = (isColon && in_ternary);
+            var isOtherColon = (isColon && !in_ternary);
+
+            switch (opt.operator_position) {
+                case OPERATOR_POSITION.before_newline:
+                    // if the current token is : and it's not a ternary statement then we set space_before to false
+                    output.space_before_token = !isOtherColon;
 
-                if (current_token.type === 'TK_RESERVED' && (current_token.text === 'case' || (current_token.text === 'default' && flags.in_case_statement))) {
-                    print_newline();
-                    if (flags.case_body || opt.jslint_happy) {
-                        // switch cases following one another
-                        deindent();
-                        flags.case_body = false;
-                    }
                     print_token();
-                    flags.in_case = true;
-                    flags.in_case_statement = true;
-                    return;
-                }
 
-                if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
-                    if (!start_of_object_property()) {
+                    if (!isColon || isTernaryColon) {
                         allow_wrap_or_preserved_newline();
                     }
-                }
-
-                if (current_token.type === 'TK_RESERVED' && current_token.text === 'function') {
-                    if (in_array(flags.last_text, ['}', ';']) ||
-                        (output.just_added_newline() && !(in_array(flags.last_text, ['(', '[', '{', ':', '=', ',']) || last_type === 'TK_OPERATOR'))) {
-                        // make sure there is a nice clean space of at least one blank line
-                        // before a new function definition
-                        if (!output.just_added_blankline() && !current_token.comments_before.length) {
-                            print_newline();
-                            print_newline(true);
-                        }
-                    }
-                    if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
-                        if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return', 'export', 'async'])) {
-                            output.space_before_token = true;
-                        } else if (last_type === 'TK_RESERVED' && flags.last_text === 'default' && last_last_text === 'export') {
-                            output.space_before_token = true;
-                        } else {
-                            print_newline();
-                        }
-                    } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') {
-                        // foo = function
-                        output.space_before_token = true;
-                    } else if (!flags.multiline_frame && (is_expression(flags.mode) || is_array(flags.mode))) {
-                        // (function
-                    } else {
-                        print_newline();
-                    }
 
-                    print_token();
-                    flags.last_word = current_token.text;
+                    output.space_before_token = true;
                     return;
-                }
 
-                prefix = 'NONE';
+                case OPERATOR_POSITION.after_newline:
+                    // if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement,
+                    //   then print a newline.
 
-                if (last_type === 'TK_END_BLOCK') {
+                    output.space_before_token = true;
 
-                    if (previous_flags.inline_frame) {
-                        prefix = 'SPACE';
-                    } else if (!(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally', 'from']))) {
-                        prefix = 'NEWLINE';
-                    } else {
-                        if (opt.brace_style === "expand" ||
-                            opt.brace_style === "end-expand" ||
-                            (opt.brace_style === "none" && current_token.wanted_newline)) {
-                            prefix = 'NEWLINE';
+                    if (!isColon || isTernaryColon) {
+                        if (get_token(1).wanted_newline) {
+                            print_newline(false, true);
                         } else {
-                            prefix = 'SPACE';
-                            output.space_before_token = true;
+                            allow_wrap_or_preserved_newline();
                         }
-                    }
-                } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) {
-                    // TODO: Should this be for STATEMENT as well?
-                    prefix = 'NEWLINE';
-                } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
-                    prefix = 'SPACE';
-                } else if (last_type === 'TK_STRING') {
-                    prefix = 'NEWLINE';
-                } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' ||
-                    (flags.last_text === '*' &&
-                        (in_array(last_last_text, ['function', 'yield']) ||
-                            (flags.mode === MODE.ObjectLiteral && in_array(last_last_text, ['{', ',']))))) {
-                    prefix = 'SPACE';
-                } else if (last_type === 'TK_START_BLOCK') {
-                    if (flags.inline_frame) {
-                        prefix = 'SPACE';
                     } else {
-                        prefix = 'NEWLINE';
+                        output.space_before_token = false;
                     }
-                } else if (last_type === 'TK_END_EXPR') {
+
+                    print_token();
+
                     output.space_before_token = true;
-                    prefix = 'NEWLINE';
-                }
+                    return;
 
-                if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
-                    if (flags.inline_frame || flags.last_text === 'else' || flags.last_text === 'export') {
-                        prefix = 'SPACE';
-                    } else {
-                        prefix = 'NEWLINE';
+                case OPERATOR_POSITION.preserve_newline:
+                    if (!isOtherColon) {
+                        allow_wrap_or_preserved_newline();
                     }
 
-                }
+                    // if we just added a newline, or the current token is : and it's not a ternary statement,
+                    //   then we set space_before to false
+                    space_before = !(output.just_added_newline() || isOtherColon);
 
-                if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally'])) {
-                    if ((!(last_type === 'TK_END_BLOCK' && previous_flags.mode === MODE.BlockStatement) ||
-                            opt.brace_style === "expand" ||
-                            opt.brace_style === "end-expand" ||
-                            (opt.brace_style === "none" && current_token.wanted_newline)) &&
-                        !flags.inline_frame) {
-                        print_newline();
-                    } else {
-                        output.trim(true);
-                        var line = output.current_line;
-                        // If we trimmed and there's something other than a close block before us
-                        // put a newline back in.  Handles '} // comment' scenario.
-                        if (line.last() !== '}') {
-                            print_newline();
-                        }
-                        output.space_before_token = true;
-                    }
-                } else if (prefix === 'NEWLINE') {
-                    if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
-                        // no newline between 'return nnn'
-                        output.space_before_token = true;
-                    } else if (last_type !== 'TK_END_EXPR') {
-                        if ((last_type !== 'TK_START_EXPR' || !(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['var', 'let', 'const']))) && flags.last_text !== ':') {
-                            // no need to force newline on 'var': for (var x = 0...)
-                            if (current_token.type === 'TK_RESERVED' && current_token.text === 'if' && flags.last_text === 'else') {
-                                // no newline for } else if {
-                                output.space_before_token = true;
-                            } else {
-                                print_newline();
-                            }
-                        }
-                    } else if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, Tokenizer.line_starters) && flags.last_text !== ')') {
-                        print_newline();
-                    }
-                } else if (flags.multiline_frame && is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') {
-                    print_newline(); // }, in lists get a newline treatment
-                } else if (prefix === 'SPACE') {
+                    output.space_before_token = space_before;
+                    print_token();
                     output.space_before_token = true;
-                }
-                print_token();
-                flags.last_word = current_token.text;
-
-                if (current_token.type === 'TK_RESERVED') {
-                    if (current_token.text === 'do') {
-                        flags.do_block = true;
-                    } else if (current_token.text === 'if') {
-                        flags.if_block = true;
-                    } else if (current_token.text === 'import') {
-                        flags.import_block = true;
-                    } else if (flags.import_block && current_token.type === 'TK_RESERVED' && current_token.text === 'from') {
-                        flags.import_block = false;
-                    }
-                }
+                    return;
             }
+        }
 
-            function handle_semicolon() {
-                if (start_of_statement()) {
-                    // The conditional starts the statement if appropriate.
-                    // Semicolon can be the start (and end) of a statement
-                    output.space_before_token = false;
-                } else {
-                    handle_whitespace_and_comments(current_token);
-                }
+        if (isGeneratorAsterisk) {
+            allow_wrap_or_preserved_newline();
+            space_before = false;
+            var next_token = get_token(1);
+            space_after = next_token && in_array(next_token.type, ['TK_WORD', 'TK_RESERVED']);
+        } else if (current_token.text === '...') {
+            allow_wrap_or_preserved_newline();
+            space_before = last_type === 'TK_START_BLOCK';
+            space_after = false;
+        } else if (in_array(current_token.text, ['--', '++', '!', '~']) || isUnary) {
+            // unary operators (and binary +/- pretending to be unary) special cases
+
+            space_before = false;
+            space_after = false;
+
+            // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
+            // if there is a newline between -- or ++ and anything else we should preserve it.
+            if (current_token.wanted_newline && (current_token.text === '--' || current_token.text === '++')) {
+                print_newline(false, true);
+            }
 
-                var next_token = get_token(1);
-                while (flags.mode === MODE.Statement &&
-                    !(flags.if_block && next_token && next_token.type === 'TK_RESERVED' && next_token.text === 'else') &&
-                    !flags.do_block) {
-                    restore_mode();
-                }
+            if (flags.last_text === ';' && is_expression(flags.mode)) {
+                // for (;; ++i)
+                //        ^^^
+                space_before = true;
+            }
 
-                // hacky but effective for the moment
-                if (flags.import_block) {
-                    flags.import_block = false;
+            if (last_type === 'TK_RESERVED') {
+                space_before = true;
+            } else if (last_type === 'TK_END_EXPR') {
+                space_before = !(flags.last_text === ']' && (current_token.text === '--' || current_token.text === '++'));
+            } else if (last_type === 'TK_OPERATOR') {
+                // a++ + ++b;
+                // a - -b
+                space_before = in_array(current_token.text, ['--', '-', '++', '+']) && in_array(flags.last_text, ['--', '-', '++', '+']);
+                // + and - are not unary when preceeded by -- or ++ operator
+                // a-- + b
+                // a * +b
+                // a - -b
+                if (in_array(current_token.text, ['+', '-']) && in_array(flags.last_text, ['--', '++'])) {
+                    space_after = true;
                 }
-                print_token();
             }
 
-            function handle_string() {
-                if (start_of_statement()) {
-                    // The conditional starts the statement if appropriate.
-                    // One difference - strings want at least a space before
-                    output.space_before_token = true;
-                } else {
-                    handle_whitespace_and_comments(current_token);
-                    if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' || flags.inline_frame) {
-                        output.space_before_token = true;
-                    } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
-                        if (!start_of_object_property()) {
-                            allow_wrap_or_preserved_newline();
-                        }
-                    } else {
-                        print_newline();
-                    }
-                }
-                print_token();
+
+            if (((flags.mode === MODE.BlockStatement && !flags.inline_frame) || flags.mode === MODE.Statement) &&
+                (flags.last_text === '{' || flags.last_text === ';')) {
+                // { foo; --i }
+                // foo(); --bar;
+                print_newline();
             }
+        }
 
-            function handle_equals() {
-                if (start_of_statement()) {
-                    // The conditional starts the statement if appropriate.
-                } else {
-                    handle_whitespace_and_comments(current_token);
-                }
+        output.space_before_token = output.space_before_token || space_before;
+        print_token();
+        output.space_before_token = space_after;
+    }
 
-                if (flags.declaration_statement) {
-                    // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
-                    flags.declaration_assignment = true;
-                }
-                output.space_before_token = true;
-                print_token();
-                output.space_before_token = true;
+    function handle_block_comment(preserve_statement_flags) {
+        if (output.raw) {
+            output.add_raw_token(current_token);
+            if (current_token.directives && current_token.directives.preserve === 'end') {
+                // If we're testing the raw output behavior, do not allow a directive to turn it off.
+                output.raw = opt.test_output_raw;
             }
+            return;
+        }
 
-            function handle_comma() {
-                handle_whitespace_and_comments(current_token, true);
+        if (current_token.directives) {
+            print_newline(false, preserve_statement_flags);
+            print_token();
+            if (current_token.directives.preserve === 'start') {
+                output.raw = true;
+            }
+            print_newline(false, true);
+            return;
+        }
 
-                print_token();
-                output.space_before_token = true;
-                if (flags.declaration_statement) {
-                    if (is_expression(flags.parent.mode)) {
-                        // do not break on comma, for(var a = 1, b = 2)
-                        flags.declaration_assignment = false;
-                    }
+        // inline block
+        if (!acorn.newline.test(current_token.text) && !current_token.wanted_newline) {
+            output.space_before_token = true;
+            print_token();
+            output.space_before_token = true;
+            return;
+        }
 
-                    if (flags.declaration_assignment) {
-                        flags.declaration_assignment = false;
-                        print_newline(false, true);
-                    } else if (opt.comma_first) {
-                        // for comma-first, we want to allow a newline before the comma
-                        // to turn into a newline after the comma, which we will fixup later
-                        allow_wrap_or_preserved_newline();
-                    }
-                } else if (flags.mode === MODE.ObjectLiteral ||
-                    (flags.mode === MODE.Statement && flags.parent.mode === MODE.ObjectLiteral)) {
-                    if (flags.mode === MODE.Statement) {
-                        restore_mode();
-                    }
+        var lines = split_linebreaks(current_token.text);
+        var j; // iterator for this case
+        var javadoc = false;
+        var starless = false;
+        var lastIndent = current_token.whitespace_before;
+        var lastIndentLength = lastIndent.length;
+
+        // block comment starts with a new line
+        print_newline(false, preserve_statement_flags);
+        if (lines.length > 1) {
+            javadoc = all_lines_start_with(lines.slice(1), '*');
+            starless = each_line_matches_indent(lines.slice(1), lastIndent);
+        }
 
-                    if (!flags.inline_frame) {
-                        print_newline();
-                    }
-                } else if (opt.comma_first) {
-                    // EXPR or DO_BLOCK
-                    // for comma-first, we want to allow a newline before the comma
-                    // to turn into a newline after the comma, which we will fixup later
-                    allow_wrap_or_preserved_newline();
-                }
+        // first line always indented
+        print_token(lines[0]);
+        for (j = 1; j < lines.length; j++) {
+            print_newline(false, true);
+            if (javadoc) {
+                // javadoc: reformat and re-indent
+                print_token(' ' + ltrim(lines[j]));
+            } else if (starless && lines[j].length > lastIndentLength) {
+                // starless: re-indent non-empty content, avoiding trim
+                print_token(lines[j].substring(lastIndentLength));
+            } else {
+                // normal comments output raw
+                output.add_token(lines[j]);
             }
+        }
 
-            function handle_operator() {
-                var isGeneratorAsterisk = current_token.text === '*' &&
-                    ((last_type === 'TK_RESERVED' && in_array(flags.last_text, ['function', 'yield'])) ||
-                        (in_array(last_type, ['TK_START_BLOCK', 'TK_COMMA', 'TK_END_BLOCK', 'TK_SEMICOLON']))
-                    );
-                var isUnary = in_array(current_token.text, ['-', '+']) && (
-                    in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) ||
-                    in_array(flags.last_text, Tokenizer.line_starters) ||
-                    flags.last_text === ','
-                );
+        // for comments of more than one line, make sure there's a new line after
+        print_newline(false, preserve_statement_flags);
+    }
 
-                if (start_of_statement()) {
-                    // The conditional starts the statement if appropriate.
-                } else {
-                    var preserve_statement_flags = !isGeneratorAsterisk;
-                    handle_whitespace_and_comments(current_token, preserve_statement_flags);
-                }
+    function handle_comment(preserve_statement_flags) {
+        if (current_token.wanted_newline) {
+            print_newline(false, preserve_statement_flags);
+        } else {
+            output.trim(true);
+        }
 
-                if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
-                    // "return" had a special handling in TK_WORD. Now we need to return the favor
-                    output.space_before_token = true;
-                    print_token();
-                    return;
-                }
+        output.space_before_token = true;
+        print_token();
+        print_newline(false, preserve_statement_flags);
+    }
 
-                // hack for actionscript's import .*;
-                if (current_token.text === '*' && last_type === 'TK_DOT') {
-                    print_token();
-                    return;
-                }
+    function handle_dot() {
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+        } else {
+            handle_whitespace_and_comments(current_token, true);
+        }
 
-                if (current_token.text === '::') {
-                    // no spaces around exotic namespacing syntax operator
-                    print_token();
-                    return;
-                }
+        if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
+            output.space_before_token = true;
+        } else {
+            // allow preserved newlines before dots in general
+            // force newlines on dots after close paren when break_chained - for bar().baz()
+            allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods);
+        }
 
-                // Allow line wrapping between operators when operator_position is
-                //   set to before or preserve
-                if (last_type === 'TK_OPERATOR' && in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)) {
-                    allow_wrap_or_preserved_newline();
-                }
+        print_token();
+    }
 
-                if (current_token.text === ':' && flags.in_case) {
-                    flags.case_body = true;
-                    indent();
-                    print_token();
-                    print_newline();
-                    flags.in_case = false;
-                    return;
-                }
+    function handle_unknown(preserve_statement_flags) {
+        print_token();
 
-                var space_before = true;
-                var space_after = true;
-                var in_ternary = false;
-                if (current_token.text === ':') {
-                    if (flags.ternary_depth === 0) {
-                        // Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
-                        space_before = false;
-                    } else {
-                        flags.ternary_depth -= 1;
-                        in_ternary = true;
-                    }
-                } else if (current_token.text === '?') {
-                    flags.ternary_depth += 1;
-                }
+        if (current_token.text[current_token.text.length - 1] === '\n') {
+            print_newline(false, preserve_statement_flags);
+        }
+    }
+
+    function handle_eof() {
+        // Unwind any open statements
+        while (flags.mode === MODE.Statement) {
+            restore_mode();
+        }
+        handle_whitespace_and_comments(current_token);
+    }
+}
 
-                // let's handle the operator_position option prior to any conflicting logic
-                if (!isUnary && !isGeneratorAsterisk && opt.preserve_newlines && in_array(current_token.text, Tokenizer.positionable_operators)) {
-                    var isColon = current_token.text === ':';
-                    var isTernaryColon = (isColon && in_ternary);
-                    var isOtherColon = (isColon && !in_ternary);
+module.exports.Beautifier = Beautifier;
 
-                    switch (opt.operator_position) {
-                        case OPERATOR_POSITION.before_newline:
-                            // if the current token is : and it's not a ternary statement then we set space_before to false
-                            output.space_before_token = !isOtherColon;
+/***/ }),
+/* 2 */
+/***/ (function(module, exports) {
 
-                            print_token();
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
 
-                            if (!isColon || isTernaryColon) {
-                                allow_wrap_or_preserved_newline();
-                            }
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
 
-                            output.space_before_token = true;
-                            return;
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
 
-                        case OPERATOR_POSITION.after_newline:
-                            // if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement,
-                            //   then print a newline.
+  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.
+*/
 
-                            output.space_before_token = true;
+function InputScanner(input) {
+    var _input = input;
+    var _input_length = _input.length;
+    var _position = 0;
 
-                            if (!isColon || isTernaryColon) {
-                                if (get_token(1).wanted_newline) {
-                                    print_newline(false, true);
-                                } else {
-                                    allow_wrap_or_preserved_newline();
-                                }
-                            } else {
-                                output.space_before_token = false;
-                            }
+    this.back = function() {
+        _position -= 1;
+    };
 
-                            print_token();
+    this.hasNext = function() {
+        return _position < _input_length;
+    };
 
-                            output.space_before_token = true;
-                            return;
+    this.next = function() {
+        var val = null;
+        if (this.hasNext()) {
+            val = _input.charAt(_position);
+            _position += 1;
+        }
+        return val;
+    };
 
-                        case OPERATOR_POSITION.preserve_newline:
-                            if (!isOtherColon) {
-                                allow_wrap_or_preserved_newline();
-                            }
+    this.peek = function(index) {
+        var val = null;
+        index = index || 0;
+        index += _position;
+        if (index >= 0 && index < _input_length) {
+            val = _input.charAt(index);
+        }
+        return val;
+    };
 
-                            // if we just added a newline, or the current token is : and it's not a ternary statement,
-                            //   then we set space_before to false
-                            space_before = !(output.just_added_newline() || isOtherColon);
+    this.peekCharCode = function(index) {
+        var val = 0;
+        index = index || 0;
+        index += _position;
+        if (index >= 0 && index < _input_length) {
+            val = _input.charCodeAt(index);
+        }
+        return val;
+    };
 
-                            output.space_before_token = space_before;
-                            print_token();
-                            output.space_before_token = true;
-                            return;
-                    }
-                }
+    this.test = function(pattern, index) {
+        index = index || 0;
+        pattern.lastIndex = _position + index;
+        return pattern.test(_input);
+    };
 
-                if (isGeneratorAsterisk) {
-                    allow_wrap_or_preserved_newline();
-                    space_before = false;
-                    var next_token = get_token(1);
-                    space_after = next_token && in_array(next_token.type, ['TK_WORD', 'TK_RESERVED']);
-                } else if (current_token.text === '...') {
-                    allow_wrap_or_preserved_newline();
-                    space_before = last_type === 'TK_START_BLOCK';
-                    space_after = false;
-                } else if (in_array(current_token.text, ['--', '++', '!', '~']) || isUnary) {
-                    // unary operators (and binary +/- pretending to be unary) special cases
-
-                    space_before = false;
-                    space_after = false;
-
-                    // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
-                    // if there is a newline between -- or ++ and anything else we should preserve it.
-                    if (current_token.wanted_newline && (current_token.text === '--' || current_token.text === '++')) {
-                        print_newline(false, true);
-                    }
+    this.testChar = function(pattern, index) {
+        var val = this.peek(index);
+        return val !== null && pattern.test(val);
+    };
 
-                    if (flags.last_text === ';' && is_expression(flags.mode)) {
-                        // for (;; ++i)
-                        //        ^^^
-                        space_before = true;
-                    }
+    this.match = function(pattern) {
+        pattern.lastIndex = _position;
+        var pattern_match = pattern.exec(_input);
+        if (pattern_match && pattern_match.index === _position) {
+            _position += pattern_match[0].length;
+        } else {
+            pattern_match = null;
+        }
+        return pattern_match;
+    };
+}
 
-                    if (last_type === 'TK_RESERVED') {
-                        space_before = true;
-                    } else if (last_type === 'TK_END_EXPR') {
-                        space_before = !(flags.last_text === ']' && (current_token.text === '--' || current_token.text === '++'));
-                    } else if (last_type === 'TK_OPERATOR') {
-                        // a++ + ++b;
-                        // a - -b
-                        space_before = in_array(current_token.text, ['--', '-', '++', '+']) && in_array(flags.last_text, ['--', '-', '++', '+']);
-                        // + and - are not unary when preceeded by -- or ++ operator
-                        // a-- + b
-                        // a * +b
-                        // a - -b
-                        if (in_array(current_token.text, ['+', '-']) && in_array(flags.last_text, ['--', '++'])) {
-                            space_after = true;
-                        }
-                    }
 
+module.exports.InputScanner = InputScanner;
 
-                    if (((flags.mode === MODE.BlockStatement && !flags.inline_frame) || flags.mode === MODE.Statement) &&
-                        (flags.last_text === '{' || flags.last_text === ';')) {
-                        // { foo; --i }
-                        // foo(); --bar;
-                        print_newline();
-                    }
-                }
 
-                output.space_before_token = output.space_before_token || space_before;
-                print_token();
-                output.space_before_token = space_after;
+/***/ }),
+/* 3 */
+/***/ (function(module, exports) {
+
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+    The MIT License (MIT)
+
+    Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+*/
+
+function mergeOpts(allOptions, targetType) {
+    var finalOpts = {};
+    var name;
+
+    for (name in allOptions) {
+        if (name !== targetType) {
+            finalOpts[name] = allOptions[name];
+        }
+    }
+
+    //merge in the per type settings for the targetType
+    if (targetType in allOptions) {
+        for (name in allOptions[targetType]) {
+            finalOpts[name] = allOptions[targetType][name];
+        }
+    }
+    return finalOpts;
+}
+
+module.exports.mergeOpts = mergeOpts;
+
+
+/***/ }),
+/* 4 */
+/***/ (function(module, exports) {
+
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+*/
+
+function OutputLine(parent) {
+    var _character_count = 0;
+    // use indent_count as a marker for lines that have preserved indentation
+    var _indent_count = -1;
+
+    var _items = [];
+    var _empty = true;
+
+    this.set_indent = function(level) {
+        _character_count = parent.baseIndentLength + level * parent.indent_length;
+        _indent_count = level;
+    };
+
+    this.get_character_count = function() {
+        return _character_count;
+    };
+
+    this.is_empty = function() {
+        return _empty;
+    };
+
+    this.last = function() {
+        if (!this._empty) {
+            return _items[_items.length - 1];
+        } else {
+            return null;
+        }
+    };
+
+    this.push = function(input) {
+        _items.push(input);
+        _character_count += input.length;
+        _empty = false;
+    };
+
+    this.pop = function() {
+        var item = null;
+        if (!_empty) {
+            item = _items.pop();
+            _character_count -= item.length;
+            _empty = _items.length === 0;
+        }
+        return item;
+    };
+
+    this.remove_indent = function() {
+        if (_indent_count > 0) {
+            _indent_count -= 1;
+            _character_count -= parent.indent_length;
+        }
+    };
+
+    this.trim = function() {
+        while (this.last() === ' ') {
+            _items.pop();
+            _character_count -= 1;
+        }
+        _empty = _items.length === 0;
+    };
+
+    this.toString = function() {
+        var result = '';
+        if (!this._empty) {
+            if (_indent_count >= 0) {
+                result = parent.indent_cache[_indent_count];
             }
+            result += _items.join('');
+        }
+        return result;
+    };
+}
 
-            function handle_block_comment(preserve_statement_flags) {
-                if (output.raw) {
-                    output.add_raw_token(current_token);
-                    if (current_token.directives && current_token.directives.preserve === 'end') {
-                        // If we're testing the raw output behavior, do not allow a directive to turn it off.
-                        output.raw = opt.test_output_raw;
-                    }
-                    return;
-                }
+function Output(indent_string, baseIndentString) {
+    baseIndentString = baseIndentString || '';
+    this.indent_cache = [baseIndentString];
+    this.baseIndentLength = baseIndentString.length;
+    this.indent_length = indent_string.length;
+    this.raw = false;
+
+    var lines = [];
+    this.baseIndentString = baseIndentString;
+    this.indent_string = indent_string;
+    this.previous_line = null;
+    this.current_line = null;
+    this.space_before_token = false;
+
+    this.add_outputline = function() {
+        this.previous_line = this.current_line;
+        this.current_line = new OutputLine(this);
+        lines.push(this.current_line);
+    };
 
-                if (current_token.directives) {
-                    print_newline(false, preserve_statement_flags);
-                    print_token();
-                    if (current_token.directives.preserve === 'start') {
-                        output.raw = true;
-                    }
-                    print_newline(false, true);
-                    return;
-                }
+    // initialize
+    this.add_outputline();
 
-                // inline block
-                if (!acorn.newline.test(current_token.text) && !current_token.wanted_newline) {
-                    output.space_before_token = true;
-                    print_token();
-                    output.space_before_token = true;
-                    return;
-                }
 
-                var lines = split_linebreaks(current_token.text);
-                var j; // iterator for this case
-                var javadoc = false;
-                var starless = false;
-                var lastIndent = current_token.whitespace_before;
-                var lastIndentLength = lastIndent.length;
-
-                // block comment starts with a new line
-                print_newline(false, preserve_statement_flags);
-                if (lines.length > 1) {
-                    javadoc = all_lines_start_with(lines.slice(1), '*');
-                    starless = each_line_matches_indent(lines.slice(1), lastIndent);
-                }
+    this.get_line_number = function() {
+        return lines.length;
+    };
 
-                // first line always indented
-                print_token(lines[0]);
-                for (j = 1; j < lines.length; j++) {
-                    print_newline(false, true);
-                    if (javadoc) {
-                        // javadoc: reformat and re-indent
-                        print_token(' ' + ltrim(lines[j]));
-                    } else if (starless && lines[j].length > lastIndentLength) {
-                        // starless: re-indent non-empty content, avoiding trim
-                        print_token(lines[j].substring(lastIndentLength));
-                    } else {
-                        // normal comments output raw
-                        output.add_token(lines[j]);
-                    }
-                }
+    // Using object instead of string to allow for later expansion of info about each line
+    this.add_new_line = function(force_newline) {
+        if (this.get_line_number() === 1 && this.just_added_newline()) {
+            return false; // no newline on start of file
+        }
 
-                // for comments of more than one line, make sure there's a new line after
-                print_newline(false, preserve_statement_flags);
+        if (force_newline || !this.just_added_newline()) {
+            if (!this.raw) {
+                this.add_outputline();
             }
+            return true;
+        }
+
+        return false;
+    };
+
+    this.get_code = function(end_with_newline, eol) {
+        var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, '');
+
+        if (end_with_newline) {
+            sweet_code += '\n';
+        }
+
+        if (eol !== '\n') {
+            sweet_code = sweet_code.replace(/[\n]/g, eol);
+        }
+
+        return sweet_code;
+    };
+
+    this.set_indent = function(level) {
+        // Never indent your first output indent at the start of the file
+        if (lines.length > 1) {
+            while (level >= this.indent_cache.length) {
+                this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string);
+            }
+
+            this.current_line.set_indent(level);
+            return true;
+        }
+        this.current_line.set_indent(0);
+        return false;
+    };
+
+    this.add_raw_token = function(token) {
+        for (var x = 0; x < token.newlines; x++) {
+            this.add_outputline();
+        }
+        this.current_line.push(token.whitespace_before);
+        this.current_line.push(token.text);
+        this.space_before_token = false;
+    };
+
+    this.add_token = function(printable_token) {
+        this.add_space_before_token();
+        this.current_line.push(printable_token);
+    };
+
+    this.add_space_before_token = function() {
+        if (this.space_before_token && !this.just_added_newline()) {
+            this.current_line.push(' ');
+        }
+        this.space_before_token = false;
+    };
 
-            function handle_comment(preserve_statement_flags) {
-                if (current_token.wanted_newline) {
-                    print_newline(false, preserve_statement_flags);
-                } else {
-                    output.trim(true);
-                }
+    this.remove_indent = function(index) {
+        var output_length = lines.length;
+        while (index < output_length) {
+            lines[index].remove_indent();
+            index++;
+        }
+    };
 
-                output.space_before_token = true;
-                print_token();
-                print_newline(false, preserve_statement_flags);
-            }
+    this.trim = function(eat_newlines) {
+        eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;
 
-            function handle_dot() {
-                if (start_of_statement()) {
-                    // The conditional starts the statement if appropriate.
-                } else {
-                    handle_whitespace_and_comments(current_token, true);
-                }
+        this.current_line.trim(indent_string, baseIndentString);
 
-                if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
-                    output.space_before_token = true;
-                } else {
-                    // allow preserved newlines before dots in general
-                    // force newlines on dots after close paren when break_chained - for bar().baz()
-                    allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods);
-                }
+        while (eat_newlines && lines.length > 1 &&
+            this.current_line.is_empty()) {
+            lines.pop();
+            this.current_line = lines[lines.length - 1];
+            this.current_line.trim();
+        }
 
-                print_token();
-            }
+        this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null;
+    };
 
-            function handle_unknown(preserve_statement_flags) {
-                print_token();
+    this.just_added_newline = function() {
+        return this.current_line.is_empty();
+    };
 
-                if (current_token.text[current_token.text.length - 1] === '\n') {
-                    print_newline(false, preserve_statement_flags);
-                }
+    this.just_added_blankline = function() {
+        if (this.just_added_newline()) {
+            if (lines.length === 1) {
+                return true; // start of the file and newline = blank
             }
 
-            function handle_eof() {
-                // Unwind any open statements
-                while (flags.mode === MODE.Statement) {
-                    restore_mode();
-                }
-                handle_whitespace_and_comments(current_token);
-            }
+            var line = lines[lines.length - 2];
+            return line.is_empty();
         }
+        return false;
+    };
+}
 
+module.exports.Output = Output;
 
-        function OutputLine(parent) {
-            var _character_count = 0;
-            // use indent_count as a marker for lines that have preserved indentation
-            var _indent_count = -1;
 
-            var _items = [];
-            var _empty = true;
+/***/ }),
+/* 5 */
+/***/ (function(module, exports) {
 
-            this.set_indent = function(level) {
-                _character_count = parent.baseIndentLength + level * parent.indent_length;
-                _indent_count = level;
-            };
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
 
-            this.get_character_count = function() {
-                return _character_count;
-            };
+  The MIT License (MIT)
 
-            this.is_empty = function() {
-                return _empty;
-            };
+  Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
 
-            this.last = function() {
-                if (!this._empty) {
-                    return _items[_items.length - 1];
-                } else {
-                    return null;
-                }
-            };
-
-            this.push = function(input) {
-                _items.push(input);
-                _character_count += input.length;
-                _empty = false;
-            };
-
-            this.pop = function() {
-                var item = null;
-                if (!_empty) {
-                    item = _items.pop();
-                    _character_count -= item.length;
-                    _empty = _items.length === 0;
-                }
-                return item;
-            };
+  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:
 
-            this.remove_indent = function() {
-                if (_indent_count > 0) {
-                    _indent_count -= 1;
-                    _character_count -= parent.indent_length;
-                }
-            };
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
 
-            this.trim = function() {
-                while (this.last() === ' ') {
-                    _items.pop();
-                    _character_count -= 1;
-                }
-                _empty = _items.length === 0;
-            };
-
-            this.toString = function() {
-                var result = '';
-                if (!this._empty) {
-                    if (_indent_count >= 0) {
-                        result = parent.indent_cache[_indent_count];
-                    }
-                    result += _items.join('');
-                }
-                return result;
-            };
-        }
-
-        function Output(indent_string, baseIndentString) {
-            baseIndentString = baseIndentString || '';
-            this.indent_cache = [baseIndentString];
-            this.baseIndentLength = baseIndentString.length;
-            this.indent_length = indent_string.length;
-            this.raw = false;
-
-            var lines = [];
-            this.baseIndentString = baseIndentString;
-            this.indent_string = indent_string;
-            this.previous_line = null;
-            this.current_line = null;
-            this.space_before_token = false;
-
-            this.add_outputline = function() {
-                this.previous_line = this.current_line;
-                this.current_line = new OutputLine(this);
-                lines.push(this.current_line);
-            };
-
-            // initialize
-            this.add_outputline();
+  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.
+*/
 
+function Token(type, text, newlines, whitespace_before, parent) {
+    this.type = type;
+    this.text = text;
 
-            this.get_line_number = function() {
-                return lines.length;
-            };
+    // comments_before are
+    // comments that have a new line before them
+    // and may or may not have a newline after
+    // this is a set of comments before
+    this.comments_before = /* inline comment*/ [];
 
-            // Using object instead of string to allow for later expansion of info about each line
-            this.add_new_line = function(force_newline) {
-                if (this.get_line_number() === 1 && this.just_added_newline()) {
-                    return false; // no newline on start of file
-                }
 
-                if (force_newline || !this.just_added_newline()) {
-                    if (!this.raw) {
-                        this.add_outputline();
-                    }
-                    return true;
-                }
+    this.comments_after = []; // no new line before and newline after
+    this.newlines = newlines || 0;
+    this.wanted_newline = newlines > 0;
+    this.whitespace_before = whitespace_before || '';
+    this.parent = parent || null;
+    this.opened = null;
+    this.directives = null;
+}
 
-                return false;
-            };
-
-            this.get_code = function() {
-                var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, '');
-                return sweet_code;
-            };
-
-            this.set_indent = function(level) {
-                // Never indent your first output indent at the start of the file
-                if (lines.length > 1) {
-                    while (level >= this.indent_cache.length) {
-                        this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string);
-                    }
+module.exports.Token = Token;
 
-                    this.current_line.set_indent(level);
-                    return true;
-                }
-                this.current_line.set_indent(0);
-                return false;
-            };
 
-            this.add_raw_token = function(token) {
-                for (var x = 0; x < token.newlines; x++) {
-                    this.add_outputline();
-                }
-                this.current_line.push(token.whitespace_before);
-                this.current_line.push(token.text);
-                this.space_before_token = false;
-            };
-
-            this.add_token = function(printable_token) {
-                this.add_space_before_token();
-                this.current_line.push(printable_token);
-            };
-
-            this.add_space_before_token = function() {
-                if (this.space_before_token && !this.just_added_newline()) {
-                    this.current_line.push(' ');
-                }
-                this.space_before_token = false;
-            };
-
-            this.remove_redundant_indentation = function(frame) {
-                // This implementation is effective but has some issues:
-                //     - can cause line wrap to happen too soon due to indent removal
-                //           after wrap points are calculated
-                // These issues are minor compared to ugly indentation.
-
-                if (frame.multiline_frame ||
-                    frame.mode === MODE.ForInitializer ||
-                    frame.mode === MODE.Conditional) {
-                    return;
-                }
+/***/ }),
+/* 6 */
+/***/ (function(module, exports, __webpack_require__) {
 
-                // remove one indent from each line inside this section
-                var index = frame.start_line_index;
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
 
-                var output_length = lines.length;
-                while (index < output_length) {
-                    lines[index].remove_indent();
-                    index++;
-                }
-            };
+    The MIT License (MIT)
+
+    Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+*/
 
-            this.trim = function(eat_newlines) {
-                eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;
+var Beautifier = __webpack_require__(1).Beautifier;
 
-                this.current_line.trim(indent_string, baseIndentString);
+function js_beautify(js_source_text, options) {
+    var beautifier = new Beautifier(js_source_text, options);
+    return beautifier.beautify();
+}
 
-                while (eat_newlines && lines.length > 1 &&
-                    this.current_line.is_empty()) {
-                    lines.pop();
-                    this.current_line = lines[lines.length - 1];
-                    this.current_line.trim();
-                }
+module.exports = js_beautify;
 
-                this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null;
-            };
+/***/ }),
+/* 7 */
+/***/ (function(module, exports, __webpack_require__) {
 
-            this.just_added_newline = function() {
-                return this.current_line.is_empty();
-            };
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
 
-            this.just_added_blankline = function() {
-                if (this.just_added_newline()) {
-                    if (lines.length === 1) {
-                        return true; // start of the file and newline = blank
-                    }
+    The MIT License (MIT)
+
+    Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+*/
 
-                    var line = lines[lines.length - 2];
-                    return line.is_empty();
-                }
-                return false;
-            };
+var InputScanner = __webpack_require__(2).InputScanner;
+var Token = __webpack_require__(5).Token;
+var acorn = __webpack_require__(0);
+
+function trim(s) {
+    return s.replace(/^\s+|\s+$/g, '');
+}
+
+function in_array(what, arr) {
+    for (var i = 0; i < arr.length; i += 1) {
+        if (arr[i] === what) {
+            return true;
         }
+    }
+    return false;
+}
 
-        var InputScanner = function(input) {
-            var _input = input;
-            var _input_length = _input.length;
-            var _position = 0;
+function Tokenizer(input_string, opts) {
 
-            this.back = function() {
-                _position -= 1;
-            };
+    var whitespace = "\n\r\t ".split('');
+    var digit = /[0-9]/;
+    var digit_bin = /[01]/;
+    var digit_oct = /[01234567]/;
+    var digit_hex = /[0123456789abcdefABCDEF]/;
 
-            this.hasNext = function() {
-                return _position < _input_length;
-            };
+    this.positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ');
+    var punct = this.positionable_operators.concat(
+        // non-positionable operators - these do not follow operator position settings
+        '! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~ ...'.split(' '));
 
-            this.next = function() {
-                var val = null;
-                if (this.hasNext()) {
-                    val = _input.charAt(_position);
-                    _position += 1;
-                }
-                return val;
-            };
-
-            this.peek = function(index) {
-                var val = null;
-                index = index || 0;
-                index += _position;
-                if (index >= 0 && index < _input_length) {
-                    val = _input.charAt(index);
-                }
-                return val;
-            };
-
-            this.peekCharCode = function(index) {
-                var val = 0;
-                index = index || 0;
-                index += _position;
-                if (index >= 0 && index < _input_length) {
-                    val = _input.charCodeAt(index);
-                }
-                return val;
-            };
-
-            this.test = function(pattern, index) {
-                index = index || 0;
-                pattern.lastIndex = _position + index;
-                return pattern.test(_input);
-            };
-
-            this.testChar = function(pattern, index) {
-                var val = this.peek(index);
-                return val !== null && pattern.test(val);
-            };
-
-            this.match = function(pattern) {
-                pattern.lastIndex = _position;
-                var pattern_match = pattern.exec(_input);
-                if (pattern_match && pattern_match.index === _position) {
-                    _position += pattern_match[0].length;
-                } else {
-                    pattern_match = null;
-                }
-                return pattern_match;
-            };
-        };
+    // words which should always start on new line.
+    this.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',');
+    var reserved_words = this.line_starters.concat(['do', 'in', 'of', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as']);
 
-        var Token = function(type, text, newlines, whitespace_before, parent) {
-            this.type = type;
-            this.text = text;
+    //  /* ... */ comment ends with nearest */ or end of file
+    var block_comment_pattern = /([\s\S]*?)((?:\*\/)|$)/g;
 
-            // comments_before are
-            // comments that have a new line before them
-            // and may or may not have a newline after
-            // this is a set of comments before
-            this.comments_before = /* inline comment*/ [];
+    // comment ends just before nearest linefeed or end of file
+    var comment_pattern = /([^\n\r\u2028\u2029]*)/g;
 
+    var directives_block_pattern = /\/\* beautify( \w+[:]\w+)+ \*\//g;
+    var directive_pattern = / (\w+)[:](\w+)/g;
+    var directives_end_ignore_pattern = /([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)/g;
 
-            this.comments_after = []; // no new line before and newline after
-            this.newlines = newlines || 0;
-            this.wanted_newline = newlines > 0;
-            this.whitespace_before = whitespace_before || '';
-            this.parent = parent || null;
-            this.opened = null;
-            this.directives = null;
-        };
+    var template_pattern = /((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)/g;
 
-        function tokenizer(input_string, opts) {
+    var n_newlines, whitespace_before_token, in_html_comment, tokens;
+    var input;
 
-            var whitespace = "\n\r\t ".split('');
-            var digit = /[0-9]/;
-            var digit_bin = /[01]/;
-            var digit_oct = /[01234567]/;
-            var digit_hex = /[0123456789abcdefABCDEF]/;
+    this.tokenize = function() {
+        input = new InputScanner(input_string);
+        in_html_comment = false;
+        tokens = [];
 
-            this.positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ');
-            var punct = this.positionable_operators.concat(
-                // non-positionable operators - these do not follow operator position settings
-                '! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~ ...'.split(' '));
+        var next, last;
+        var token_values;
+        var open = null;
+        var open_stack = [];
+        var comments = [];
 
-            // words which should always start on new line.
-            this.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',');
-            var reserved_words = this.line_starters.concat(['do', 'in', 'of', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as']);
+        while (!(last && last.type === 'TK_EOF')) {
+            token_values = tokenize_next();
+            next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
+            while (next.type === 'TK_COMMENT' || next.type === 'TK_BLOCK_COMMENT' || next.type === 'TK_UNKNOWN') {
+                if (next.type === 'TK_BLOCK_COMMENT') {
+                    next.directives = token_values[2];
+                }
+                comments.push(next);
+                token_values = tokenize_next();
+                next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
+            }
 
-            //  /* ... */ comment ends with nearest */ or end of file
-            var block_comment_pattern = /([\s\S]*?)((?:\*\/)|$)/g;
+            if (comments.length) {
+                next.comments_before = comments;
+                comments = [];
+            }
 
-            // comment ends just before nearest linefeed or end of file
-            var comment_pattern = /([^\n\r\u2028\u2029]*)/g;
+            if (next.type === 'TK_START_BLOCK' || next.type === 'TK_START_EXPR') {
+                next.parent = last;
+                open_stack.push(open);
+                open = next;
+            } else if ((next.type === 'TK_END_BLOCK' || next.type === 'TK_END_EXPR') &&
+                (open && (
+                    (next.text === ']' && open.text === '[') ||
+                    (next.text === ')' && open.text === '(') ||
+                    (next.text === '}' && open.text === '{')))) {
+                next.parent = open.parent;
+                next.opened = open;
+
+                open = open_stack.pop();
+            }
 
-            var directives_block_pattern = /\/\* beautify( \w+[:]\w+)+ \*\//g;
-            var directive_pattern = / (\w+)[:](\w+)/g;
-            var directives_end_ignore_pattern = /([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)/g;
+            tokens.push(next);
+            last = next;
+        }
 
-            var template_pattern = /((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)/g;
+        return tokens;
+    };
 
-            var n_newlines, whitespace_before_token, in_html_comment, tokens;
-            var input;
+    function get_directives(text) {
+        if (!text.match(directives_block_pattern)) {
+            return null;
+        }
 
-            this.tokenize = function() {
-                input = new InputScanner(input_string);
-                in_html_comment = false;
-                tokens = [];
+        var directives = {};
+        directive_pattern.lastIndex = 0;
+        var directive_match = directive_pattern.exec(text);
 
-                var next, last;
-                var token_values;
-                var open = null;
-                var open_stack = [];
-                var comments = [];
+        while (directive_match) {
+            directives[directive_match[1]] = directive_match[2];
+            directive_match = directive_pattern.exec(text);
+        }
 
-                while (!(last && last.type === 'TK_EOF')) {
-                    token_values = tokenize_next();
-                    next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
-                    while (next.type === 'TK_COMMENT' || next.type === 'TK_BLOCK_COMMENT' || next.type === 'TK_UNKNOWN') {
-                        if (next.type === 'TK_BLOCK_COMMENT') {
-                            next.directives = token_values[2];
-                        }
-                        comments.push(next);
-                        token_values = tokenize_next();
-                        next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
-                    }
+        return directives;
+    }
 
-                    if (comments.length) {
-                        next.comments_before = comments;
-                        comments = [];
-                    }
+    function tokenize_next() {
+        var resulting_string;
+        var whitespace_on_this_line = [];
 
-                    if (next.type === 'TK_START_BLOCK' || next.type === 'TK_START_EXPR') {
-                        next.parent = last;
-                        open_stack.push(open);
-                        open = next;
-                    } else if ((next.type === 'TK_END_BLOCK' || next.type === 'TK_END_EXPR') &&
-                        (open && (
-                            (next.text === ']' && open.text === '[') ||
-                            (next.text === ')' && open.text === '(') ||
-                            (next.text === '}' && open.text === '{')))) {
-                        next.parent = open.parent;
-                        next.opened = open;
-
-                        open = open_stack.pop();
-                    }
+        n_newlines = 0;
+        whitespace_before_token = '';
 
-                    tokens.push(next);
-                    last = next;
-                }
+        var c = input.next();
 
-                return tokens;
-            };
+        if (c === null) {
+            return ['', 'TK_EOF'];
+        }
 
-            function get_directives(text) {
-                if (!text.match(directives_block_pattern)) {
-                    return null;
-                }
+        var last_token;
+        if (tokens.length) {
+            last_token = tokens[tokens.length - 1];
+        } else {
+            // For the sake of tokenizing we can pretend that there was on open brace to start
+            last_token = new Token('TK_START_BLOCK', '{');
+        }
 
-                var directives = {};
-                directive_pattern.lastIndex = 0;
-                var directive_match = directive_pattern.exec(text);
+        while (in_array(c, whitespace)) {
 
-                while (directive_match) {
-                    directives[directive_match[1]] = directive_match[2];
-                    directive_match = directive_pattern.exec(text);
+            if (acorn.newline.test(c)) {
+                if (!(c === '\n' && input.peek(-2) === '\r')) {
+                    n_newlines += 1;
+                    whitespace_on_this_line = [];
                 }
-
-                return directives;
+            } else {
+                whitespace_on_this_line.push(c);
             }
 
-            function tokenize_next() {
-                var resulting_string;
-                var whitespace_on_this_line = [];
-
-                n_newlines = 0;
-                whitespace_before_token = '';
+            c = input.next();
 
-                var c = input.next();
+            if (c === null) {
+                return ['', 'TK_EOF'];
+            }
+        }
 
-                if (c === null) {
-                    return ['', 'TK_EOF'];
-                }
+        if (whitespace_on_this_line.length) {
+            whitespace_before_token = whitespace_on_this_line.join('');
+        }
 
-                var last_token;
-                if (tokens.length) {
-                    last_token = tokens[tokens.length - 1];
+        if (digit.test(c) || (c === '.' && input.testChar(digit))) {
+            var allow_decimal = true;
+            var allow_e = true;
+            var local_digit = digit;
+
+            if (c === '0' && input.testChar(/[XxOoBb]/)) {
+                // switch to hex/oct/bin number, no decimal or e, just hex/oct/bin digits
+                allow_decimal = false;
+                allow_e = false;
+                if (input.testChar(/[Bb]/)) {
+                    local_digit = digit_bin;
+                } else if (input.testChar(/[Oo]/)) {
+                    local_digit = digit_oct;
                 } else {
-                    // For the sake of tokenizing we can pretend that there was on open brace to start
-                    last_token = new Token('TK_START_BLOCK', '{');
-                }
-
-                while (in_array(c, whitespace)) {
-
-                    if (acorn.newline.test(c)) {
-                        if (!(c === '\n' && input.peek(-2) === '\r')) {
-                            n_newlines += 1;
-                            whitespace_on_this_line = [];
-                        }
-                    } else {
-                        whitespace_on_this_line.push(c);
-                    }
+                    local_digit = digit_hex;
+                }
+                c += input.next();
+            } else if (c === '.') {
+                // Already have a decimal for this literal, don't allow another
+                allow_decimal = false;
+            } else {
+                // we know this first loop will run.  It keeps the logic simpler.
+                c = '';
+                input.back();
+            }
 
-                    c = input.next();
+            // Add the digits
+            while (input.testChar(local_digit)) {
+                c += input.next();
 
-                    if (c === null) {
-                        return ['', 'TK_EOF'];
-                    }
+                if (allow_decimal && input.peek() === '.') {
+                    c += input.next();
+                    allow_decimal = false;
                 }
 
-                if (whitespace_on_this_line.length) {
-                    whitespace_before_token = whitespace_on_this_line.join('');
-                }
+                // a = 1.e-7 is valid, so we test for . then e in one loop
+                if (allow_e && input.testChar(/[Ee]/)) {
+                    c += input.next();
 
-                if (digit.test(c) || (c === '.' && input.testChar(digit))) {
-                    var allow_decimal = true;
-                    var allow_e = true;
-                    var local_digit = digit;
-
-                    if (c === '0' && input.testChar(/[XxOoBb]/)) {
-                        // switch to hex/oct/bin number, no decimal or e, just hex/oct/bin digits
-                        allow_decimal = false;
-                        allow_e = false;
-                        if (input.testChar(/[Bb]/)) {
-                            local_digit = digit_bin;
-                        } else if (input.testChar(/[Oo]/)) {
-                            local_digit = digit_oct;
-                        } else {
-                            local_digit = digit_hex;
-                        }
+                    if (input.testChar(/[+-]/)) {
                         c += input.next();
-                    } else if (c === '.') {
-                        // Already have a decimal for this literal, don't allow another
-                        allow_decimal = false;
-                    } else {
-                        // we know this first loop will run.  It keeps the logic simpler.
-                        c = '';
-                        input.back();
                     }
 
-                    // Add the digits
-                    while (input.testChar(local_digit)) {
-                        c += input.next();
-
-                        if (allow_decimal && input.peek() === '.') {
-                            c += input.next();
-                            allow_decimal = false;
-                        }
-
-                        // a = 1.e-7 is valid, so we test for . then e in one loop
-                        if (allow_e && input.testChar(/[Ee]/)) {
-                            c += input.next();
+                    allow_e = false;
+                    allow_decimal = false;
+                }
+            }
 
-                            if (input.testChar(/[+-]/)) {
-                                c += input.next();
-                            }
+            return [c, 'TK_WORD'];
+        }
 
-                            allow_e = false;
-                            allow_decimal = false;
-                        }
+        if (acorn.isIdentifierStart(input.peekCharCode(-1))) {
+            if (input.hasNext()) {
+                while (acorn.isIdentifierChar(input.peekCharCode())) {
+                    c += input.next();
+                    if (!input.hasNext()) {
+                        break;
                     }
+                }
+            }
 
-                    return [c, 'TK_WORD'];
+            if (!(last_token.type === 'TK_DOT' ||
+                    (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get']))) &&
+                in_array(c, reserved_words)) {
+                if (c === 'in' || c === 'of') { // hack for 'in' and 'of' operators
+                    return [c, 'TK_OPERATOR'];
                 }
+                return [c, 'TK_RESERVED'];
+            }
 
-                if (acorn.isIdentifierStart(input.peekCharCode(-1))) {
-                    if (input.hasNext()) {
-                        while (acorn.isIdentifierChar(input.peekCharCode())) {
-                            c += input.next();
-                            if (!input.hasNext()) {
-                                break;
-                            }
-                        }
-                    }
+            return [c, 'TK_WORD'];
+        }
 
-                    if (!(last_token.type === 'TK_DOT' ||
-                            (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get']))) &&
-                        in_array(c, reserved_words)) {
-                        if (c === 'in' || c === 'of') { // hack for 'in' and 'of' operators
-                            return [c, 'TK_OPERATOR'];
-                        }
-                        return [c, 'TK_RESERVED'];
-                    }
+        if (c === '(' || c === '[') {
+            return [c, 'TK_START_EXPR'];
+        }
 
-                    return [c, 'TK_WORD'];
-                }
+        if (c === ')' || c === ']') {
+            return [c, 'TK_END_EXPR'];
+        }
 
-                if (c === '(' || c === '[') {
-                    return [c, 'TK_START_EXPR'];
-                }
+        if (c === '{') {
+            return [c, 'TK_START_BLOCK'];
+        }
 
-                if (c === ')' || c === ']') {
-                    return [c, 'TK_END_EXPR'];
-                }
+        if (c === '}') {
+            return [c, 'TK_END_BLOCK'];
+        }
 
-                if (c === '{') {
-                    return [c, 'TK_START_BLOCK'];
-                }
+        if (c === ';') {
+            return [c, 'TK_SEMICOLON'];
+        }
 
-                if (c === '}') {
-                    return [c, 'TK_END_BLOCK'];
-                }
+        if (c === '/') {
+            var comment = '';
+            var comment_match;
+            // peek for comment /* ... */
+            if (input.peek() === '*') {
+                input.next();
+                comment_match = input.match(block_comment_pattern);
+                comment = '/*' + comment_match[0];
+                var directives = get_directives(comment);
+                if (directives && directives.ignore === 'start') {
+                    comment_match = input.match(directives_end_ignore_pattern);
+                    comment += comment_match[0];
+                }
+                comment = comment.replace(acorn.allLineBreaks, '\n');
+                return [comment, 'TK_BLOCK_COMMENT', directives];
+            }
+            // peek for comment // ...
+            if (input.peek() === '/') {
+                input.next();
+                comment_match = input.match(comment_pattern);
+                comment = '//' + comment_match[0];
+                return [comment, 'TK_COMMENT'];
+            }
 
-                if (c === ';') {
-                    return [c, 'TK_SEMICOLON'];
-                }
+        }
 
-                if (c === '/') {
-                    var comment = '';
-                    var comment_match;
-                    // peek for comment /* ... */
-                    if (input.peek() === '*') {
-                        input.next();
-                        comment_match = input.match(block_comment_pattern);
-                        comment = '/*' + comment_match[0];
-                        var directives = get_directives(comment);
-                        if (directives && directives.ignore === 'start') {
-                            comment_match = input.match(directives_end_ignore_pattern);
-                            comment += comment_match[0];
+        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;
+
+        if (c === '`' || c === "'" || c === '"' || // string
+            (
+                (c === '/') || // regexp
+                (opts.e4x && c === "<" && input.test(startXmlRegExp, -1)) // xml
+            ) && ( // regex and xml can only appear in specific locations during parsing
+                (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) ||
+                (last_token.type === 'TK_END_EXPR' && last_token.text === ')' &&
+                    last_token.parent && last_token.parent.type === 'TK_RESERVED' && in_array(last_token.parent.text, ['if', 'while', 'for'])) ||
+                (in_array(last_token.type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK',
+                    'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'
+                ]))
+            )) {
+
+            var sep = c,
+                esc = false,
+                has_char_escapes = false;
+
+            resulting_string = c;
+
+            if (sep === '/') {
+                //
+                // handle regexp
+                //
+                var in_char_class = false;
+                while (input.hasNext() &&
+                    ((esc || in_char_class || input.peek() !== sep) &&
+                        !input.testChar(acorn.newline))) {
+                    resulting_string += input.peek();
+                    if (!esc) {
+                        esc = input.peek() === '\\';
+                        if (input.peek() === '[') {
+                            in_char_class = true;
+                        } else if (input.peek() === ']') {
+                            in_char_class = false;
                         }
-                        comment = comment.replace(acorn.allLineBreaks, '\n');
-                        return [comment, 'TK_BLOCK_COMMENT', directives];
-                    }
-                    // peek for comment // ...
-                    if (input.peek() === '/') {
-                        input.next();
-                        comment_match = input.match(comment_pattern);
-                        comment = '//' + comment_match[0];
-                        return [comment, 'TK_COMMENT'];
-                    }
-
-                }
-
-                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;
-
-                if (c === '`' || c === "'" || c === '"' || // string
-                    (
-                        (c === '/') || // regexp
-                        (opts.e4x && c === "<" && input.test(startXmlRegExp, -1)) // xml
-                    ) && ( // regex and xml can only appear in specific locations during parsing
-                        (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) ||
-                        (last_token.type === 'TK_END_EXPR' && last_token.text === ')' &&
-                            last_token.parent && last_token.parent.type === 'TK_RESERVED' && in_array(last_token.parent.text, ['if', 'while', 'for'])) ||
-                        (in_array(last_token.type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK',
-                            'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'
-                        ]))
-                    )) {
-
-                    var sep = c,
-                        esc = false,
-                        has_char_escapes = false;
-
-                    resulting_string = c;
-
-                    if (sep === '/') {
-                        //
-                        // handle regexp
-                        //
-                        var in_char_class = false;
-                        while (input.hasNext() &&
-                            ((esc || in_char_class || input.peek() !== sep) &&
-                                !input.testChar(acorn.newline))) {
-                            resulting_string += input.peek();
-                            if (!esc) {
-                                esc = input.peek() === '\\';
-                                if (input.peek() === '[') {
-                                    in_char_class = true;
-                                } else if (input.peek() === ']') {
-                                    in_char_class = false;
-                                }
+                    } else {
+                        esc = false;
+                    }
+                    input.next();
+                }
+            } else if (opts.e4x && sep === '<') {
+                //
+                // handle e4x xml literals
+                //
+
+                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;
+                input.back();
+                var xmlStr = '';
+                var match = input.match(startXmlRegExp);
+                if (match) {
+                    // Trim root tag to attempt to
+                    var rootTag = match[2].replace(/^{\s+/, '{').replace(/\s+}$/, '}');
+                    var isCurlyRoot = rootTag.indexOf('{') === 0;
+                    var depth = 0;
+                    while (match) {
+                        var isEndTag = !!match[1];
+                        var tagName = match[2];
+                        var isSingletonTag = (!!match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA[");
+                        if (!isSingletonTag &&
+                            (tagName === rootTag || (isCurlyRoot && tagName.replace(/^{\s+/, '{').replace(/\s+}$/, '}')))) {
+                            if (isEndTag) {
+                                --depth;
                             } else {
-                                esc = false;
+                                ++depth;
                             }
-                            input.next();
                         }
-                    } else if (opts.e4x && sep === '<') {
-                        //
-                        // handle e4x xml literals
-                        //
-
-                        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;
-                        input.back();
-                        var xmlStr = '';
-                        var match = input.match(startXmlRegExp);
-                        if (match) {
-                            // Trim root tag to attempt to
-                            var rootTag = match[2].replace(/^{\s+/, '{').replace(/\s+}$/, '}');
-                            var isCurlyRoot = rootTag.indexOf('{') === 0;
-                            var depth = 0;
-                            while (match) {
-                                var isEndTag = !!match[1];
-                                var tagName = match[2];
-                                var isSingletonTag = (!!match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA[");
-                                if (!isSingletonTag &&
-                                    (tagName === rootTag || (isCurlyRoot && tagName.replace(/^{\s+/, '{').replace(/\s+}$/, '}')))) {
-                                    if (isEndTag) {
-                                        --depth;
-                                    } else {
-                                        ++depth;
-                                    }
-                                }
-                                xmlStr += match[0];
-                                if (depth <= 0) {
-                                    break;
-                                }
-                                match = input.match(xmlRegExp);
-                            }
-                            // if we didn't close correctly, keep unformatted.
-                            if (!match) {
-                                xmlStr += input.match(/[\s\S]*/g)[0];
-                            }
-                            xmlStr = xmlStr.replace(acorn.allLineBreaks, '\n');
-                            return [xmlStr, "TK_STRING"];
+                        xmlStr += match[0];
+                        if (depth <= 0) {
+                            break;
+                        }
+                        match = input.match(xmlRegExp);
+                    }
+                    // if we didn't close correctly, keep unformatted.
+                    if (!match) {
+                        xmlStr += input.match(/[\s\S]*/g)[0];
+                    }
+                    xmlStr = xmlStr.replace(acorn.allLineBreaks, '\n');
+                    return [xmlStr, "TK_STRING"];
+                }
+            } else {
+                //
+                // handle string
+                //
+                var parse_string = function(delimiter, allow_unescaped_newlines, start_sub) {
+                    // Template strings can travers lines without escape characters.
+                    // Other strings cannot
+                    var current_char;
+                    while (input.hasNext()) {
+                        current_char = input.peek();
+                        if (!(esc || (current_char !== delimiter &&
+                                (allow_unescaped_newlines || !acorn.newline.test(current_char))))) {
+                            break;
                         }
-                    } else {
-                        //
-                        // handle string
-                        //
-                        var parse_string = function(delimiter, allow_unescaped_newlines, start_sub) {
-                            // Template strings can travers lines without escape characters.
-                            // Other strings cannot
-                            var current_char;
-                            while (input.hasNext()) {
-                                current_char = input.peek();
-                                if (!(esc || (current_char !== delimiter &&
-                                        (allow_unescaped_newlines || !acorn.newline.test(current_char))))) {
-                                    break;
-                                }
-
-                                // Handle \r\n linebreaks after escapes or in template strings
-                                if ((esc || allow_unescaped_newlines) && acorn.newline.test(current_char)) {
-                                    if (current_char === '\r' && input.peek(1) === '\n') {
-                                        input.next();
-                                        current_char = input.peek();
-                                    }
-                                    resulting_string += '\n';
-                                } else {
-                                    resulting_string += current_char;
-                                }
-
-                                if (esc) {
-                                    if (current_char === 'x' || current_char === 'u') {
-                                        has_char_escapes = true;
-                                    }
-                                    esc = false;
-                                } else {
-                                    esc = current_char === '\\';
-                                }
 
+                        // Handle \r\n linebreaks after escapes or in template strings
+                        if ((esc || allow_unescaped_newlines) && acorn.newline.test(current_char)) {
+                            if (current_char === '\r' && input.peek(1) === '\n') {
                                 input.next();
-
-                                if (start_sub && resulting_string.indexOf(start_sub, resulting_string.length - start_sub.length) !== -1) {
-                                    if (delimiter === '`') {
-                                        parse_string('}', allow_unescaped_newlines, '`');
-                                    } else {
-                                        parse_string('`', allow_unescaped_newlines, '${');
-                                    }
-
-                                    if (input.hasNext()) {
-                                        resulting_string += input.next();
-                                    }
-                                }
+                                current_char = input.peek();
                             }
-                        };
-
-                        if (sep === '`') {
-                            parse_string('`', true, '${');
+                            resulting_string += '\n';
                         } else {
-                            parse_string(sep);
+                            resulting_string += current_char;
                         }
-                    }
 
-                    if (has_char_escapes && opts.unescape_strings) {
-                        resulting_string = unescape_string(resulting_string);
-                    }
+                        if (esc) {
+                            if (current_char === 'x' || current_char === 'u') {
+                                has_char_escapes = true;
+                            }
+                            esc = false;
+                        } else {
+                            esc = current_char === '\\';
+                        }
 
-                    if (input.peek() === sep) {
-                        resulting_string += sep;
                         input.next();
 
-                        if (sep === '/') {
-                            // regexps may have modifiers /regexp/MOD , so fetch those, too
-                            // Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
-                            while (input.hasNext() && acorn.isIdentifierStart(input.peekCharCode())) {
+                        if (start_sub && resulting_string.indexOf(start_sub, resulting_string.length - start_sub.length) !== -1) {
+                            if (delimiter === '`') {
+                                parse_string('}', allow_unescaped_newlines, '`');
+                            } else {
+                                parse_string('`', allow_unescaped_newlines, '${');
+                            }
+
+                            if (input.hasNext()) {
                                 resulting_string += input.next();
                             }
                         }
                     }
-                    return [resulting_string, 'TK_STRING'];
-                }
-
-                if (c === '#') {
+                };
 
-                    if (tokens.length === 0 && input.peek() === '!') {
-                        // shebang
-                        resulting_string = c;
-                        while (input.hasNext() && c !== '\n') {
-                            c = input.next();
-                            resulting_string += c;
-                        }
-                        return [trim(resulting_string) + '\n', 'TK_UNKNOWN'];
-                    }
+                if (sep === '`') {
+                    parse_string('`', true, '${');
+                } else {
+                    parse_string(sep);
+                }
+            }
 
+            if (has_char_escapes && opts.unescape_strings) {
+                resulting_string = unescape_string(resulting_string);
+            }
 
+            if (input.peek() === sep) {
+                resulting_string += sep;
+                input.next();
 
-                    // Spidermonkey-specific sharp variables for circular references
-                    // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
-                    // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
-                    var sharp = '#';
-                    if (input.hasNext() && input.testChar(digit)) {
-                        do {
-                            c = input.next();
-                            sharp += c;
-                        } while (input.hasNext() && c !== '#' && c !== '=');
-                        if (c === '#') {
-                            //
-                        } else if (input.peek() === '[' && input.peek(1) === ']') {
-                            sharp += '[]';
-                            input.next();
-                            input.next();
-                        } else if (input.peek() === '{' && input.peek(1) === '}') {
-                            sharp += '{}';
-                            input.next();
-                            input.next();
-                        }
-                        return [sharp, 'TK_WORD'];
+                if (sep === '/') {
+                    // regexps may have modifiers /regexp/MOD , so fetch those, too
+                    // Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
+                    while (input.hasNext() && acorn.isIdentifierStart(input.peekCharCode())) {
+                        resulting_string += input.next();
                     }
                 }
+            }
+            return [resulting_string, 'TK_STRING'];
+        }
 
-                if (c === '<' && (input.peek() === '?' || input.peek() === '%')) {
-                    input.back();
-                    var template_match = input.match(template_pattern);
-                    if (template_match) {
-                        c = template_match[0];
-                        c = c.replace(acorn.allLineBreaks, '\n');
-                        return [c, 'TK_STRING'];
-                    }
-                }
+        if (c === '#') {
 
-                if (c === '<' && input.match(/\!--/g)) {
-                    c = '', 'TK_COMMENT'];
-                }
 
-                if (c === '.') {
-                    if (input.peek() === '.' && input.peek(1) === '.') {
-                        c += input.next() + input.next();
-                        return [c, 'TK_OPERATOR'];
-                    }
-                    return [c, 'TK_DOT'];
-                }
 
-                if (in_array(c, punct)) {
-                    while (input.hasNext() && in_array(c + input.peek(), punct)) {
-                        c += input.next();
-                        if (!input.hasNext()) {
-                            break;
-                        }
-                    }
+            // Spidermonkey-specific sharp variables for circular references
+            // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
+            // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
+            var sharp = '#';
+            if (input.hasNext() && input.testChar(digit)) {
+                do {
+                    c = input.next();
+                    sharp += c;
+                } while (input.hasNext() && c !== '#' && c !== '=');
+                if (c === '#') {
+                    //
+                } else if (input.peek() === '[' && input.peek(1) === ']') {
+                    sharp += '[]';
+                    input.next();
+                    input.next();
+                } else if (input.peek() === '{' && input.peek(1) === '}') {
+                    sharp += '{}';
+                    input.next();
+                    input.next();
+                }
+                return [sharp, 'TK_WORD'];
+            }
+        }
 
-                    if (c === ',') {
-                        return [c, 'TK_COMMA'];
-                    } else if (c === '=') {
-                        return [c, 'TK_EQUALS'];
-                    } else {
-                        return [c, 'TK_OPERATOR'];
-                    }
-                }
+        if (c === '<' && (input.peek() === '?' || input.peek() === '%')) {
+            input.back();
+            var template_match = input.match(template_pattern);
+            if (template_match) {
+                c = template_match[0];
+                c = c.replace(acorn.allLineBreaks, '\n');
+                return [c, 'TK_STRING'];
+            }
+        }
 
-                return [c, 'TK_UNKNOWN'];
+        if (c === '<' && input.match(/\!--/g)) {
+            c = '', 'TK_COMMENT'];
+        }
 
-            function unescape_string(s) {
-                // You think that a regex would work for this
-                // return s.replace(/\\x([0-9a-f]{2})/gi, function(match, val) {
-                //         return String.fromCharCode(parseInt(val, 16));
-                //     })
-                // However, dealing with '\xff', '\\xff', '\\\xff' makes this more fun.
-                var out = '',
-                    escaped = 0;
+        if (c === '.') {
+            if (input.peek() === '.' && input.peek(1) === '.') {
+                c += input.next() + input.next();
+                return [c, 'TK_OPERATOR'];
+            }
+            return [c, 'TK_DOT'];
+        }
 
-                var input_scan = new InputScanner(s);
-                var matched = null;
+        if (in_array(c, punct)) {
+            while (input.hasNext() && in_array(c + input.peek(), punct)) {
+                c += input.next();
+                if (!input.hasNext()) {
+                    break;
+                }
+            }
 
-                while (input_scan.hasNext()) {
-                    // Keep any whitespace, non-slash characters
-                    // also keep slash pairs.
-                    matched = input_scan.match(/([\s]|[^\\]|\\\\)+/g);
+            if (c === ',') {
+                return [c, 'TK_COMMA'];
+            } else if (c === '=') {
+                return [c, 'TK_EQUALS'];
+            } else {
+                return [c, 'TK_OPERATOR'];
+            }
+        }
 
-                    if (matched) {
-                        out += matched[0];
-                    }
+        return [c, 'TK_UNKNOWN'];
+    }
 
-                    if (input_scan.peek() === '\\') {
-                        input_scan.next();
-                        if (input_scan.peek() === 'x') {
-                            matched = input_scan.match(/x([0-9A-Fa-f]{2})/g);
-                        } else if (input_scan.peek() === 'u') {
-                            matched = input_scan.match(/u([0-9A-Fa-f]{4})/g);
-                        } else {
-                            out += '\\';
-                            if (input_scan.hasNext()) {
-                                out += input_scan.next();
-                            }
-                            continue;
-                        }
 
-                        // If there's some error decoding, return the original string
-                        if (!matched) {
-                            return s;
-                        }
+    function unescape_string(s) {
+        // You think that a regex would work for this
+        // return s.replace(/\\x([0-9a-f]{2})/gi, function(match, val) {
+        //         return String.fromCharCode(parseInt(val, 16));
+        //     })
+        // However, dealing with '\xff', '\\xff', '\\\xff' makes this more fun.
+        var out = '',
+            escaped = 0;
 
-                        escaped = parseInt(matched[1], 16);
-
-                        if (escaped > 0x7e && escaped <= 0xff && matched[0].indexOf('x') === 0) {
-                            // we bail out on \x7f..\xff,
-                            // leaving whole string escaped,
-                            // as it's probably completely binary
-                            return s;
-                        } else if (escaped >= 0x00 && escaped < 0x20) {
-                            // leave 0x00...0x1f escaped
-                            out += '\\' + matched[0];
-                            continue;
-                        } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) {
-                            // single-quote, apostrophe, backslash - escape these
-                            out += '\\' + String.fromCharCode(escaped);
-                        } else {
-                            out += String.fromCharCode(escaped);
-                        }
+        var input_scan = new InputScanner(s);
+        var matched = null;
+
+        while (input_scan.hasNext()) {
+            // Keep any whitespace, non-slash characters
+            // also keep slash pairs.
+            matched = input_scan.match(/([\s]|[^\\]|\\\\)+/g);
+
+            if (matched) {
+                out += matched[0];
+            }
+
+            if (input_scan.peek() === '\\') {
+                input_scan.next();
+                if (input_scan.peek() === 'x') {
+                    matched = input_scan.match(/x([0-9A-Fa-f]{2})/g);
+                } else if (input_scan.peek() === 'u') {
+                    matched = input_scan.match(/u([0-9A-Fa-f]{4})/g);
+                } else {
+                    out += '\\';
+                    if (input_scan.hasNext()) {
+                        out += input_scan.next();
                     }
+                    continue;
+                }
+
+                // If there's some error decoding, return the original string
+                if (!matched) {
+                    return s;
                 }
 
-                return out;
+                escaped = parseInt(matched[1], 16);
+
+                if (escaped > 0x7e && escaped <= 0xff && matched[0].indexOf('x') === 0) {
+                    // we bail out on \x7f..\xff,
+                    // leaving whole string escaped,
+                    // as it's probably completely binary
+                    return s;
+                } else if (escaped >= 0x00 && escaped < 0x20) {
+                    // leave 0x00...0x1f escaped
+                    out += '\\' + matched[0];
+                    continue;
+                } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) {
+                    // single-quote, apostrophe, backslash - escape these
+                    out += '\\' + String.fromCharCode(escaped);
+                } else {
+                    out += String.fromCharCode(escaped);
+                }
             }
         }
 
-        var beautifier = new Beautifier(js_source_text, options);
-        return beautifier.beautify();
-
+        return out;
     }
+}
 
-    if (typeof define === "function" && define.amd) {
-        // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
-        define([], function() {
-            return { js_beautify: js_beautify };
-        });
-    } else if (typeof exports !== "undefined") {
-        // Add support for CommonJS. Just put this file somewhere on your require.paths
-        // and you will be able to `var js_beautify = require("beautify").js_beautify`.
-        exports.js_beautify = js_beautify;
-    } else if (typeof window !== "undefined") {
-        // If we're running a web page and don't have either of the above, add our one global
-        window.js_beautify = js_beautify;
-    } else if (typeof global !== "undefined") {
-        // If we don't even have window, try global.
-        global.js_beautify = js_beautify;
-    }
+module.exports.Tokenizer = Tokenizer;
+
+/***/ })
+/******/ ]);
+var js_beautify = legacy_beautify_js;
+/* Footer */
+if (typeof define === "function" && define.amd) {
+    // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
+    define([], function() {
+        return { js_beautify: js_beautify };
+    });
+} else if (typeof exports !== "undefined") {
+    // Add support for CommonJS. Just put this file somewhere on your require.paths
+    // and you will be able to `var js_beautify = require("beautify").js_beautify`.
+    exports.js_beautify = js_beautify;
+} else if (typeof window !== "undefined") {
+    // If we're running a web page and don't have either of the above, add our one global
+    window.js_beautify = js_beautify;
+} else if (typeof global !== "undefined") {
+    // If we don't even have window, try global.
+    global.js_beautify = js_beautify;
+}
 
-}());
\ No newline at end of file
+}());
diff --git a/js/src/core/acorn.js b/js/src/core/acorn.js
new file mode 100644
index 000000000..bbfc48cef
--- /dev/null
+++ b/js/src/core/acorn.js
@@ -0,0 +1,63 @@
+/* jshint curly: false */
+// This section of code is taken from acorn.
+//
+// Acorn was written by Marijn Haverbeke and released under an MIT
+// license. The Unicode regexps (for identifiers and whitespace) were
+// taken from [Esprima](http://esprima.org) by Ariya Hidayat.
+//
+// Git repositories for Acorn are available at
+//
+//     http://marijnhaverbeke.nl/git/acorn
+//     https://github.com/marijnh/acorn.git
+
+// ## Character categories
+
+// Big ugly regular expressions that match characters in the
+// whitespace, identifier, and identifier-start categories. These
+// are only applied when a character is found to actually have a
+// code point above 128.
+
+var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; // jshint ignore:line
+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";
+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";
+var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
+var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
+
+// Whether a single character denotes a newline.
+
+exports.newline = /[\n\r\u2028\u2029]/;
+
+// Matches a whole line break (where CRLF is considered a single
+// line break). Used to count lines.
+
+// in javascript, these two differ
+// in python they are the same, different methods are called on them
+exports.lineBreak = new RegExp('\r\n|' + exports.newline.source);
+exports.allLineBreaks = new RegExp(exports.lineBreak.source, 'g');
+
+
+// Test whether a given character code starts an identifier.
+
+exports.isIdentifierStart = function(code) {
+    // permit $ (36) and @ (64). @ is used in ES7 decorators.
+    if (code < 65) return code === 36 || code === 64;
+    // 65 through 91 are uppercase letters.
+    if (code < 91) return true;
+    // permit _ (95).
+    if (code < 97) return code === 95;
+    // 97 through 123 are lowercase letters.
+    if (code < 123) return true;
+    return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
+};
+
+// Test whether a given character is part of an identifier.
+
+exports.isIdentifierChar = function(code) {
+    if (code < 48) return code === 36;
+    if (code < 58) return true;
+    if (code < 65) return false;
+    if (code < 91) return true;
+    if (code < 97) return code === 95;
+    if (code < 123) return true;
+    return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
+};
diff --git a/js/src/core/inputscanner.js b/js/src/core/inputscanner.js
new file mode 100644
index 000000000..a1a733fe3
--- /dev/null
+++ b/js/src/core/inputscanner.js
@@ -0,0 +1,95 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+*/
+
+function InputScanner(input) {
+    var _input = input;
+    var _input_length = _input.length;
+    var _position = 0;
+
+    this.back = function() {
+        _position -= 1;
+    };
+
+    this.hasNext = function() {
+        return _position < _input_length;
+    };
+
+    this.next = function() {
+        var val = null;
+        if (this.hasNext()) {
+            val = _input.charAt(_position);
+            _position += 1;
+        }
+        return val;
+    };
+
+    this.peek = function(index) {
+        var val = null;
+        index = index || 0;
+        index += _position;
+        if (index >= 0 && index < _input_length) {
+            val = _input.charAt(index);
+        }
+        return val;
+    };
+
+    this.peekCharCode = function(index) {
+        var val = 0;
+        index = index || 0;
+        index += _position;
+        if (index >= 0 && index < _input_length) {
+            val = _input.charCodeAt(index);
+        }
+        return val;
+    };
+
+    this.test = function(pattern, index) {
+        index = index || 0;
+        pattern.lastIndex = _position + index;
+        return pattern.test(_input);
+    };
+
+    this.testChar = function(pattern, index) {
+        var val = this.peek(index);
+        return val !== null && pattern.test(val);
+    };
+
+    this.match = function(pattern) {
+        pattern.lastIndex = _position;
+        var pattern_match = pattern.exec(_input);
+        if (pattern_match && pattern_match.index === _position) {
+            _position += pattern_match[0].length;
+        } else {
+            pattern_match = null;
+        }
+        return pattern_match;
+    };
+}
+
+
+module.exports.InputScanner = InputScanner;
diff --git a/js/src/core/options.js b/js/src/core/options.js
new file mode 100644
index 000000000..d3473cdea
--- /dev/null
+++ b/js/src/core/options.js
@@ -0,0 +1,48 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+    The MIT License (MIT)
+
+    Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+*/
+
+function mergeOpts(allOptions, targetType) {
+    var finalOpts = {};
+    var name;
+
+    for (name in allOptions) {
+        if (name !== targetType) {
+            finalOpts[name] = allOptions[name];
+        }
+    }
+
+    //merge in the per type settings for the targetType
+    if (targetType in allOptions) {
+        for (name in allOptions[targetType]) {
+            finalOpts[name] = allOptions[targetType][name];
+        }
+    }
+    return finalOpts;
+}
+
+module.exports.mergeOpts = mergeOpts;
diff --git a/js/src/core/output.js b/js/src/core/output.js
new file mode 100644
index 000000000..fc191af48
--- /dev/null
+++ b/js/src/core/output.js
@@ -0,0 +1,234 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+*/
+
+function OutputLine(parent) {
+    var _character_count = 0;
+    // use indent_count as a marker for lines that have preserved indentation
+    var _indent_count = -1;
+
+    var _items = [];
+    var _empty = true;
+
+    this.set_indent = function(level) {
+        _character_count = parent.baseIndentLength + level * parent.indent_length;
+        _indent_count = level;
+    };
+
+    this.get_character_count = function() {
+        return _character_count;
+    };
+
+    this.is_empty = function() {
+        return _empty;
+    };
+
+    this.last = function() {
+        if (!this._empty) {
+            return _items[_items.length - 1];
+        } else {
+            return null;
+        }
+    };
+
+    this.push = function(input) {
+        _items.push(input);
+        _character_count += input.length;
+        _empty = false;
+    };
+
+    this.pop = function() {
+        var item = null;
+        if (!_empty) {
+            item = _items.pop();
+            _character_count -= item.length;
+            _empty = _items.length === 0;
+        }
+        return item;
+    };
+
+    this.remove_indent = function() {
+        if (_indent_count > 0) {
+            _indent_count -= 1;
+            _character_count -= parent.indent_length;
+        }
+    };
+
+    this.trim = function() {
+        while (this.last() === ' ') {
+            _items.pop();
+            _character_count -= 1;
+        }
+        _empty = _items.length === 0;
+    };
+
+    this.toString = function() {
+        var result = '';
+        if (!this._empty) {
+            if (_indent_count >= 0) {
+                result = parent.indent_cache[_indent_count];
+            }
+            result += _items.join('');
+        }
+        return result;
+    };
+}
+
+function Output(indent_string, baseIndentString) {
+    baseIndentString = baseIndentString || '';
+    this.indent_cache = [baseIndentString];
+    this.baseIndentLength = baseIndentString.length;
+    this.indent_length = indent_string.length;
+    this.raw = false;
+
+    var lines = [];
+    this.baseIndentString = baseIndentString;
+    this.indent_string = indent_string;
+    this.previous_line = null;
+    this.current_line = null;
+    this.space_before_token = false;
+
+    this.add_outputline = function() {
+        this.previous_line = this.current_line;
+        this.current_line = new OutputLine(this);
+        lines.push(this.current_line);
+    };
+
+    // initialize
+    this.add_outputline();
+
+
+    this.get_line_number = function() {
+        return lines.length;
+    };
+
+    // Using object instead of string to allow for later expansion of info about each line
+    this.add_new_line = function(force_newline) {
+        if (this.get_line_number() === 1 && this.just_added_newline()) {
+            return false; // no newline on start of file
+        }
+
+        if (force_newline || !this.just_added_newline()) {
+            if (!this.raw) {
+                this.add_outputline();
+            }
+            return true;
+        }
+
+        return false;
+    };
+
+    this.get_code = function(end_with_newline, eol) {
+        var sweet_code = lines.join('\n').replace(/[\r\n\t ]+$/, '');
+
+        if (end_with_newline) {
+            sweet_code += '\n';
+        }
+
+        if (eol !== '\n') {
+            sweet_code = sweet_code.replace(/[\n]/g, eol);
+        }
+
+        return sweet_code;
+    };
+
+    this.set_indent = function(level) {
+        // Never indent your first output indent at the start of the file
+        if (lines.length > 1) {
+            while (level >= this.indent_cache.length) {
+                this.indent_cache.push(this.indent_cache[this.indent_cache.length - 1] + this.indent_string);
+            }
+
+            this.current_line.set_indent(level);
+            return true;
+        }
+        this.current_line.set_indent(0);
+        return false;
+    };
+
+    this.add_raw_token = function(token) {
+        for (var x = 0; x < token.newlines; x++) {
+            this.add_outputline();
+        }
+        this.current_line.push(token.whitespace_before);
+        this.current_line.push(token.text);
+        this.space_before_token = false;
+    };
+
+    this.add_token = function(printable_token) {
+        this.add_space_before_token();
+        this.current_line.push(printable_token);
+    };
+
+    this.add_space_before_token = function() {
+        if (this.space_before_token && !this.just_added_newline()) {
+            this.current_line.push(' ');
+        }
+        this.space_before_token = false;
+    };
+
+    this.remove_indent = function(index) {
+        var output_length = lines.length;
+        while (index < output_length) {
+            lines[index].remove_indent();
+            index++;
+        }
+    };
+
+    this.trim = function(eat_newlines) {
+        eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;
+
+        this.current_line.trim(indent_string, baseIndentString);
+
+        while (eat_newlines && lines.length > 1 &&
+            this.current_line.is_empty()) {
+            lines.pop();
+            this.current_line = lines[lines.length - 1];
+            this.current_line.trim();
+        }
+
+        this.previous_line = lines.length > 1 ? lines[lines.length - 2] : null;
+    };
+
+    this.just_added_newline = function() {
+        return this.current_line.is_empty();
+    };
+
+    this.just_added_blankline = function() {
+        if (this.just_added_newline()) {
+            if (lines.length === 1) {
+                return true; // start of the file and newline = blank
+            }
+
+            var line = lines[lines.length - 2];
+            return line.is_empty();
+        }
+        return false;
+    };
+}
+
+module.exports.Output = Output;
diff --git a/js/src/core/token.js b/js/src/core/token.js
new file mode 100644
index 000000000..188c48d45
--- /dev/null
+++ b/js/src/core/token.js
@@ -0,0 +1,49 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+*/
+
+function Token(type, text, newlines, whitespace_before, parent) {
+    this.type = type;
+    this.text = text;
+
+    // comments_before are
+    // comments that have a new line before them
+    // and may or may not have a newline after
+    // this is a set of comments before
+    this.comments_before = /* inline comment*/ [];
+
+
+    this.comments_after = []; // no new line before and newline after
+    this.newlines = newlines || 0;
+    this.wanted_newline = newlines > 0;
+    this.whitespace_before = whitespace_before || '';
+    this.parent = parent || null;
+    this.opened = null;
+    this.directives = null;
+}
+
+module.exports.Token = Token;
diff --git a/js/src/css/beautifier.js b/js/src/css/beautifier.js
new file mode 100644
index 000000000..25af557ee
--- /dev/null
+++ b/js/src/css/beautifier.js
@@ -0,0 +1,477 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+*/
+
+var mergeOpts = require('core/options').mergeOpts;
+var acorn = require('core/acorn');
+var Output = require('core/output').Output;
+
+
+var lineBreak = acorn.lineBreak;
+var allLineBreaks = acorn.allLineBreaks;
+
+function Beautifier(source_text, options) {
+    options = options || {};
+
+    // Allow the setting of language/file-type specific options
+    // with inheritance of overall settings
+    options = mergeOpts(options, 'css');
+
+    source_text = source_text || '';
+
+    var newlinesFromLastWSEat = 0;
+    var indentSize = options.indent_size ? parseInt(options.indent_size, 10) : 4;
+    var indentCharacter = options.indent_char || ' ';
+    var preserve_newlines = (options.preserve_newlines === undefined) ? false : options.preserve_newlines;
+    var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
+    var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
+    var newline_between_rules = (options.newline_between_rules === undefined) ? true : options.newline_between_rules;
+    var space_around_combinator = (options.space_around_combinator === undefined) ? false : options.space_around_combinator;
+    space_around_combinator = space_around_combinator || ((options.space_around_selector_separator === undefined) ? false : options.space_around_selector_separator);
+    var eol = options.eol ? options.eol : 'auto';
+
+    if (options.indent_with_tabs) {
+        indentCharacter = '\t';
+        indentSize = 1;
+    }
+
+    if (eol === 'auto') {
+        eol = '\n';
+        if (source_text && lineBreak.test(source_text || '')) {
+            eol = source_text.match(lineBreak)[0];
+        }
+    }
+
+    eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n');
+
+    // HACK: newline parsing inconsistent. This brute force normalizes the input.
+    source_text = source_text.replace(allLineBreaks, '\n');
+
+    // tokenizer
+    var whiteRe = /^\s+$/;
+
+    var pos = -1,
+        ch;
+    var parenLevel = 0;
+
+    function next() {
+        ch = source_text.charAt(++pos);
+        return ch || '';
+    }
+
+    function peek(skipWhitespace) {
+        var result = '';
+        var prev_pos = pos;
+        if (skipWhitespace) {
+            eatWhitespace();
+        }
+        result = source_text.charAt(pos + 1) || '';
+        pos = prev_pos - 1;
+        next();
+        return result;
+    }
+
+    function eatString(endChars) {
+        var start = pos;
+        while (next()) {
+            if (ch === "\\") {
+                next();
+            } else if (endChars.indexOf(ch) !== -1) {
+                break;
+            } else if (ch === "\n") {
+                break;
+            }
+        }
+        return source_text.substring(start, pos + 1);
+    }
+
+    function peekString(endChar) {
+        var prev_pos = pos;
+        var str = eatString(endChar);
+        pos = prev_pos - 1;
+        next();
+        return str;
+    }
+
+    function eatWhitespace(preserve_newlines_local) {
+        var result = 0;
+        while (whiteRe.test(peek())) {
+            next();
+            if (ch === '\n' && preserve_newlines_local && preserve_newlines) {
+                output.add_new_line(true);
+                result++;
+            }
+        }
+        newlinesFromLastWSEat = result;
+        return result;
+    }
+
+    function skipWhitespace() {
+        var result = '';
+        if (ch && whiteRe.test(ch)) {
+            result = ch;
+        }
+        while (whiteRe.test(next())) {
+            result += ch;
+        }
+        return result;
+    }
+
+    function eatComment() {
+        var start = pos;
+        var singleLine = peek() === "/";
+        next();
+        while (next()) {
+            if (!singleLine && ch === "*" && peek() === "/") {
+                next();
+                break;
+            } else if (singleLine && ch === "\n") {
+                return source_text.substring(start, pos);
+            }
+        }
+
+        return source_text.substring(start, pos) + ch;
+    }
+
+
+    function lookBack(str) {
+        return source_text.substring(pos - str.length, pos).toLowerCase() ===
+            str;
+    }
+
+    // Nested pseudo-class if we are insideRule
+    // and the next special character found opens
+    // a new block
+    function foundNestedPseudoClass() {
+        var openParen = 0;
+        for (var i = pos + 1; i < source_text.length; i++) {
+            var ch = source_text.charAt(i);
+            if (ch === "{") {
+                return true;
+            } else if (ch === '(') {
+                // pseudoclasses can contain ()
+                openParen += 1;
+            } else if (ch === ')') {
+                if (openParen === 0) {
+                    return false;
+                }
+                openParen -= 1;
+            } else if (ch === ";" || ch === "}") {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    // printer
+    var baseIndentString = '';
+    var preindent_index = 0;
+    if (source_text && source_text.length) {
+        while ((source_text.charAt(preindent_index) === ' ' ||
+                source_text.charAt(preindent_index) === '\t')) {
+            preindent_index += 1;
+        }
+        baseIndentString = source_text.substring(0, preindent_index);
+        js_source_text = source_text.substring(preindent_index);
+    }
+
+
+    var singleIndent = new Array(indentSize + 1).join(indentCharacter);
+    var indentLevel;
+    var nestedLevel;
+    var output;
+
+    function print_string(output_string) {
+        if (output.just_added_newline()) {
+            output.set_indent(indentLevel);
+        }
+        output.add_token(output_string);
+    }
+
+    function preserveSingleSpace(isAfterSpace) {
+        if (isAfterSpace) {
+            output.space_before_token = true;
+        }
+    }
+
+    function indent() {
+        indentLevel++;
+    }
+
+    function outdent() {
+        if (indentLevel > 0) {
+            indentLevel--;
+        }
+    }
+
+    /*_____________________--------------------_____________________*/
+
+    this.beautify = function() {
+        // reset
+        output = new Output(singleIndent, baseIndentString);
+        indentLevel = 0;
+        nestedLevel = 0;
+
+        pos = -1;
+        ch = null;
+        parenLevel = 0;
+
+        var insideRule = false;
+        var insidePropertyValue = false;
+        var enteringConditionalGroup = false;
+        var top_ch = '';
+        var last_top_ch = '';
+
+        while (true) {
+            var whitespace = skipWhitespace();
+            var isAfterSpace = whitespace !== '';
+            var isAfterNewline = whitespace.indexOf('\n') !== -1;
+            last_top_ch = top_ch;
+            top_ch = ch;
+
+            if (!ch) {
+                break;
+            } else if (ch === '/' && peek() === '*') { /* css comment */
+                var header = indentLevel === 0;
+
+                if (isAfterNewline || header) {
+                    output.add_new_line();
+                }
+
+                print_string(eatComment());
+                output.add_new_line();
+                if (header) {
+                    output.add_new_line(true);
+                }
+            } else if (ch === '/' && peek() === '/') { // single line comment
+                if (!isAfterNewline && last_top_ch !== '{') {
+                    output.trim(true);
+                }
+                output.space_before_token = true;
+                print_string(eatComment());
+                output.add_new_line();
+            } else if (ch === '@') {
+                preserveSingleSpace(isAfterSpace);
+
+                // deal with less propery mixins @{...}
+                if (peek() === '{') {
+                    print_string(eatString('}'));
+                } else {
+                    print_string(ch);
+
+                    // strip trailing space, if present, for hash property checks
+                    var variableOrRule = peekString(": ,;{}()[]/='\"");
+
+                    if (variableOrRule.match(/[ :]$/)) {
+                        // we have a variable or pseudo-class, add it and insert one space before continuing
+                        next();
+                        variableOrRule = eatString(": ").replace(/\s$/, '');
+                        print_string(variableOrRule);
+                        output.space_before_token = true;
+                    }
+
+                    variableOrRule = variableOrRule.replace(/\s$/, '');
+
+                    // might be a nesting at-rule
+                    if (variableOrRule in this.NESTED_AT_RULE) {
+                        nestedLevel += 1;
+                        if (variableOrRule in this.CONDITIONAL_GROUP_RULE) {
+                            enteringConditionalGroup = true;
+                        }
+                    }
+                }
+            } else if (ch === '#' && peek() === '{') {
+                preserveSingleSpace(isAfterSpace);
+                print_string(eatString('}'));
+            } else if (ch === '{') {
+                if (peek(true) === '}') {
+                    eatWhitespace();
+                    next();
+                    output.space_before_token = true;
+                    print_string("{}");
+                    if (!eatWhitespace(true)) {
+                        output.add_new_line();
+                    }
+
+                    if (newlinesFromLastWSEat < 2 && newline_between_rules && indentLevel === 0) {
+                        output.add_new_line(true);
+                    }
+                } else {
+                    indent();
+                    output.space_before_token = true;
+                    print_string(ch);
+                    if (!eatWhitespace(true)) {
+                        output.add_new_line();
+                    }
+
+                    // when entering conditional groups, only rulesets are allowed
+                    if (enteringConditionalGroup) {
+                        enteringConditionalGroup = false;
+                        insideRule = (indentLevel > nestedLevel);
+                    } else {
+                        // otherwise, declarations are also allowed
+                        insideRule = (indentLevel >= nestedLevel);
+                    }
+                }
+            } else if (ch === '}') {
+                outdent();
+                output.add_new_line();
+                print_string(ch);
+                insideRule = false;
+                insidePropertyValue = false;
+                if (nestedLevel) {
+                    nestedLevel--;
+                }
+
+                if (!eatWhitespace(true)) {
+                    output.add_new_line();
+                }
+
+                if (newlinesFromLastWSEat < 2 && newline_between_rules && indentLevel === 0) {
+                    output.add_new_line(true);
+                }
+            } else if (ch === ":") {
+                eatWhitespace();
+                if ((insideRule || enteringConditionalGroup) &&
+                    !(lookBack("&") || foundNestedPseudoClass()) &&
+                    !lookBack("(")) {
+                    // 'property: value' delimiter
+                    // which could be in a conditional group query
+                    print_string(':');
+                    if (!insidePropertyValue) {
+                        insidePropertyValue = true;
+                        output.space_before_token = true;
+                    }
+                } else {
+                    // sass/less parent reference don't use a space
+                    // sass nested pseudo-class don't use a space
+
+                    // preserve space before pseudoclasses/pseudoelements, as it means "in any child"
+                    if (lookBack(" ")) {
+                        output.space_before_token = true;
+                    }
+                    if (peek() === ":") {
+                        // pseudo-element
+                        next();
+                        print_string("::");
+                    } else {
+                        // pseudo-class
+                        print_string(':');
+                    }
+                }
+            } else if (ch === '"' || ch === '\'') {
+                preserveSingleSpace(isAfterSpace);
+                print_string(eatString(ch));
+            } else if (ch === ';') {
+                insidePropertyValue = false;
+                print_string(ch);
+                if (!eatWhitespace(true)) {
+                    output.add_new_line();
+                }
+            } else if (ch === '(') { // may be a url
+                if (lookBack("url")) {
+                    print_string(ch);
+                    eatWhitespace();
+                    if (next()) {
+                        if (ch !== ')' && ch !== '"' && ch !== '\'') {
+                            print_string(eatString(')'));
+                        } else {
+                            pos--;
+                        }
+                    }
+                } else {
+                    parenLevel++;
+                    preserveSingleSpace(isAfterSpace);
+                    print_string(ch);
+                    eatWhitespace();
+                }
+            } else if (ch === ')') {
+                print_string(ch);
+                parenLevel--;
+            } else if (ch === ',') {
+                print_string(ch);
+                if (!eatWhitespace(true) && selectorSeparatorNewline && !insidePropertyValue && parenLevel < 1) {
+                    output.add_new_line();
+                } else {
+                    output.space_before_token = true;
+                }
+            } else if ((ch === '>' || ch === '+' || ch === '~') &&
+                !insidePropertyValue && parenLevel < 1) {
+                //handle combinator spacing
+                if (space_around_combinator) {
+                    output.space_before_token = true;
+                    print_string(ch);
+                    output.space_before_token = true;
+                } else {
+                    print_string(ch);
+                    eatWhitespace();
+                    // squash extra whitespace
+                    if (ch && whiteRe.test(ch)) {
+                        ch = '';
+                    }
+                }
+            } else if (ch === ']') {
+                print_string(ch);
+            } else if (ch === '[') {
+                preserveSingleSpace(isAfterSpace);
+                print_string(ch);
+            } else if (ch === '=') { // no whitespace before or after
+                eatWhitespace();
+                print_string('=');
+                if (whiteRe.test(ch)) {
+                    ch = '';
+                }
+
+            } else {
+                preserveSingleSpace(isAfterSpace);
+                print_string(ch);
+            }
+        }
+
+        var sweetCode = output.get_code(end_with_newline, eol);
+
+        return sweetCode;
+    };
+
+    // https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
+    this.NESTED_AT_RULE = {
+        "@page": true,
+        "@font-face": true,
+        "@keyframes": true,
+        // also in CONDITIONAL_GROUP_RULE below
+        "@media": true,
+        "@supports": true,
+        "@document": true
+    };
+    this.CONDITIONAL_GROUP_RULE = {
+        "@media": true,
+        "@supports": true,
+        "@document": true
+    };
+}
+
+module.exports.Beautifier = Beautifier;
diff --git a/js/src/css/index.js b/js/src/css/index.js
new file mode 100644
index 000000000..fbf2dfad2
--- /dev/null
+++ b/js/src/css/index.js
@@ -0,0 +1,36 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+    The MIT License (MIT)
+
+    Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+*/
+
+var Beautifier = require('./beautifier').Beautifier;
+
+function css_beautify(source_text, options) {
+    var beautifier = new Beautifier(source_text, options);
+    return beautifier.beautify();
+}
+
+module.exports = css_beautify;
\ No newline at end of file
diff --git a/js/src/html/beautifier.js b/js/src/html/beautifier.js
new file mode 100644
index 000000000..61f5f5a68
--- /dev/null
+++ b/js/src/html/beautifier.js
@@ -0,0 +1,1035 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+*/
+
+var mergeOpts = require('core/options').mergeOpts;
+var acorn = require('core/acorn');
+
+
+var lineBreak = acorn.lineBreak;
+var allLineBreaks = acorn.allLineBreaks;
+
+// function trim(s) {
+//     return s.replace(/^\s+|\s+$/g, '');
+// }
+
+function ltrim(s) {
+    return s.replace(/^\s+/g, '');
+}
+
+function rtrim(s) {
+    return s.replace(/\s+$/g, '');
+}
+
+function Beautifier(html_source, options, js_beautify, css_beautify) {
+    //Wrapper function to invoke all the necessary constructors and deal with the output.
+    html_source = html_source || '';
+
+    var multi_parser,
+        indent_inner_html,
+        indent_body_inner_html,
+        indent_head_inner_html,
+        indent_size,
+        indent_character,
+        wrap_line_length,
+        brace_style,
+        unformatted,
+        content_unformatted,
+        preserve_newlines,
+        max_preserve_newlines,
+        indent_handlebars,
+        wrap_attributes,
+        wrap_attributes_indent_size,
+        is_wrap_attributes_force,
+        is_wrap_attributes_force_expand_multiline,
+        is_wrap_attributes_force_aligned,
+        end_with_newline,
+        extra_liners,
+        eol;
+
+    options = options || {};
+
+    // Allow the setting of language/file-type specific options
+    // with inheritance of overall settings
+    options = mergeOpts(options, 'html');
+
+    // backwards compatibility to 1.3.4
+    if ((options.wrap_line_length === undefined || parseInt(options.wrap_line_length, 10) === 0) &&
+        (options.max_char !== undefined && parseInt(options.max_char, 10) !== 0)) {
+        options.wrap_line_length = options.max_char;
+    }
+
+    indent_inner_html = (options.indent_inner_html === undefined) ? false : options.indent_inner_html;
+    indent_body_inner_html = (options.indent_body_inner_html === undefined) ? true : options.indent_body_inner_html;
+    indent_head_inner_html = (options.indent_head_inner_html === undefined) ? true : options.indent_head_inner_html;
+    indent_size = (options.indent_size === undefined) ? 4 : parseInt(options.indent_size, 10);
+    indent_character = (options.indent_char === undefined) ? ' ' : options.indent_char;
+    brace_style = (options.brace_style === undefined) ? 'collapse' : options.brace_style;
+    wrap_line_length = parseInt(options.wrap_line_length, 10) === 0 ? 32786 : parseInt(options.wrap_line_length || 250, 10);
+    unformatted = options.unformatted || [
+        // https://www.w3.org/TR/html5/dom.html#phrasing-content
+        'a', 'abbr', 'area', 'audio', 'b', 'bdi', 'bdo', 'br', 'button', 'canvas', 'cite',
+        'code', 'data', 'datalist', 'del', 'dfn', 'em', 'embed', 'i', 'iframe', 'img',
+        'input', 'ins', 'kbd', 'keygen', 'label', 'map', 'mark', 'math', 'meter', 'noscript',
+        'object', 'output', 'progress', 'q', 'ruby', 's', 'samp', /* 'script', */ 'select', 'small',
+        'span', 'strong', 'sub', 'sup', 'svg', 'template', 'textarea', 'time', 'u', 'var',
+        'video', 'wbr', 'text',
+        // prexisting - not sure of full effect of removing, leaving in
+        'acronym', 'address', 'big', 'dt', 'ins', 'strike', 'tt',
+    ];
+    content_unformatted = options.content_unformatted || [
+        'pre',
+    ];
+    preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
+    max_preserve_newlines = preserve_newlines ?
+        (isNaN(parseInt(options.max_preserve_newlines, 10)) ? 32786 : parseInt(options.max_preserve_newlines, 10)) :
+        0;
+    indent_handlebars = (options.indent_handlebars === undefined) ? false : options.indent_handlebars;
+    wrap_attributes = (options.wrap_attributes === undefined) ? 'auto' : options.wrap_attributes;
+    wrap_attributes_indent_size = (isNaN(parseInt(options.wrap_attributes_indent_size, 10))) ? indent_size : parseInt(options.wrap_attributes_indent_size, 10);
+    is_wrap_attributes_force = wrap_attributes.substr(0, 'force'.length) === 'force';
+    is_wrap_attributes_force_expand_multiline = (wrap_attributes === 'force-expand-multiline');
+    is_wrap_attributes_force_aligned = (wrap_attributes === 'force-aligned');
+    end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
+    extra_liners = (typeof options.extra_liners === 'object') && options.extra_liners ?
+        options.extra_liners.concat() : (typeof options.extra_liners === 'string') ?
+        options.extra_liners.split(',') : 'head,body,/html'.split(',');
+    eol = options.eol ? options.eol : 'auto';
+
+    if (options.indent_with_tabs) {
+        indent_character = '\t';
+        indent_size = 1;
+    }
+
+    if (eol === 'auto') {
+        eol = '\n';
+        if (html_source && lineBreak.test(html_source || '')) {
+            eol = html_source.match(lineBreak)[0];
+        }
+    }
+
+    eol = eol.replace(/\\r/, '\r').replace(/\\n/, '\n');
+
+    // HACK: newline parsing inconsistent. This brute force normalizes the input.
+    html_source = html_source.replace(allLineBreaks, '\n');
+
+    function Parser() {
+
+        this.pos = 0; //Parser position
+        this.token = '';
+        this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT
+        this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values
+            parent: 'parent1',
+            parentcount: 1,
+            parent1: ''
+        };
+        this.tag_type = '';
+        this.token_text = this.last_token = this.last_text = this.token_type = '';
+        this.newlines = 0;
+        this.indent_content = indent_inner_html;
+        this.indent_body_inner_html = indent_body_inner_html;
+        this.indent_head_inner_html = indent_head_inner_html;
+
+        this.Utils = { //Uilities made available to the various functions
+            whitespace: "\n\r\t ".split(''),
+
+            single_token: options.void_elements || [
+                // HTLM void elements - aka self-closing tags - aka singletons
+                // https://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
+                'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen',
+                'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr',
+                // NOTE: Optional tags - are not understood.
+                // https://www.w3.org/TR/html5/syntax.html#optional-tags
+                // The rules for optional tags are too complex for a simple list
+                // Also, the content of these tags should still be indented in many cases.
+                // 'li' is a good exmple.
+
+                // Doctype and xml elements
+                '!doctype', '?xml',
+                // ?php tag
+                '?php',
+                // other tags that were in this list, keeping just in case
+                'basefont', 'isindex'
+            ],
+            extra_liners: extra_liners, //for tags that need a line of whitespace before them
+            in_array: function(what, arr) {
+                for (var i = 0; i < arr.length; i++) {
+                    if (what === arr[i]) {
+                        return true;
+                    }
+                }
+                return false;
+            }
+        };
+
+        // Return true if the given text is composed entirely of whitespace.
+        this.is_whitespace = function(text) {
+            for (var n = 0; n < text.length; n++) {
+                if (!this.Utils.in_array(text.charAt(n), this.Utils.whitespace)) {
+                    return false;
+                }
+            }
+            return true;
+        };
+
+        this.traverse_whitespace = function() {
+            var input_char = '';
+
+            input_char = this.input.charAt(this.pos);
+            if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
+                this.newlines = 0;
+                while (this.Utils.in_array(input_char, this.Utils.whitespace)) {
+                    if (preserve_newlines && input_char === '\n' && this.newlines <= max_preserve_newlines) {
+                        this.newlines += 1;
+                    }
+
+                    this.pos++;
+                    input_char = this.input.charAt(this.pos);
+                }
+                return true;
+            }
+            return false;
+        };
+
+        // Append a space to the given content (string array) or, if we are
+        // at the wrap_line_length, append a newline/indentation.
+        // return true if a newline was added, false if a space was added
+        this.space_or_wrap = function(content) {
+            if (this.line_char_count >= this.wrap_line_length) { //insert a line when the wrap_line_length is reached
+                this.print_newline(false, content);
+                this.print_indentation(content);
+                return true;
+            } else {
+                this.line_char_count++;
+                content.push(' ');
+                return false;
+            }
+        };
+
+        this.get_content = function() { //function to capture regular content between tags
+            var input_char = '',
+                content = [],
+                handlebarsStarted = 0;
+
+            while (this.input.charAt(this.pos) !== '<' || handlebarsStarted === 2) {
+                if (this.pos >= this.input.length) {
+                    return content.length ? content.join('') : ['', 'TK_EOF'];
+                }
+
+                if (handlebarsStarted < 2 && this.traverse_whitespace()) {
+                    this.space_or_wrap(content);
+                    continue;
+                }
+
+                input_char = this.input.charAt(this.pos);
+
+                if (indent_handlebars) {
+                    if (input_char === '{') {
+                        handlebarsStarted += 1;
+                    } else if (handlebarsStarted < 2) {
+                        handlebarsStarted = 0;
+                    }
+
+                    if (input_char === '}' && handlebarsStarted > 0) {
+                        if (handlebarsStarted-- === 0) {
+                            break;
+                        }
+                    }
+                    // Handlebars parsing is complicated.
+                    // {{#foo}} and {{/foo}} are formatted tags.
+                    // {{something}} should get treated as content, except:
+                    // {{else}} specifically behaves like {{#if}} and {{/if}}
+                    var peek3 = this.input.substr(this.pos, 3);
+                    if (peek3 === '{{#' || peek3 === '{{/') {
+                        // These are tags and not content.
+                        break;
+                    } else if (peek3 === '{{!') {
+                        return [this.get_tag(), 'TK_TAG_HANDLEBARS_COMMENT'];
+                    } else if (this.input.substr(this.pos, 2) === '{{') {
+                        if (this.get_tag(true) === '{{else}}') {
+                            break;
+                        }
+                    }
+                }
+
+                this.pos++;
+                this.line_char_count++;
+                content.push(input_char); //letter at-a-time (or string) inserted to an array
+            }
+            return content.length ? content.join('') : '';
+        };
+
+        this.get_contents_to = function(name) { //get the full content of a script or style to pass to js_beautify
+            if (this.pos === this.input.length) {
+                return ['', 'TK_EOF'];
+            }
+            var content = '';
+            var reg_match = new RegExp('', 'igm');
+            reg_match.lastIndex = this.pos;
+            var reg_array = reg_match.exec(this.input);
+            var end_script = reg_array ? reg_array.index : this.input.length; //absolute end of script
+            if (this.pos < end_script) { //get everything in between the script tags
+                content = this.input.substring(this.pos, end_script);
+                this.pos = end_script;
+            }
+            return content;
+        };
+
+        this.record_tag = function(tag) { //function to record a tag and its parent in this.tags Object
+            if (this.tags[tag + 'count']) { //check for the existence of this tag type
+                this.tags[tag + 'count']++;
+                this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
+            } else { //otherwise initialize this tag type
+                this.tags[tag + 'count'] = 1;
+                this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level
+            }
+            this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent)
+            this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1')
+        };
+
+        this.retrieve_tag = function(tag) { //function to retrieve the opening tag to the corresponding closer
+            if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it
+                var temp_parent = this.tags.parent; //check to see if it's a closable tag.
+                while (temp_parent) { //till we reach '' (the initial value);
+                    if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it
+                        break;
+                    }
+                    temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree
+                }
+                if (temp_parent) { //if we caught something
+                    this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly
+                    this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent
+                }
+                delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference...
+                delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself
+                if (this.tags[tag + 'count'] === 1) {
+                    delete this.tags[tag + 'count'];
+                } else {
+                    this.tags[tag + 'count']--;
+                }
+            }
+        };
+
+        this.indent_to_tag = function(tag) {
+            // Match the indentation level to the last use of this tag, but don't remove it.
+            if (!this.tags[tag + 'count']) {
+                return;
+            }
+            var temp_parent = this.tags.parent;
+            while (temp_parent) {
+                if (tag + this.tags[tag + 'count'] === temp_parent) {
+                    break;
+                }
+                temp_parent = this.tags[temp_parent + 'parent'];
+            }
+            if (temp_parent) {
+                this.indent_level = this.tags[tag + this.tags[tag + 'count']];
+            }
+        };
+
+        this.get_tag = function(peek) { //function to get a full tag and parse its type
+            var input_char = '',
+                content = [],
+                comment = '',
+                space = false,
+                first_attr = true,
+                has_wrapped_attrs = false,
+                tag_start, tag_end,
+                tag_start_char,
+                orig_pos = this.pos,
+                orig_line_char_count = this.line_char_count,
+                is_tag_closed = false,
+                tail;
+
+            peek = peek !== undefined ? peek : false;
+
+            do {
+                if (this.pos >= this.input.length) {
+                    if (peek) {
+                        this.pos = orig_pos;
+                        this.line_char_count = orig_line_char_count;
+                    }
+                    return content.length ? content.join('') : ['', 'TK_EOF'];
+                }
+
+                input_char = this.input.charAt(this.pos);
+                this.pos++;
+
+                if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space
+                    space = true;
+                    continue;
+                }
+
+                if (input_char === "'" || input_char === '"') {
+                    input_char += this.get_unformatted(input_char);
+                    space = true;
+                }
+
+                if (input_char === '=') { //no space before =
+                    space = false;
+                }
+                tail = this.input.substr(this.pos - 1);
+                if (is_wrap_attributes_force_expand_multiline && has_wrapped_attrs && !is_tag_closed && (input_char === '>' || input_char === '/')) {
+                    if (tail.match(/^\/?\s*>/)) {
+                        space = false;
+                        is_tag_closed = true;
+                        this.print_newline(false, content);
+                        this.print_indentation(content);
+                    }
+                }
+                if (content.length && content[content.length - 1] !== '=' && input_char !== '>' && space) {
+                    //no space after = or before >
+                    var wrapped = this.space_or_wrap(content);
+                    var indentAttrs = wrapped && input_char !== '/' && !is_wrap_attributes_force;
+                    space = false;
+
+                    if (is_wrap_attributes_force && input_char !== '/') {
+                        var force_first_attr_wrap = false;
+                        if (is_wrap_attributes_force_expand_multiline && first_attr) {
+                            var is_only_attribute = tail.match(/^\S*(="([^"]|\\")*")?\s*\/?\s*>/) !== null;
+                            force_first_attr_wrap = !is_only_attribute;
+                        }
+                        if (!first_attr || force_first_attr_wrap) {
+                            this.print_newline(false, content);
+                            this.print_indentation(content);
+                            indentAttrs = true;
+                        }
+                    }
+                    if (indentAttrs) {
+                        has_wrapped_attrs = true;
+
+                        //indent attributes an auto, forced, or forced-align line-wrap
+                        var alignment_size = wrap_attributes_indent_size;
+                        if (is_wrap_attributes_force_aligned) {
+                            alignment_size = content.indexOf(' ') + 1;
+                        }
+
+                        for (var count = 0; count < alignment_size; count++) {
+                            // only ever further indent with spaces since we're trying to align characters
+                            content.push(' ');
+                        }
+                    }
+                    if (first_attr) {
+                        for (var i = 0; i < content.length; i++) {
+                            if (content[i] === ' ') {
+                                first_attr = false;
+                                break;
+                            }
+                        }
+                    }
+                }
+
+                if (indent_handlebars && tag_start_char === '<') {
+                    // When inside an angle-bracket tag, put spaces around
+                    // handlebars not inside of strings.
+                    if ((input_char + this.input.charAt(this.pos)) === '{{') {
+                        input_char += this.get_unformatted('}}');
+                        if (content.length && content[content.length - 1] !== ' ' && content[content.length - 1] !== '<') {
+                            input_char = ' ' + input_char;
+                        }
+                        space = true;
+                    }
+                }
+
+                if (input_char === '<' && !tag_start_char) {
+                    tag_start = this.pos - 1;
+                    tag_start_char = '<';
+                }
+
+                if (indent_handlebars && !tag_start_char) {
+                    if (content.length >= 2 && content[content.length - 1] === '{' && content[content.length - 2] === '{') {
+                        if (input_char === '#' || input_char === '/' || input_char === '!') {
+                            tag_start = this.pos - 3;
+                        } else {
+                            tag_start = this.pos - 2;
+                        }
+                        tag_start_char = '{';
+                    }
+                }
+
+                this.line_char_count++;
+                content.push(input_char); //inserts character at-a-time (or string)
+
+                if (content[1] && (content[1] === '!' || content[1] === '?' || content[1] === '%')) { //if we're in a comment, do something special
+                    // We treat all comments as literals, even more than preformatted tags
+                    // we just look for the appropriate close tag
+                    content = [this.get_comment(tag_start)];
+                    break;
+                }
+
+                if (indent_handlebars && content[1] && content[1] === '{' && content[2] && content[2] === '!') { //if we're in a comment, do something special
+                    // We treat all comments as literals, even more than preformatted tags
+                    // we just look for the appropriate close tag
+                    content = [this.get_comment(tag_start)];
+                    break;
+                }
+
+                if (indent_handlebars && tag_start_char === '{' && content.length > 2 && content[content.length - 2] === '}' && content[content.length - 1] === '}') {
+                    break;
+                }
+            } while (input_char !== '>');
+
+            var tag_complete = content.join('');
+            var tag_index;
+            var tag_offset;
+
+            // must check for space first otherwise the tag could have the first attribute included, and
+            // then not un-indent correctly
+            if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends
+                tag_index = tag_complete.indexOf(' ');
+            } else if (tag_complete.indexOf('\n') !== -1) { //if there's a line break, thats where the tag name ends
+                tag_index = tag_complete.indexOf('\n');
+            } else if (tag_complete.charAt(0) === '{') {
+                tag_index = tag_complete.indexOf('}');
+            } else { //otherwise go with the tag ending
+                tag_index = tag_complete.indexOf('>');
+            }
+            if (tag_complete.charAt(0) === '<' || !indent_handlebars) {
+                tag_offset = 1;
+            } else {
+                tag_offset = tag_complete.charAt(2) === '#' ? 3 : 2;
+            }
+            var tag_check = tag_complete.substring(tag_offset, tag_index).toLowerCase();
+            if (tag_complete.charAt(tag_complete.length - 2) === '/' ||
+                this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /)
+                if (!peek) {
+                    this.tag_type = 'SINGLE';
+                }
+            } else if (indent_handlebars && tag_complete.charAt(0) === '{' && tag_check === 'else') {
+                if (!peek) {
+                    this.indent_to_tag('if');
+                    this.tag_type = 'HANDLEBARS_ELSE';
+                    this.indent_content = true;
+                    this.traverse_whitespace();
+                }
+            } else if (this.is_unformatted(tag_check, unformatted) ||
+                this.is_unformatted(tag_check, content_unformatted)) {
+                // do not reformat the "unformatted" or "content_unformatted" tags
+                comment = this.get_unformatted('', tag_complete); //...delegate to get_unformatted function
+                content.push(comment);
+                tag_end = this.pos - 1;
+                this.tag_type = 'SINGLE';
+            } else if (tag_check === 'script' &&
+                (tag_complete.search('type') === -1 ||
+                    (tag_complete.search('type') > -1 &&
+                        tag_complete.search(/\b(text|application|dojo)\/(x-)?(javascript|ecmascript|jscript|livescript|(ld\+)?json|method|aspect)/) > -1))) {
+                if (!peek) {
+                    this.record_tag(tag_check);
+                    this.tag_type = 'SCRIPT';
+                }
+            } else if (tag_check === 'style' &&
+                (tag_complete.search('type') === -1 ||
+                    (tag_complete.search('type') > -1 && tag_complete.search('text/css') > -1))) {
+                if (!peek) {
+                    this.record_tag(tag_check);
+                    this.tag_type = 'STYLE';
+                }
+            } else if (tag_check.charAt(0) === '!') { //peek for ',
+                matched = false;
+
+            this.pos = start_pos;
+            var input_char = this.input.charAt(this.pos);
+            this.pos++;
+
+            while (this.pos <= this.input.length) {
+                comment += input_char;
+
+                // only need to check for the delimiter if the last chars match
+                if (comment.charAt(comment.length - 1) === delimiter.charAt(delimiter.length - 1) &&
+                    comment.indexOf(delimiter) !== -1) {
+                    break;
+                }
+
+                // only need to search for custom delimiter for the first few characters
+                if (!matched && comment.length < 10) {
+                    if (comment.indexOf('';
+                        matched = true;
+                    } else if (comment.indexOf('';
+                        matched = true;
+                    } else if (comment.indexOf('';
+                        matched = true;
+                    } else if (comment.indexOf('';
+                        matched = true;
+                    } else if (comment.indexOf('{{!--') === 0) { // {{!-- handlebars comment
+                        delimiter = '--}}';
+                        matched = true;
+                    } else if (comment.indexOf('{{!') === 0) { // {{! handlebars comment
+                        if (comment.length === 5 && comment.indexOf('{{!--') === -1) {
+                            delimiter = '}}';
+                            matched = true;
+                        }
+                    } else if (comment.indexOf('';
+                        matched = true;
+                    } else if (comment.indexOf('<%') === 0) { // {{! handlebars comment
+                        delimiter = '%>';
+                        matched = true;
+                    }
+                }
+
+                input_char = this.input.charAt(this.pos);
+                this.pos++;
+            }
+
+            return comment;
+        };
+
+        function tokenMatcher(delimiter) {
+            var token = '';
+
+            var add = function(str) {
+                var newToken = token + str.toLowerCase();
+                token = newToken.length <= delimiter.length ? newToken : newToken.substr(newToken.length - delimiter.length, delimiter.length);
+            };
+
+            var doesNotMatch = function() {
+                return token.indexOf(delimiter) === -1;
+            };
+
+            return {
+                add: add,
+                doesNotMatch: doesNotMatch
+            };
+        }
+
+        this.get_unformatted = function(delimiter, orig_tag) { //function to return unformatted content in its entirety
+            if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) !== -1) {
+                return '';
+            }
+            var input_char = '';
+            var content = '';
+            var space = true;
+
+            var delimiterMatcher = tokenMatcher(delimiter);
+
+            do {
+
+                if (this.pos >= this.input.length) {
+                    return content;
+                }
+
+                input_char = this.input.charAt(this.pos);
+                this.pos++;
+
+                if (this.Utils.in_array(input_char, this.Utils.whitespace)) {
+                    if (!space) {
+                        this.line_char_count--;
+                        continue;
+                    }
+                    if (input_char === '\n' || input_char === '\r') {
+                        content += '\n';
+                        /*  Don't change tab indention for unformatted blocks.  If using code for html editing, this will greatly affect 
 tags if they are specified in the 'unformatted array'
+            for (var i=0; i]*>\s*$/);
+
+            // if next_tag comes back but is not an isolated tag, then
+            // let's treat the 'a' tag as having content
+            // and respect the unformatted option
+            if (!tag || this.Utils.in_array(tag[1], unformatted)) {
+                return true;
+            } else {
+                return false;
+            }
+        };
+
+        this.printer = function(js_source, indent_character, indent_size, wrap_line_length, brace_style) { //handles input/output and some other printing functions
+
+            this.input = js_source || ''; //gets the input for the Parser
+
+            // HACK: newline parsing inconsistent. This brute force normalizes the input.
+            this.input = this.input.replace(/\r\n|[\r\u2028\u2029]/g, '\n');
+
+            this.output = [];
+            this.indent_character = indent_character;
+            this.indent_string = '';
+            this.indent_size = indent_size;
+            this.brace_style = brace_style;
+            this.indent_level = 0;
+            this.wrap_line_length = wrap_line_length;
+            this.line_char_count = 0; //count to see if wrap_line_length was exceeded
+
+            for (var i = 0; i < this.indent_size; i++) {
+                this.indent_string += this.indent_character;
+            }
+
+            this.print_newline = function(force, arr) {
+                this.line_char_count = 0;
+                if (!arr || !arr.length) {
+                    return;
+                }
+                if (force || (arr[arr.length - 1] !== '\n')) { //we might want the extra line
+                    if ((arr[arr.length - 1] !== '\n')) {
+                        arr[arr.length - 1] = rtrim(arr[arr.length - 1]);
+                    }
+                    arr.push('\n');
+                }
+            };
+
+            this.print_indentation = function(arr) {
+                for (var i = 0; i < this.indent_level; i++) {
+                    arr.push(this.indent_string);
+                    this.line_char_count += this.indent_string.length;
+                }
+            };
+
+            this.print_token = function(text) {
+                // Avoid printing initial whitespace.
+                if (this.is_whitespace(text) && !this.output.length) {
+                    return;
+                }
+                if (text || text !== '') {
+                    if (this.output.length && this.output[this.output.length - 1] === '\n') {
+                        this.print_indentation(this.output);
+                        text = ltrim(text);
+                    }
+                }
+                this.print_token_raw(text);
+            };
+
+            this.print_token_raw = function(text) {
+                // If we are going to print newlines, truncate trailing
+                // whitespace, as the newlines will represent the space.
+                if (this.newlines > 0) {
+                    text = rtrim(text);
+                }
+
+                if (text && text !== '') {
+                    if (text.length > 1 && text.charAt(text.length - 1) === '\n') {
+                        // unformatted tags can grab newlines as their last character
+                        this.output.push(text.slice(0, -1));
+                        this.print_newline(false, this.output);
+                    } else {
+                        this.output.push(text);
+                    }
+                }
+
+                for (var n = 0; n < this.newlines; n++) {
+                    this.print_newline(n > 0, this.output);
+                }
+                this.newlines = 0;
+            };
+
+            this.indent = function() {
+                this.indent_level++;
+            };
+
+            this.unindent = function() {
+                if (this.indent_level > 0) {
+                    this.indent_level--;
+                }
+            };
+        };
+        return this;
+    }
+
+    /*_____________________--------------------_____________________*/
+
+    this.beautify = function() {
+        multi_parser = new Parser(); //wrapping functions Parser
+        multi_parser.printer(html_source, indent_character, indent_size, wrap_line_length, brace_style); //initialize starting values
+        while (true) {
+            var t = multi_parser.get_token();
+            multi_parser.token_text = t[0];
+            multi_parser.token_type = t[1];
+
+            if (multi_parser.token_type === 'TK_EOF') {
+                break;
+            }
+
+            switch (multi_parser.token_type) {
+                case 'TK_TAG_START':
+                    multi_parser.print_newline(false, multi_parser.output);
+                    multi_parser.print_token(multi_parser.token_text);
+                    if (multi_parser.indent_content) {
+                        if ((multi_parser.indent_body_inner_html || !multi_parser.token_text.match(//)) &&
+                            (multi_parser.indent_head_inner_html || !multi_parser.token_text.match(//))) {
+
+                            multi_parser.indent();
+                        }
+
+                        multi_parser.indent_content = false;
+                    }
+                    multi_parser.current_mode = 'CONTENT';
+                    break;
+                case 'TK_TAG_STYLE':
+                case 'TK_TAG_SCRIPT':
+                    multi_parser.print_newline(false, multi_parser.output);
+                    multi_parser.print_token(multi_parser.token_text);
+                    multi_parser.current_mode = 'CONTENT';
+                    break;
+                case 'TK_TAG_END':
+                    //Print new line only if the tag has no content and has child
+                    if (multi_parser.last_token === 'TK_CONTENT' && multi_parser.last_text === '') {
+                        var tag_name = (multi_parser.token_text.match(/\w+/) || [])[0];
+                        var tag_extracted_from_last_output = null;
+                        if (multi_parser.output.length) {
+                            tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length - 1].match(/(?:<|{{#)\s*(\w+)/);
+                        }
+                        if (tag_extracted_from_last_output === null ||
+                            (tag_extracted_from_last_output[1] !== tag_name && !multi_parser.Utils.in_array(tag_extracted_from_last_output[1], unformatted))) {
+                            multi_parser.print_newline(false, multi_parser.output);
+                        }
+                    }
+                    multi_parser.print_token(multi_parser.token_text);
+                    multi_parser.current_mode = 'CONTENT';
+                    break;
+                case 'TK_TAG_SINGLE':
+                    // Don't add a newline before elements that should remain unformatted.
+                    var tag_check = multi_parser.token_text.match(/^\s*<([a-z-]+)/i);
+                    if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)) {
+                        multi_parser.print_newline(false, multi_parser.output);
+                    }
+                    multi_parser.print_token(multi_parser.token_text);
+                    multi_parser.current_mode = 'CONTENT';
+                    break;
+                case 'TK_TAG_HANDLEBARS_ELSE':
+                    // Don't add a newline if opening {{#if}} tag is on the current line
+                    var foundIfOnCurrentLine = false;
+                    for (var lastCheckedOutput = multi_parser.output.length - 1; lastCheckedOutput >= 0; lastCheckedOutput--) {
+                        if (multi_parser.output[lastCheckedOutput] === '\n') {
+                            break;
+                        } else {
+                            if (multi_parser.output[lastCheckedOutput].match(/{{#if/)) {
+                                foundIfOnCurrentLine = true;
+                                break;
+                            }
+                        }
+                    }
+                    if (!foundIfOnCurrentLine) {
+                        multi_parser.print_newline(false, multi_parser.output);
+                    }
+                    multi_parser.print_token(multi_parser.token_text);
+                    if (multi_parser.indent_content) {
+                        multi_parser.indent();
+                        multi_parser.indent_content = false;
+                    }
+                    multi_parser.current_mode = 'CONTENT';
+                    break;
+                case 'TK_TAG_HANDLEBARS_COMMENT':
+                    multi_parser.print_token(multi_parser.token_text);
+                    multi_parser.current_mode = 'TAG';
+                    break;
+                case 'TK_CONTENT':
+                    multi_parser.print_token(multi_parser.token_text);
+                    multi_parser.current_mode = 'TAG';
+                    break;
+                case 'TK_STYLE':
+                case 'TK_SCRIPT':
+                    if (multi_parser.token_text !== '') {
+                        multi_parser.print_newline(false, multi_parser.output);
+                        var text = multi_parser.token_text,
+                            _beautifier,
+                            script_indent_level = 1;
+                        if (multi_parser.token_type === 'TK_SCRIPT') {
+                            _beautifier = typeof js_beautify === 'function' && js_beautify;
+                        } else if (multi_parser.token_type === 'TK_STYLE') {
+                            _beautifier = typeof css_beautify === 'function' && css_beautify;
+                        }
+
+                        if (options.indent_scripts === "keep") {
+                            script_indent_level = 0;
+                        } else if (options.indent_scripts === "separate") {
+                            script_indent_level = -multi_parser.indent_level;
+                        }
+
+                        var indentation = multi_parser.get_full_indent(script_indent_level);
+                        if (_beautifier) {
+
+                            // call the Beautifier if avaliable
+                            var Child_options = function() {
+                                this.eol = '\n';
+                            };
+                            Child_options.prototype = options;
+                            var child_options = new Child_options();
+                            text = _beautifier(text.replace(/^\s*/, indentation), child_options);
+                        } else {
+                            // simply indent the string otherwise
+                            var white = text.match(/^\s*/)[0];
+                            var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
+                            var reindent = multi_parser.get_full_indent(script_indent_level - _level);
+                            text = text.replace(/^\s*/, indentation)
+                                .replace(/\r\n|\r|\n/g, '\n' + reindent)
+                                .replace(/\s+$/, '');
+                        }
+                        if (text) {
+                            multi_parser.print_token_raw(text);
+                            multi_parser.print_newline(true, multi_parser.output);
+                        }
+                    }
+                    multi_parser.current_mode = 'TAG';
+                    break;
+                default:
+                    // We should not be getting here but we don't want to drop input on the floor
+                    // Just output the text and move on
+                    if (multi_parser.token_text !== '') {
+                        multi_parser.print_token(multi_parser.token_text);
+                    }
+                    break;
+            }
+            multi_parser.last_token = multi_parser.token_type;
+            multi_parser.last_text = multi_parser.token_text;
+        }
+        var sweet_code = multi_parser.output.join('').replace(/[\r\n\t ]+$/, '');
+
+        // establish end_with_newline
+        if (end_with_newline) {
+            sweet_code += '\n';
+        }
+
+        if (eol !== '\n') {
+            sweet_code = sweet_code.replace(/[\n]/g, eol);
+        }
+
+        return sweet_code;
+    };
+}
+
+module.exports.Beautifier = Beautifier;
diff --git a/js/src/html/index.js b/js/src/html/index.js
new file mode 100644
index 000000000..bd7f5f534
--- /dev/null
+++ b/js/src/html/index.js
@@ -0,0 +1,36 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+    The MIT License (MIT)
+
+    Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+*/
+
+var Beautifier = require('./beautifier').Beautifier;
+
+function style_html(html_source, options, js_beautify, css_beautify) {
+    var beautifier = new Beautifier(html_source, options, js_beautify, css_beautify);
+    return beautifier.beautify();
+}
+
+module.exports = style_html;
\ No newline at end of file
diff --git a/js/src/index.js b/js/src/index.js
new file mode 100644
index 000000000..bfa42f544
--- /dev/null
+++ b/js/src/index.js
@@ -0,0 +1,27 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+*/
diff --git a/js/src/javascript/beautifier.js b/js/src/javascript/beautifier.js
new file mode 100644
index 000000000..494e40b22
--- /dev/null
+++ b/js/src/javascript/beautifier.js
@@ -0,0 +1,1437 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+    The MIT License (MIT)
+
+    Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+*/
+
+var mergeOpts = require('core/options').mergeOpts;
+var acorn = require('core/acorn');
+var Output = require('core/output').Output;
+var Tokenizer = require('./tokenizer').Tokenizer;
+
+function remove_redundant_indentation(output, frame) {
+    // This implementation is effective but has some issues:
+    //     - can cause line wrap to happen too soon due to indent removal
+    //           after wrap points are calculated
+    // These issues are minor compared to ugly indentation.
+
+    if (frame.multiline_frame ||
+        frame.mode === MODE.ForInitializer ||
+        frame.mode === MODE.Conditional) {
+        return;
+    }
+
+    // remove one indent from each line inside this section
+    var start_index = frame.start_line_index;
+
+    output.remove_indent(start_index);
+}
+
+function in_array(what, arr) {
+    for (var i = 0; i < arr.length; i += 1) {
+        if (arr[i] === what) {
+            return true;
+        }
+    }
+    return false;
+}
+
+function trim(s) {
+    return s.replace(/^\s+|\s+$/g, '');
+}
+
+function ltrim(s) {
+    return s.replace(/^\s+/g, '');
+}
+
+// function rtrim(s) {
+//     return s.replace(/\s+$/g, '');
+// }
+
+function sanitizeOperatorPosition(opPosition) {
+    opPosition = opPosition || OPERATOR_POSITION.before_newline;
+
+    var validPositionValues = Object.values(OPERATOR_POSITION);
+
+    if (!in_array(opPosition, validPositionValues)) {
+        throw new Error("Invalid Option Value: The option 'operator_position' must be one of the following values\n" +
+            validPositionValues +
+            "\nYou passed in: '" + opPosition + "'");
+    }
+
+    return opPosition;
+}
+
+var OPERATOR_POSITION = {
+    before_newline: 'before-newline',
+    after_newline: 'after-newline',
+    preserve_newline: 'preserve-newline',
+};
+
+var OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION.before_newline, OPERATOR_POSITION.preserve_newline];
+
+var MODE = {
+    BlockStatement: 'BlockStatement', // 'BLOCK'
+    Statement: 'Statement', // 'STATEMENT'
+    ObjectLiteral: 'ObjectLiteral', // 'OBJECT',
+    ArrayLiteral: 'ArrayLiteral', //'[EXPRESSION]',
+    ForInitializer: 'ForInitializer', //'(FOR-EXPRESSION)',
+    Conditional: 'Conditional', //'(COND-EXPRESSION)',
+    Expression: 'Expression' //'(EXPRESSION)'
+};
+
+function Beautifier(js_source_text, options) {
+    "use strict";
+    var output;
+    var tokens = [],
+        token_pos;
+    var tokenizer;
+    var current_token;
+    var last_type, last_last_text, indent_string;
+    var flags, previous_flags, flag_store;
+    var prefix;
+
+    var handlers, opt;
+    var baseIndentString = '';
+
+    handlers = {
+        'TK_START_EXPR': handle_start_expr,
+        'TK_END_EXPR': handle_end_expr,
+        'TK_START_BLOCK': handle_start_block,
+        'TK_END_BLOCK': handle_end_block,
+        'TK_WORD': handle_word,
+        'TK_RESERVED': handle_word,
+        'TK_SEMICOLON': handle_semicolon,
+        'TK_STRING': handle_string,
+        'TK_EQUALS': handle_equals,
+        'TK_OPERATOR': handle_operator,
+        'TK_COMMA': handle_comma,
+        'TK_BLOCK_COMMENT': handle_block_comment,
+        'TK_COMMENT': handle_comment,
+        'TK_DOT': handle_dot,
+        'TK_UNKNOWN': handle_unknown,
+        'TK_EOF': handle_eof
+    };
+
+    function create_flags(flags_base, mode) {
+        var next_indent_level = 0;
+        if (flags_base) {
+            next_indent_level = flags_base.indentation_level;
+            if (!output.just_added_newline() &&
+                flags_base.line_indent_level > next_indent_level) {
+                next_indent_level = flags_base.line_indent_level;
+            }
+        }
+
+        var next_flags = {
+            mode: mode,
+            parent: flags_base,
+            last_text: flags_base ? flags_base.last_text : '', // last token text
+            last_word: flags_base ? flags_base.last_word : '', // last 'TK_WORD' passed
+            declaration_statement: false,
+            declaration_assignment: false,
+            multiline_frame: false,
+            inline_frame: false,
+            if_block: false,
+            else_block: false,
+            do_block: false,
+            do_while: false,
+            import_block: false,
+            in_case_statement: false, // switch(..){ INSIDE HERE }
+            in_case: false, // we're on the exact line with "case 0:"
+            case_body: false, // the indented case-action block
+            indentation_level: next_indent_level,
+            line_indent_level: flags_base ? flags_base.line_indent_level : next_indent_level,
+            start_line_index: output.get_line_number(),
+            ternary_depth: 0
+        };
+        return next_flags;
+    }
+
+    // Some interpreters have unexpected results with foo = baz || bar;
+    options = options ? options : {};
+
+    // Allow the setting of language/file-type specific options
+    // with inheritance of overall settings
+    options = mergeOpts(options, 'js');
+
+    opt = {};
+
+    // compatibility, re
+    if (options.brace_style === "expand-strict") { //graceful handling of deprecated option
+        options.brace_style = "expand";
+    } else if (options.brace_style === "collapse-preserve-inline") { //graceful handling of deprecated option
+        options.brace_style = "collapse,preserve-inline";
+    } else if (options.braces_on_own_line !== undefined) { //graceful handling of deprecated option
+        options.brace_style = options.braces_on_own_line ? "expand" : "collapse";
+    } else if (!options.brace_style) //Nothing exists to set it
+    {
+        options.brace_style = "collapse";
+    }
+
+
+    var brace_style_split = options.brace_style.split(/[^a-zA-Z0-9_\-]+/);
+    opt.brace_style = brace_style_split[0];
+    opt.brace_preserve_inline = brace_style_split[1] ? brace_style_split[1] : false;
+
+    opt.indent_size = options.indent_size ? parseInt(options.indent_size, 10) : 4;
+    opt.indent_char = options.indent_char ? options.indent_char : ' ';
+    opt.eol = options.eol ? options.eol : 'auto';
+    opt.preserve_newlines = (options.preserve_newlines === undefined) ? true : options.preserve_newlines;
+    opt.break_chained_methods = (options.break_chained_methods === undefined) ? false : options.break_chained_methods;
+    opt.max_preserve_newlines = (options.max_preserve_newlines === undefined) ? 0 : parseInt(options.max_preserve_newlines, 10);
+    opt.space_in_paren = (options.space_in_paren === undefined) ? false : options.space_in_paren;
+    opt.space_in_empty_paren = (options.space_in_empty_paren === undefined) ? false : options.space_in_empty_paren;
+    opt.jslint_happy = (options.jslint_happy === undefined) ? false : options.jslint_happy;
+    opt.space_after_anon_function = (options.space_after_anon_function === undefined) ? false : options.space_after_anon_function;
+    opt.keep_array_indentation = (options.keep_array_indentation === undefined) ? false : options.keep_array_indentation;
+    opt.space_before_conditional = (options.space_before_conditional === undefined) ? true : options.space_before_conditional;
+    opt.unescape_strings = (options.unescape_strings === undefined) ? false : options.unescape_strings;
+    opt.wrap_line_length = (options.wrap_line_length === undefined) ? 0 : parseInt(options.wrap_line_length, 10);
+    opt.e4x = (options.e4x === undefined) ? false : options.e4x;
+    opt.end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
+    opt.comma_first = (options.comma_first === undefined) ? false : options.comma_first;
+    opt.operator_position = sanitizeOperatorPosition(options.operator_position);
+
+    // For testing of beautify ignore:start directive
+    opt.test_output_raw = (options.test_output_raw === undefined) ? false : options.test_output_raw;
+
+    // force opt.space_after_anon_function to true if opt.jslint_happy
+    if (opt.jslint_happy) {
+        opt.space_after_anon_function = true;
+    }
+
+    if (options.indent_with_tabs) {
+        opt.indent_char = '\t';
+        opt.indent_size = 1;
+    }
+
+    if (opt.eol === 'auto') {
+        opt.eol = '\n';
+        if (js_source_text && acorn.lineBreak.test(js_source_text || '')) {
+            opt.eol = js_source_text.match(acorn.lineBreak)[0];
+        }
+    }
+
+    opt.eol = opt.eol.replace(/\\r/, '\r').replace(/\\n/, '\n');
+
+    //----------------------------------
+    indent_string = '';
+    while (opt.indent_size > 0) {
+        indent_string += opt.indent_char;
+        opt.indent_size -= 1;
+    }
+
+    var preindent_index = 0;
+    if (js_source_text && js_source_text.length) {
+        while ((js_source_text.charAt(preindent_index) === ' ' ||
+                js_source_text.charAt(preindent_index) === '\t')) {
+            preindent_index += 1;
+        }
+        baseIndentString = js_source_text.substring(0, preindent_index);
+        js_source_text = js_source_text.substring(preindent_index);
+    }
+
+    last_type = 'TK_START_BLOCK'; // last token type
+    last_last_text = ''; // pre-last token text
+    output = new Output(indent_string, baseIndentString);
+
+    // If testing the ignore directive, start with output disable set to true
+    output.raw = opt.test_output_raw;
+
+
+    // Stack of parsing/formatting states, including MODE.
+    // We tokenize, parse, and output in an almost purely a forward-only stream of token input
+    // and formatted output.  This makes the beautifier less accurate than full parsers
+    // but also far more tolerant of syntax errors.
+    //
+    // For example, the default mode is MODE.BlockStatement. If we see a '{' we push a new frame of type
+    // MODE.BlockStatement on the the stack, even though it could be object literal.  If we later
+    // encounter a ":", we'll switch to to MODE.ObjectLiteral.  If we then see a ";",
+    // most full parsers would die, but the beautifier gracefully falls back to
+    // MODE.BlockStatement and continues on.
+    flag_store = [];
+    set_mode(MODE.BlockStatement);
+
+    this.beautify = function() {
+
+        /*jshint onevar:true */
+        var sweet_code;
+        tokenizer = new Tokenizer(js_source_text, opt, indent_string);
+        tokens = tokenizer.tokenize();
+        token_pos = 0;
+
+        current_token = get_token();
+        while (current_token) {
+            handlers[current_token.type]();
+
+            last_last_text = flags.last_text;
+            last_type = current_token.type;
+            flags.last_text = current_token.text;
+
+            token_pos += 1;
+            current_token = get_token();
+        }
+
+        sweet_code = output.get_code(opt.end_with_newline, opt.eol);
+
+        return sweet_code;
+    };
+
+    function handle_whitespace_and_comments(local_token, preserve_statement_flags) {
+        var newlines = local_token.newlines;
+        var keep_whitespace = opt.keep_array_indentation && is_array(flags.mode);
+        var temp_token = current_token;
+
+        for (var h = 0; h < local_token.comments_before.length; h++) {
+            // The cleanest handling of inline comments is to treat them as though they aren't there.
+            // Just continue formatting and the behavior should be logical.
+            // Also ignore unknown tokens.  Again, this should result in better behavior.
+            current_token = local_token.comments_before[h];
+            handle_whitespace_and_comments(current_token, preserve_statement_flags);
+            handlers[current_token.type](preserve_statement_flags);
+        }
+        current_token = temp_token;
+
+        if (keep_whitespace) {
+            for (var i = 0; i < newlines; i += 1) {
+                print_newline(i > 0, preserve_statement_flags);
+            }
+        } else {
+            if (opt.max_preserve_newlines && newlines > opt.max_preserve_newlines) {
+                newlines = opt.max_preserve_newlines;
+            }
+
+            if (opt.preserve_newlines) {
+                if (local_token.newlines > 1) {
+                    print_newline(false, preserve_statement_flags);
+                    for (var j = 1; j < newlines; j += 1) {
+                        print_newline(true, preserve_statement_flags);
+                    }
+                }
+            }
+        }
+
+    }
+
+    // we could use just string.split, but
+    // IE doesn't like returning empty strings
+    function split_linebreaks(s) {
+        //return s.split(/\x0d\x0a|\x0a/);
+
+        s = s.replace(acorn.allLineBreaks, '\n');
+        var out = [],
+            idx = s.indexOf("\n");
+        while (idx !== -1) {
+            out.push(s.substring(0, idx));
+            s = s.substring(idx + 1);
+            idx = s.indexOf("\n");
+        }
+        if (s.length) {
+            out.push(s);
+        }
+        return out;
+    }
+
+    var newline_restricted_tokens = ['break', 'continue', 'return', 'throw'];
+
+    function allow_wrap_or_preserved_newline(force_linewrap) {
+        force_linewrap = (force_linewrap === undefined) ? false : force_linewrap;
+
+        // Never wrap the first token on a line
+        if (output.just_added_newline()) {
+            return;
+        }
+
+        var shouldPreserveOrForce = (opt.preserve_newlines && current_token.wanted_newline) || force_linewrap;
+        var operatorLogicApplies = in_array(flags.last_text, tokenizer.positionable_operators) || in_array(current_token.text, tokenizer.positionable_operators);
+
+        if (operatorLogicApplies) {
+            var shouldPrintOperatorNewline = (
+                    in_array(flags.last_text, tokenizer.positionable_operators) &&
+                    in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)
+                ) ||
+                in_array(current_token.text, tokenizer.positionable_operators);
+            shouldPreserveOrForce = shouldPreserveOrForce && shouldPrintOperatorNewline;
+        }
+
+        if (shouldPreserveOrForce) {
+            print_newline(false, true);
+        } else if (opt.wrap_line_length) {
+            if (last_type === 'TK_RESERVED' && in_array(flags.last_text, newline_restricted_tokens)) {
+                // These tokens should never have a newline inserted
+                // between them and the following expression.
+                return;
+            }
+            var proposed_line_length = output.current_line.get_character_count() + current_token.text.length +
+                (output.space_before_token ? 1 : 0);
+            if (proposed_line_length >= opt.wrap_line_length) {
+                print_newline(false, true);
+            }
+        }
+    }
+
+    function print_newline(force_newline, preserve_statement_flags) {
+        if (!preserve_statement_flags) {
+            if (flags.last_text !== ';' && flags.last_text !== ',' && flags.last_text !== '=' && last_type !== 'TK_OPERATOR') {
+                var next_token = get_token(1);
+                while (flags.mode === MODE.Statement &&
+                    !(flags.if_block && next_token && next_token.type === 'TK_RESERVED' && next_token.text === 'else') &&
+                    !flags.do_block) {
+                    restore_mode();
+                }
+            }
+        }
+
+        if (output.add_new_line(force_newline)) {
+            flags.multiline_frame = true;
+        }
+    }
+
+    function print_token_line_indentation() {
+        if (output.just_added_newline()) {
+            if (opt.keep_array_indentation && is_array(flags.mode) && current_token.wanted_newline) {
+                output.current_line.push(current_token.whitespace_before);
+                output.space_before_token = false;
+            } else if (output.set_indent(flags.indentation_level)) {
+                flags.line_indent_level = flags.indentation_level;
+            }
+        }
+    }
+
+    function print_token(printable_token) {
+        if (output.raw) {
+            output.add_raw_token(current_token);
+            return;
+        }
+
+        if (opt.comma_first && last_type === 'TK_COMMA' &&
+            output.just_added_newline()) {
+            if (output.previous_line.last() === ',') {
+                var popped = output.previous_line.pop();
+                // if the comma was already at the start of the line,
+                // pull back onto that line and reprint the indentation
+                if (output.previous_line.is_empty()) {
+                    output.previous_line.push(popped);
+                    output.trim(true);
+                    output.current_line.pop();
+                    output.trim();
+                }
+
+                // add the comma in front of the next token
+                print_token_line_indentation();
+                output.add_token(',');
+                output.space_before_token = true;
+            }
+        }
+
+        printable_token = printable_token || current_token.text;
+        print_token_line_indentation();
+        output.add_token(printable_token);
+    }
+
+    function indent() {
+        flags.indentation_level += 1;
+    }
+
+    function deindent() {
+        if (flags.indentation_level > 0 &&
+            ((!flags.parent) || flags.indentation_level > flags.parent.indentation_level)) {
+            flags.indentation_level -= 1;
+
+        }
+    }
+
+    function set_mode(mode) {
+        if (flags) {
+            flag_store.push(flags);
+            previous_flags = flags;
+        } else {
+            previous_flags = create_flags(null, mode);
+        }
+
+        flags = create_flags(previous_flags, mode);
+    }
+
+    function is_array(mode) {
+        return mode === MODE.ArrayLiteral;
+    }
+
+    function is_expression(mode) {
+        return in_array(mode, [MODE.Expression, MODE.ForInitializer, MODE.Conditional]);
+    }
+
+    function restore_mode() {
+        if (flag_store.length > 0) {
+            previous_flags = flags;
+            flags = flag_store.pop();
+            if (previous_flags.mode === MODE.Statement) {
+                remove_redundant_indentation(output, previous_flags);
+            }
+        }
+    }
+
+    function start_of_object_property() {
+        return flags.parent.mode === MODE.ObjectLiteral && flags.mode === MODE.Statement && (
+            (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set'])));
+    }
+
+    function start_of_statement() {
+        if (
+            (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') ||
+            (last_type === 'TK_RESERVED' && flags.last_text === 'do') ||
+            (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw']) && !current_token.wanted_newline) ||
+            (last_type === 'TK_RESERVED' && flags.last_text === 'else' &&
+                !(current_token.type === 'TK_RESERVED' && current_token.text === 'if' && !current_token.comments_before.length)) ||
+            (last_type === 'TK_END_EXPR' && (previous_flags.mode === MODE.ForInitializer || previous_flags.mode === MODE.Conditional)) ||
+            (last_type === 'TK_WORD' && flags.mode === MODE.BlockStatement &&
+                !flags.in_case &&
+                !(current_token.text === '--' || current_token.text === '++') &&
+                last_last_text !== 'function' &&
+                current_token.type !== 'TK_WORD' && current_token.type !== 'TK_RESERVED') ||
+            (flags.mode === MODE.ObjectLiteral && (
+                (flags.last_text === ':' && flags.ternary_depth === 0) || (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set']))))
+        ) {
+
+            set_mode(MODE.Statement);
+            indent();
+
+            handle_whitespace_and_comments(current_token, true);
+
+            // Issue #276:
+            // If starting a new statement with [if, for, while, do], push to a new line.
+            // if (a) if (b) if(c) d(); else e(); else f();
+            if (!start_of_object_property()) {
+                allow_wrap_or_preserved_newline(
+                    current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['do', 'for', 'if', 'while']));
+            }
+
+            return true;
+        }
+        return false;
+    }
+
+    function all_lines_start_with(lines, c) {
+        for (var i = 0; i < lines.length; i++) {
+            var line = trim(lines[i]);
+            if (line.charAt(0) !== c) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    function each_line_matches_indent(lines, indent) {
+        var i = 0,
+            len = lines.length,
+            line;
+        for (; i < len; i++) {
+            line = lines[i];
+            // allow empty lines to pass through
+            if (line && line.indexOf(indent) !== 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    function is_special_word(word) {
+        return in_array(word, ['case', 'return', 'do', 'if', 'throw', 'else']);
+    }
+
+    function get_token(offset) {
+        var index = token_pos + (offset || 0);
+        return (index < 0 || index >= tokens.length) ? null : tokens[index];
+    }
+
+    function handle_start_expr() {
+        // The conditional starts the statement if appropriate.
+        if (!start_of_statement()) {
+            handle_whitespace_and_comments(current_token);
+        }
+
+        var next_mode = MODE.Expression;
+        if (current_token.text === '[') {
+
+            if (last_type === 'TK_WORD' || flags.last_text === ')') {
+                // this is array index specifier, break immediately
+                // a[x], fn()[x]
+                if (last_type === 'TK_RESERVED' && in_array(flags.last_text, tokenizer.line_starters)) {
+                    output.space_before_token = true;
+                }
+                set_mode(next_mode);
+                print_token();
+                indent();
+                if (opt.space_in_paren) {
+                    output.space_before_token = true;
+                }
+                return;
+            }
+
+            next_mode = MODE.ArrayLiteral;
+            if (is_array(flags.mode)) {
+                if (flags.last_text === '[' ||
+                    (flags.last_text === ',' && (last_last_text === ']' || last_last_text === '}'))) {
+                    // ], [ goes to new line
+                    // }, [ goes to new line
+                    if (!opt.keep_array_indentation) {
+                        print_newline();
+                    }
+                }
+            }
+
+        } else {
+            if (last_type === 'TK_RESERVED' && flags.last_text === 'for') {
+                next_mode = MODE.ForInitializer;
+            } else if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['if', 'while'])) {
+                next_mode = MODE.Conditional;
+            } else {
+                // next_mode = MODE.Expression;
+            }
+        }
+
+        if (flags.last_text === ';' || last_type === 'TK_START_BLOCK') {
+            print_newline();
+        } else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || flags.last_text === '.') {
+            // TODO: Consider whether forcing this is required.  Review failing tests when removed.
+            allow_wrap_or_preserved_newline(current_token.wanted_newline);
+            // do nothing on (( and )( and ][ and ]( and .(
+        } else if (!(last_type === 'TK_RESERVED' && current_token.text === '(') && last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
+            output.space_before_token = true;
+        } else if ((last_type === 'TK_RESERVED' && (flags.last_word === 'function' || flags.last_word === 'typeof')) ||
+            (flags.last_text === '*' &&
+                (in_array(last_last_text, ['function', 'yield']) ||
+                    (flags.mode === MODE.ObjectLiteral && in_array(last_last_text, ['{', ',']))))) {
+            // function() vs function ()
+            // yield*() vs yield* ()
+            // function*() vs function* ()
+            if (opt.space_after_anon_function) {
+                output.space_before_token = true;
+            }
+        } else if (last_type === 'TK_RESERVED' && (in_array(flags.last_text, tokenizer.line_starters) || flags.last_text === 'catch')) {
+            if (opt.space_before_conditional) {
+                output.space_before_token = true;
+            }
+        }
+
+        // Should be a space between await and an IIFE
+        if (current_token.text === '(' && last_type === 'TK_RESERVED' && flags.last_word === 'await') {
+            output.space_before_token = true;
+        }
+
+        // Support of this kind of newline preservation.
+        // a = (b &&
+        //     (c || d));
+        if (current_token.text === '(') {
+            if (last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
+                if (!start_of_object_property()) {
+                    allow_wrap_or_preserved_newline();
+                }
+            }
+        }
+
+        // Support preserving wrapped arrow function expressions
+        // a.b('c',
+        //     () => d.e
+        // )
+        if (current_token.text === '(' && last_type !== 'TK_WORD' && last_type !== 'TK_RESERVED') {
+            allow_wrap_or_preserved_newline();
+        }
+
+        set_mode(next_mode);
+        print_token();
+        if (opt.space_in_paren) {
+            output.space_before_token = true;
+        }
+
+        // In all cases, if we newline while inside an expression it should be indented.
+        indent();
+    }
+
+    function handle_end_expr() {
+        // statements inside expressions are not valid syntax, but...
+        // statements must all be closed when their container closes
+        while (flags.mode === MODE.Statement) {
+            restore_mode();
+        }
+
+        handle_whitespace_and_comments(current_token);
+
+        if (flags.multiline_frame) {
+            allow_wrap_or_preserved_newline(current_token.text === ']' && is_array(flags.mode) && !opt.keep_array_indentation);
+        }
+
+        if (opt.space_in_paren) {
+            if (last_type === 'TK_START_EXPR' && !opt.space_in_empty_paren) {
+                // () [] no inner space in empty parens like these, ever, ref #320
+                output.trim();
+                output.space_before_token = false;
+            } else {
+                output.space_before_token = true;
+            }
+        }
+        if (current_token.text === ']' && opt.keep_array_indentation) {
+            print_token();
+            restore_mode();
+        } else {
+            restore_mode();
+            print_token();
+        }
+        remove_redundant_indentation(output, previous_flags);
+
+        // do {} while () // no statement required after
+        if (flags.do_while && previous_flags.mode === MODE.Conditional) {
+            previous_flags.mode = MODE.Expression;
+            flags.do_block = false;
+            flags.do_while = false;
+
+        }
+    }
+
+    function handle_start_block() {
+        handle_whitespace_and_comments(current_token);
+
+        // Check if this is should be treated as a ObjectLiteral
+        var next_token = get_token(1);
+        var second_token = get_token(2);
+        if (second_token && (
+                (in_array(second_token.text, [':', ',']) && in_array(next_token.type, ['TK_STRING', 'TK_WORD', 'TK_RESERVED'])) ||
+                (in_array(next_token.text, ['get', 'set', '...']) && in_array(second_token.type, ['TK_WORD', 'TK_RESERVED']))
+            )) {
+            // We don't support TypeScript,but we didn't break it for a very long time.
+            // We'll try to keep not breaking it.
+            if (!in_array(last_last_text, ['class', 'interface'])) {
+                set_mode(MODE.ObjectLiteral);
+            } else {
+                set_mode(MODE.BlockStatement);
+            }
+        } else if (last_type === 'TK_OPERATOR' && flags.last_text === '=>') {
+            // arrow function: (param1, paramN) => { statements }
+            set_mode(MODE.BlockStatement);
+        } else if (in_array(last_type, ['TK_EQUALS', 'TK_START_EXPR', 'TK_COMMA', 'TK_OPERATOR']) ||
+            (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['return', 'throw', 'import', 'default']))
+        ) {
+            // Detecting shorthand function syntax is difficult by scanning forward,
+            //     so check the surrounding context.
+            // If the block is being returned, imported, export default, passed as arg,
+            //     assigned with = or assigned in a nested object, treat as an ObjectLiteral.
+            set_mode(MODE.ObjectLiteral);
+        } else {
+            set_mode(MODE.BlockStatement);
+        }
+
+        var empty_braces = !next_token.comments_before.length && next_token.text === '}';
+        var empty_anonymous_function = empty_braces && flags.last_word === 'function' &&
+            last_type === 'TK_END_EXPR';
+
+        if (opt.brace_preserve_inline) // check for inline, set inline_frame if so
+        {
+            // search forward for a newline wanted inside this block
+            var index = 0;
+            var check_token = null;
+            flags.inline_frame = true;
+            do {
+                index += 1;
+                check_token = get_token(index);
+                if (check_token.wanted_newline) {
+                    flags.inline_frame = false;
+                    break;
+                }
+            } while (check_token.type !== 'TK_EOF' &&
+                !(check_token.type === 'TK_END_BLOCK' && check_token.opened === current_token));
+        }
+
+        if ((opt.brace_style === "expand" ||
+                (opt.brace_style === "none" && current_token.wanted_newline)) &&
+            !flags.inline_frame) {
+            if (last_type !== 'TK_OPERATOR' &&
+                (empty_anonymous_function ||
+                    last_type === 'TK_EQUALS' ||
+                    (last_type === 'TK_RESERVED' && is_special_word(flags.last_text) && flags.last_text !== 'else'))) {
+                output.space_before_token = true;
+            } else {
+                print_newline(false, true);
+            }
+        } else { // collapse || inline_frame
+            if (is_array(previous_flags.mode) && (last_type === 'TK_START_EXPR' || last_type === 'TK_COMMA')) {
+                if (last_type === 'TK_COMMA' || opt.space_in_paren) {
+                    output.space_before_token = true;
+                }
+
+                if (last_type === 'TK_COMMA' || (last_type === 'TK_START_EXPR' && flags.inline_frame)) {
+                    allow_wrap_or_preserved_newline();
+                    previous_flags.multiline_frame = previous_flags.multiline_frame || flags.multiline_frame;
+                    flags.multiline_frame = false;
+                }
+            }
+            if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
+                if (last_type === 'TK_START_BLOCK' && !flags.inline_frame) {
+                    print_newline();
+                } else {
+                    output.space_before_token = true;
+                }
+            }
+        }
+        print_token();
+        indent();
+    }
+
+    function handle_end_block() {
+        // statements must all be closed when their container closes
+        handle_whitespace_and_comments(current_token);
+
+        while (flags.mode === MODE.Statement) {
+            restore_mode();
+        }
+
+        var empty_braces = last_type === 'TK_START_BLOCK';
+
+        if (flags.inline_frame && !empty_braces) { // try inline_frame (only set if opt.braces-preserve-inline) first
+            output.space_before_token = true;
+        } else if (opt.brace_style === "expand") {
+            if (!empty_braces) {
+                print_newline();
+            }
+        } else {
+            // skip {}
+            if (!empty_braces) {
+                if (is_array(flags.mode) && opt.keep_array_indentation) {
+                    // we REALLY need a newline here, but newliner would skip that
+                    opt.keep_array_indentation = false;
+                    print_newline();
+                    opt.keep_array_indentation = true;
+
+                } else {
+                    print_newline();
+                }
+            }
+        }
+        restore_mode();
+        print_token();
+    }
+
+    function handle_word() {
+        if (current_token.type === 'TK_RESERVED') {
+            if (in_array(current_token.text, ['set', 'get']) && flags.mode !== MODE.ObjectLiteral) {
+                current_token.type = 'TK_WORD';
+            } else if (in_array(current_token.text, ['as', 'from']) && !flags.import_block) {
+                current_token.type = 'TK_WORD';
+            } else if (flags.mode === MODE.ObjectLiteral) {
+                var next_token = get_token(1);
+                if (next_token.text === ':') {
+                    current_token.type = 'TK_WORD';
+                }
+            }
+        }
+
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+            if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const']) && current_token.type === 'TK_WORD') {
+                flags.declaration_statement = true;
+            }
+        } else if (current_token.wanted_newline && !is_expression(flags.mode) &&
+            (last_type !== 'TK_OPERATOR' || (flags.last_text === '--' || flags.last_text === '++')) &&
+            last_type !== 'TK_EQUALS' &&
+            (opt.preserve_newlines || !(last_type === 'TK_RESERVED' && in_array(flags.last_text, ['var', 'let', 'const', 'set', 'get'])))) {
+            handle_whitespace_and_comments(current_token);
+            print_newline();
+        } else {
+            handle_whitespace_and_comments(current_token);
+        }
+
+        if (flags.do_block && !flags.do_while) {
+            if (current_token.type === 'TK_RESERVED' && current_token.text === 'while') {
+                // do {} ## while ()
+                output.space_before_token = true;
+                print_token();
+                output.space_before_token = true;
+                flags.do_while = true;
+                return;
+            } else {
+                // do {} should always have while as the next word.
+                // if we don't see the expected while, recover
+                print_newline();
+                flags.do_block = false;
+            }
+        }
+
+        // if may be followed by else, or not
+        // Bare/inline ifs are tricky
+        // Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
+        if (flags.if_block) {
+            if (!flags.else_block && (current_token.type === 'TK_RESERVED' && current_token.text === 'else')) {
+                flags.else_block = true;
+            } else {
+                while (flags.mode === MODE.Statement) {
+                    restore_mode();
+                }
+                flags.if_block = false;
+                flags.else_block = false;
+            }
+        }
+
+        if (current_token.type === 'TK_RESERVED' && (current_token.text === 'case' || (current_token.text === 'default' && flags.in_case_statement))) {
+            print_newline();
+            if (flags.case_body || opt.jslint_happy) {
+                // switch cases following one another
+                deindent();
+                flags.case_body = false;
+            }
+            print_token();
+            flags.in_case = true;
+            flags.in_case_statement = true;
+            return;
+        }
+
+        if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
+            if (!start_of_object_property()) {
+                allow_wrap_or_preserved_newline();
+            }
+        }
+
+        if (current_token.type === 'TK_RESERVED' && current_token.text === 'function') {
+            if (in_array(flags.last_text, ['}', ';']) ||
+                (output.just_added_newline() && !(in_array(flags.last_text, ['(', '[', '{', ':', '=', ',']) || last_type === 'TK_OPERATOR'))) {
+                // make sure there is a nice clean space of at least one blank line
+                // before a new function definition
+                if (!output.just_added_blankline() && !current_token.comments_before.length) {
+                    print_newline();
+                    print_newline(true);
+                }
+            }
+            if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD') {
+                if (last_type === 'TK_RESERVED' && in_array(flags.last_text, ['get', 'set', 'new', 'return', 'export', 'async'])) {
+                    output.space_before_token = true;
+                } else if (last_type === 'TK_RESERVED' && flags.last_text === 'default' && last_last_text === 'export') {
+                    output.space_before_token = true;
+                } else {
+                    print_newline();
+                }
+            } else if (last_type === 'TK_OPERATOR' || flags.last_text === '=') {
+                // foo = function
+                output.space_before_token = true;
+            } else if (!flags.multiline_frame && (is_expression(flags.mode) || is_array(flags.mode))) {
+                // (function
+            } else {
+                print_newline();
+            }
+
+            print_token();
+            flags.last_word = current_token.text;
+            return;
+        }
+
+        prefix = 'NONE';
+
+        if (last_type === 'TK_END_BLOCK') {
+
+            if (previous_flags.inline_frame) {
+                prefix = 'SPACE';
+            } else if (!(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally', 'from']))) {
+                prefix = 'NEWLINE';
+            } else {
+                if (opt.brace_style === "expand" ||
+                    opt.brace_style === "end-expand" ||
+                    (opt.brace_style === "none" && current_token.wanted_newline)) {
+                    prefix = 'NEWLINE';
+                } else {
+                    prefix = 'SPACE';
+                    output.space_before_token = true;
+                }
+            }
+        } else if (last_type === 'TK_SEMICOLON' && flags.mode === MODE.BlockStatement) {
+            // TODO: Should this be for STATEMENT as well?
+            prefix = 'NEWLINE';
+        } else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) {
+            prefix = 'SPACE';
+        } else if (last_type === 'TK_STRING') {
+            prefix = 'NEWLINE';
+        } else if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' ||
+            (flags.last_text === '*' &&
+                (in_array(last_last_text, ['function', 'yield']) ||
+                    (flags.mode === MODE.ObjectLiteral && in_array(last_last_text, ['{', ',']))))) {
+            prefix = 'SPACE';
+        } else if (last_type === 'TK_START_BLOCK') {
+            if (flags.inline_frame) {
+                prefix = 'SPACE';
+            } else {
+                prefix = 'NEWLINE';
+            }
+        } else if (last_type === 'TK_END_EXPR') {
+            output.space_before_token = true;
+            prefix = 'NEWLINE';
+        }
+
+        if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, tokenizer.line_starters) && flags.last_text !== ')') {
+            if (flags.inline_frame || flags.last_text === 'else' || flags.last_text === 'export') {
+                prefix = 'SPACE';
+            } else {
+                prefix = 'NEWLINE';
+            }
+
+        }
+
+        if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['else', 'catch', 'finally'])) {
+            if ((!(last_type === 'TK_END_BLOCK' && previous_flags.mode === MODE.BlockStatement) ||
+                    opt.brace_style === "expand" ||
+                    opt.brace_style === "end-expand" ||
+                    (opt.brace_style === "none" && current_token.wanted_newline)) &&
+                !flags.inline_frame) {
+                print_newline();
+            } else {
+                output.trim(true);
+                var line = output.current_line;
+                // If we trimmed and there's something other than a close block before us
+                // put a newline back in.  Handles '} // comment' scenario.
+                if (line.last() !== '}') {
+                    print_newline();
+                }
+                output.space_before_token = true;
+            }
+        } else if (prefix === 'NEWLINE') {
+            if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
+                // no newline between 'return nnn'
+                output.space_before_token = true;
+            } else if (last_type !== 'TK_END_EXPR') {
+                if ((last_type !== 'TK_START_EXPR' || !(current_token.type === 'TK_RESERVED' && in_array(current_token.text, ['var', 'let', 'const']))) && flags.last_text !== ':') {
+                    // no need to force newline on 'var': for (var x = 0...)
+                    if (current_token.type === 'TK_RESERVED' && current_token.text === 'if' && flags.last_text === 'else') {
+                        // no newline for } else if {
+                        output.space_before_token = true;
+                    } else {
+                        print_newline();
+                    }
+                }
+            } else if (current_token.type === 'TK_RESERVED' && in_array(current_token.text, tokenizer.line_starters) && flags.last_text !== ')') {
+                print_newline();
+            }
+        } else if (flags.multiline_frame && is_array(flags.mode) && flags.last_text === ',' && last_last_text === '}') {
+            print_newline(); // }, in lists get a newline treatment
+        } else if (prefix === 'SPACE') {
+            output.space_before_token = true;
+        }
+        print_token();
+        flags.last_word = current_token.text;
+
+        if (current_token.type === 'TK_RESERVED') {
+            if (current_token.text === 'do') {
+                flags.do_block = true;
+            } else if (current_token.text === 'if') {
+                flags.if_block = true;
+            } else if (current_token.text === 'import') {
+                flags.import_block = true;
+            } else if (flags.import_block && current_token.type === 'TK_RESERVED' && current_token.text === 'from') {
+                flags.import_block = false;
+            }
+        }
+    }
+
+    function handle_semicolon() {
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+            // Semicolon can be the start (and end) of a statement
+            output.space_before_token = false;
+        } else {
+            handle_whitespace_and_comments(current_token);
+        }
+
+        var next_token = get_token(1);
+        while (flags.mode === MODE.Statement &&
+            !(flags.if_block && next_token && next_token.type === 'TK_RESERVED' && next_token.text === 'else') &&
+            !flags.do_block) {
+            restore_mode();
+        }
+
+        // hacky but effective for the moment
+        if (flags.import_block) {
+            flags.import_block = false;
+        }
+        print_token();
+    }
+
+    function handle_string() {
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+            // One difference - strings want at least a space before
+            output.space_before_token = true;
+        } else {
+            handle_whitespace_and_comments(current_token);
+            if (last_type === 'TK_RESERVED' || last_type === 'TK_WORD' || flags.inline_frame) {
+                output.space_before_token = true;
+            } else if (last_type === 'TK_COMMA' || last_type === 'TK_START_EXPR' || last_type === 'TK_EQUALS' || last_type === 'TK_OPERATOR') {
+                if (!start_of_object_property()) {
+                    allow_wrap_or_preserved_newline();
+                }
+            } else {
+                print_newline();
+            }
+        }
+        print_token();
+    }
+
+    function handle_equals() {
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+        } else {
+            handle_whitespace_and_comments(current_token);
+        }
+
+        if (flags.declaration_statement) {
+            // just got an '=' in a var-line, different formatting/line-breaking, etc will now be done
+            flags.declaration_assignment = true;
+        }
+        output.space_before_token = true;
+        print_token();
+        output.space_before_token = true;
+    }
+
+    function handle_comma() {
+        handle_whitespace_and_comments(current_token, true);
+
+        print_token();
+        output.space_before_token = true;
+        if (flags.declaration_statement) {
+            if (is_expression(flags.parent.mode)) {
+                // do not break on comma, for(var a = 1, b = 2)
+                flags.declaration_assignment = false;
+            }
+
+            if (flags.declaration_assignment) {
+                flags.declaration_assignment = false;
+                print_newline(false, true);
+            } else if (opt.comma_first) {
+                // for comma-first, we want to allow a newline before the comma
+                // to turn into a newline after the comma, which we will fixup later
+                allow_wrap_or_preserved_newline();
+            }
+        } else if (flags.mode === MODE.ObjectLiteral ||
+            (flags.mode === MODE.Statement && flags.parent.mode === MODE.ObjectLiteral)) {
+            if (flags.mode === MODE.Statement) {
+                restore_mode();
+            }
+
+            if (!flags.inline_frame) {
+                print_newline();
+            }
+        } else if (opt.comma_first) {
+            // EXPR or DO_BLOCK
+            // for comma-first, we want to allow a newline before the comma
+            // to turn into a newline after the comma, which we will fixup later
+            allow_wrap_or_preserved_newline();
+        }
+    }
+
+    function handle_operator() {
+        var isGeneratorAsterisk = current_token.text === '*' &&
+            ((last_type === 'TK_RESERVED' && in_array(flags.last_text, ['function', 'yield'])) ||
+                (in_array(last_type, ['TK_START_BLOCK', 'TK_COMMA', 'TK_END_BLOCK', 'TK_SEMICOLON']))
+            );
+        var isUnary = in_array(current_token.text, ['-', '+']) && (
+            in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) ||
+            in_array(flags.last_text, tokenizer.line_starters) ||
+            flags.last_text === ','
+        );
+
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+        } else {
+            var preserve_statement_flags = !isGeneratorAsterisk;
+            handle_whitespace_and_comments(current_token, preserve_statement_flags);
+        }
+
+        if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
+            // "return" had a special handling in TK_WORD. Now we need to return the favor
+            output.space_before_token = true;
+            print_token();
+            return;
+        }
+
+        // hack for actionscript's import .*;
+        if (current_token.text === '*' && last_type === 'TK_DOT') {
+            print_token();
+            return;
+        }
+
+        if (current_token.text === '::') {
+            // no spaces around exotic namespacing syntax operator
+            print_token();
+            return;
+        }
+
+        // Allow line wrapping between operators when operator_position is
+        //   set to before or preserve
+        if (last_type === 'TK_OPERATOR' && in_array(opt.operator_position, OPERATOR_POSITION_BEFORE_OR_PRESERVE)) {
+            allow_wrap_or_preserved_newline();
+        }
+
+        if (current_token.text === ':' && flags.in_case) {
+            flags.case_body = true;
+            indent();
+            print_token();
+            print_newline();
+            flags.in_case = false;
+            return;
+        }
+
+        var space_before = true;
+        var space_after = true;
+        var in_ternary = false;
+        if (current_token.text === ':') {
+            if (flags.ternary_depth === 0) {
+                // Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
+                space_before = false;
+            } else {
+                flags.ternary_depth -= 1;
+                in_ternary = true;
+            }
+        } else if (current_token.text === '?') {
+            flags.ternary_depth += 1;
+        }
+
+        // let's handle the operator_position option prior to any conflicting logic
+        if (!isUnary && !isGeneratorAsterisk && opt.preserve_newlines && in_array(current_token.text, tokenizer.positionable_operators)) {
+            var isColon = current_token.text === ':';
+            var isTernaryColon = (isColon && in_ternary);
+            var isOtherColon = (isColon && !in_ternary);
+
+            switch (opt.operator_position) {
+                case OPERATOR_POSITION.before_newline:
+                    // if the current token is : and it's not a ternary statement then we set space_before to false
+                    output.space_before_token = !isOtherColon;
+
+                    print_token();
+
+                    if (!isColon || isTernaryColon) {
+                        allow_wrap_or_preserved_newline();
+                    }
+
+                    output.space_before_token = true;
+                    return;
+
+                case OPERATOR_POSITION.after_newline:
+                    // if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement,
+                    //   then print a newline.
+
+                    output.space_before_token = true;
+
+                    if (!isColon || isTernaryColon) {
+                        if (get_token(1).wanted_newline) {
+                            print_newline(false, true);
+                        } else {
+                            allow_wrap_or_preserved_newline();
+                        }
+                    } else {
+                        output.space_before_token = false;
+                    }
+
+                    print_token();
+
+                    output.space_before_token = true;
+                    return;
+
+                case OPERATOR_POSITION.preserve_newline:
+                    if (!isOtherColon) {
+                        allow_wrap_or_preserved_newline();
+                    }
+
+                    // if we just added a newline, or the current token is : and it's not a ternary statement,
+                    //   then we set space_before to false
+                    space_before = !(output.just_added_newline() || isOtherColon);
+
+                    output.space_before_token = space_before;
+                    print_token();
+                    output.space_before_token = true;
+                    return;
+            }
+        }
+
+        if (isGeneratorAsterisk) {
+            allow_wrap_or_preserved_newline();
+            space_before = false;
+            var next_token = get_token(1);
+            space_after = next_token && in_array(next_token.type, ['TK_WORD', 'TK_RESERVED']);
+        } else if (current_token.text === '...') {
+            allow_wrap_or_preserved_newline();
+            space_before = last_type === 'TK_START_BLOCK';
+            space_after = false;
+        } else if (in_array(current_token.text, ['--', '++', '!', '~']) || isUnary) {
+            // unary operators (and binary +/- pretending to be unary) special cases
+
+            space_before = false;
+            space_after = false;
+
+            // http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
+            // if there is a newline between -- or ++ and anything else we should preserve it.
+            if (current_token.wanted_newline && (current_token.text === '--' || current_token.text === '++')) {
+                print_newline(false, true);
+            }
+
+            if (flags.last_text === ';' && is_expression(flags.mode)) {
+                // for (;; ++i)
+                //        ^^^
+                space_before = true;
+            }
+
+            if (last_type === 'TK_RESERVED') {
+                space_before = true;
+            } else if (last_type === 'TK_END_EXPR') {
+                space_before = !(flags.last_text === ']' && (current_token.text === '--' || current_token.text === '++'));
+            } else if (last_type === 'TK_OPERATOR') {
+                // a++ + ++b;
+                // a - -b
+                space_before = in_array(current_token.text, ['--', '-', '++', '+']) && in_array(flags.last_text, ['--', '-', '++', '+']);
+                // + and - are not unary when preceeded by -- or ++ operator
+                // a-- + b
+                // a * +b
+                // a - -b
+                if (in_array(current_token.text, ['+', '-']) && in_array(flags.last_text, ['--', '++'])) {
+                    space_after = true;
+                }
+            }
+
+
+            if (((flags.mode === MODE.BlockStatement && !flags.inline_frame) || flags.mode === MODE.Statement) &&
+                (flags.last_text === '{' || flags.last_text === ';')) {
+                // { foo; --i }
+                // foo(); --bar;
+                print_newline();
+            }
+        }
+
+        output.space_before_token = output.space_before_token || space_before;
+        print_token();
+        output.space_before_token = space_after;
+    }
+
+    function handle_block_comment(preserve_statement_flags) {
+        if (output.raw) {
+            output.add_raw_token(current_token);
+            if (current_token.directives && current_token.directives.preserve === 'end') {
+                // If we're testing the raw output behavior, do not allow a directive to turn it off.
+                output.raw = opt.test_output_raw;
+            }
+            return;
+        }
+
+        if (current_token.directives) {
+            print_newline(false, preserve_statement_flags);
+            print_token();
+            if (current_token.directives.preserve === 'start') {
+                output.raw = true;
+            }
+            print_newline(false, true);
+            return;
+        }
+
+        // inline block
+        if (!acorn.newline.test(current_token.text) && !current_token.wanted_newline) {
+            output.space_before_token = true;
+            print_token();
+            output.space_before_token = true;
+            return;
+        }
+
+        var lines = split_linebreaks(current_token.text);
+        var j; // iterator for this case
+        var javadoc = false;
+        var starless = false;
+        var lastIndent = current_token.whitespace_before;
+        var lastIndentLength = lastIndent.length;
+
+        // block comment starts with a new line
+        print_newline(false, preserve_statement_flags);
+        if (lines.length > 1) {
+            javadoc = all_lines_start_with(lines.slice(1), '*');
+            starless = each_line_matches_indent(lines.slice(1), lastIndent);
+        }
+
+        // first line always indented
+        print_token(lines[0]);
+        for (j = 1; j < lines.length; j++) {
+            print_newline(false, true);
+            if (javadoc) {
+                // javadoc: reformat and re-indent
+                print_token(' ' + ltrim(lines[j]));
+            } else if (starless && lines[j].length > lastIndentLength) {
+                // starless: re-indent non-empty content, avoiding trim
+                print_token(lines[j].substring(lastIndentLength));
+            } else {
+                // normal comments output raw
+                output.add_token(lines[j]);
+            }
+        }
+
+        // for comments of more than one line, make sure there's a new line after
+        print_newline(false, preserve_statement_flags);
+    }
+
+    function handle_comment(preserve_statement_flags) {
+        if (current_token.wanted_newline) {
+            print_newline(false, preserve_statement_flags);
+        } else {
+            output.trim(true);
+        }
+
+        output.space_before_token = true;
+        print_token();
+        print_newline(false, preserve_statement_flags);
+    }
+
+    function handle_dot() {
+        if (start_of_statement()) {
+            // The conditional starts the statement if appropriate.
+        } else {
+            handle_whitespace_and_comments(current_token, true);
+        }
+
+        if (last_type === 'TK_RESERVED' && is_special_word(flags.last_text)) {
+            output.space_before_token = true;
+        } else {
+            // allow preserved newlines before dots in general
+            // force newlines on dots after close paren when break_chained - for bar().baz()
+            allow_wrap_or_preserved_newline(flags.last_text === ')' && opt.break_chained_methods);
+        }
+
+        print_token();
+    }
+
+    function handle_unknown(preserve_statement_flags) {
+        print_token();
+
+        if (current_token.text[current_token.text.length - 1] === '\n') {
+            print_newline(false, preserve_statement_flags);
+        }
+    }
+
+    function handle_eof() {
+        // Unwind any open statements
+        while (flags.mode === MODE.Statement) {
+            restore_mode();
+        }
+        handle_whitespace_and_comments(current_token);
+    }
+}
+
+module.exports.Beautifier = Beautifier;
\ No newline at end of file
diff --git a/js/src/javascript/index.js b/js/src/javascript/index.js
new file mode 100644
index 000000000..18e3fda50
--- /dev/null
+++ b/js/src/javascript/index.js
@@ -0,0 +1,36 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+    The MIT License (MIT)
+
+    Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+*/
+
+var Beautifier = require('./beautifier').Beautifier;
+
+function js_beautify(js_source_text, options) {
+    var beautifier = new Beautifier(js_source_text, options);
+    return beautifier.beautify();
+}
+
+module.exports = js_beautify;
\ No newline at end of file
diff --git a/js/src/javascript/tokenizer.js b/js/src/javascript/tokenizer.js
new file mode 100644
index 000000000..a39eb7809
--- /dev/null
+++ b/js/src/javascript/tokenizer.js
@@ -0,0 +1,620 @@
+/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
+/*
+
+    The MIT License (MIT)
+
+    Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+    SOFTWARE.
+*/
+
+var InputScanner = require('core/inputscanner').InputScanner;
+var Token = require('core/token').Token;
+var acorn = require('core/acorn');
+
+function trim(s) {
+    return s.replace(/^\s+|\s+$/g, '');
+}
+
+function in_array(what, arr) {
+    for (var i = 0; i < arr.length; i += 1) {
+        if (arr[i] === what) {
+            return true;
+        }
+    }
+    return false;
+}
+
+function Tokenizer(input_string, opts) {
+
+    var whitespace = "\n\r\t ".split('');
+    var digit = /[0-9]/;
+    var digit_bin = /[01]/;
+    var digit_oct = /[01234567]/;
+    var digit_hex = /[0123456789abcdefABCDEF]/;
+
+    this.positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ');
+    var punct = this.positionable_operators.concat(
+        // non-positionable operators - these do not follow operator position settings
+        '! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~ ...'.split(' '));
+
+    // words which should always start on new line.
+    this.line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',');
+    var reserved_words = this.line_starters.concat(['do', 'in', 'of', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as']);
+
+    //  /* ... */ comment ends with nearest */ or end of file
+    var block_comment_pattern = /([\s\S]*?)((?:\*\/)|$)/g;
+
+    // comment ends just before nearest linefeed or end of file
+    var comment_pattern = /([^\n\r\u2028\u2029]*)/g;
+
+    var directives_block_pattern = /\/\* beautify( \w+[:]\w+)+ \*\//g;
+    var directive_pattern = / (\w+)[:](\w+)/g;
+    var directives_end_ignore_pattern = /([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)/g;
+
+    var template_pattern = /((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)/g;
+
+    var n_newlines, whitespace_before_token, in_html_comment, tokens;
+    var input;
+
+    this.tokenize = function() {
+        input = new InputScanner(input_string);
+        in_html_comment = false;
+        tokens = [];
+
+        var next, last;
+        var token_values;
+        var open = null;
+        var open_stack = [];
+        var comments = [];
+
+        while (!(last && last.type === 'TK_EOF')) {
+            token_values = tokenize_next();
+            next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
+            while (next.type === 'TK_COMMENT' || next.type === 'TK_BLOCK_COMMENT' || next.type === 'TK_UNKNOWN') {
+                if (next.type === 'TK_BLOCK_COMMENT') {
+                    next.directives = token_values[2];
+                }
+                comments.push(next);
+                token_values = tokenize_next();
+                next = new Token(token_values[1], token_values[0], n_newlines, whitespace_before_token);
+            }
+
+            if (comments.length) {
+                next.comments_before = comments;
+                comments = [];
+            }
+
+            if (next.type === 'TK_START_BLOCK' || next.type === 'TK_START_EXPR') {
+                next.parent = last;
+                open_stack.push(open);
+                open = next;
+            } else if ((next.type === 'TK_END_BLOCK' || next.type === 'TK_END_EXPR') &&
+                (open && (
+                    (next.text === ']' && open.text === '[') ||
+                    (next.text === ')' && open.text === '(') ||
+                    (next.text === '}' && open.text === '{')))) {
+                next.parent = open.parent;
+                next.opened = open;
+
+                open = open_stack.pop();
+            }
+
+            tokens.push(next);
+            last = next;
+        }
+
+        return tokens;
+    };
+
+    function get_directives(text) {
+        if (!text.match(directives_block_pattern)) {
+            return null;
+        }
+
+        var directives = {};
+        directive_pattern.lastIndex = 0;
+        var directive_match = directive_pattern.exec(text);
+
+        while (directive_match) {
+            directives[directive_match[1]] = directive_match[2];
+            directive_match = directive_pattern.exec(text);
+        }
+
+        return directives;
+    }
+
+    function tokenize_next() {
+        var resulting_string;
+        var whitespace_on_this_line = [];
+
+        n_newlines = 0;
+        whitespace_before_token = '';
+
+        var c = input.next();
+
+        if (c === null) {
+            return ['', 'TK_EOF'];
+        }
+
+        var last_token;
+        if (tokens.length) {
+            last_token = tokens[tokens.length - 1];
+        } else {
+            // For the sake of tokenizing we can pretend that there was on open brace to start
+            last_token = new Token('TK_START_BLOCK', '{');
+        }
+
+        while (in_array(c, whitespace)) {
+
+            if (acorn.newline.test(c)) {
+                if (!(c === '\n' && input.peek(-2) === '\r')) {
+                    n_newlines += 1;
+                    whitespace_on_this_line = [];
+                }
+            } else {
+                whitespace_on_this_line.push(c);
+            }
+
+            c = input.next();
+
+            if (c === null) {
+                return ['', 'TK_EOF'];
+            }
+        }
+
+        if (whitespace_on_this_line.length) {
+            whitespace_before_token = whitespace_on_this_line.join('');
+        }
+
+        if (digit.test(c) || (c === '.' && input.testChar(digit))) {
+            var allow_decimal = true;
+            var allow_e = true;
+            var local_digit = digit;
+
+            if (c === '0' && input.testChar(/[XxOoBb]/)) {
+                // switch to hex/oct/bin number, no decimal or e, just hex/oct/bin digits
+                allow_decimal = false;
+                allow_e = false;
+                if (input.testChar(/[Bb]/)) {
+                    local_digit = digit_bin;
+                } else if (input.testChar(/[Oo]/)) {
+                    local_digit = digit_oct;
+                } else {
+                    local_digit = digit_hex;
+                }
+                c += input.next();
+            } else if (c === '.') {
+                // Already have a decimal for this literal, don't allow another
+                allow_decimal = false;
+            } else {
+                // we know this first loop will run.  It keeps the logic simpler.
+                c = '';
+                input.back();
+            }
+
+            // Add the digits
+            while (input.testChar(local_digit)) {
+                c += input.next();
+
+                if (allow_decimal && input.peek() === '.') {
+                    c += input.next();
+                    allow_decimal = false;
+                }
+
+                // a = 1.e-7 is valid, so we test for . then e in one loop
+                if (allow_e && input.testChar(/[Ee]/)) {
+                    c += input.next();
+
+                    if (input.testChar(/[+-]/)) {
+                        c += input.next();
+                    }
+
+                    allow_e = false;
+                    allow_decimal = false;
+                }
+            }
+
+            return [c, 'TK_WORD'];
+        }
+
+        if (acorn.isIdentifierStart(input.peekCharCode(-1))) {
+            if (input.hasNext()) {
+                while (acorn.isIdentifierChar(input.peekCharCode())) {
+                    c += input.next();
+                    if (!input.hasNext()) {
+                        break;
+                    }
+                }
+            }
+
+            if (!(last_token.type === 'TK_DOT' ||
+                    (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['set', 'get']))) &&
+                in_array(c, reserved_words)) {
+                if (c === 'in' || c === 'of') { // hack for 'in' and 'of' operators
+                    return [c, 'TK_OPERATOR'];
+                }
+                return [c, 'TK_RESERVED'];
+            }
+
+            return [c, 'TK_WORD'];
+        }
+
+        if (c === '(' || c === '[') {
+            return [c, 'TK_START_EXPR'];
+        }
+
+        if (c === ')' || c === ']') {
+            return [c, 'TK_END_EXPR'];
+        }
+
+        if (c === '{') {
+            return [c, 'TK_START_BLOCK'];
+        }
+
+        if (c === '}') {
+            return [c, 'TK_END_BLOCK'];
+        }
+
+        if (c === ';') {
+            return [c, 'TK_SEMICOLON'];
+        }
+
+        if (c === '/') {
+            var comment = '';
+            var comment_match;
+            // peek for comment /* ... */
+            if (input.peek() === '*') {
+                input.next();
+                comment_match = input.match(block_comment_pattern);
+                comment = '/*' + comment_match[0];
+                var directives = get_directives(comment);
+                if (directives && directives.ignore === 'start') {
+                    comment_match = input.match(directives_end_ignore_pattern);
+                    comment += comment_match[0];
+                }
+                comment = comment.replace(acorn.allLineBreaks, '\n');
+                return [comment, 'TK_BLOCK_COMMENT', directives];
+            }
+            // peek for comment // ...
+            if (input.peek() === '/') {
+                input.next();
+                comment_match = input.match(comment_pattern);
+                comment = '//' + comment_match[0];
+                return [comment, 'TK_COMMENT'];
+            }
+
+        }
+
+        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;
+
+        if (c === '`' || c === "'" || c === '"' || // string
+            (
+                (c === '/') || // regexp
+                (opts.e4x && c === "<" && input.test(startXmlRegExp, -1)) // xml
+            ) && ( // regex and xml can only appear in specific locations during parsing
+                (last_token.type === 'TK_RESERVED' && in_array(last_token.text, ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield'])) ||
+                (last_token.type === 'TK_END_EXPR' && last_token.text === ')' &&
+                    last_token.parent && last_token.parent.type === 'TK_RESERVED' && in_array(last_token.parent.text, ['if', 'while', 'for'])) ||
+                (in_array(last_token.type, ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK',
+                    'TK_END_BLOCK', 'TK_OPERATOR', 'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'
+                ]))
+            )) {
+
+            var sep = c,
+                esc = false,
+                has_char_escapes = false;
+
+            resulting_string = c;
+
+            if (sep === '/') {
+                //
+                // handle regexp
+                //
+                var in_char_class = false;
+                while (input.hasNext() &&
+                    ((esc || in_char_class || input.peek() !== sep) &&
+                        !input.testChar(acorn.newline))) {
+                    resulting_string += input.peek();
+                    if (!esc) {
+                        esc = input.peek() === '\\';
+                        if (input.peek() === '[') {
+                            in_char_class = true;
+                        } else if (input.peek() === ']') {
+                            in_char_class = false;
+                        }
+                    } else {
+                        esc = false;
+                    }
+                    input.next();
+                }
+            } else if (opts.e4x && sep === '<') {
+                //
+                // handle e4x xml literals
+                //
+
+                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;
+                input.back();
+                var xmlStr = '';
+                var match = input.match(startXmlRegExp);
+                if (match) {
+                    // Trim root tag to attempt to
+                    var rootTag = match[2].replace(/^{\s+/, '{').replace(/\s+}$/, '}');
+                    var isCurlyRoot = rootTag.indexOf('{') === 0;
+                    var depth = 0;
+                    while (match) {
+                        var isEndTag = !!match[1];
+                        var tagName = match[2];
+                        var isSingletonTag = (!!match[match.length - 1]) || (tagName.slice(0, 8) === "![CDATA[");
+                        if (!isSingletonTag &&
+                            (tagName === rootTag || (isCurlyRoot && tagName.replace(/^{\s+/, '{').replace(/\s+}$/, '}')))) {
+                            if (isEndTag) {
+                                --depth;
+                            } else {
+                                ++depth;
+                            }
+                        }
+                        xmlStr += match[0];
+                        if (depth <= 0) {
+                            break;
+                        }
+                        match = input.match(xmlRegExp);
+                    }
+                    // if we didn't close correctly, keep unformatted.
+                    if (!match) {
+                        xmlStr += input.match(/[\s\S]*/g)[0];
+                    }
+                    xmlStr = xmlStr.replace(acorn.allLineBreaks, '\n');
+                    return [xmlStr, "TK_STRING"];
+                }
+            } else {
+                //
+                // handle string
+                //
+                var parse_string = function(delimiter, allow_unescaped_newlines, start_sub) {
+                    // Template strings can travers lines without escape characters.
+                    // Other strings cannot
+                    var current_char;
+                    while (input.hasNext()) {
+                        current_char = input.peek();
+                        if (!(esc || (current_char !== delimiter &&
+                                (allow_unescaped_newlines || !acorn.newline.test(current_char))))) {
+                            break;
+                        }
+
+                        // Handle \r\n linebreaks after escapes or in template strings
+                        if ((esc || allow_unescaped_newlines) && acorn.newline.test(current_char)) {
+                            if (current_char === '\r' && input.peek(1) === '\n') {
+                                input.next();
+                                current_char = input.peek();
+                            }
+                            resulting_string += '\n';
+                        } else {
+                            resulting_string += current_char;
+                        }
+
+                        if (esc) {
+                            if (current_char === 'x' || current_char === 'u') {
+                                has_char_escapes = true;
+                            }
+                            esc = false;
+                        } else {
+                            esc = current_char === '\\';
+                        }
+
+                        input.next();
+
+                        if (start_sub && resulting_string.indexOf(start_sub, resulting_string.length - start_sub.length) !== -1) {
+                            if (delimiter === '`') {
+                                parse_string('}', allow_unescaped_newlines, '`');
+                            } else {
+                                parse_string('`', allow_unescaped_newlines, '${');
+                            }
+
+                            if (input.hasNext()) {
+                                resulting_string += input.next();
+                            }
+                        }
+                    }
+                };
+
+                if (sep === '`') {
+                    parse_string('`', true, '${');
+                } else {
+                    parse_string(sep);
+                }
+            }
+
+            if (has_char_escapes && opts.unescape_strings) {
+                resulting_string = unescape_string(resulting_string);
+            }
+
+            if (input.peek() === sep) {
+                resulting_string += sep;
+                input.next();
+
+                if (sep === '/') {
+                    // regexps may have modifiers /regexp/MOD , so fetch those, too
+                    // Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
+                    while (input.hasNext() && acorn.isIdentifierStart(input.peekCharCode())) {
+                        resulting_string += input.next();
+                    }
+                }
+            }
+            return [resulting_string, 'TK_STRING'];
+        }
+
+        if (c === '#') {
+
+            if (tokens.length === 0 && input.peek() === '!') {
+                // shebang
+                resulting_string = c;
+                while (input.hasNext() && c !== '\n') {
+                    c = input.next();
+                    resulting_string += c;
+                }
+                return [trim(resulting_string) + '\n', 'TK_UNKNOWN'];
+            }
+
+
+
+            // Spidermonkey-specific sharp variables for circular references
+            // https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
+            // http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
+            var sharp = '#';
+            if (input.hasNext() && input.testChar(digit)) {
+                do {
+                    c = input.next();
+                    sharp += c;
+                } while (input.hasNext() && c !== '#' && c !== '=');
+                if (c === '#') {
+                    //
+                } else if (input.peek() === '[' && input.peek(1) === ']') {
+                    sharp += '[]';
+                    input.next();
+                    input.next();
+                } else if (input.peek() === '{' && input.peek(1) === '}') {
+                    sharp += '{}';
+                    input.next();
+                    input.next();
+                }
+                return [sharp, 'TK_WORD'];
+            }
+        }
+
+        if (c === '<' && (input.peek() === '?' || input.peek() === '%')) {
+            input.back();
+            var template_match = input.match(template_pattern);
+            if (template_match) {
+                c = template_match[0];
+                c = c.replace(acorn.allLineBreaks, '\n');
+                return [c, 'TK_STRING'];
+            }
+        }
+
+        if (c === '<' && input.match(/\!--/g)) {
+            c = '', 'TK_COMMENT'];
+        }
+
+        if (c === '.') {
+            if (input.peek() === '.' && input.peek(1) === '.') {
+                c += input.next() + input.next();
+                return [c, 'TK_OPERATOR'];
+            }
+            return [c, 'TK_DOT'];
+        }
+
+        if (in_array(c, punct)) {
+            while (input.hasNext() && in_array(c + input.peek(), punct)) {
+                c += input.next();
+                if (!input.hasNext()) {
+                    break;
+                }
+            }
+
+            if (c === ',') {
+                return [c, 'TK_COMMA'];
+            } else if (c === '=') {
+                return [c, 'TK_EQUALS'];
+            } else {
+                return [c, 'TK_OPERATOR'];
+            }
+        }
+
+        return [c, 'TK_UNKNOWN'];
+    }
+
+
+    function unescape_string(s) {
+        // You think that a regex would work for this
+        // return s.replace(/\\x([0-9a-f]{2})/gi, function(match, val) {
+        //         return String.fromCharCode(parseInt(val, 16));
+        //     })
+        // However, dealing with '\xff', '\\xff', '\\\xff' makes this more fun.
+        var out = '',
+            escaped = 0;
+
+        var input_scan = new InputScanner(s);
+        var matched = null;
+
+        while (input_scan.hasNext()) {
+            // Keep any whitespace, non-slash characters
+            // also keep slash pairs.
+            matched = input_scan.match(/([\s]|[^\\]|\\\\)+/g);
+
+            if (matched) {
+                out += matched[0];
+            }
+
+            if (input_scan.peek() === '\\') {
+                input_scan.next();
+                if (input_scan.peek() === 'x') {
+                    matched = input_scan.match(/x([0-9A-Fa-f]{2})/g);
+                } else if (input_scan.peek() === 'u') {
+                    matched = input_scan.match(/u([0-9A-Fa-f]{4})/g);
+                } else {
+                    out += '\\';
+                    if (input_scan.hasNext()) {
+                        out += input_scan.next();
+                    }
+                    continue;
+                }
+
+                // If there's some error decoding, return the original string
+                if (!matched) {
+                    return s;
+                }
+
+                escaped = parseInt(matched[1], 16);
+
+                if (escaped > 0x7e && escaped <= 0xff && matched[0].indexOf('x') === 0) {
+                    // we bail out on \x7f..\xff,
+                    // leaving whole string escaped,
+                    // as it's probably completely binary
+                    return s;
+                } else if (escaped >= 0x00 && escaped < 0x20) {
+                    // leave 0x00...0x1f escaped
+                    out += '\\' + matched[0];
+                    continue;
+                } else if (escaped === 0x22 || escaped === 0x27 || escaped === 0x5c) {
+                    // single-quote, apostrophe, backslash - escape these
+                    out += '\\' + String.fromCharCode(escaped);
+                } else {
+                    out += String.fromCharCode(escaped);
+                }
+            }
+        }
+
+        return out;
+    }
+}
+
+module.exports.Tokenizer = Tokenizer;
\ No newline at end of file
diff --git a/package.json b/package.json
index 24b4403c8..4120276f8 100644
--- a/package.json
+++ b/package.json
@@ -44,10 +44,11 @@
     "nopt": "~3.0.1"
   },
   "devDependencies": {
+    "benchmark": "2.1.0",
     "jshint": "~2.9.1",
-    "node-static": "~0.7.1",
     "mustache": "~2.2.1",
-    "requirejs": "2.1.x",
-    "benchmark": "2.1.0"
+    "node-static": "~0.7.1",
+    "requirejs": "^2.3.3",
+    "webpack": "^2.2.1"
   }
 }
diff --git a/python/cssbeautifier/__init__.py b/python/cssbeautifier/__init__.py
index 8507f45fe..0f229c5f5 100644
--- a/python/cssbeautifier/__init__.py
+++ b/python/cssbeautifier/__init__.py
@@ -1,9 +1,3 @@
-from __future__ import print_function
-import sys
-import re
-import copy
-from cssbeautifier.__version__ import __version__
-
 #
 # The MIT License (MIT)
 
@@ -29,51 +23,13 @@
 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
-
-class BeautifierOptions:
-    def __init__(self):
-        self.indent_size = 4
-        self.indent_char = ' '
-        self.indent_with_tabs = False
-        self.preserve_newlines = False
-        self.selector_separator_newline = True
-        self.end_with_newline = False
-        self.newline_between_rules = True
-        self.space_around_combinator = False
-        self.eol = 'auto'
-
-        self.css = None
-        self.js = None
-        self.html = None
-
-        # deprecated
-        self.space_around_selector_separator = False
-
-    def mergeOpts(self, targetType):
-        finalOpts = copy.copy(self)
-
-        local = getattr(finalOpts, targetType)
-        if (local):
-            delattr(finalOpts, targetType)
-            for key in local:
-                setattr(finalOpts, key, local[key])
-
-        return finalOpts
-
-
-    def __repr__(self):
-        return \
-"""indent_size = %d
-indent_char = [%s]
-indent_with_tabs = [%s]
-preserve_newlines = [%s]
-separate_selectors_newline = [%s]
-end_with_newline = [%s]
-newline_between_rules = [%s]
-space_around_combinator = [%s]
-""" % (self.indent_size, self.indent_char, self.indent_with_tabs, self.preserve_newlines,
-    self.selector_separator_newline, self.end_with_newline, self.newline_between_rules,
-    self.space_around_combinator)
+from __future__ import print_function
+import sys
+import re
+import copy
+from jsbeautifier.__version__ import __version__
+from cssbeautifier.css.options import BeautifierOptions
+from cssbeautifier.css.beautifier import Beautifier
 
 
 def default_options():
@@ -106,434 +62,3 @@ def usage(stream=sys.stdout):
         return 1
     else:
         return 0
-
-WHITE_RE = re.compile("^\s+$")
-WORD_RE = re.compile("[\w$\-_]")
-
-
-class Printer:
-
-    def __init__(self, beautifier, indent_char, indent_size, default_indent=""):
-        self.beautifier = beautifier
-        self.newlines_from_last_ws_eat = 0
-        self.indentSize = indent_size
-        self.singleIndent = (indent_size) * indent_char
-        self.indentLevel = 0
-        self.nestedLevel = 0
-
-        self.baseIndentString = default_indent
-        self.output = []
-
-    def __lastCharWhitespace(self):
-        return len(self.output) > 0 and WHITE_RE.search(self.output[-1]) is not None
-
-    def indent(self):
-        self.indentLevel += 1
-        self.baseIndentString += self.singleIndent
-
-    def outdent(self):
-        if self.indentLevel:
-            self.indentLevel -= 1
-            self.baseIndentString = self.baseIndentString[:-(len(self.singleIndent))]
-
-    def push(self, string):
-        self.output.append(string)
-
-    def openBracket(self):
-        self.singleSpace()
-        self.output.append("{")
-        if self.beautifier.eatWhitespace(True) == 0:
-            self.newLine()
-
-    def closeBracket(self,newLine):
-        if newLine:
-            self.newLine()
-        self.output.append("}")
-        self.beautifier.eatWhitespace(True)
-        if self.beautifier.newlines_from_last_ws_eat == 0:
-            self.newLine()
-
-    def semicolon(self):
-        self.output.append(";")
-
-    def comment(self, comment):
-        self.output.append(comment)
-
-    def newLine(self, keepWhitespace=False):
-        if len(self.output) > 0 :
-            if not keepWhitespace and self.output[-1] != '\n':
-                self.trim()
-            elif self.output[-1] == self.baseIndentString:
-                self.output.pop()
-
-            self.output.append("\n")
-
-            if len(self.baseIndentString) > 0:
-                self.output.append(self.baseIndentString)
-
-    def trim(self):
-        while self.__lastCharWhitespace():
-            self.output.pop()
-
-    def singleSpace(self):
-        if len(self.output) > 0 and not self.__lastCharWhitespace():
-            self.output.append(" ")
-
-    def preserveSingleSpace(self,isAfterSpace):
-        if isAfterSpace:
-            self.singleSpace()
-
-    def result(self):
-        if self.baseIndentString:
-            return self.baseIndentString + "".join(self.output);
-        else:
-            return "".join(self.output)
-
-
-class Beautifier:
-
-    def __init__(self, source_text, opts=default_options()):
-        # This is not pretty, but given how we did the version import
-        # it is the only way to do this without having setup.py fail on a missing six dependency.
-        self.six = __import__("six")
-
-        # in javascript, these two differ
-        # in python they are the same, different methods are called on them
-        self.lineBreak = re.compile(self.six.u("\r\n|[\n\r\u2028\u2029]"))
-        self.allLineBreaks = self.lineBreak
-
-        if not source_text:
-            source_text = ''
-
-        opts = opts.mergeOpts('css')
-
-        # Continue to accept deprecated option
-        opts.space_around_combinator = opts.space_around_combinator or opts.space_around_selector_separator
-
-        self.opts = opts
-        self.indentSize = opts.indent_size
-        self.indentChar = opts.indent_char
-        self.pos = -1
-        self.ch = None
-
-        if self.opts.indent_with_tabs:
-            self.indentChar = "\t"
-            self.indentSize = 1
-
-        if self.opts.eol == 'auto':
-            self.opts.eol = '\n'
-            if self.lineBreak.search(source_text or ''):
-                self.opts.eol = self.lineBreak.search(source_text).group()
-
-        self.opts.eol = self.opts.eol.replace('\\r', '\r').replace('\\n', '\n')
-
-        # HACK: newline parsing inconsistent. This brute force normalizes the input newlines.
-        self.source_text = re.sub(self.allLineBreaks, '\n', source_text)
-
-        # https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
-        # also in CONDITIONAL_GROUP_RULE below
-        self.NESTED_AT_RULE = [ \
-            "@page", \
-            "@font-face", \
-            "@keyframes", \
-            "@media", \
-            "@supports", \
-            "@document"]
-        self.CONDITIONAL_GROUP_RULE = [ \
-            "@media", \
-            "@supports", \
-            "@document"]
-
-        m = re.search("^[\t ]*", self.source_text)
-        baseIndentString = m.group(0)
-        self.printer = Printer(self, self.indentChar, self.indentSize, baseIndentString)
-
-    def next(self):
-        self.pos = self.pos + 1
-        if self.pos < len(self.source_text):
-            self.ch = self.source_text[self.pos]
-        else:
-            self.ch = ''
-        return self.ch
-
-    def peek(self,skipWhitespace=False):
-        start = self.pos
-        if skipWhitespace:
-            self.eatWhitespace()
-        result = ""
-        if self.pos + 1 < len(self.source_text):
-            result = self.source_text[self.pos + 1]
-        if skipWhitespace:
-            self.pos = start - 1
-            self.next()
-
-        return result
-
-    def eatString(self, endChars):
-        start = self.pos
-        while self.next():
-            if self.ch == "\\":
-                self.next()
-            elif self.ch in endChars:
-                break
-            elif self.ch == "\n":
-                break
-        return self.source_text[start:self.pos] + self.ch
-
-    def peekString(self, endChar):
-        start = self.pos
-        st = self.eatString(endChar)
-        self.pos = start - 1
-        self.next()
-        return st
-
-    def eatWhitespace(self, pn=False):
-        result = 0
-        while WHITE_RE.search(self.peek()) is not None:
-            self.next()
-            if self.ch == "\n" and pn and self.opts.preserve_newlines:
-                self.printer.newLine(True)
-                result += 1
-        self.newlines_from_last_ws_eat = result
-        return result
-
-    def skipWhitespace(self):
-        result = ''
-        if self.ch and WHITE_RE.search(self.ch):
-            result = self.ch
-
-        while WHITE_RE.search(self.next()) is not None:
-            result += self.ch
-        return result
-
-    def eatComment(self):
-        start = self.pos
-        singleLine = self.peek() == "/"
-        self.next()
-        while self.next():
-            if not singleLine and self.ch == "*" and self.peek() == "/":
-                self.next()
-                break
-            elif singleLine and self.ch == "\n":
-                return self.source_text[start:self.pos]
-        return self.source_text[start:self.pos] + self.ch
-
-    def lookBack(self, string):
-        past = self.source_text[self.pos - len(string):self.pos]
-        return past.lower() == string
-
-    # Nested pseudo-class if we are insideRule
-    # and the next special character found opens
-    # a new block
-    def foundNestedPseudoClass(self):
-        i = self.pos + 1
-        openParen = 0
-        while i < len(self.source_text):
-            ch = self.source_text[i]
-            if ch == "{":
-                return True
-            elif ch == "(":
-                # pseudoclasses can contain ()
-                openParen += 1
-            elif ch == ")":
-                if openParen == 0:
-                    return False
-                openParen -= 1
-            elif ch == ";" or ch == "}":
-                return False
-            i += 1;
-
-        return False
-
-    def beautify(self):
-        printer = self.printer
-        insideRule = False
-        insidePropertyValue = False
-        enteringConditionalGroup = False
-        top_ch = ''
-        last_top_ch = ''
-        parenLevel = 0
-
-        while True:
-            whitespace = self.skipWhitespace()
-            isAfterSpace = whitespace != ''
-            isAfterNewline = '\n' in whitespace
-            last_top_ch = top_ch
-            top_ch = self.ch
-
-            if not self.ch:
-                break
-            elif self.ch == '/' and self.peek() == '*':
-                header = printer.indentLevel == 0
-
-                if not isAfterNewline or header:
-                    printer.newLine()
-
-
-                comment = self.eatComment()
-                printer.comment(comment)
-                printer.newLine()
-                if header:
-                    printer.newLine(True)
-            elif self.ch == '/' and self.peek() == '/':
-                if not isAfterNewline and last_top_ch != '{':
-                    printer.trim()
-
-                printer.singleSpace()
-                printer.comment(self.eatComment())
-                printer.newLine()
-            elif self.ch == '@':
-                printer.preserveSingleSpace(isAfterSpace)
-
-                # deal with less propery mixins @{...}
-                if self.peek(True) == '{':
-                    printer.push(self.eatString('}'));
-                else:
-                    printer.push(self.ch)
-                    # strip trailing space, if present, for hash property check
-                    variableOrRule = self.peekString(": ,;{}()[]/='\"")
-
-                    if variableOrRule[-1] in ": ":
-                        # wwe have a variable or pseudo-class, add it and insert one space before continuing
-                        self.next()
-                        variableOrRule = self.eatString(": ")
-                        if variableOrRule[-1].isspace():
-                            variableOrRule = variableOrRule[:-1]
-                        printer.push(variableOrRule)
-                        printer.singleSpace();
-
-                    if variableOrRule[-1].isspace():
-                        variableOrRule = variableOrRule[:-1]
-
-                    # might be a nesting at-rule
-                    if variableOrRule in self.NESTED_AT_RULE:
-                        printer.nestedLevel += 1
-                        if variableOrRule in self.CONDITIONAL_GROUP_RULE:
-                            enteringConditionalGroup = True
-            elif self.ch == '#' and self.peek() == '{':
-                printer.preserveSingleSpace(isAfterSpace)
-                printer.push(self.eatString('}'));
-            elif self.ch == '{':
-                if self.peek(True) == '}':
-                    self.eatWhitespace()
-                    self.next()
-                    printer.singleSpace()
-                    printer.push("{")
-                    printer.closeBracket(False)
-                    if self.newlines_from_last_ws_eat < 2 and self.opts.newline_between_rules and printer.indentLevel == 0:
-                        printer.newLine(True)
-                else:
-                    printer.indent()
-                    printer.openBracket()
-                    # when entering conditional groups, only rulesets are allowed
-                    if enteringConditionalGroup:
-                        enteringConditionalGroup = False
-                        insideRule = printer.indentLevel > printer.nestedLevel
-                    else:
-                        # otherwise, declarations are also allowed
-                        insideRule = printer.indentLevel >= printer.nestedLevel
-            elif self.ch == '}':
-                printer.outdent()
-                printer.closeBracket(True)
-                insideRule = False
-                insidePropertyValue = False
-                if printer.nestedLevel:
-                    printer.nestedLevel -= 1
-                if self.newlines_from_last_ws_eat < 2 and self.opts.newline_between_rules and printer.indentLevel == 0:
-                    printer.newLine(True)
-            elif self.ch == ":":
-                self.eatWhitespace()
-                if (insideRule or enteringConditionalGroup) and \
-                        not (self.lookBack('&') or self.foundNestedPseudoClass()) and \
-                        not self.lookBack('('):
-                    # 'property: value' delimiter
-                    # which could be in a conditional group query
-                    printer.push(":")
-                    if not insidePropertyValue:
-                        insidePropertyValue = True
-                        printer.singleSpace()
-                else:
-                    # sass/less parent reference don't use a space
-                    # sass nested pseudo-class don't use a space
-
-                    # preserve space before pseudoclasses/pseudoelements, as it means "in any child"
-                    if (self.lookBack(' ')) and (printer.output[-1] != ' '):
-                        printer.push(" ")
-                    if self.peek() == ":":
-                        # pseudo-element
-                        self.next()
-                        printer.push("::")
-                    else:
-                        # pseudo-element
-                        printer.push(":")
-            elif self.ch == '"' or self.ch == '\'':
-                printer.preserveSingleSpace(isAfterSpace)
-                printer.push(self.eatString(self.ch))
-            elif self.ch == ';':
-                insidePropertyValue = False
-                printer.semicolon()
-                if self.eatWhitespace(True) == 0:
-                    printer.newLine()
-            elif self.ch == '(':
-                # may be a url
-                if self.lookBack("url"):
-                    printer.push(self.ch)
-                    self.eatWhitespace()
-                    if self.next():
-                        if self.ch is not ')' and self.ch is not '"' \
-                        and self.ch is not '\'':
-                            printer.push(self.eatString(')'))
-                        else:
-                            self.pos = self.pos - 1
-                else:
-                    parenLevel += 1
-                    printer.preserveSingleSpace(isAfterSpace)
-                    printer.push(self.ch)
-                    self.eatWhitespace()
-            elif self.ch == ')':
-                printer.push(self.ch)
-                parenLevel -= 1
-            elif self.ch == ',':
-                printer.push(self.ch)
-                if self.eatWhitespace(True) == 0 and not insidePropertyValue and self.opts.selector_separator_newline and parenLevel < 1:
-                    printer.newLine()
-                else:
-                    printer.singleSpace()
-            elif (self.ch == '>' or self.ch == '+' or self.ch == '~') and \
-                not insidePropertyValue and parenLevel < 1:
-                # handle combinator spacing
-                if self.opts.space_around_combinator:
-                    printer.singleSpace()
-                    printer.push(self.ch)
-                    printer.singleSpace()
-                else:
-                    printer.push(self.ch)
-                    self.eatWhitespace()
-                    # squash extra whitespace
-                    if self.ch and WHITE_RE.search(self.ch):
-                        self.ch = ''
-            elif self.ch == ']':
-                printer.push(self.ch)
-            elif self.ch == '[':
-                printer.preserveSingleSpace(isAfterSpace)
-                printer.push(self.ch)
-            elif self.ch == '=':
-                # no whitespace before or after
-                self.eatWhitespace()
-                printer.push('=')
-                if WHITE_RE.search(self.ch):
-                    self.ch = ''
-            else:
-                printer.preserveSingleSpace(isAfterSpace)
-                printer.push(self.ch)
-
-        sweet_code = re.sub('[\r\n\t ]+$', '', printer.result())
-
-        # establish end_with_newline
-        if self.opts.end_with_newline:
-            sweet_code += '\n'
-
-        if not self.opts.eol == '\n':
-            sweet_code = sweet_code.replace('\n', self.opts.eol)
-
-        return sweet_code
diff --git a/python/cssbeautifier/__version__.py b/python/cssbeautifier/__version__.py
deleted file mode 100644
index 1f356cc57..000000000
--- a/python/cssbeautifier/__version__.py
+++ /dev/null
@@ -1 +0,0 @@
-__version__ = '1.0.0'
diff --git a/python/cssbeautifier/css/__init__.py b/python/cssbeautifier/css/__init__.py
new file mode 100644
index 000000000..0c0105543
--- /dev/null
+++ b/python/cssbeautifier/css/__init__.py
@@ -0,0 +1 @@
+# Empty file :)
diff --git a/python/cssbeautifier/css/beautifier.py b/python/cssbeautifier/css/beautifier.py
new file mode 100644
index 000000000..1c35a2c55
--- /dev/null
+++ b/python/cssbeautifier/css/beautifier.py
@@ -0,0 +1,452 @@
+from __future__ import print_function
+import sys
+import re
+import copy
+from .options import BeautifierOptions
+from jsbeautifier.core.options import mergeOpts
+from jsbeautifier.core.output import Output
+from jsbeautifier.__version__ import __version__
+
+#
+# The MIT License (MIT)
+
+# Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+
+def default_options():
+    return BeautifierOptions()
+
+
+def beautify(string, opts=default_options()):
+    b = Beautifier(string, opts)
+    return b.beautify()
+
+
+def beautify_file(file_name, opts=default_options()):
+    if file_name == '-':  # stdin
+        stream = sys.stdin
+    else:
+        stream = open(file_name)
+    content = ''.join(stream.readlines())
+    b = Beautifier(content, opts)
+    return b.beautify()
+
+
+def usage(stream=sys.stdout):
+
+    print("cssbeautifier.py@" + __version__ + """
+
+CSS beautifier (http://jsbeautifier.org/)
+
+""", file=stream)
+    if stream == sys.stderr:
+        return 1
+    else:
+        return 0
+
+WHITE_RE = re.compile("^\s+$")
+WORD_RE = re.compile("[\w$\-_]")
+
+
+class Printer:
+
+    def __init__(self, beautifier, indent_char, indent_size, default_indent=""):
+        self.beautifier = beautifier
+        self.newlines_from_last_ws_eat = 0
+        self.indentSize = indent_size
+        self.singleIndent = (indent_size) * indent_char
+        self.indentLevel = 0
+        self.nestedLevel = 0
+
+        self.baseIndentString = default_indent
+        self.output = Output(self.singleIndent, self.baseIndentString)
+
+    def indent(self):
+        self.indentLevel += 1
+
+    def outdent(self):
+        if self.indentLevel > 0:
+            self.indentLevel -= 1
+
+    def preserveSingleSpace(self,isAfterSpace):
+        if isAfterSpace:
+            self.output.space_before_token = True
+
+    def print_string(self, output_string):
+        if self.output.just_added_newline():
+            self.output.set_indent(self.indentLevel)
+
+        self.output.add_token(output_string)
+
+
+class Beautifier:
+
+    def __init__(self, source_text, opts=default_options()):
+        import jsbeautifier.core.acorn as acorn
+        self.lineBreak = acorn.lineBreak
+        self.allLineBreaks = acorn.allLineBreaks
+
+        if not source_text:
+            source_text = ''
+
+        opts = mergeOpts(opts, 'css')
+
+        # Continue to accept deprecated option
+        opts.space_around_combinator = opts.space_around_combinator or opts.space_around_selector_separator
+
+        self.opts = opts
+        self.indentSize = opts.indent_size
+        self.indentChar = opts.indent_char
+        self.pos = -1
+        self.ch = None
+
+        if self.opts.indent_with_tabs:
+            self.indentChar = "\t"
+            self.indentSize = 1
+
+        if self.opts.eol == 'auto':
+            self.opts.eol = '\n'
+            if self.lineBreak.search(source_text or ''):
+                self.opts.eol = self.lineBreak.search(source_text).group()
+
+        self.opts.eol = self.opts.eol.replace('\\r', '\r').replace('\\n', '\n')
+
+        # HACK: newline parsing inconsistent. This brute force normalizes the input newlines.
+        self.source_text = re.sub(self.allLineBreaks, '\n', source_text)
+
+        # https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
+        # also in CONDITIONAL_GROUP_RULE below
+        self.NESTED_AT_RULE = [ \
+            "@page", \
+            "@font-face", \
+            "@keyframes", \
+            "@media", \
+            "@supports", \
+            "@document"]
+        self.CONDITIONAL_GROUP_RULE = [ \
+            "@media", \
+            "@supports", \
+            "@document"]
+
+        m = re.search("^[\t ]*", self.source_text)
+        self.baseIndentString = m.group(0)
+
+    def next(self):
+        self.pos = self.pos + 1
+        if self.pos < len(self.source_text):
+            self.ch = self.source_text[self.pos]
+        else:
+            self.ch = ''
+        return self.ch
+
+    def peek(self,skipWhitespace=False):
+        start = self.pos
+        if skipWhitespace:
+            self.eatWhitespace()
+        result = ""
+        if self.pos + 1 < len(self.source_text):
+            result = self.source_text[self.pos + 1]
+        if skipWhitespace:
+            self.pos = start - 1
+            self.next()
+
+        return result
+
+    def eatString(self, endChars):
+        start = self.pos
+        while self.next():
+            if self.ch == "\\":
+                self.next()
+            elif self.ch in endChars:
+                break
+            elif self.ch == "\n":
+                break
+        return self.source_text[start:self.pos] + self.ch
+
+    def peekString(self, endChar):
+        start = self.pos
+        st = self.eatString(endChar)
+        self.pos = start - 1
+        self.next()
+        return st
+
+    def eatWhitespace(self, preserve_newlines_local=False):
+        result = 0
+        while WHITE_RE.search(self.peek()) is not None:
+            self.next()
+            if self.ch == "\n" and preserve_newlines_local and self.opts.preserve_newlines:
+                self.output.add_new_line(True)
+                result += 1
+        self.newlines_from_last_ws_eat = result
+        return result
+
+    def skipWhitespace(self):
+        result = ''
+        if self.ch and WHITE_RE.search(self.ch):
+            result = self.ch
+
+        while WHITE_RE.search(self.next()) is not None:
+            result += self.ch
+        return result
+
+    def eatComment(self):
+        start = self.pos
+        singleLine = self.peek() == "/"
+        self.next()
+        while self.next():
+            if not singleLine and self.ch == "*" and self.peek() == "/":
+                self.next()
+                break
+            elif singleLine and self.ch == "\n":
+                return self.source_text[start:self.pos]
+        return self.source_text[start:self.pos] + self.ch
+
+    def lookBack(self, string):
+        past = self.source_text[self.pos - len(string):self.pos]
+        return past.lower() == string
+
+    # Nested pseudo-class if we are insideRule
+    # and the next special character found opens
+    # a new block
+    def foundNestedPseudoClass(self):
+        i = self.pos + 1
+        openParen = 0
+        while i < len(self.source_text):
+            ch = self.source_text[i]
+            if ch == "{":
+                return True
+            elif ch == "(":
+                # pseudoclasses can contain ()
+                openParen += 1
+            elif ch == ")":
+                if openParen == 0:
+                    return False
+                openParen -= 1
+            elif ch == ";" or ch == "}":
+                return False
+            i += 1
+
+        return False
+
+    def beautify(self):
+        printer = Printer(self, self.indentChar, self.indentSize, self.baseIndentString)
+        self.output = printer.output
+        output = self.output
+
+        self.pos = -1
+        self.ch = None
+
+        insideRule = False
+        insidePropertyValue = False
+        enteringConditionalGroup = False
+        top_ch = ''
+        last_top_ch = ''
+        parenLevel = 0
+
+        while True:
+            whitespace = self.skipWhitespace()
+            isAfterSpace = whitespace != ''
+            isAfterNewline = '\n' in whitespace
+            last_top_ch = top_ch
+            top_ch = self.ch
+
+            if not self.ch:
+                break
+            elif self.ch == '/' and self.peek() == '*':
+                header = printer.indentLevel == 0
+
+                if not isAfterNewline or header:
+                    output.add_new_line()
+
+                printer.print_string(self.eatComment())
+                output.add_new_line()
+                if header:
+                    output.add_new_line(True)
+            elif self.ch == '/' and self.peek() == '/':
+                if not isAfterNewline and last_top_ch != '{':
+                    output.trim(True)
+
+                output.space_before_token = True
+                printer.print_string(self.eatComment())
+                output.add_new_line()
+            elif self.ch == '@':
+                printer.preserveSingleSpace(isAfterSpace)
+
+                # deal with less propery mixins @{...}
+                if self.peek(True) == '{':
+                    printer.print_string(self.eatString('}'));
+                else:
+                    printer.print_string(self.ch)
+                    # strip trailing space, if present, for hash property check
+                    variableOrRule = self.peekString(": ,;{}()[]/='\"")
+
+                    if variableOrRule[-1] in ": ":
+                        # wwe have a variable or pseudo-class, add it and insert one space before continuing
+                        self.next()
+                        variableOrRule = self.eatString(": ")
+                        if variableOrRule[-1].isspace():
+                            variableOrRule = variableOrRule[:-1]
+                        printer.print_string(variableOrRule)
+                        output.space_before_token = True
+
+                    if variableOrRule[-1].isspace():
+                        variableOrRule = variableOrRule[:-1]
+
+                    # might be a nesting at-rule
+                    if variableOrRule in self.NESTED_AT_RULE:
+                        printer.nestedLevel += 1
+                        if variableOrRule in self.CONDITIONAL_GROUP_RULE:
+                            enteringConditionalGroup = True
+            elif self.ch == '#' and self.peek() == '{':
+                printer.preserveSingleSpace(isAfterSpace)
+                printer.print_string(self.eatString('}'));
+            elif self.ch == '{':
+                if self.peek(True) == '}':
+                    self.eatWhitespace()
+                    self.next()
+                    output.space_before_token = True
+                    printer.print_string("{}")
+                    if self.eatWhitespace(True) == 0:
+                        output.add_new_line()
+
+                    if self.newlines_from_last_ws_eat < 2 and self.opts.newline_between_rules and printer.indentLevel == 0:
+                        output.add_new_line(True)
+                else:
+                    printer.indent()
+                    output.space_before_token = True
+                    printer.print_string(self.ch)
+                    if self.eatWhitespace(True) == 0:
+                        output.add_new_line()
+
+                    # when entering conditional groups, only rulesets are allowed
+                    if enteringConditionalGroup:
+                        enteringConditionalGroup = False
+                        insideRule = printer.indentLevel > printer.nestedLevel
+                    else:
+                        # otherwise, declarations are also allowed
+                        insideRule = printer.indentLevel >= printer.nestedLevel
+            elif self.ch == '}':
+                printer.outdent()
+                output.add_new_line()
+                printer.print_string(self.ch)
+                insideRule = False
+                insidePropertyValue = False
+                if printer.nestedLevel:
+                    printer.nestedLevel -= 1
+
+                if self.eatWhitespace(True) == 0:
+                    output.add_new_line()
+
+
+                if self.newlines_from_last_ws_eat < 2 and self.opts.newline_between_rules and printer.indentLevel == 0:
+                    output.add_new_line(True)
+            elif self.ch == ":":
+                self.eatWhitespace()
+                if (insideRule or enteringConditionalGroup) and \
+                        not (self.lookBack('&') or self.foundNestedPseudoClass()) and \
+                        not self.lookBack('('):
+                    # 'property: value' delimiter
+                    # which could be in a conditional group query
+                    printer.print_string(":")
+                    if not insidePropertyValue:
+                        insidePropertyValue = True
+                        output.space_before_token = True
+
+                else:
+                    # sass/less parent reference don't use a space
+                    # sass nested pseudo-class don't use a space
+
+                    # preserve space before pseudoclasses/pseudoelements, as it means "in any child"
+                    if self.lookBack(' '):
+                        output.space_before_token = True
+                    if self.peek() == ":":
+                        # pseudo-element
+                        self.next()
+                        printer.print_string("::")
+                    else:
+                        # pseudo-element
+                        printer.print_string(":")
+            elif self.ch == '"' or self.ch == '\'':
+                printer.preserveSingleSpace(isAfterSpace)
+                printer.print_string(self.eatString(self.ch))
+            elif self.ch == ';':
+                insidePropertyValue = False
+                printer.print_string(self.ch)
+                if self.eatWhitespace(True) == 0:
+                    output.add_new_line()
+            elif self.ch == '(':
+                # may be a url
+                if self.lookBack("url"):
+                    printer.print_string(self.ch)
+                    self.eatWhitespace()
+                    if self.next():
+                        if self.ch is not ')' and self.ch is not '"' \
+                        and self.ch is not '\'':
+                            printer.print_string(self.eatString(')'))
+                        else:
+                            self.pos = self.pos - 1
+                else:
+                    parenLevel += 1
+                    printer.preserveSingleSpace(isAfterSpace)
+                    printer.print_string(self.ch)
+                    self.eatWhitespace()
+            elif self.ch == ')':
+                printer.print_string(self.ch)
+                parenLevel -= 1
+            elif self.ch == ',':
+                printer.print_string(self.ch)
+                if self.eatWhitespace(True) == 0 and not insidePropertyValue and self.opts.selector_separator_newline and parenLevel < 1:
+                    output.add_new_line()
+                else:
+                    output.space_before_token = True
+            elif (self.ch == '>' or self.ch == '+' or self.ch == '~') and \
+                not insidePropertyValue and parenLevel < 1:
+                # handle combinator spacing
+                if self.opts.space_around_combinator:
+                    output.space_before_token = True
+                    printer.print_string(self.ch)
+                    output.space_before_token = True
+                else:
+                    printer.print_string(self.ch)
+                    self.eatWhitespace()
+                    # squash extra whitespace
+                    if self.ch and WHITE_RE.search(self.ch):
+                        self.ch = ''
+            elif self.ch == ']':
+                printer.print_string(self.ch)
+            elif self.ch == '[':
+                printer.preserveSingleSpace(isAfterSpace)
+                printer.print_string(self.ch)
+            elif self.ch == '=':
+                # no whitespace before or after
+                self.eatWhitespace()
+                printer.print_string('=')
+                if WHITE_RE.search(self.ch):
+                    self.ch = ''
+            else:
+                printer.preserveSingleSpace(isAfterSpace)
+                printer.print_string(self.ch)
+
+        sweet_code = output.get_code(self.opts.end_with_newline, self.opts.eol)
+
+        return sweet_code
diff --git a/python/cssbeautifier/css/options.py b/python/cssbeautifier/css/options.py
new file mode 100644
index 000000000..38db937fe
--- /dev/null
+++ b/python/cssbeautifier/css/options.py
@@ -0,0 +1,57 @@
+#
+# The MIT License (MIT)
+
+# Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+class BeautifierOptions:
+    def __init__(self):
+        self.indent_size = 4
+        self.indent_char = ' '
+        self.indent_with_tabs = False
+        self.preserve_newlines = False
+        self.selector_separator_newline = True
+        self.end_with_newline = False
+        self.newline_between_rules = True
+        self.space_around_combinator = False
+        self.eol = 'auto'
+
+        self.css = None
+        self.js = None
+        self.html = None
+
+        # deprecated
+        self.space_around_selector_separator = False
+
+    def __repr__(self):
+        return \
+"""indent_size = %d
+indent_char = [%s]
+indent_with_tabs = [%s]
+preserve_newlines = [%s]
+separate_selectors_newline = [%s]
+end_with_newline = [%s]
+newline_between_rules = [%s]
+space_around_combinator = [%s]
+""" % (self.indent_size, self.indent_char, self.indent_with_tabs, self.preserve_newlines,
+    self.selector_separator_newline, self.end_with_newline, self.newline_between_rules,
+    self.space_around_combinator)
diff --git a/python/jsbeautifier/__init__.py b/python/jsbeautifier/__init__.py
index 9eecb59de..444895b80 100644
--- a/python/jsbeautifier/__init__.py
+++ b/python/jsbeautifier/__init__.py
@@ -8,6 +8,8 @@
 import errno
 import copy
 from jsbeautifier.__version__ import __version__
+from jsbeautifier.javascript.options import BeautifierOptions
+from jsbeautifier.javascript.beautifier import Beautifier
 
 #
 # The MIT License (MIT)
@@ -60,199 +62,6 @@
 # Here are the available options: (read source)
 
 
-class BeautifierOptions:
-    def __init__(self):
-        self.indent_size = 4
-        self.indent_char = ' '
-        self.indent_with_tabs = False
-        self.eol = 'auto'
-        self.preserve_newlines = True
-        self.max_preserve_newlines = 10
-        self.space_in_paren = False
-        self.space_in_empty_paren = False
-        self.e4x = False
-        self.jslint_happy = False
-        self.space_after_anon_function = False
-        self.brace_style = 'collapse'
-        self.keep_array_indentation = False
-        self.keep_function_indentation = False
-        self.eval_code = False
-        self.unescape_strings = False
-        self.wrap_line_length = 0
-        self.break_chained_methods = False
-        self.end_with_newline = False
-        self.comma_first = False
-        self.operator_position = 'before-newline'
-
-        self.css = None
-        self.js = None
-        self.html = None
-
-        # For testing of beautify ignore:start directive
-        self.test_output_raw = False
-        self.editorconfig = False
-
-
-
-    def mergeOpts(self, targetType):
-        finalOpts = copy.copy(self)
-
-        local = getattr(finalOpts, targetType)
-        if (local):
-            delattr(finalOpts, targetType)
-            for key in local:
-                setattr(finalOpts, key, local[key])
-
-        return finalOpts
-
-    def __repr__(self):
-        return \
-"""indent_size = %d
-indent_char = [%s]
-preserve_newlines = %s
-max_preserve_newlines = %d
-space_in_paren = %s
-jslint_happy = %s
-space_after_anon_function = %s
-indent_with_tabs = %s
-brace_style = %s
-keep_array_indentation = %s
-eval_code = %s
-wrap_line_length = %s
-unescape_strings = %s
-""" % ( self.indent_size,
-        self.indent_char,
-        self.preserve_newlines,
-        self.max_preserve_newlines,
-        self.space_in_paren,
-        self.jslint_happy,
-        self.space_after_anon_function,
-        self.indent_with_tabs,
-        self.brace_style,
-        self.keep_array_indentation,
-        self.eval_code,
-        self.wrap_line_length,
-        self.unescape_strings,
-        )
-
-
-class BeautifierFlags:
-    def __init__(self, mode):
-        self.mode = mode
-        self.parent = None
-        self.last_text = ''
-        self.last_word = ''
-        self.declaration_statement = False
-        self.declaration_assignment = False
-        self.multiline_frame = False
-        self.inline_frame = False
-        self.if_block = False
-        self.else_block = False
-        self.do_block = False
-        self.do_while = False
-        self.import_block = False
-        self.in_case = False
-        self.in_case_statement = False
-        self.case_body = False
-        self.indentation_level = 0
-        self.line_indent_level = 0
-        self.start_line_index = 0
-        self.ternary_depth = 0
-
-    def apply_base(self, flags_base, added_newline):
-        next_indent_level = flags_base.indentation_level
-        if not added_newline and \
-            flags_base.line_indent_level > next_indent_level:
-            next_indent_level = flags_base.line_indent_level
-
-        self.parent = flags_base
-        self.last_text = flags_base.last_text
-        self.last_word = flags_base.last_word
-        self.indentation_level = next_indent_level
-
-class Acorn:
-    def __init__(self):
-        # This is not pretty, but given how we did the version import
-        # it is the only way to do this without having setup.py fail on a missing six dependency.
-        self.six = __import__("six")
-        # This section of code was translated to python from acorn (javascript).
-        #
-        # Acorn was written by Marijn Haverbeke and released under an MIT
-        # license. The Unicode regexps (for identifiers and whitespace) were
-        # taken from [Esprima](http://esprima.org) by Ariya Hidayat.
-        #
-        # Git repositories for Acorn are available at
-        #
-        #     http://marijnhaverbeke.nl/git/acorn
-        #     https://github.com/marijnh/acorn.git
-
-        # ## Character categories
-
-        # Big ugly regular expressions that match characters in the
-        # whitespace, identifier, and identifier-start categories. These
-        # are only applied when a character is found to actually have a
-        # code point above 128.
-
-        self.nonASCIIwhitespace = re.compile(self.six.u("[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]"))
-        self.nonASCIIidentifierStartChars = self.six.u("\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")
-        self.nonASCIIidentifierChars = self.six.u("\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")
-        self.nonASCIIidentifierStart = re.compile("[" + self.nonASCIIidentifierStartChars + "]")
-        self.nonASCIIidentifier = re.compile("[" + self.nonASCIIidentifierStartChars + self.nonASCIIidentifierChars + "]")
-
-        # Whether a single character denotes a newline.
-
-        self.newline = re.compile(self.six.u("[\n\r\u2028\u2029]"))
-
-        # Matches a whole line break (where CRLF is considered a single
-        # line break). Used to count lines.
-
-        # in javascript, these two differ
-        # in python they are the same, different methods are called on them
-        self.lineBreak = re.compile(self.six.u("\r\n|[\n\r\u2028\u2029]"))
-        self.allLineBreaks = self.lineBreak
-
-
-    # Test whether a given character code starts an identifier.
-    def isIdentifierStart(self, code):
-        if code < 65:
-            return code in [36, 64] # permit $ (36) and @ (64). @ is used in ES7 decorators.
-        if code < 91:
-            return True # 65 through 91 are uppercase letters
-        if code < 97:
-            return code == 95 # permit _ (95)
-        if code < 123:
-            return True # 97 through 123 are lowercase letters
-        return code >= 0xaa and self.nonASCIIidentifierStart.match(self.six.unichr(code)) != None
-
-    # Test whether a given character is part of an identifier.
-    def isIdentifierChar(self, code):
-        if code < 48:
-            return code == 36
-        if code < 58:
-            return True
-        if code < 65:
-            return False
-        if code < 91:
-            return True
-        if code < 97:
-            return code == 95
-        if code < 123:
-            return True
-        return code >= 0xaa and self.nonASCIIidentifier.match(self.six.unichr(code)) != None
-
-class Token:
-    def __init__(self, type, text, newlines = 0, whitespace_before = '', mode = None, parent = None):
-        self.type = type
-        self.text = text
-        self.comments_before = []
-        self.newlines = newlines
-        self.wanted_newline = newlines > 0
-        self.whitespace_before = whitespace_before
-        self.parent = None
-        self.opened = None
-        self.directives = None
-
-
 def default_options():
     return BeautifierOptions()
 
@@ -374,1109 +183,6 @@ def usage(stream=sys.stdout):
     else:
         return 0
 
-OPERATOR_POSITION = {
-    'before_newline': 'before-newline',
-    'after_newline': 'after-newline',
-    'preserve_newline': 'preserve-newline'
-}
-OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION['before_newline'], OPERATOR_POSITION['preserve_newline']];
-
-def sanitizeOperatorPosition(opPosition):
-    if not opPosition:
-        return OPERATOR_POSITION['before_newline']
-    elif opPosition not in OPERATOR_POSITION.values():
-        raise ValueError("Invalid Option Value: The option 'operator_position' must be one of the following values\n" +
-            str(OPERATOR_POSITION.values()) +
-            "\nYou passed in: '" + opPosition + "'")
-
-    return opPosition
-
-class MODE:
-      BlockStatement, Statement, ObjectLiteral, ArrayLiteral, \
-      ForInitializer, Conditional, Expression = range(7)
-
-class Beautifier:
-
-    def __init__(self, opts = default_options() ):
-
-        self.opts = copy.copy(opts)
-        self.acorn = Acorn()
-        self.blank_state()
-
-    def blank_state(self, js_source_text = None):
-
-        # internal flags
-        self.flags = None
-        self.previous_flags = None
-        self.flag_store = []
-        self.tokens = []
-        self.token_pos = 0
-
-
-        # force opts.space_after_anon_function to true if opts.jslint_happy
-        if self.opts.jslint_happy:
-            self.opts.space_after_anon_function = True
-
-        if self.opts.indent_with_tabs:
-            self.opts.indent_char = "\t"
-            self.opts.indent_size = 1
-
-        if self.opts.eol == 'auto':
-            self.opts.eol = '\n'
-            if self.acorn.lineBreak.search(js_source_text or ''):
-                self.opts.eol = self.acorn.lineBreak.search(js_source_text).group()
-
-        self.opts.eol = self.opts.eol.replace('\\r', '\r').replace('\\n', '\n')
-
-        self.indent_string = self.opts.indent_char * self.opts.indent_size
-
-        self.baseIndentString = ''
-        self.last_type = 'TK_START_BLOCK' # last token type
-        self.last_last_text = ''         # pre-last token text
-
-        preindent_index = 0;
-        if not js_source_text == None and len(js_source_text) > 0:
-            while preindent_index < len(js_source_text) and \
-                    js_source_text[preindent_index] in [' ', '\t'] :
-                self.baseIndentString += js_source_text[preindent_index]
-                preindent_index += 1
-            js_source_text = js_source_text[preindent_index:]
-
-        self.output = Output(self.indent_string, self.baseIndentString)
-        # If testing the ignore directive, start with output disable set to true
-        self.output.raw = self.opts.test_output_raw;
-
-        self.set_mode(MODE.BlockStatement)
-        return js_source_text
-
-    def beautify(self, s, opts = None ):
-
-        if opts != None:
-            opts = opts.mergeOpts('js')
-            self.opts = copy.copy(opts)
-
-
-        #Compat with old form
-        if self.opts.brace_style == 'collapse-preserve-inline':
-            self.opts.brace_style = 'collapse,preserve-inline'
-
-        split = re.compile("[^a-zA-Z0-9_\-]+").split(self.opts.brace_style)
-        self.opts.brace_style = split[0]
-        self.opts.brace_preserve_inline = (True if bool(split[1] == 'preserve-inline') else None) if len(split) > 1 else False
-
-        if self.opts.brace_style not in ['expand', 'collapse', 'end-expand', 'none']:
-            raise(Exception('opts.brace_style must be "expand", "collapse", "end-expand", or "none".'))
-
-        if self.opts.brace_preserve_inline == None:
-            raise(Exception('opts.brace_style second item must be "preserve-inline"'))
-
-        s = self.blank_state(s)
-
-        input = self.unpack(s, self.opts.eval_code)
-
-        self.handlers = {
-            'TK_START_EXPR': self.handle_start_expr,
-            'TK_END_EXPR': self.handle_end_expr,
-            'TK_START_BLOCK': self.handle_start_block,
-            'TK_END_BLOCK': self.handle_end_block,
-            'TK_WORD': self.handle_word,
-            'TK_RESERVED': self.handle_word,
-            'TK_SEMICOLON': self.handle_semicolon,
-            'TK_STRING': self.handle_string,
-            'TK_EQUALS': self.handle_equals,
-            'TK_OPERATOR': self.handle_operator,
-            'TK_COMMA': self.handle_comma,
-            'TK_BLOCK_COMMENT': self.handle_block_comment,
-            'TK_COMMENT': self.handle_comment,
-            'TK_DOT': self.handle_dot,
-            'TK_UNKNOWN': self.handle_unknown,
-            'TK_EOF': self.handle_eof
-        }
-
-        self.tokens = Tokenizer(input, self.opts, self.indent_string).tokenize()
-        self.token_pos = 0
-
-        current_token = self.get_token()
-        while current_token != None:
-            self.handlers[current_token.type](current_token)
-
-            self.last_last_text = self.flags.last_text
-            self.last_type = current_token.type
-            self.flags.last_text = current_token.text
-            self.token_pos += 1
-            current_token = self.get_token()
-
-
-        sweet_code = self.output.get_code()
-        if self.opts.end_with_newline:
-            sweet_code += '\n'
-
-        if not self.opts.eol == '\n':
-            sweet_code = sweet_code.replace('\n', self.opts.eol)
-
-        return sweet_code
-
-
-    def handle_whitespace_and_comments(self, local_token, preserve_statement_flags = False):
-        newlines = local_token.newlines
-        keep_whitespace = self.opts.keep_array_indentation and self.is_array(self.flags.mode)
-
-        for comment_token in local_token.comments_before:
-            # The cleanest handling of inline comments is to treat them as though they aren't there.
-            # Just continue formatting and the behavior should be logical.
-            # Also ignore unknown tokens.  Again, this should result in better behavior.
-            self.handle_whitespace_and_comments(comment_token, preserve_statement_flags)
-            self.handlers[comment_token.type](comment_token, preserve_statement_flags)
-
-
-        if keep_whitespace:
-             for i in range(newlines):
-                    self.print_newline(i > 0, preserve_statement_flags)
-        else: # not keep_whitespace
-            if self.opts.max_preserve_newlines != 0 and newlines > self.opts.max_preserve_newlines:
-                newlines = self.opts.max_preserve_newlines
-
-            if self.opts.preserve_newlines and newlines > 1:
-                self.print_newline(False, preserve_statement_flags)
-                for i in range(1, newlines):
-                    self.print_newline(True, preserve_statement_flags)
-
-
-    def unpack(self, source, evalcode=False):
-        import jsbeautifier.unpackers as unpackers
-        try:
-            return unpackers.run(source, evalcode)
-        except unpackers.UnpackingError as error:
-            return source
-
-    def is_special_word(self, s):
-        return s in ['case', 'return', 'do', 'if', 'throw', 'else']
-
-    def is_array(self, mode):
-        return mode == MODE.ArrayLiteral
-
-
-    def is_expression(self, mode):
-        return mode in [MODE.Expression, MODE.ForInitializer, MODE.Conditional]
-
-
-    _newline_restricted_tokens = ['break','continue','return', 'throw']
-    def allow_wrap_or_preserved_newline(self, current_token, force_linewrap = False):
-        # never wrap the first token of a line.
-        if self.output.just_added_newline():
-            return
-
-        shouldPreserveOrForce = (self.opts.preserve_newlines and current_token.wanted_newline) or force_linewrap
-        operatorLogicApplies = self.flags.last_text in Tokenizer.positionable_operators or current_token.text in Tokenizer.positionable_operators
-
-        if operatorLogicApplies:
-            shouldPrintOperatorNewline = (self.flags.last_text in Tokenizer.positionable_operators and self.opts.operator_position in OPERATOR_POSITION_BEFORE_OR_PRESERVE) \
-                or current_token.text in Tokenizer.positionable_operators
-            shouldPreserveOrForce = shouldPreserveOrForce and shouldPrintOperatorNewline
-
-        if shouldPreserveOrForce:
-            self.print_newline(preserve_statement_flags = True)
-        elif self.opts.wrap_line_length > 0:
-            if self.last_type == 'TK_RESERVED' and self.flags.last_text in self._newline_restricted_tokens:
-                # These tokens should never have a newline inserted between
-                # them and the following expression.
-                return
-            proposed_line_length = self.output.current_line.get_character_count() + len(current_token.text)
-            if self.output.space_before_token:
-                proposed_line_length += 1
-
-            if proposed_line_length >= self.opts.wrap_line_length:
-                self.print_newline(preserve_statement_flags = True)
-
-
-    def print_newline(self, force_newline = False, preserve_statement_flags = False):
-        if not preserve_statement_flags:
-            if self.flags.last_text != ';' and self.flags.last_text != ',' and self.flags.last_text != '=' and self.last_type != 'TK_OPERATOR':
-                next_token = self.get_token(1)
-                while (self.flags.mode == MODE.Statement and
-                        not (self.flags.if_block and next_token and next_token.type == 'TK_RESERVED' and next_token.text == 'else') and
-                        not self.flags.do_block):
-                    self.restore_mode()
-
-        if self.output.add_new_line(force_newline):
-            self.flags.multiline_frame = True
-
-    def print_token_line_indentation(self, current_token):
-        if self.output.just_added_newline():
-            line = self.output.current_line
-            if self.opts.keep_array_indentation and self.is_array(self.flags.mode) and current_token.wanted_newline:
-                line.push(current_token.whitespace_before)
-                self.output.space_before_token = False
-            elif self.output.set_indent(self.flags.indentation_level):
-                self.flags.line_indent_level = self.flags.indentation_level
-
-
-    def print_token(self, current_token, s=None):
-        if self.output.raw:
-            self.output.add_raw_token(current_token)
-            return
-
-        if self.opts.comma_first and self.last_type == 'TK_COMMA' and self.output.just_added_newline():
-            if self.output.previous_line.last() == ',':
-                # if the comma was already at the start of the line,
-                # pull back onto that line and reprint the indentation
-                popped = self.output.previous_line.pop()
-                if  self.output.previous_line.is_empty():
-                     self.output.previous_line.push(popped)
-                     self.output.trim(True)
-                     self.output.current_line.pop()
-                     self.output.trim()
-
-                # add the comma in front of the next token
-                self.print_token_line_indentation(current_token)
-                self.output.add_token(',')
-                self.output.space_before_token = True
-
-        if s == None:
-            s = current_token.text
-
-        self.print_token_line_indentation(current_token)
-        self.output.add_token(s);
-
-
-    def indent(self):
-        self.flags.indentation_level += 1
-
-    def deindent(self):
-        allow_deindent = self.flags.indentation_level > 0 and ((self.flags.parent == None) or self.flags.indentation_level > self.flags.parent.indentation_level)
-
-        if allow_deindent:
-            self.flags.indentation_level -= 1
-
-    def set_mode(self, mode):
-        if self.flags:
-            self.flag_store.append(self.flags)
-            self.previous_flags = self.flags
-        else:
-            self.previous_flags = BeautifierFlags(mode)
-
-        self.flags = BeautifierFlags(mode)
-        self.flags.apply_base(self.previous_flags, self.output.just_added_newline())
-        self.flags.start_line_index = self.output.get_line_number();
-
-    def restore_mode(self):
-        if len(self.flag_store) > 0:
-            self.previous_flags = self.flags
-            self.flags = self.flag_store.pop()
-            if self.previous_flags.mode == MODE.Statement:
-                self.output.remove_redundant_indentation(self.previous_flags)
-
-
-    def start_of_object_property(self):
-        return self.flags.parent.mode == MODE.ObjectLiteral and self.flags.mode == MODE.Statement and \
-                ((self.flags.last_text == ':' and self.flags.ternary_depth == 0) or (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set']))
-
-    def start_of_statement(self, current_token):
-        if (
-            (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const'] and current_token.type == 'TK_WORD') \
-                or (self.last_type == 'TK_RESERVED' and self.flags.last_text== 'do') \
-                or (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['return', 'throw'] and not current_token.wanted_newline) \
-                or (self.last_type == 'TK_RESERVED' and self.flags.last_text == 'else' \
-                    and not (current_token.type == 'TK_RESERVED' and current_token.text == 'if' and not len(current_token.comments_before))) \
-                or (self.last_type == 'TK_END_EXPR' and (self.previous_flags.mode == MODE.ForInitializer or self.previous_flags.mode == MODE.Conditional)) \
-                or (self.last_type == 'TK_WORD' and self.flags.mode == MODE.BlockStatement \
-                    and not self.flags.in_case
-                    and not (current_token.text == '--' or current_token.text == '++')
-                    and self.last_last_text != 'function'
-                    and current_token.type != 'TK_WORD' and current_token.type != 'TK_RESERVED') \
-                or (self.flags.mode == MODE.ObjectLiteral and \
-                    ((self.flags.last_text == ':' and self.flags.ternary_depth == 0) or (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set'])))
-                ):
-
-            self.set_mode(MODE.Statement)
-            self.indent()
-
-            self.handle_whitespace_and_comments(current_token, True);
-
-            # Issue #276:
-            # If starting a new statement with [if, for, while, do], push to a new line.
-            # if (a) if (b) if(c) d(); else e(); else f();
-            if not self.start_of_object_property():
-                self.allow_wrap_or_preserved_newline(current_token, current_token.type == 'TK_RESERVED' and current_token.text in ['do', 'for', 'if', 'while'])
-
-            return True
-        else:
-            return False
-
-    def get_token(self, offset = 0):
-        index = self.token_pos + offset
-        if index < 0 or index >= len(self.tokens):
-            return None
-        else:
-            return self.tokens[index]
-
-
-    def handle_start_expr(self, current_token):
-        if self.start_of_statement(current_token):
-            # The conditional starts the statement if appropriate.
-            pass
-        else:
-            self.handle_whitespace_and_comments(current_token)
-
-        next_mode = MODE.Expression
-
-        if current_token.text == '[':
-            if self.last_type == 'TK_WORD' or self.flags.last_text == ')':
-                if self.last_type == 'TK_RESERVED' and self.flags.last_text in Tokenizer.line_starters:
-                    self.output.space_before_token = True
-                self.set_mode(next_mode)
-                self.print_token(current_token)
-                self.indent()
-                if self.opts.space_in_paren:
-                    self.output.space_before_token = True
-                return
-
-            next_mode = MODE.ArrayLiteral
-
-            if self.is_array(self.flags.mode):
-                if self.flags.last_text == '[' or (
-                    self.flags.last_text == ',' and (self.last_last_text == ']' or self.last_last_text == '}')):
-                    # ], [ goes to a new line
-                    # }, [ goes to a new line
-                    if not self.opts.keep_array_indentation:
-                        self.print_newline()
-
-        else:
-            if self.last_type == 'TK_RESERVED' and self.flags.last_text == 'for':
-                next_mode = MODE.ForInitializer
-            elif self.last_type == 'TK_RESERVED' and self.flags.last_text in ['if', 'while']:
-                next_mode = MODE.Conditional
-            else:
-                next_mode = MODE.Expression
-
-
-        if self.flags.last_text == ';' or self.last_type == 'TK_START_BLOCK':
-            self.print_newline()
-        elif self.last_type in ['TK_END_EXPR', 'TK_START_EXPR', 'TK_END_BLOCK'] or self.flags.last_text == '.':
-            # do nothing on (( and )( and ][ and ]( and .(
-            # TODO: Consider whether forcing this is required.  Review failing tests when removed.
-            self.allow_wrap_or_preserved_newline(current_token, current_token.wanted_newline)
-
-        elif not (self.last_type == 'TK_RESERVED' and current_token.text == '(') and self.last_type not in ['TK_WORD', 'TK_OPERATOR']:
-            self.output.space_before_token = True
-        elif (self.last_type == 'TK_RESERVED' and (self.flags.last_word == 'function' or self.flags.last_word == 'typeof')) or \
-            (self.flags.last_text == '*' and (
-                self.last_last_text in ['function', 'yield'] or
-                (self.flags.mode == MODE.ObjectLiteral and self.last_last_text in ['{', ',']))):
-            # function() vs function (), typeof() vs typeof ()
-            # function*() vs function* (), yield*() vs yield* ()
-            if self.opts.space_after_anon_function:
-                self.output.space_before_token = True
-        elif self.last_type == 'TK_RESERVED' and (self.flags.last_text in Tokenizer.line_starters or self.flags.last_text == 'catch'):
-            # TODO: option space_before_conditional
-            self.output.space_before_token = True
-
-        elif current_token.text == '(' and self.last_type == 'TK_RESERVED' and self.flags.last_word == 'await':
-            self.output.space_before_token = True
-
-
-        # Support of this kind of newline preservation:
-        # a = (b &&
-        #     (c || d));
-        if self.last_type in ['TK_EQUALS', 'TK_OPERATOR']:
-            if not self.start_of_object_property():
-                self.allow_wrap_or_preserved_newline(current_token)
-
-
-        # Support preserving wrapped arrow function expressions
-        # a.b('c',
-        #     () => d.e
-        # )
-        if current_token.text == '(' and self.last_type not in ['TK_WORD', 'TK_RESERVED']:
-            self.allow_wrap_or_preserved_newline(current_token)
-
-
-        self.set_mode(next_mode)
-        self.print_token(current_token)
-
-        if self.opts.space_in_paren:
-            self.output.space_before_token = True
-
-        # In all cases, if we newline while inside an expression it should be indented.
-        self.indent()
-
-
-
-    def handle_end_expr(self, current_token):
-        # statements inside expressions are not valid syntax, but...
-        # statements must all be closed when their container closes
-        while self.flags.mode == MODE.Statement:
-            self.restore_mode()
-
-        self.handle_whitespace_and_comments(current_token)
-
-        if self.flags.multiline_frame:
-            self.allow_wrap_or_preserved_newline(current_token, current_token.text == ']' and self.is_array(self.flags.mode) and not self.opts.keep_array_indentation)
-
-        if self.opts.space_in_paren:
-            if self.last_type == 'TK_START_EXPR' and not self.opts.space_in_empty_paren:
-                # empty parens are always "()" and "[]", not "( )" or "[ ]"
-                self.output.space_before_token = False
-                self.output.trim()
-            else:
-                self.output.space_before_token = True
-
-        if current_token.text == ']' and self.opts.keep_array_indentation:
-            self.print_token(current_token)
-            self.restore_mode()
-        else:
-            self.restore_mode()
-            self.print_token(current_token)
-
-        self.output.remove_redundant_indentation(self.previous_flags)
-
-        # do {} while () // no statement required after
-        if self.flags.do_while and self.previous_flags.mode == MODE.Conditional:
-            self.previous_flags.mode = MODE.Expression
-            self.flags.do_block = False
-            self.flags.do_while = False
-
-    def handle_start_block(self, current_token):
-        self.handle_whitespace_and_comments(current_token)
-
-        # Check if this is a BlockStatement that should be treated as a ObjectLiteral
-        next_token = self.get_token(1)
-        second_token = self.get_token(2)
-        if second_token != None and \
-            ((second_token.text in [':', ','] and next_token.type in ['TK_STRING', 'TK_WORD', 'TK_RESERVED']) \
-                or (next_token.text in ['get', 'set', '...'] and second_token.type in ['TK_WORD', 'TK_RESERVED'])):
-            # We don't support TypeScript,but we didn't break it for a very long time.
-            # We'll try to keep not breaking it.
-            if not self.last_last_text in ['class','interface']:
-                self.set_mode(MODE.ObjectLiteral)
-            else:
-                self.set_mode(MODE.BlockStatement)
-        elif self.last_type == 'TK_OPERATOR' and self.flags.last_text == '=>':
-            # arrow function: (param1, paramN) => { statements }
-            self.set_mode(MODE.BlockStatement)
-        elif self.last_type in ['TK_EQUALS', 'TK_START_EXPR', 'TK_COMMA', 'TK_OPERATOR'] or \
-            (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['return', 'throw', 'import', 'default']):
-            # Detecting shorthand function syntax is difficult by scanning forward,
-            #     so check the surrounding context.
-            # If the block is being returned, imported, export default, passed as arg,
-            #     assigned with = or assigned in a nested object, treat as an ObjectLiteral.
-            self.set_mode(MODE.ObjectLiteral)
-        else:
-            self.set_mode(MODE.BlockStatement)
-
-        empty_braces = (not next_token == None) and len(next_token.comments_before) == 0 and next_token.text == '}'
-        empty_anonymous_function = empty_braces and self.flags.last_word == 'function' and \
-            self.last_type == 'TK_END_EXPR'
-
-        if self.opts.brace_preserve_inline: # check for inline, set inline_frame if so
-            # search forward for newline wanted inside this block
-            index = 0
-            check_token = None
-            self.flags.inline_frame = True
-            do_loop = True
-            while (do_loop):
-                index += 1
-                check_token = self.get_token(index)
-                if check_token.wanted_newline:
-                    self.flags.inline_frame = False
-
-                do_loop = (check_token.type != 'TK_EOF' and
-                      not (check_token.type == 'TK_END_BLOCK' and check_token.opened == current_token))
-
-        if (self.opts.brace_style == 'expand' or \
-            (self.opts.brace_style == 'none' and current_token.wanted_newline)) and \
-            not self.flags.inline_frame:
-            if self.last_type != 'TK_OPERATOR' and \
-                (empty_anonymous_function or
-                    self.last_type == 'TK_EQUALS' or
-                    (self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text) and self.flags.last_text != 'else')):
-                self.output.space_before_token = True
-            else:
-                self.print_newline(preserve_statement_flags = True)
-        else: # collapse || inline_frame
-            if self.is_array(self.previous_flags.mode) and (self.last_type == 'TK_START_EXPR' or self.last_type == 'TK_COMMA'):
-                # if we're preserving inline,
-                # allow newline between comma and next brace.
-                if self.flags.inline_frame:
-                    self.allow_wrap_or_preserved_newline(current_token)
-                    self.flags.inline_frame = True
-                    self.previous_flags.multiline_frame = self.previous_flags.multiline_frame or self.flags.multiline_frame
-                    self.flags.multiline_frame = False
-                elif self.last_type == 'TK_COMMA':
-                    self.output.space_before_token = True
-
-            elif self.last_type not in ['TK_OPERATOR', 'TK_START_EXPR']:
-                if self.last_type == 'TK_START_BLOCK' and not self.flags.inline_frame:
-                    self.print_newline()
-                else:
-                    self.output.space_before_token = True
-
-        self.print_token(current_token)
-        self.indent()
-
-
-    def handle_end_block(self, current_token):
-        # statements must all be closed when their container closes
-        self.handle_whitespace_and_comments(current_token)
-
-        while self.flags.mode == MODE.Statement:
-            self.restore_mode()
-
-        empty_braces = self.last_type == 'TK_START_BLOCK'
-
-        if self.flags.inline_frame and not empty_braces: # try inline_frame (only set if opt.braces-preserve-inline) first
-            self.output.space_before_token = True;
-        elif self.opts.brace_style == 'expand':
-            if not empty_braces:
-                self.print_newline()
-        else:
-            # skip {}
-            if not empty_braces:
-                if self.is_array(self.flags.mode) and self.opts.keep_array_indentation:
-                    self.opts.keep_array_indentation = False
-                    self.print_newline()
-                    self.opts.keep_array_indentation = True
-                else:
-                    self.print_newline()
-
-        self.restore_mode()
-        self.print_token(current_token)
-
-
-    def handle_word(self, current_token):
-        if current_token.type == 'TK_RESERVED':
-            if current_token.text in ['set', 'get'] and self.flags.mode != MODE.ObjectLiteral:
-                current_token.type = 'TK_WORD'
-            elif current_token.text in ['as', 'from'] and not self.flags.import_block:
-                current_token.type = 'TK_WORD'
-            elif self.flags.mode == MODE.ObjectLiteral:
-                next_token = self.get_token(1)
-                if next_token.text == ':':
-                    current_token.type = 'TK_WORD'
-
-        if self.start_of_statement(current_token):
-            # The conditional starts the statement if appropriate.
-            if self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const'] and current_token.type == 'TK_WORD':
-                self.flags.declaration_statement = True
-
-        elif current_token.wanted_newline and \
-                not self.is_expression(self.flags.mode) and \
-                (self.last_type != 'TK_OPERATOR' or (self.flags.last_text == '--' or self.flags.last_text == '++')) and \
-                self.last_type != 'TK_EQUALS' and \
-                (self.opts.preserve_newlines or not (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const', 'set', 'get'])):
-            self.handle_whitespace_and_comments(current_token)
-            self.print_newline()
-        else:
-            self.handle_whitespace_and_comments(current_token)
-
-
-        if self.flags.do_block and not self.flags.do_while:
-            if current_token.type == 'TK_RESERVED' and current_token.text == 'while':
-                # do {} ## while ()
-                self.output.space_before_token = True
-                self.print_token(current_token)
-                self.output.space_before_token = True
-                self.flags.do_while = True
-                return
-            else:
-                # do {} should always have while as the next word.
-                # if we don't see the expected while, recover
-                self.print_newline()
-                self.flags.do_block = False
-
-        # if may be followed by else, or not
-        # Bare/inline ifs are tricky
-        # Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
-        if self.flags.if_block:
-            if (not self.flags.else_block) and (current_token.type == 'TK_RESERVED' and current_token.text == 'else'):
-                self.flags.else_block = True
-            else:
-                while self.flags.mode == MODE.Statement:
-                    self.restore_mode()
-
-                self.flags.if_block = False
-
-        if current_token.type == 'TK_RESERVED' and (current_token.text == 'case' or (current_token.text == 'default' and self.flags.in_case_statement)):
-            self.print_newline()
-            if self.flags.case_body or self.opts.jslint_happy:
-                self.flags.case_body = False
-                self.deindent()
-            self.print_token(current_token)
-            self.flags.in_case = True
-            self.flags.in_case_statement = True
-            return
-
-        if self.last_type in ['TK_COMMA', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']:
-            if not self.start_of_object_property():
-                self.allow_wrap_or_preserved_newline(current_token)
-
-        if current_token.type == 'TK_RESERVED' and current_token.text == 'function':
-            if (self.flags.last_text in ['}', ';'] or
-                (self.output.just_added_newline() and not (self.flags.last_text in ['(', '[', '{', ':', '=', ','] or self.last_type == 'TK_OPERATOR'))):
-                # make sure there is a nice clean space of at least one blank line
-                # before a new function definition, except in arrays
-                if not self.output.just_added_blankline() and len(current_token.comments_before) == 0:
-                    self.print_newline()
-                    self.print_newline(True)
-
-            if self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD':
-                if self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set', 'new', 'return', 'export', 'async']:
-                    self.output.space_before_token = True
-                elif self.last_type == 'TK_RESERVED' and self.flags.last_text == 'default' and self.last_last_text == 'export':
-                    self.output.space_before_token = True
-                else:
-                    self.print_newline()
-            elif self.last_type == 'TK_OPERATOR' or self.flags.last_text == '=':
-                # foo = function
-                self.output.space_before_token = True
-            elif not self.flags.multiline_frame and (self.is_expression(self.flags.mode) or self.is_array(self.flags.mode)):
-                # (function
-                pass
-            else:
-                self.print_newline()
-
-            self.print_token(current_token)
-            self.flags.last_word = current_token.text
-            return
-
-        prefix = 'NONE'
-
-        if self.last_type == 'TK_END_BLOCK':
-            if self.previous_flags.inline_frame:
-                prefix = 'SPACE'
-            elif not (current_token.type == 'TK_RESERVED' and current_token.text in ['else', 'catch', 'finally', 'from']):
-                prefix = 'NEWLINE'
-            else:
-                if self.opts.brace_style in ['expand', 'end-expand'] or \
-                    (self.opts.brace_style == 'none' and current_token.wanted_newline):
-                    prefix = 'NEWLINE'
-                else:
-                    prefix = 'SPACE'
-                    self.output.space_before_token = True
-        elif self.last_type == 'TK_SEMICOLON' and self.flags.mode == MODE.BlockStatement:
-            # TODO: Should this be for STATEMENT as well?
-            prefix = 'NEWLINE'
-        elif self.last_type == 'TK_SEMICOLON' and self.is_expression(self.flags.mode):
-            prefix = 'SPACE'
-        elif self.last_type == 'TK_STRING':
-            prefix = 'NEWLINE'
-        elif self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD' or \
-            (self.flags.last_text == '*' and (
-                self.last_last_text in ['function', 'yield'] or
-                (self.flags.mode == MODE.ObjectLiteral and self.last_last_text in ['{', ',']))):
-            prefix = 'SPACE'
-        elif self.last_type == 'TK_START_BLOCK':
-            if self.flags.inline_frame:
-                prefix = 'SPACE'
-            else:
-                prefix = 'NEWLINE'
-        elif self.last_type == 'TK_END_EXPR':
-            self.output.space_before_token = True
-            prefix = 'NEWLINE'
-
-        if current_token.type == 'TK_RESERVED' and current_token.text in Tokenizer.line_starters and self.flags.last_text != ')':
-            if self.flags.inline_frame or self.flags.last_text == 'else ' or self.flags.last_text == 'export':
-                prefix = 'SPACE'
-            else:
-                prefix = 'NEWLINE'
-
-        if current_token.type == 'TK_RESERVED' and current_token.text in ['else', 'catch', 'finally']:
-            if ((not (self.last_type == 'TK_END_BLOCK' and self.previous_flags.mode == MODE.BlockStatement)) \
-               or self.opts.brace_style == 'expand' \
-               or self.opts.brace_style == 'end-expand' \
-               or (self.opts.brace_style == 'none' and current_token.wanted_newline)) \
-               and not self.flags.inline_frame:
-                self.print_newline()
-            else:
-                self.output.trim(True)
-                # If we trimmed and there's something other than a close block before us
-                # put a newline back in.  Handles '} // comment' scenario.
-                if self.output.current_line.last() != '}':
-                    self.print_newline()
-
-                self.output.space_before_token = True
-
-        elif prefix == 'NEWLINE':
-            if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text):
-                # no newline between return nnn
-                self.output.space_before_token = True
-            elif self.last_type != 'TK_END_EXPR':
-                if (self.last_type != 'TK_START_EXPR' or not (current_token.type == 'TK_RESERVED' and current_token.text in ['var', 'let', 'const'])) and self.flags.last_text != ':':
-                    # no need to force newline on VAR -
-                    # for (var x = 0...
-                    if current_token.type == 'TK_RESERVED' and current_token.text == 'if' and self.flags.last_text == 'else':
-                        self.output.space_before_token = True
-                    else:
-                        self.print_newline()
-            elif current_token.type == 'TK_RESERVED' and current_token.text in Tokenizer.line_starters and self.flags.last_text != ')':
-                self.print_newline()
-        elif self.flags.multiline_frame and self.is_array(self.flags.mode) and self.flags.last_text == ',' and self.last_last_text == '}':
-            self.print_newline() # }, in lists get a newline
-        elif prefix == 'SPACE':
-            self.output.space_before_token = True
-
-
-        self.print_token(current_token)
-        self.flags.last_word = current_token.text
-
-        if current_token.type == 'TK_RESERVED':
-            if current_token.text == 'do':
-                self.flags.do_block = True
-            elif current_token.text == 'if':
-                self.flags.if_block = True
-            elif current_token.text == 'import':
-                self.flags.import_block = True
-            elif current_token.text == 'from' and self.flags.import_block:
-                self.flags.import_block = False
-
-
-    def handle_semicolon(self, current_token):
-        if self.start_of_statement(current_token):
-            # The conditional starts the statement if appropriate.
-            # Semicolon can be the start (and end) of a statement
-            self.output.space_before_token = False
-        else:
-            self.handle_whitespace_and_comments(current_token)
-
-        next_token = self.get_token(1)
-        while (self.flags.mode == MODE.Statement and
-                not (self.flags.if_block and next_token and next_token.type == 'TK_RESERVED' and next_token.text == 'else') and
-                not self.flags.do_block):
-            self.restore_mode()
-
-        if self.flags.import_block:
-            self.flags.import_block = False
-
-        self.print_token(current_token)
-
-
-    def handle_string(self, current_token):
-        if self.start_of_statement(current_token):
-            # The conditional starts the statement if appropriate.
-            # One difference - strings want at least a space before
-            self.output.space_before_token = True
-        else:
-            self.handle_whitespace_and_comments(current_token)
-
-            if self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD' or self.flags.inline_frame:
-                self.output.space_before_token = True
-            elif self.last_type in ['TK_COMMA', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']:
-                if not self.start_of_object_property():
-                    self.allow_wrap_or_preserved_newline(current_token)
-            else:
-                self.print_newline()
-
-        self.print_token(current_token)
-
-
-    def handle_equals(self, current_token):
-        if self.start_of_statement(current_token):
-            # The conditional starts the statement if appropriate.
-            pass
-        else:
-            self.handle_whitespace_and_comments(current_token)
-
-
-        if self.flags.declaration_statement:
-            # just got an '=' in a var-line, different line breaking rules will apply
-            self.flags.declaration_assignment = True
-
-        self.output.space_before_token = True
-        self.print_token(current_token)
-        self.output.space_before_token = True
-
-
-    def handle_comma(self, current_token):
-        self.handle_whitespace_and_comments(current_token, True)
-
-        self.print_token(current_token)
-        self.output.space_before_token = True
-
-        if self.flags.declaration_statement:
-            if self.is_expression(self.flags.parent.mode):
-                # do not break on comma, for ( var a = 1, b = 2
-                self.flags.declaration_assignment = False
-
-            if self.flags.declaration_assignment:
-                self.flags.declaration_assignment = False
-                self.print_newline(preserve_statement_flags = True)
-            elif self.opts.comma_first:
-                # for comma-first, we want to allow a newline before the comma
-                # to turn into a newline after the comma, which we will fixup later
-                self.allow_wrap_or_preserved_newline(current_token)
-
-        elif self.flags.mode == MODE.ObjectLiteral \
-            or (self.flags.mode == MODE.Statement and self.flags.parent.mode ==  MODE.ObjectLiteral):
-            if self.flags.mode == MODE.Statement:
-                self.restore_mode()
-
-            if not self.flags.inline_frame:
-                self.print_newline()
-        elif self.opts.comma_first:
-            # EXPR or DO_BLOCK
-            # for comma-first, we want to allow a newline before the comma
-            # to turn into a newline after the comma, which we will fixup later
-            self.allow_wrap_or_preserved_newline(current_token)
-
-
-    def handle_operator(self, current_token):
-        isGeneratorAsterisk = current_token.text == '*' and \
-            ((self.last_type == 'TK_RESERVED' and self.flags.last_text in ['function', 'yield']) or
-                (self.last_type in ['TK_START_BLOCK', 'TK_COMMA', 'TK_END_BLOCK', 'TK_SEMICOLON']))
-        isUnary = current_token.text in ['+', '-'] \
-            and (self.last_type in ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR'] \
-            or self.flags.last_text in Tokenizer.line_starters or self.flags.last_text == ',')
-
-        if self.start_of_statement(current_token):
-            # The conditional starts the statement if appropriate.
-            pass
-        else:
-            preserve_statement_flags = not isGeneratorAsterisk
-            self.handle_whitespace_and_comments(current_token, preserve_statement_flags)
-
-        if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text):
-            # return had a special handling in TK_WORD
-            self.output.space_before_token = True
-            self.print_token(current_token)
-            return
-
-        # hack for actionscript's import .*;
-        if current_token.text == '*' and self.last_type == 'TK_DOT':
-            self.print_token(current_token)
-            return
-
-        if current_token.text == '::':
-            # no spaces around the exotic namespacing syntax operator
-            self.print_token(current_token)
-            return
-
-        # Allow line wrapping between operators when operator_position is
-        #   set to before or preserve
-        if self.last_type == 'TK_OPERATOR' and self.opts.operator_position in OPERATOR_POSITION_BEFORE_OR_PRESERVE:
-            self.allow_wrap_or_preserved_newline(current_token)
-
-        if current_token.text == ':' and self.flags.in_case:
-            self.flags.case_body = True
-            self.indent()
-            self.print_token(current_token)
-            self.print_newline()
-            self.flags.in_case = False
-            return
-
-        space_before = True
-        space_after = True
-        in_ternary = False
-
-        if current_token.text == ':':
-            if self.flags.ternary_depth == 0:
-                # Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
-                space_before = False
-            else:
-                self.flags.ternary_depth -= 1
-                in_ternary = True
-        elif current_token.text == '?':
-            self.flags.ternary_depth += 1
-
-        # let's handle the operator_position option prior to any conflicting logic
-        if (not isUnary) and (not isGeneratorAsterisk) and \
-            self.opts.preserve_newlines and current_token.text in Tokenizer.positionable_operators:
-
-            isColon = current_token.text == ':'
-            isTernaryColon = isColon and in_ternary
-            isOtherColon = isColon and not in_ternary
-
-            if self.opts.operator_position == OPERATOR_POSITION['before_newline']:
-                # if the current token is : and it's not a ternary statement then we set space_before to false
-                self.output.space_before_token = not isOtherColon
-
-                self.print_token(current_token)
-
-                if (not isColon) or isTernaryColon:
-                    self.allow_wrap_or_preserved_newline(current_token)
-
-                self.output.space_before_token = True
-
-                return
-
-            elif self.opts.operator_position == OPERATOR_POSITION['after_newline']:
-                # if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement,
-                #   then print a newline.
-                self.output.space_before_token = True
-
-                if (not isColon) or isTernaryColon:
-                    if self.get_token(1).wanted_newline:
-                        self.print_newline(preserve_statement_flags = True)
-                    else:
-                        self.allow_wrap_or_preserved_newline(current_token)
-                else:
-                    self.output.space_before_token = False
-
-                self.print_token(current_token)
-
-                self.output.space_before_token = True
-                return
-
-            elif self.opts.operator_position == OPERATOR_POSITION['preserve_newline']:
-                if not isOtherColon:
-                    self.allow_wrap_or_preserved_newline(current_token)
-
-                # if we just added a newline, or the current token is : and it's not a ternary statement,
-                #   then we set space_before to false
-                self.output.space_before_token = not (self.output.just_added_newline() or isOtherColon)
-
-                self.print_token(current_token)
-
-                self.output.space_before_token = True
-                return
-
-        if isGeneratorAsterisk:
-            self.allow_wrap_or_preserved_newline(current_token)
-            space_before = False
-            next_token = self.get_token(1)
-            space_after = next_token and next_token.type in ['TK_WORD','TK_RESERVED']
-        elif current_token.text == '...':
-            self.allow_wrap_or_preserved_newline(current_token)
-            space_before = self.last_type == 'TK_START_BLOCK'
-            space_after = False
-        elif current_token.text in ['--', '++', '!', '~'] or isUnary:
-            space_before = False
-            space_after = False
-
-            # http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
-            # if there is a newline between -- or ++ and anything else we should preserve it.
-            if current_token.wanted_newline and (current_token.text == '--' or current_token.text == '++'):
-                self.print_newline(preserve_statement_flags = True)
-
-            if self.flags.last_text == ';' and self.is_expression(self.flags.mode):
-                # for (;; ++i)
-                #         ^^
-                space_before = True
-
-            if self.last_type == 'TK_RESERVED':
-                space_before = True
-            elif self.last_type == 'TK_END_EXPR':
-                space_before = not (self.flags.last_text == ']' and current_token.text in ['--', '++'])
-            elif self.last_type == 'TK_OPERATOR':
-                # a++ + ++b
-                # a - -b
-                space_before = current_token.text in ['--', '-','++', '+'] and self.flags.last_text in ['--', '-','++', '+']
-                # + and - are not unary when preceeded by -- or ++ operator
-                # a-- + b
-                # a * +b
-                # a - -b
-                if current_token.text in ['-', '+'] and self.flags.last_text in ['--', '++']:
-                    space_after = True
-
-            if (((self.flags.mode == MODE.BlockStatement and not self.flags.inline_frame) or self.flags.mode == MODE.Statement)
-                    and self.flags.last_text in ['{', ';']):
-                # { foo: --i }
-                # foo(): --bar
-                self.print_newline()
-
-        if space_before:
-            self.output.space_before_token = True
-
-        self.print_token(current_token)
-
-        if space_after:
-            self.output.space_before_token = True
-
-
-
-    def handle_block_comment(self, current_token, preserve_statement_flags):
-        if self.output.raw:
-            self.output.add_raw_token(current_token)
-            if current_token.directives and current_token.directives.get('preserve') == 'end':
-                # If we're testing the raw output behavior, do not allow a directive to turn it off.
-                self.output.raw = self.opts.test_output_raw
-            return
-
-        if current_token.directives:
-            self.print_newline(preserve_statement_flags = preserve_statement_flags)
-            self.print_token(current_token)
-            if current_token.directives.get('preserve') == 'start':
-                self.output.raw = True
-
-            self.print_newline(preserve_statement_flags = True)
-            return
-
-        # inline block
-        if not self.acorn.newline.search(current_token.text) and not current_token.wanted_newline:
-            self.output.space_before_token = True
-            self.print_token(current_token)
-            self.output.space_before_token = True
-            return
-
-        lines = self.acorn.allLineBreaks.split(current_token.text)
-        javadoc = False
-        starless = False
-        last_indent = current_token.whitespace_before
-        last_indent_length = len(last_indent)
-
-        # block comment starts with a new line
-        self.print_newline(preserve_statement_flags = preserve_statement_flags)
-        if  len(lines) > 1:
-            javadoc = not any(l for l in lines[1:] if ( l.strip() == '' or (l.lstrip())[0] != '*'))
-            starless = all(l.startswith(last_indent) or l.strip() == '' for l in lines[1:])
-
-        # first line always indented
-        self.print_token(current_token, lines[0])
-        for line in lines[1:]:
-            self.print_newline(preserve_statement_flags = True)
-            if javadoc:
-                # javadoc: reformat and re-indent
-                self.print_token(current_token, ' ' + line.lstrip())
-            elif starless and len(line) > last_indent_length:
-                # starless: re-indent non-empty content, avoiding trim
-                self.print_token(current_token, line[last_indent_length:])
-            else:
-                # normal comments output raw
-                self.output.add_token(line)
-
-        self.print_newline(preserve_statement_flags = preserve_statement_flags)
-
-    def handle_comment(self, current_token, preserve_statement_flags):
-        if current_token.wanted_newline:
-            self.print_newline(preserve_statement_flags = preserve_statement_flags)
-
-        if not current_token.wanted_newline:
-            self.output.trim(True)
-
-        self.output.space_before_token = True
-        self.print_token(current_token)
-        self.print_newline(preserve_statement_flags = preserve_statement_flags)
-
-
-    def handle_dot(self, current_token):
-        if self.start_of_statement(current_token):
-            # The conditional starts the statement if appropriate.
-            pass
-        else:
-            self.handle_whitespace_and_comments(current_token, True)
-
-        if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text):
-            self.output.space_before_token = True
-        else:
-            # allow preserved newlines before dots in general
-            # force newlines on dots after close paren when break_chained - for bar().baz()
-            self.allow_wrap_or_preserved_newline(current_token,
-                self.flags.last_text == ')' and self.opts.break_chained_methods)
-
-        self.print_token(current_token)
-
-    def handle_unknown(self, current_token, preserve_statement_flags):
-        self.print_token(current_token)
-        if current_token.text[-1] == '\n':
-            self.print_newline(preserve_statement_flags = preserve_statement_flags)
-
-    def handle_eof(self, current_token):
-        # Unwind any open statements
-        while self.flags.mode == MODE.Statement:
-            self.restore_mode()
-
-        self.handle_whitespace_and_comments(current_token)
-
-
 
 def mkdir_p(path):
     try:
@@ -1488,719 +194,8 @@ def mkdir_p(path):
         else:
             raise Exception()
 
-# Using object instead of string to allow for later expansion of info about each line
-class OutputLine:
-    def __init__(self, parent):
-        self.__parent = parent
-        self.__character_count = 0
-        self.__indent_count = -1
-
-        self.__items = []
-        self.__empty = True
-
-    def get_character_count(self):
-        return self.__character_count
-
-    def is_empty(self):
-        return self.__empty
-
-    def set_indent(self, level):
-        self.__character_count = self.__parent.baseIndentLength + level * self.__parent.indent_length
-        self.__indent_count = level;
-
-    def last(self):
-        if not self.is_empty():
-            return self.__items[-1]
-        else:
-            return None
-
-    def push(self, input):
-        self.__items.append(input)
-        self.__character_count += len(input)
-        self.__empty = False
-
-
-    def pop(self):
-        item = None
-        if not self.is_empty():
-            item = self.__items.pop()
-            self.__character_count -= len(item)
-            self.__empty = len(self.__items) == 0
-        return item
-
-    def remove_indent(self):
-        if self.__indent_count > 0:
-            self.__indent_count -= 1
-            self.__character_count -= self.__parent.indent_length
-
-    def trim(self):
-        while self.last() == ' ':
-            item = self._items.pop()
-            self.__character_count -= 1
-        self.__empty = len(self.__items) == 0
-
-    def toString(self):
-        result = ''
-        if not self.is_empty():
-            if self.__indent_count >= 0:
-                result = self.__parent.indent_cache[self.__indent_count]
-            result += ''.join(self.__items)
-        return result
-
-
-class Output:
-    def __init__(self, indent_string, baseIndentString = ''):
-
-        self.indent_string = indent_string
-        self.baseIndentString = baseIndentString
-        self.indent_cache = [ baseIndentString ]
-        self.baseIndentLength = len(baseIndentString)
-        self.indent_length = len(indent_string)
-        self.raw = False
-        self.lines = []
-        self.previous_line = None
-        self.current_line = None
-        self.space_before_token = False
-        self.add_outputline()
-
-    def add_outputline(self):
-        self.previous_line = self.current_line
-        self.current_line = OutputLine(self)
-        self.lines.append(self.current_line)
-
-    def get_line_number(self):
-        return len(self.lines)
-
-    def add_new_line(self, force_newline):
-        if len(self.lines) == 1 and self.just_added_newline():
-            # no newline on start of file
-            return False
-
-        if force_newline or not self.just_added_newline():
-            if not self.raw:
-                self.add_outputline()
-            return True
-        return False
-
-    def get_code(self):
-        sweet_code = "\n".join(line.toString() for line in self.lines)
-        return re.sub('[\r\n\t ]+$', '', sweet_code)
-
-    def set_indent(self, level):
-        # Never indent your first output indent at the start of the file
-        if len(self.lines) > 1:
-            while level >= len(self.indent_cache):
-                self.indent_cache.append(self.indent_cache[-1] + self.indent_string)
-
-
-            self.current_line.set_indent(level)
-            return True
-        self.current_line.set_indent(0)
-        return False
-
-    def add_raw_token(self, token):
-        for _ in range(token.newlines):
-            self.add_outputline()
-
-        self.current_line.push(token.whitespace_before)
-        self.current_line.push(token.text)
-        self.space_before_token = False
-
-    def add_token(self, printable_token):
-        self.add_space_before_token()
-        self.current_line.push(printable_token)
-
-    def add_space_before_token(self):
-        if self.space_before_token and not self.just_added_newline():
-            self.current_line.push(' ')
-        self.space_before_token = False
-
-    def remove_redundant_indentation(self, frame):
-        # This implementation is effective but has some issues:
-        #     - can cause line wrap to happen too soon due to indent removal
-        #           after wrap points are calculated
-        # These issues are minor compared to ugly indentation.
-
-        if frame.multiline_frame or frame.mode == MODE.ForInitializer or frame.mode == MODE.Conditional:
-            return
-
-        # remove one indent from each line inside this section
-        index = frame.start_line_index
-        while index < len(self.lines):
-            self.lines[index].remove_indent()
-            index += 1
-
-    def trim(self, eat_newlines = False):
-        self.current_line.trim()
-
-        while eat_newlines and len(self.lines) > 1 and self.current_line.is_empty():
-            self.lines.pop()
-            self.current_line = self.lines[-1]
-            self.current_line.trim()
-
-        if len(self.lines) > 1:
-            self.previous_line = self.lines[-2]
-        else:
-            self.previous_line = None
-
-    def just_added_newline(self):
-        return self.current_line.is_empty()
-
-    def just_added_blankline(self):
-        if self.just_added_newline():
-            if len(self.lines) == 1:
-                return True
-
-            line = self.lines[-2]
-            return line.is_empty()
-
-        return False
-
-class InputScanner:
-    def __init__(self, input):
-        self.__input = input
-        self.__input_length = len(self.__input)
-        self.__position = 0
-
-    def back(self):
-        self.__position -= 1
-
-    def hasNext(self):
-        return self.__position < self.__input_length
-
-    def next(self):
-        val = None
-        if self.hasNext():
-            val = self.__input[self.__position]
-            self.__position += 1
-
-        return val;
-
-    def peek(self, index = 0):
-        val = None
-        index += self.__position;
-        if index >= 0 and index < self.__input_length:
-            val = self.__input[index];
-
-        return val;
-
-    def peekCharCode(self, index = 0):
-        val = 0
-        index += self.__position;
-        if index >= 0 and index < self.__input_length:
-            val = ord(self.__input[index])
-
-        return val
-
-    def test(self, pattern, index = 0):
-        index += self.__position;
-        return index >= 0 and index < self.__input_length and pattern.match(self.__input, index)
-
-    def testChar(self, pattern, index = 0):
-        val = self.peek(index)
-        return val != None and pattern.match(val)
 
-    def match(self, pattern):
-        pattern_match = None
-        if self.hasNext():
-            pattern_match = pattern.match(self.__input, self.__position)
-            if pattern_match:
-                self.__position += len(pattern_match.group(0));
-
-        return pattern_match
-
-
-class Tokenizer:
-
-    whitespace = ["\n", "\r", "\t", " "]
-    digit = re.compile('[0-9]')
-    digit_bin = re.compile('[01]')
-    digit_oct = re.compile('[01234567]')
-    digit_hex = re.compile('[0123456789abcdefABCDEF]')
-
-    positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ')
-    punct = (positionable_operators +
-        # non-positionable operators - these do not follow operator position settings
-        '! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~ ...'.split(' '))
-
-    # Words which always should start on a new line
-    line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',')
-    reserved_words = line_starters + ['do', 'in', 'of', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as']
-
-    def __init__ (self, input_string, opts, indent_string):
-        self.input = InputScanner(input_string)
-        self.opts = opts
-        self.indent_string = indent_string
-        self.acorn = Acorn()
-        #  /* ... */ comment ends with nearest */ or end of file
-        self.block_comment_pattern = re.compile('([\s\S]*?)((?:\*\/)|$)')
-
-        # comment ends just before nearest linefeed or end of file
-        self.comment_pattern = re.compile(self.acorn.six.u('([^\n\r\u2028\u2029]*)'))
-
-        self.directives_block_pattern = re.compile('\/\* beautify( \w+[:]\w+)+ \*\/')
-        self.directive_pattern = re.compile(' (\w+)[:](\w+)')
-        self.directives_end_ignore_pattern = re.compile('([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)')
-
-        self.template_pattern = re.compile('((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)')
-
-    def tokenize(self):
-        self.in_html_comment = False
-        self.tokens = []
-
-        next = None
-        last = None
-        open = None
-        open_stack = []
-        comments = []
-
-        while not (not last == None and last.type == 'TK_EOF'):
-            token_values = self.__tokenize_next()
-            next = Token(token_values[1], token_values[0], self.n_newlines, self.whitespace_before_token)
-
-            while next.type == 'TK_COMMENT' or next.type == 'TK_BLOCK_COMMENT' or next.type == 'TK_UNKNOWN':
-                if next.type == 'TK_BLOCK_COMMENT':
-                    next.directives = token_values[2]
-
-                comments.append(next)
-                token_values = self.__tokenize_next()
-                next = Token(token_values[1], token_values[0], self.n_newlines, self.whitespace_before_token)
-
-            if len(comments) > 0:
-                next.comments_before = comments
-                comments = []
-
-            if next.type == 'TK_START_BLOCK' or next.type == 'TK_START_EXPR':
-                next.parent = last
-                open_stack.append(open)
-                open = next
-            elif (next.type == 'TK_END_BLOCK' or next.type == 'TK_END_EXPR') and \
-                (not open == None and ( \
-                    (next.text == ']' and open.text == '[') or \
-                    (next.text == ')' and open.text == '(') or \
-                    (next.text == '}' and open.text == '{'))):
-                next.parent = open.parent
-                next.opened = open
-                open = open_stack.pop()
-
-            self.tokens.append(next)
-            last = next
-        return self.tokens
-
-    def get_directives (self, text):
-        if not self.directives_block_pattern.match(text):
-            return None
-
-        directives = {}
-        directive_match = self.directive_pattern.search(text)
-        while directive_match:
-            directives[directive_match.group(1)] = directive_match.group(2)
-            directive_match = self.directive_pattern.search(text, directive_match.end())
-
-        return directives
-
-
-    def __tokenize_next(self):
-
-        whitespace_on_this_line = []
-        self.n_newlines = 0
-        self.whitespace_before_token = ''
-
-        c = self.input.next()
-
-        if c == None:
-            return '', 'TK_EOF'
-
-        if len(self.tokens) > 0:
-            last_token = self.tokens[-1]
-        else:
-            # For the sake of tokenizing we can pretend that there was on open brace to start
-            last_token = Token('TK_START_BLOCK', '{')
-
-        while c in self.whitespace:
-            if self.acorn.newline.match(c):
-                # treat \r\n as one newline
-                if not (c == '\n' and self.input.peek(-2) == '\r'):
-                    self.n_newlines += 1
-                    whitespace_on_this_line = []
-            else:
-                whitespace_on_this_line.append(c)
-
-            c = self.input.next()
-
-            if c == None:
-                return '', 'TK_EOF'
-
-        if len(whitespace_on_this_line) != 0:
-            self.whitespace_before_token = ''.join(whitespace_on_this_line)
-
-        if self.digit.match(c) or (c == '.' and self.input.testChar(self.digit)):
-            allow_decimal = True
-            allow_e = True
-            local_digit = self.digit
-
-            if c == '0' and self.input.testChar(re.compile('[XxOoBb]')):
-                # switch to hex/oct/bin number, no decimal or e, just hex/oct/bin digits
-                allow_decimal = False
-                allow_e = False
-                if self.input.testChar(re.compile('[Bb]')):
-                    local_digit = self.digit_bin
-                elif self.input.testChar(re.compile('[Oo]')):
-                    local_digit = self.digit_oct
-                else:
-                    local_digit = self.digit_hex
-                c += self.input.next()
-            elif c == '.':
-                # Already have a decimal for this literal, don't allow another
-                allow_decimal = False
-            else:
-                # we know this first loop will run.  It keeps the logic simpler.
-                c = ''
-                self.input.back()
-
-            # Add the digits
-            while self.input.testChar(local_digit):
-                c += self.input.next()
-
-                if allow_decimal and self.input.peek() == '.':
-                    c += self.input.next()
-                    allow_decimal = False
-
-                # a = 1.e-7 is valid, so we test for . then e in one loop
-                if allow_e and self.input.testChar(re.compile('[Ee]')):
-                    c += self.input.next()
-
-                    if self.input.testChar(re.compile('[+-]')):
-                        c += self.input.next()
-
-                    allow_e = False
-                    allow_decimal = False
-
-            return c, 'TK_WORD'
-
-        if self.acorn.isIdentifierStart(self.input.peekCharCode(-1)):
-            if self.input.hasNext():
-                while self.acorn.isIdentifierChar(self.input.peekCharCode()):
-                    c += self.input.next()
-                    if not self.input.hasNext():
-                        break
-
-            if not (last_token.type == 'TK_DOT' \
-                        or (last_token.type == 'TK_RESERVED' and last_token.text in ['set', 'get'])) \
-                    and c in self.reserved_words:
-                if c == 'in' or c == 'of': # in and of are operators, need to hack
-                    return c, 'TK_OPERATOR'
-
-                return c, 'TK_RESERVED'
-
-            return c, 'TK_WORD'
-
-        if c in '([':
-            return c, 'TK_START_EXPR'
-
-        if c in ')]':
-            return c, 'TK_END_EXPR'
-
-        if c == '{':
-            return c, 'TK_START_BLOCK'
-
-        if c == '}':
-            return c, 'TK_END_BLOCK'
-
-        if c == ';':
-            return c, 'TK_SEMICOLON'
-
-        if c == '/':
-            comment = ''
-            inline_comment = True
-            if self.input.peek() == '*': # peek /* .. */ comment
-                self.input.next()
-                comment_match = self.input.match(self.block_comment_pattern)
-                comment = '/*' + comment_match.group(0)
-
-                directives = self.get_directives(comment)
-                if directives and directives.get('ignore') == 'start':
-                    comment_match = self.input.match(self.directives_end_ignore_pattern)
-                    comment += comment_match.group(0)
-                comment = re.sub(self.acorn.allLineBreaks, '\n', comment)
-                return comment, 'TK_BLOCK_COMMENT', directives
-
-            if self.input.peek() == '/': # peek // comment
-                self.input.next()
-                comment_match = self.input.match(self.comment_pattern)
-                comment = '//' + comment_match.group(0)
-                return comment, 'TK_COMMENT'
-
-        startXmlRegExp = re.compile('<()([-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*>')
-
-        self.has_char_escapes = False
-
-        if c == '`' or c == "'" or c == '"' or \
-            ( \
-                (c == '/') or \
-                (self.opts.e4x and c == "<" and self.input.test(startXmlRegExp, -1)) \
-            ) and ( \
-                (last_token.type == 'TK_RESERVED' and last_token.text in ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield']) or \
-                (last_token.type == 'TK_END_EXPR' and last_token.text == ')' and \
-                            last_token.parent and last_token.parent.type == 'TK_RESERVED' and last_token.parent.text in ['if', 'while', 'for']) or \
-                (last_token.type in ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_OPERATOR', \
-                                   'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'])):
-            sep = c
-            esc = False
-            esc1 = 0
-            esc2 = 0
-            resulting_string = c
-            in_char_class = False
-
-            if sep == '/':
-                # handle regexp
-                in_char_class = False
-                while self.input.hasNext() and \
-                        (esc or in_char_class or self.input.peek()!= sep) and \
-                        not self.input.testChar(self.acorn.newline):
-                    resulting_string += self.input.peek()
-                    if not esc:
-                        esc = self.input.peek() == '\\'
-                        if self.input.peek() == '[':
-                            in_char_class = True
-                        elif self.input.peek() == ']':
-                            in_char_class = False
-                    else:
-                        esc = False
-                    self.input.next()
-
-            elif self.opts.e4x and sep == '<':
-                # handle e4x xml literals
-                xmlRegExp = re.compile('[\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*>')
-                self.input.back()
-                xmlStr = ""
-                match = self.input.match(xmlRegExp)
-                if match:
-                    rootTag = match.group(2)
-                    rootTag = re.sub(r'^{\s+', '{', re.sub(r'\s+}$', '}', rootTag))
-                    isCurlyRoot = rootTag.startswith('{')
-                    depth = 0
-                    while (match):
-                        isEndTag = match.group(1)
-                        tagName = match.group(2)
-                        isSingletonTag = (match.groups()[-1] != "") or (match.group(2)[0:8] == "![CDATA[")
-                        if not isSingletonTag and (
-                            tagName == rootTag or (isCurlyRoot and re.sub(r'^{\s+', '{', re.sub(r'\s+}$', '}', tagName)))):
-                            if isEndTag:
-                                depth -= 1
-                            else:
-                                depth += 1
-
-                        xmlStr += match.group(0)
-                        if depth <= 0:
-                            break
-
-                        match = self.input.match(xmlRegExp)
-
-
-                    # if we didn't close correctly, keep unformatted.
-                    if not match:
-                        xmlStr += self.input.match(re.compile('[\s\S]*')).group(0)
-
-                    xmlStr = re.sub(self.acorn.allLineBreaks, '\n', xmlStr)
-                    return xmlStr, 'TK_STRING'
-
-            else:
-
-                # handle string
-                def parse_string(self, resulting_string, delimiter, allow_unescaped_newlines = False, start_sub = None):
-                    esc = False
-                    while self.input.hasNext():
-                        current_char = self.input.peek()
-                        if not (esc or (current_char != delimiter and
-                                (allow_unescaped_newlines or not self.acorn.newline.match(current_char)))):
-                            break
-
-                        # Handle \r\n linebreaks after escapes or in template strings
-                        if (esc or allow_unescaped_newlines) and self.acorn.newline.match(current_char):
-                            if current_char == '\r' and self.input.peek(1) == '\n':
-                                self.input.next()
-                                current_char = self.input.peek()
-
-                            resulting_string += '\n'
-                        else:
-                            resulting_string += current_char
-
-                        if esc:
-                            if current_char == 'x' or current_char == 'u':
-                                self.has_char_escapes = True
-
-                            esc = False
-                        else:
-                            esc = current_char == '\\'
-
-                        self.input.next()
-
-                        if start_sub and resulting_string.endswith(start_sub):
-                            if delimiter == '`':
-                                resulting_string = parse_string(self, resulting_string, '}', allow_unescaped_newlines, '`')
-                            else:
-                                resulting_string = parse_string(self, resulting_string, '`', allow_unescaped_newlines, '${')
-
-                            if self.input.hasNext():
-                                resulting_string += self.input.next()
-
-                    return resulting_string
-
-                if sep == '`':
-                    resulting_string = parse_string(self, resulting_string, '`', True, '${')
-                else:
-                    resulting_string = parse_string(self, resulting_string, sep)
-
-
-            if self.has_char_escapes and self.opts.unescape_strings:
-                resulting_string = self.unescape_string(resulting_string)
-
-            if self.input.peek() == sep:
-                resulting_string += self.input.next()
-
-                if sep == '/':
-                    # regexps may have modifiers /regexp/MOD, so fetch those too
-                    # Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
-                    while self.input.hasNext() and self.acorn.isIdentifierStart(self.input.peekCharCode()):
-                        resulting_string += self.input.next()
-
-            resulting_string = re.sub(self.acorn.allLineBreaks, '\n', resulting_string)
-
-            return resulting_string, 'TK_STRING'
-
-        if c == '#':
-
-            # she-bang
-            if len(self.tokens) == 0 and self.input.peek() == '!':
-                resulting_string = c
-                while self.input.hasNext() and c != '\n':
-                    c = self.input.next()
-                    resulting_string += c
-                return resulting_string.strip() + '\n', 'TK_UNKNOWN'
-
-
-            # Spidermonkey-specific sharp variables for circular references
-            # https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
-            # http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
-            sharp = '#'
-            if self.input.hasNext() and self.input.testChar(self.digit):
-                while True:
-                    c = self.input.next()
-                    sharp += c
-                    if (not self.input.hasNext()) or c == '#' or c == '=':
-                        break
-            if c == '#':
-                pass
-            elif self.input.peek() == '[' and self.input.peek(1) == ']':
-                sharp += '[]'
-                self.input.next()
-                self.input.next()
-            elif self.input.peek() == '{' and self.input.peek(1) == '}':
-                sharp += '{}'
-                self.input.next()
-                self.input.next()
-            return sharp, 'TK_WORD'
-
-        if c == '<' and self.input.peek() in ['?', '%']:
-            self.input.back()
-            template_match = self.input.match(self.template_pattern)
-            if template_match:
-                c = template_match.group(0)
-                c = re.sub(self.acorn.allLineBreaks, '\n', c)
-                return c, 'TK_STRING'
-
-
-        if c == '<' and self.input.match(re.compile('\!--')):
-            c = '', 'TK_COMMENT'
-
-        if c == '.':
-            if self.input.peek() == '.' and self.input.peek(1) == '.':
-                c += self.input.next() + self.input.next()
-                return c, 'TK_OPERATOR'
-
-            return c, 'TK_DOT'
-
-        if c in self.punct:
-            while self.input.hasNext() and c + self.input.peek() in self.punct:
-                c += self.input.next()
-                if not self.input.hasNext():
-                    break
-
-            if c == ',':
-                return c, 'TK_COMMA'
-            if c == '=':
-                return c, 'TK_EQUALS'
-
-            return c, 'TK_OPERATOR'
-
-        return c, 'TK_UNKNOWN'
-
-    def unescape_string(self, s):
-        # You think that a regex would work for this
-        # return s.replace(/\\x([0-9a-f]{2})/gi, function(match, val) {
-        #         return String.fromCharCode(parseInt(val, 16));
-        #     })
-        # However, dealing with '\xff', '\\xff', '\\\xff' makes this more fun.
-        out = self.acorn.six.u('')
-        escaped = 0
-
-        input_scan = InputScanner(s)
-        matched = None
-
-        while input_scan.hasNext():
-            # Keep any whitespace, non-slash characters
-            # also keep slash pairs.
-            matched = input_scan.match(re.compile(r'([\s]|[^\\]|\\\\)+'))
-
-            if matched:
-                out += matched.group(0)
-
-            if input_scan.peek() != '\\':
-                continue
-
-            input_scan.next()
-            if input_scan.peek() == 'x':
-                matched = input_scan.match(re.compile('x([0-9A-Fa-f]{2})'))
-            elif input_scan.peek() == 'u':
-                matched = input_scan.match(re.compile('u([0-9A-Fa-f]{4})'));
-            else:
-                out += '\\'
-                if input_scan.hasNext():
-                    out += input_scan.next()
-                continue
-
-            # If there's some error decoding, return the original string
-            if not matched:
-                return s
-
-            escaped = int(matched.group(1), 16)
-
-            if escaped > 0x7e and escaped <= 0xff and matched.group(0).startswith('x'):
-                # we bail out on \x7f..\xff,
-                # leaving whole string escaped,
-                # as it's probably completely binary
-                return s
-            elif escaped >= 0x00 and escaped < 0x20:
-                # leave 0x00...0x1f escaped
-                out += '\\' + matched.group(0)
-                continue
-            elif escaped == 0x22 or escaped == 0x27 or escaped == 0x5c:
-                # single-quote, apostrophe, backslash - escape these
-                out += ('\\' + chr(escaped))
-            else:
-                out += self.acorn.six.unichr(escaped)
 
-        return out
 
 def isFileDifferent(filepath, expected):
     try:
diff --git a/python/jsbeautifier/core/__init__.py b/python/jsbeautifier/core/__init__.py
new file mode 100644
index 000000000..0c0105543
--- /dev/null
+++ b/python/jsbeautifier/core/__init__.py
@@ -0,0 +1 @@
+# Empty file :)
diff --git a/python/jsbeautifier/core/acorn.py b/python/jsbeautifier/core/acorn.py
new file mode 100644
index 000000000..fbe5adacd
--- /dev/null
+++ b/python/jsbeautifier/core/acorn.py
@@ -0,0 +1,70 @@
+import re
+
+# This section of code was translated to python from acorn (javascript).
+#
+# Acorn was written by Marijn Haverbeke and released under an MIT
+# license. The Unicode regexps (for identifiers and whitespace) were
+# taken from [Esprima](http://esprima.org) by Ariya Hidayat.
+#
+# Git repositories for Acorn are available at
+#
+#     http://marijnhaverbeke.nl/git/acorn
+#     https://github.com/marijnh/acorn.git
+
+# This is not pretty, but given how we did the version import
+# it is the only way to do this without having setup.py fail on a missing six dependency.
+six = __import__("six")
+
+# ## Character categories
+
+# Big ugly regular expressions that match characters in the
+# whitespace, identifier, and identifier-start categories. These
+# are only applied when a character is found to actually have a
+# code point above 128.
+
+_nonASCIIwhitespace = re.compile(six.u("[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]"))
+_nonASCIIidentifierStartChars = six.u("\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")
+_nonASCIIidentifierChars = six.u("\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")
+_nonASCIIidentifierStart = re.compile("[" + _nonASCIIidentifierStartChars + "]")
+_nonASCIIidentifier = re.compile("[" + _nonASCIIidentifierStartChars + _nonASCIIidentifierChars + "]")
+
+# Whether a single character denotes a newline.
+
+newline = re.compile(six.u("[\n\r\u2028\u2029]"))
+
+# Matches a whole line break (where CRLF is considered a single
+# line break). Used to count lines.
+
+# in javascript, these two differ
+# in python they are the same, different methods are called on them
+lineBreak = re.compile(six.u("\r\n|[\n\r\u2028\u2029]"))
+allLineBreaks = lineBreak
+
+
+# Test whether a given character code starts an identifier.
+def isIdentifierStart(code):
+    if code < 65:
+        return code in [36, 64] # permit $ (36) and @ (64). @ is used in ES7 decorators.
+    if code < 91:
+        return True # 65 through 91 are uppercase letters
+    if code < 97:
+        return code == 95 # permit _ (95)
+    if code < 123:
+        return True # 97 through 123 are lowercase letters
+    return code >= 0xaa and _nonASCIIidentifierStart.match(six.unichr(code)) != None
+
+# Test whether a given character is part of an identifier.
+def isIdentifierChar(code):
+    if code < 48:
+        return code == 36
+    if code < 58:
+        return True
+    if code < 65:
+        return False
+    if code < 91:
+        return True
+    if code < 97:
+        return code == 95
+    if code < 123:
+        return True
+    return code >= 0xaa and _nonASCIIidentifier.match(six.unichr(code)) != None
diff --git a/python/jsbeautifier/core/inputscanner.py b/python/jsbeautifier/core/inputscanner.py
new file mode 100644
index 000000000..0d8cd6f27
--- /dev/null
+++ b/python/jsbeautifier/core/inputscanner.py
@@ -0,0 +1,77 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+
+class InputScanner:
+    def __init__(self, input):
+        self.__input = input
+        self.__input_length = len(self.__input)
+        self.__position = 0
+
+    def back(self):
+        self.__position -= 1
+
+    def hasNext(self):
+        return self.__position < self.__input_length
+
+    def next(self):
+        val = None
+        if self.hasNext():
+            val = self.__input[self.__position]
+            self.__position += 1
+
+        return val;
+
+    def peek(self, index = 0):
+        val = None
+        index += self.__position;
+        if index >= 0 and index < self.__input_length:
+            val = self.__input[index];
+
+        return val;
+
+    def peekCharCode(self, index = 0):
+        val = 0
+        index += self.__position;
+        if index >= 0 and index < self.__input_length:
+            val = ord(self.__input[index])
+
+        return val
+
+    def test(self, pattern, index = 0):
+        index += self.__position;
+        return index >= 0 and index < self.__input_length and pattern.match(self.__input, index)
+
+    def testChar(self, pattern, index = 0):
+        val = self.peek(index)
+        return val != None and pattern.match(val)
+
+    def match(self, pattern):
+        pattern_match = None
+        if self.hasNext():
+            pattern_match = pattern.match(self.__input, self.__position)
+            if pattern_match:
+                self.__position += len(pattern_match.group(0));
+
+        return pattern_match
diff --git a/python/jsbeautifier/core/options.py b/python/jsbeautifier/core/options.py
new file mode 100644
index 000000000..f56775497
--- /dev/null
+++ b/python/jsbeautifier/core/options.py
@@ -0,0 +1,36 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import copy
+
+def mergeOpts(options, targetType):
+    finalOpts = copy.copy(options)
+
+    local = getattr(finalOpts, targetType)
+    if (local):
+        delattr(finalOpts, targetType)
+        for key in local:
+            setattr(finalOpts, key, local[key])
+
+    return finalOpts
diff --git a/python/jsbeautifier/core/output.py b/python/jsbeautifier/core/output.py
new file mode 100644
index 000000000..3505128a1
--- /dev/null
+++ b/python/jsbeautifier/core/output.py
@@ -0,0 +1,186 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import re
+
+# Using object instead of string to allow for later expansion of info about each line
+class OutputLine:
+    def __init__(self, parent):
+        self.__parent = parent
+        self.__character_count = 0
+        self.__indent_count = -1
+
+        self.__items = []
+        self.__empty = True
+
+    def get_character_count(self):
+        return self.__character_count
+
+    def is_empty(self):
+        return self.__empty
+
+    def set_indent(self, level):
+        self.__character_count = self.__parent.baseIndentLength + level * self.__parent.indent_length
+        self.__indent_count = level;
+
+    def last(self):
+        if not self.is_empty():
+            return self.__items[-1]
+        else:
+            return None
+
+    def push(self, input):
+        self.__items.append(input)
+        self.__character_count += len(input)
+        self.__empty = False
+
+
+    def pop(self):
+        item = None
+        if not self.is_empty():
+            item = self.__items.pop()
+            self.__character_count -= len(item)
+            self.__empty = len(self.__items) == 0
+        return item
+
+    def remove_indent(self):
+        if self.__indent_count > 0:
+            self.__indent_count -= 1
+            self.__character_count -= self.__parent.indent_length
+
+    def trim(self):
+        while self.last() == ' ':
+            item = self._items.pop()
+            self.__character_count -= 1
+        self.__empty = len(self.__items) == 0
+
+    def toString(self):
+        result = ''
+        if not self.is_empty():
+            if self.__indent_count >= 0:
+                result = self.__parent.indent_cache[self.__indent_count]
+            result += ''.join(self.__items)
+        return result
+
+
+class Output:
+    def __init__(self, indent_string, baseIndentString = ''):
+
+        self.indent_string = indent_string
+        self.baseIndentString = baseIndentString
+        self.indent_cache = [ baseIndentString ]
+        self.baseIndentLength = len(baseIndentString)
+        self.indent_length = len(indent_string)
+        self.raw = False
+        self.lines = []
+        self.previous_line = None
+        self.current_line = None
+        self.space_before_token = False
+        self.add_outputline()
+
+    def add_outputline(self):
+        self.previous_line = self.current_line
+        self.current_line = OutputLine(self)
+        self.lines.append(self.current_line)
+
+    def get_line_number(self):
+        return len(self.lines)
+
+    def add_new_line(self, force_newline=False):
+        if len(self.lines) == 1 and self.just_added_newline():
+            # no newline on start of file
+            return False
+
+        if force_newline or not self.just_added_newline():
+            if not self.raw:
+                self.add_outputline()
+            return True
+        return False
+
+    def get_code(self, end_with_newline, eol):
+        sweet_code = "\n".join(line.toString() for line in self.lines)
+        sweet_code = re.sub('[\r\n\t ]+$', '', sweet_code)
+
+        if end_with_newline:
+            sweet_code += '\n'
+
+        if not eol == '\n':
+            sweet_code = sweet_code.replace('\n', eol)
+
+        return sweet_code
+
+    def set_indent(self, level):
+        # Never indent your first output indent at the start of the file
+        if len(self.lines) > 1:
+            while level >= len(self.indent_cache):
+                self.indent_cache.append(self.indent_cache[-1] + self.indent_string)
+
+
+            self.current_line.set_indent(level)
+            return True
+        self.current_line.set_indent(0)
+        return False
+
+    def add_raw_token(self, token):
+        for _ in range(token.newlines):
+            self.add_outputline()
+
+        self.current_line.push(token.whitespace_before)
+        self.current_line.push(token.text)
+        self.space_before_token = False
+
+    def add_token(self, printable_token):
+        self.add_space_before_token()
+        self.current_line.push(printable_token)
+
+    def add_space_before_token(self):
+        if self.space_before_token and not self.just_added_newline():
+            self.current_line.push(' ')
+        self.space_before_token = False
+
+    def trim(self, eat_newlines = False):
+        self.current_line.trim()
+
+        while eat_newlines and len(self.lines) > 1 and self.current_line.is_empty():
+            self.lines.pop()
+            self.current_line = self.lines[-1]
+            self.current_line.trim()
+
+        if len(self.lines) > 1:
+            self.previous_line = self.lines[-2]
+        else:
+            self.previous_line = None
+
+    def just_added_newline(self):
+        return self.current_line.is_empty()
+
+    def just_added_blankline(self):
+        if self.just_added_newline():
+            if len(self.lines) == 1:
+                return True
+
+            line = self.lines[-2]
+            return line.is_empty()
+
+        return False
diff --git a/python/jsbeautifier/core/token.py b/python/jsbeautifier/core/token.py
new file mode 100644
index 000000000..06884d002
--- /dev/null
+++ b/python/jsbeautifier/core/token.py
@@ -0,0 +1,35 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+class Token:
+    def __init__(self, type, text, newlines = 0, whitespace_before = '', mode = None, parent = None):
+        self.type = type
+        self.text = text
+        self.comments_before = []
+        self.newlines = newlines
+        self.wanted_newline = newlines > 0
+        self.whitespace_before = whitespace_before
+        self.parent = None
+        self.opened = None
+        self.directives = None
diff --git a/python/jsbeautifier/javascript/__init__.py b/python/jsbeautifier/javascript/__init__.py
new file mode 100644
index 000000000..0c0105543
--- /dev/null
+++ b/python/jsbeautifier/javascript/__init__.py
@@ -0,0 +1 @@
+# Empty file :)
diff --git a/python/jsbeautifier/javascript/beautifier.py b/python/jsbeautifier/javascript/beautifier.py
new file mode 100644
index 000000000..03cf5cddc
--- /dev/null
+++ b/python/jsbeautifier/javascript/beautifier.py
@@ -0,0 +1,1185 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import re
+import string
+import copy
+from .tokenizer import Tokenizer
+from .options import BeautifierOptions
+from ..core.options import mergeOpts
+from ..core.output import Output
+
+def default_options():
+    return BeautifierOptions()
+
+class BeautifierFlags:
+    def __init__(self, mode):
+        self.mode = mode
+        self.parent = None
+        self.last_text = ''
+        self.last_word = ''
+        self.declaration_statement = False
+        self.declaration_assignment = False
+        self.multiline_frame = False
+        self.inline_frame = False
+        self.if_block = False
+        self.else_block = False
+        self.do_block = False
+        self.do_while = False
+        self.import_block = False
+        self.in_case = False
+        self.in_case_statement = False
+        self.case_body = False
+        self.indentation_level = 0
+        self.line_indent_level = 0
+        self.start_line_index = 0
+        self.ternary_depth = 0
+
+    def apply_base(self, flags_base, added_newline):
+        next_indent_level = flags_base.indentation_level
+        if not added_newline and \
+            flags_base.line_indent_level > next_indent_level:
+            next_indent_level = flags_base.line_indent_level
+
+        self.parent = flags_base
+        self.last_text = flags_base.last_text
+        self.last_word = flags_base.last_word
+        self.indentation_level = next_indent_level
+
+
+OPERATOR_POSITION = {
+    'before_newline': 'before-newline',
+    'after_newline': 'after-newline',
+    'preserve_newline': 'preserve-newline'
+}
+OPERATOR_POSITION_BEFORE_OR_PRESERVE = [OPERATOR_POSITION['before_newline'], OPERATOR_POSITION['preserve_newline']];
+
+def sanitizeOperatorPosition(opPosition):
+    if not opPosition:
+        return OPERATOR_POSITION['before_newline']
+    elif opPosition not in OPERATOR_POSITION.values():
+        raise ValueError("Invalid Option Value: The option 'operator_position' must be one of the following values\n" +
+            str(OPERATOR_POSITION.values()) +
+            "\nYou passed in: '" + opPosition + "'")
+
+    return opPosition
+
+class MODE:
+      BlockStatement, Statement, ObjectLiteral, ArrayLiteral, \
+      ForInitializer, Conditional, Expression = range(7)
+
+
+def remove_redundant_indentation(output, frame):
+    # This implementation is effective but has some issues:
+    #     - can cause line wrap to happen too soon due to indent removal
+    #           after wrap points are calculated
+    # These issues are minor compared to ugly indentation.
+
+    if frame.multiline_frame or frame.mode == MODE.ForInitializer or frame.mode == MODE.Conditional:
+        return
+
+    # remove one indent from each line inside this section
+    index = frame.start_line_index
+    while index < len(output.lines):
+        output.lines[index].remove_indent()
+        index += 1
+
+
+class Beautifier:
+
+    def __init__(self, opts = default_options() ):
+        import jsbeautifier.core.acorn as acorn
+        self.acorn = acorn
+        self.opts = copy.copy(opts)
+        self.blank_state()
+
+    def blank_state(self, js_source_text = None):
+
+        # internal flags
+        self.flags = None
+        self.previous_flags = None
+        self.flag_store = []
+        self.tokens = []
+        self.token_pos = 0
+
+
+        # force opts.space_after_anon_function to true if opts.jslint_happy
+        if self.opts.jslint_happy:
+            self.opts.space_after_anon_function = True
+
+        if self.opts.indent_with_tabs:
+            self.opts.indent_char = "\t"
+            self.opts.indent_size = 1
+
+        if self.opts.eol == 'auto':
+            self.opts.eol = '\n'
+            if self.acorn.lineBreak.search(js_source_text or ''):
+                self.opts.eol = self.acorn.lineBreak.search(js_source_text).group()
+
+        self.opts.eol = self.opts.eol.replace('\\r', '\r').replace('\\n', '\n')
+
+        self.indent_string = self.opts.indent_char * self.opts.indent_size
+
+        self.baseIndentString = ''
+        self.last_type = 'TK_START_BLOCK' # last token type
+        self.last_last_text = ''         # pre-last token text
+
+        preindent_index = 0;
+        if not js_source_text == None and len(js_source_text) > 0:
+            while preindent_index < len(js_source_text) and \
+                    js_source_text[preindent_index] in [' ', '\t'] :
+                self.baseIndentString += js_source_text[preindent_index]
+                preindent_index += 1
+            js_source_text = js_source_text[preindent_index:]
+
+        self.output = Output(self.indent_string, self.baseIndentString)
+        # If testing the ignore directive, start with output disable set to true
+        self.output.raw = self.opts.test_output_raw;
+
+        self.set_mode(MODE.BlockStatement)
+        return js_source_text
+
+
+
+    def beautify(self, s, opts = None ):
+
+        if opts != None:
+            opts = mergeOpts(opts, 'js')
+            self.opts = copy.copy(opts)
+
+
+        #Compat with old form
+        if self.opts.brace_style == 'collapse-preserve-inline':
+            self.opts.brace_style = 'collapse,preserve-inline'
+
+        split = re.compile("[^a-zA-Z0-9_\-]+").split(self.opts.brace_style)
+        self.opts.brace_style = split[0]
+        self.opts.brace_preserve_inline = (True if bool(split[1] == 'preserve-inline') else None) if len(split) > 1 else False
+
+        if self.opts.brace_style not in ['expand', 'collapse', 'end-expand', 'none']:
+            raise(Exception('opts.brace_style must be "expand", "collapse", "end-expand", or "none".'))
+
+        if self.opts.brace_preserve_inline == None:
+            raise(Exception('opts.brace_style second item must be "preserve-inline"'))
+
+        s = self.blank_state(s)
+
+        input = self.unpack(s, self.opts.eval_code)
+
+        self.handlers = {
+            'TK_START_EXPR': self.handle_start_expr,
+            'TK_END_EXPR': self.handle_end_expr,
+            'TK_START_BLOCK': self.handle_start_block,
+            'TK_END_BLOCK': self.handle_end_block,
+            'TK_WORD': self.handle_word,
+            'TK_RESERVED': self.handle_word,
+            'TK_SEMICOLON': self.handle_semicolon,
+            'TK_STRING': self.handle_string,
+            'TK_EQUALS': self.handle_equals,
+            'TK_OPERATOR': self.handle_operator,
+            'TK_COMMA': self.handle_comma,
+            'TK_BLOCK_COMMENT': self.handle_block_comment,
+            'TK_COMMENT': self.handle_comment,
+            'TK_DOT': self.handle_dot,
+            'TK_UNKNOWN': self.handle_unknown,
+            'TK_EOF': self.handle_eof
+        }
+
+        self.tokens = Tokenizer(input, self.opts, self.indent_string).tokenize()
+        self.token_pos = 0
+
+        current_token = self.get_token()
+        while current_token != None:
+            self.handlers[current_token.type](current_token)
+
+            self.last_last_text = self.flags.last_text
+            self.last_type = current_token.type
+            self.flags.last_text = current_token.text
+            self.token_pos += 1
+            current_token = self.get_token()
+
+
+        sweet_code = self.output.get_code(self.opts.end_with_newline, self.opts.eol)
+
+        return sweet_code
+
+
+    def handle_whitespace_and_comments(self, local_token, preserve_statement_flags = False):
+        newlines = local_token.newlines
+        keep_whitespace = self.opts.keep_array_indentation and self.is_array(self.flags.mode)
+
+        for comment_token in local_token.comments_before:
+            # The cleanest handling of inline comments is to treat them as though they aren't there.
+            # Just continue formatting and the behavior should be logical.
+            # Also ignore unknown tokens.  Again, this should result in better behavior.
+            self.handle_whitespace_and_comments(comment_token, preserve_statement_flags)
+            self.handlers[comment_token.type](comment_token, preserve_statement_flags)
+
+
+        if keep_whitespace:
+             for i in range(newlines):
+                    self.print_newline(i > 0, preserve_statement_flags)
+        else: # not keep_whitespace
+            if self.opts.max_preserve_newlines != 0 and newlines > self.opts.max_preserve_newlines:
+                newlines = self.opts.max_preserve_newlines
+
+            if self.opts.preserve_newlines and newlines > 1:
+                self.print_newline(False, preserve_statement_flags)
+                for i in range(1, newlines):
+                    self.print_newline(True, preserve_statement_flags)
+
+
+    def unpack(self, source, evalcode=False):
+        import jsbeautifier.unpackers as unpackers
+        try:
+            return unpackers.run(source, evalcode)
+        except unpackers.UnpackingError as error:
+            return source
+
+    def is_special_word(self, s):
+        return s in ['case', 'return', 'do', 'if', 'throw', 'else']
+
+    def is_array(self, mode):
+        return mode == MODE.ArrayLiteral
+
+
+    def is_expression(self, mode):
+        return mode in [MODE.Expression, MODE.ForInitializer, MODE.Conditional]
+
+
+    _newline_restricted_tokens = ['break','continue','return', 'throw']
+    def allow_wrap_or_preserved_newline(self, current_token, force_linewrap = False):
+        # never wrap the first token of a line.
+        if self.output.just_added_newline():
+            return
+
+        shouldPreserveOrForce = (self.opts.preserve_newlines and current_token.wanted_newline) or force_linewrap
+        operatorLogicApplies = self.flags.last_text in Tokenizer.positionable_operators or current_token.text in Tokenizer.positionable_operators
+
+        if operatorLogicApplies:
+            shouldPrintOperatorNewline = (self.flags.last_text in Tokenizer.positionable_operators and self.opts.operator_position in OPERATOR_POSITION_BEFORE_OR_PRESERVE) \
+                or current_token.text in Tokenizer.positionable_operators
+            shouldPreserveOrForce = shouldPreserveOrForce and shouldPrintOperatorNewline
+
+        if shouldPreserveOrForce:
+            self.print_newline(preserve_statement_flags = True)
+        elif self.opts.wrap_line_length > 0:
+            if self.last_type == 'TK_RESERVED' and self.flags.last_text in self._newline_restricted_tokens:
+                # These tokens should never have a newline inserted between
+                # them and the following expression.
+                return
+            proposed_line_length = self.output.current_line.get_character_count() + len(current_token.text)
+            if self.output.space_before_token:
+                proposed_line_length += 1
+
+            if proposed_line_length >= self.opts.wrap_line_length:
+                self.print_newline(preserve_statement_flags = True)
+
+
+    def print_newline(self, force_newline = False, preserve_statement_flags = False):
+        if not preserve_statement_flags:
+            if self.flags.last_text != ';' and self.flags.last_text != ',' and self.flags.last_text != '=' and self.last_type != 'TK_OPERATOR':
+                next_token = self.get_token(1)
+                while (self.flags.mode == MODE.Statement and
+                        not (self.flags.if_block and next_token and next_token.type == 'TK_RESERVED' and next_token.text == 'else') and
+                        not self.flags.do_block):
+                    self.restore_mode()
+
+        if self.output.add_new_line(force_newline):
+            self.flags.multiline_frame = True
+
+    def print_token_line_indentation(self, current_token):
+        if self.output.just_added_newline():
+            line = self.output.current_line
+            if self.opts.keep_array_indentation and self.is_array(self.flags.mode) and current_token.wanted_newline:
+                line.push(current_token.whitespace_before)
+                self.output.space_before_token = False
+            elif self.output.set_indent(self.flags.indentation_level):
+                self.flags.line_indent_level = self.flags.indentation_level
+
+
+    def print_token(self, current_token, s=None):
+        if self.output.raw:
+            self.output.add_raw_token(current_token)
+            return
+
+        if self.opts.comma_first and self.last_type == 'TK_COMMA' and self.output.just_added_newline():
+            if self.output.previous_line.last() == ',':
+                # if the comma was already at the start of the line,
+                # pull back onto that line and reprint the indentation
+                popped = self.output.previous_line.pop()
+                if  self.output.previous_line.is_empty():
+                     self.output.previous_line.push(popped)
+                     self.output.trim(True)
+                     self.output.current_line.pop()
+                     self.output.trim()
+
+                # add the comma in front of the next token
+                self.print_token_line_indentation(current_token)
+                self.output.add_token(',')
+                self.output.space_before_token = True
+
+        if s == None:
+            s = current_token.text
+
+        self.print_token_line_indentation(current_token)
+        self.output.add_token(s);
+
+
+    def indent(self):
+        self.flags.indentation_level += 1
+
+    def deindent(self):
+        allow_deindent = self.flags.indentation_level > 0 and ((self.flags.parent == None) or self.flags.indentation_level > self.flags.parent.indentation_level)
+
+        if allow_deindent:
+            self.flags.indentation_level -= 1
+
+    def set_mode(self, mode):
+        if self.flags:
+            self.flag_store.append(self.flags)
+            self.previous_flags = self.flags
+        else:
+            self.previous_flags = BeautifierFlags(mode)
+
+        self.flags = BeautifierFlags(mode)
+        self.flags.apply_base(self.previous_flags, self.output.just_added_newline())
+        self.flags.start_line_index = self.output.get_line_number();
+
+    def restore_mode(self):
+        if len(self.flag_store) > 0:
+            self.previous_flags = self.flags
+            self.flags = self.flag_store.pop()
+            if self.previous_flags.mode == MODE.Statement:
+                remove_redundant_indentation(self.output, self.previous_flags)
+
+
+    def start_of_object_property(self):
+        return self.flags.parent.mode == MODE.ObjectLiteral and self.flags.mode == MODE.Statement and \
+                ((self.flags.last_text == ':' and self.flags.ternary_depth == 0) or (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set']))
+
+    def start_of_statement(self, current_token):
+        if (
+            (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const'] and current_token.type == 'TK_WORD') \
+                or (self.last_type == 'TK_RESERVED' and self.flags.last_text== 'do') \
+                or (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['return', 'throw'] and not current_token.wanted_newline) \
+                or (self.last_type == 'TK_RESERVED' and self.flags.last_text == 'else' \
+                    and not (current_token.type == 'TK_RESERVED' and current_token.text == 'if' and not len(current_token.comments_before))) \
+                or (self.last_type == 'TK_END_EXPR' and (self.previous_flags.mode == MODE.ForInitializer or self.previous_flags.mode == MODE.Conditional)) \
+                or (self.last_type == 'TK_WORD' and self.flags.mode == MODE.BlockStatement \
+                    and not self.flags.in_case
+                    and not (current_token.text == '--' or current_token.text == '++')
+                    and self.last_last_text != 'function'
+                    and current_token.type != 'TK_WORD' and current_token.type != 'TK_RESERVED') \
+                or (self.flags.mode == MODE.ObjectLiteral and \
+                    ((self.flags.last_text == ':' and self.flags.ternary_depth == 0) or (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set'])))
+                ):
+
+            self.set_mode(MODE.Statement)
+            self.indent()
+
+            self.handle_whitespace_and_comments(current_token, True);
+
+            # Issue #276:
+            # If starting a new statement with [if, for, while, do], push to a new line.
+            # if (a) if (b) if(c) d(); else e(); else f();
+            if not self.start_of_object_property():
+                self.allow_wrap_or_preserved_newline(current_token, current_token.type == 'TK_RESERVED' and current_token.text in ['do', 'for', 'if', 'while'])
+
+            return True
+        else:
+            return False
+
+    def get_token(self, offset = 0):
+        index = self.token_pos + offset
+        if index < 0 or index >= len(self.tokens):
+            return None
+        else:
+            return self.tokens[index]
+
+
+    def handle_start_expr(self, current_token):
+        if self.start_of_statement(current_token):
+            # The conditional starts the statement if appropriate.
+            pass
+        else:
+            self.handle_whitespace_and_comments(current_token)
+
+        next_mode = MODE.Expression
+
+        if current_token.text == '[':
+            if self.last_type == 'TK_WORD' or self.flags.last_text == ')':
+                if self.last_type == 'TK_RESERVED' and self.flags.last_text in Tokenizer.line_starters:
+                    self.output.space_before_token = True
+                self.set_mode(next_mode)
+                self.print_token(current_token)
+                self.indent()
+                if self.opts.space_in_paren:
+                    self.output.space_before_token = True
+                return
+
+            next_mode = MODE.ArrayLiteral
+
+            if self.is_array(self.flags.mode):
+                if self.flags.last_text == '[' or (
+                    self.flags.last_text == ',' and (self.last_last_text == ']' or self.last_last_text == '}')):
+                    # ], [ goes to a new line
+                    # }, [ goes to a new line
+                    if not self.opts.keep_array_indentation:
+                        self.print_newline()
+
+        else:
+            if self.last_type == 'TK_RESERVED' and self.flags.last_text == 'for':
+                next_mode = MODE.ForInitializer
+            elif self.last_type == 'TK_RESERVED' and self.flags.last_text in ['if', 'while']:
+                next_mode = MODE.Conditional
+            else:
+                next_mode = MODE.Expression
+
+
+        if self.flags.last_text == ';' or self.last_type == 'TK_START_BLOCK':
+            self.print_newline()
+        elif self.last_type in ['TK_END_EXPR', 'TK_START_EXPR', 'TK_END_BLOCK'] or self.flags.last_text == '.':
+            # do nothing on (( and )( and ][ and ]( and .(
+            # TODO: Consider whether forcing this is required.  Review failing tests when removed.
+            self.allow_wrap_or_preserved_newline(current_token, current_token.wanted_newline)
+
+        elif not (self.last_type == 'TK_RESERVED' and current_token.text == '(') and self.last_type not in ['TK_WORD', 'TK_OPERATOR']:
+            self.output.space_before_token = True
+        elif (self.last_type == 'TK_RESERVED' and (self.flags.last_word == 'function' or self.flags.last_word == 'typeof')) or \
+            (self.flags.last_text == '*' and (
+                self.last_last_text in ['function', 'yield'] or
+                (self.flags.mode == MODE.ObjectLiteral and self.last_last_text in ['{', ',']))):
+            # function() vs function (), typeof() vs typeof ()
+            # function*() vs function* (), yield*() vs yield* ()
+            if self.opts.space_after_anon_function:
+                self.output.space_before_token = True
+        elif self.last_type == 'TK_RESERVED' and (self.flags.last_text in Tokenizer.line_starters or self.flags.last_text == 'catch'):
+            # TODO: option space_before_conditional
+            self.output.space_before_token = True
+
+        elif current_token.text == '(' and self.last_type == 'TK_RESERVED' and self.flags.last_word == 'await':
+            self.output.space_before_token = True
+
+
+        # Support of this kind of newline preservation:
+        # a = (b &&
+        #     (c || d));
+        if self.last_type in ['TK_EQUALS', 'TK_OPERATOR']:
+            if not self.start_of_object_property():
+                self.allow_wrap_or_preserved_newline(current_token)
+
+
+        # Support preserving wrapped arrow function expressions
+        # a.b('c',
+        #     () => d.e
+        # )
+        if current_token.text == '(' and self.last_type not in ['TK_WORD', 'TK_RESERVED']:
+            self.allow_wrap_or_preserved_newline(current_token)
+
+
+        self.set_mode(next_mode)
+        self.print_token(current_token)
+
+        if self.opts.space_in_paren:
+            self.output.space_before_token = True
+
+        # In all cases, if we newline while inside an expression it should be indented.
+        self.indent()
+
+
+
+    def handle_end_expr(self, current_token):
+        # statements inside expressions are not valid syntax, but...
+        # statements must all be closed when their container closes
+        while self.flags.mode == MODE.Statement:
+            self.restore_mode()
+
+        self.handle_whitespace_and_comments(current_token)
+
+        if self.flags.multiline_frame:
+            self.allow_wrap_or_preserved_newline(current_token, current_token.text == ']' and self.is_array(self.flags.mode) and not self.opts.keep_array_indentation)
+
+        if self.opts.space_in_paren:
+            if self.last_type == 'TK_START_EXPR' and not self.opts.space_in_empty_paren:
+                # empty parens are always "()" and "[]", not "( )" or "[ ]"
+                self.output.space_before_token = False
+                self.output.trim()
+            else:
+                self.output.space_before_token = True
+
+        if current_token.text == ']' and self.opts.keep_array_indentation:
+            self.print_token(current_token)
+            self.restore_mode()
+        else:
+            self.restore_mode()
+            self.print_token(current_token)
+
+        remove_redundant_indentation(self.output, self.previous_flags)
+
+        # do {} while () // no statement required after
+        if self.flags.do_while and self.previous_flags.mode == MODE.Conditional:
+            self.previous_flags.mode = MODE.Expression
+            self.flags.do_block = False
+            self.flags.do_while = False
+
+    def handle_start_block(self, current_token):
+        self.handle_whitespace_and_comments(current_token)
+
+        # Check if this is a BlockStatement that should be treated as a ObjectLiteral
+        next_token = self.get_token(1)
+        second_token = self.get_token(2)
+        if second_token != None and \
+            ((second_token.text in [':', ','] and next_token.type in ['TK_STRING', 'TK_WORD', 'TK_RESERVED']) \
+                or (next_token.text in ['get', 'set', '...'] and second_token.type in ['TK_WORD', 'TK_RESERVED'])):
+            # We don't support TypeScript,but we didn't break it for a very long time.
+            # We'll try to keep not breaking it.
+            if not self.last_last_text in ['class','interface']:
+                self.set_mode(MODE.ObjectLiteral)
+            else:
+                self.set_mode(MODE.BlockStatement)
+        elif self.last_type == 'TK_OPERATOR' and self.flags.last_text == '=>':
+            # arrow function: (param1, paramN) => { statements }
+            self.set_mode(MODE.BlockStatement)
+        elif self.last_type in ['TK_EQUALS', 'TK_START_EXPR', 'TK_COMMA', 'TK_OPERATOR'] or \
+            (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['return', 'throw', 'import', 'default']):
+            # Detecting shorthand function syntax is difficult by scanning forward,
+            #     so check the surrounding context.
+            # If the block is being returned, imported, export default, passed as arg,
+            #     assigned with = or assigned in a nested object, treat as an ObjectLiteral.
+            self.set_mode(MODE.ObjectLiteral)
+        else:
+            self.set_mode(MODE.BlockStatement)
+
+        empty_braces = (not next_token == None) and len(next_token.comments_before) == 0 and next_token.text == '}'
+        empty_anonymous_function = empty_braces and self.flags.last_word == 'function' and \
+            self.last_type == 'TK_END_EXPR'
+
+        if self.opts.brace_preserve_inline: # check for inline, set inline_frame if so
+            # search forward for newline wanted inside this block
+            index = 0
+            check_token = None
+            self.flags.inline_frame = True
+            do_loop = True
+            while (do_loop):
+                index += 1
+                check_token = self.get_token(index)
+                if check_token.wanted_newline:
+                    self.flags.inline_frame = False
+
+                do_loop = (check_token.type != 'TK_EOF' and
+                      not (check_token.type == 'TK_END_BLOCK' and check_token.opened == current_token))
+
+        if (self.opts.brace_style == 'expand' or \
+            (self.opts.brace_style == 'none' and current_token.wanted_newline)) and \
+            not self.flags.inline_frame:
+            if self.last_type != 'TK_OPERATOR' and \
+                (empty_anonymous_function or
+                    self.last_type == 'TK_EQUALS' or
+                    (self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text) and self.flags.last_text != 'else')):
+                self.output.space_before_token = True
+            else:
+                self.print_newline(preserve_statement_flags = True)
+        else: # collapse || inline_frame
+            if self.is_array(self.previous_flags.mode) and (self.last_type == 'TK_START_EXPR' or self.last_type == 'TK_COMMA'):
+                # if we're preserving inline,
+                # allow newline between comma and next brace.
+                if self.flags.inline_frame:
+                    self.allow_wrap_or_preserved_newline(current_token)
+                    self.flags.inline_frame = True
+                    self.previous_flags.multiline_frame = self.previous_flags.multiline_frame or self.flags.multiline_frame
+                    self.flags.multiline_frame = False
+                elif self.last_type == 'TK_COMMA':
+                    self.output.space_before_token = True
+
+            elif self.last_type not in ['TK_OPERATOR', 'TK_START_EXPR']:
+                if self.last_type == 'TK_START_BLOCK' and not self.flags.inline_frame:
+                    self.print_newline()
+                else:
+                    self.output.space_before_token = True
+
+        self.print_token(current_token)
+        self.indent()
+
+
+    def handle_end_block(self, current_token):
+        # statements must all be closed when their container closes
+        self.handle_whitespace_and_comments(current_token)
+
+        while self.flags.mode == MODE.Statement:
+            self.restore_mode()
+
+        empty_braces = self.last_type == 'TK_START_BLOCK'
+
+        if self.flags.inline_frame and not empty_braces: # try inline_frame (only set if opt.braces-preserve-inline) first
+            self.output.space_before_token = True;
+        elif self.opts.brace_style == 'expand':
+            if not empty_braces:
+                self.print_newline()
+        else:
+            # skip {}
+            if not empty_braces:
+                if self.is_array(self.flags.mode) and self.opts.keep_array_indentation:
+                    self.opts.keep_array_indentation = False
+                    self.print_newline()
+                    self.opts.keep_array_indentation = True
+                else:
+                    self.print_newline()
+
+        self.restore_mode()
+        self.print_token(current_token)
+
+
+    def handle_word(self, current_token):
+        if current_token.type == 'TK_RESERVED':
+            if current_token.text in ['set', 'get'] and self.flags.mode != MODE.ObjectLiteral:
+                current_token.type = 'TK_WORD'
+            elif current_token.text in ['as', 'from'] and not self.flags.import_block:
+                current_token.type = 'TK_WORD'
+            elif self.flags.mode == MODE.ObjectLiteral:
+                next_token = self.get_token(1)
+                if next_token.text == ':':
+                    current_token.type = 'TK_WORD'
+
+        if self.start_of_statement(current_token):
+            # The conditional starts the statement if appropriate.
+            if self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const'] and current_token.type == 'TK_WORD':
+                self.flags.declaration_statement = True
+
+        elif current_token.wanted_newline and \
+                not self.is_expression(self.flags.mode) and \
+                (self.last_type != 'TK_OPERATOR' or (self.flags.last_text == '--' or self.flags.last_text == '++')) and \
+                self.last_type != 'TK_EQUALS' and \
+                (self.opts.preserve_newlines or not (self.last_type == 'TK_RESERVED' and self.flags.last_text in ['var', 'let', 'const', 'set', 'get'])):
+            self.handle_whitespace_and_comments(current_token)
+            self.print_newline()
+        else:
+            self.handle_whitespace_and_comments(current_token)
+
+
+        if self.flags.do_block and not self.flags.do_while:
+            if current_token.type == 'TK_RESERVED' and current_token.text == 'while':
+                # do {} ## while ()
+                self.output.space_before_token = True
+                self.print_token(current_token)
+                self.output.space_before_token = True
+                self.flags.do_while = True
+                return
+            else:
+                # do {} should always have while as the next word.
+                # if we don't see the expected while, recover
+                self.print_newline()
+                self.flags.do_block = False
+
+        # if may be followed by else, or not
+        # Bare/inline ifs are tricky
+        # Need to unwind the modes correctly: if (a) if (b) c(); else d(); else e();
+        if self.flags.if_block:
+            if (not self.flags.else_block) and (current_token.type == 'TK_RESERVED' and current_token.text == 'else'):
+                self.flags.else_block = True
+            else:
+                while self.flags.mode == MODE.Statement:
+                    self.restore_mode()
+
+                self.flags.if_block = False
+
+        if current_token.type == 'TK_RESERVED' and (current_token.text == 'case' or (current_token.text == 'default' and self.flags.in_case_statement)):
+            self.print_newline()
+            if self.flags.case_body or self.opts.jslint_happy:
+                self.flags.case_body = False
+                self.deindent()
+            self.print_token(current_token)
+            self.flags.in_case = True
+            self.flags.in_case_statement = True
+            return
+
+        if self.last_type in ['TK_COMMA', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']:
+            if not self.start_of_object_property():
+                self.allow_wrap_or_preserved_newline(current_token)
+
+        if current_token.type == 'TK_RESERVED' and current_token.text == 'function':
+            if (self.flags.last_text in ['}', ';'] or
+                (self.output.just_added_newline() and not (self.flags.last_text in ['(', '[', '{', ':', '=', ','] or self.last_type == 'TK_OPERATOR'))):
+                # make sure there is a nice clean space of at least one blank line
+                # before a new function definition, except in arrays
+                if not self.output.just_added_blankline() and len(current_token.comments_before) == 0:
+                    self.print_newline()
+                    self.print_newline(True)
+
+            if self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD':
+                if self.last_type == 'TK_RESERVED' and self.flags.last_text in ['get', 'set', 'new', 'return', 'export', 'async']:
+                    self.output.space_before_token = True
+                elif self.last_type == 'TK_RESERVED' and self.flags.last_text == 'default' and self.last_last_text == 'export':
+                    self.output.space_before_token = True
+                else:
+                    self.print_newline()
+            elif self.last_type == 'TK_OPERATOR' or self.flags.last_text == '=':
+                # foo = function
+                self.output.space_before_token = True
+            elif not self.flags.multiline_frame and (self.is_expression(self.flags.mode) or self.is_array(self.flags.mode)):
+                # (function
+                pass
+            else:
+                self.print_newline()
+
+            self.print_token(current_token)
+            self.flags.last_word = current_token.text
+            return
+
+        prefix = 'NONE'
+
+        if self.last_type == 'TK_END_BLOCK':
+            if self.previous_flags.inline_frame:
+                prefix = 'SPACE'
+            elif not (current_token.type == 'TK_RESERVED' and current_token.text in ['else', 'catch', 'finally', 'from']):
+                prefix = 'NEWLINE'
+            else:
+                if self.opts.brace_style in ['expand', 'end-expand'] or \
+                    (self.opts.brace_style == 'none' and current_token.wanted_newline):
+                    prefix = 'NEWLINE'
+                else:
+                    prefix = 'SPACE'
+                    self.output.space_before_token = True
+        elif self.last_type == 'TK_SEMICOLON' and self.flags.mode == MODE.BlockStatement:
+            # TODO: Should this be for STATEMENT as well?
+            prefix = 'NEWLINE'
+        elif self.last_type == 'TK_SEMICOLON' and self.is_expression(self.flags.mode):
+            prefix = 'SPACE'
+        elif self.last_type == 'TK_STRING':
+            prefix = 'NEWLINE'
+        elif self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD' or \
+            (self.flags.last_text == '*' and (
+                self.last_last_text in ['function', 'yield'] or
+                (self.flags.mode == MODE.ObjectLiteral and self.last_last_text in ['{', ',']))):
+            prefix = 'SPACE'
+        elif self.last_type == 'TK_START_BLOCK':
+            if self.flags.inline_frame:
+                prefix = 'SPACE'
+            else:
+                prefix = 'NEWLINE'
+        elif self.last_type == 'TK_END_EXPR':
+            self.output.space_before_token = True
+            prefix = 'NEWLINE'
+
+        if current_token.type == 'TK_RESERVED' and current_token.text in Tokenizer.line_starters and self.flags.last_text != ')':
+            if self.flags.inline_frame or self.flags.last_text == 'else ' or self.flags.last_text == 'export':
+                prefix = 'SPACE'
+            else:
+                prefix = 'NEWLINE'
+
+        if current_token.type == 'TK_RESERVED' and current_token.text in ['else', 'catch', 'finally']:
+            if ((not (self.last_type == 'TK_END_BLOCK' and self.previous_flags.mode == MODE.BlockStatement)) \
+               or self.opts.brace_style == 'expand' \
+               or self.opts.brace_style == 'end-expand' \
+               or (self.opts.brace_style == 'none' and current_token.wanted_newline)) \
+               and not self.flags.inline_frame:
+                self.print_newline()
+            else:
+                self.output.trim(True)
+                # If we trimmed and there's something other than a close block before us
+                # put a newline back in.  Handles '} // comment' scenario.
+                if self.output.current_line.last() != '}':
+                    self.print_newline()
+
+                self.output.space_before_token = True
+
+        elif prefix == 'NEWLINE':
+            if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text):
+                # no newline between return nnn
+                self.output.space_before_token = True
+            elif self.last_type != 'TK_END_EXPR':
+                if (self.last_type != 'TK_START_EXPR' or not (current_token.type == 'TK_RESERVED' and current_token.text in ['var', 'let', 'const'])) and self.flags.last_text != ':':
+                    # no need to force newline on VAR -
+                    # for (var x = 0...
+                    if current_token.type == 'TK_RESERVED' and current_token.text == 'if' and self.flags.last_text == 'else':
+                        self.output.space_before_token = True
+                    else:
+                        self.print_newline()
+            elif current_token.type == 'TK_RESERVED' and current_token.text in Tokenizer.line_starters and self.flags.last_text != ')':
+                self.print_newline()
+        elif self.flags.multiline_frame and self.is_array(self.flags.mode) and self.flags.last_text == ',' and self.last_last_text == '}':
+            self.print_newline() # }, in lists get a newline
+        elif prefix == 'SPACE':
+            self.output.space_before_token = True
+
+
+        self.print_token(current_token)
+        self.flags.last_word = current_token.text
+
+        if current_token.type == 'TK_RESERVED':
+            if current_token.text == 'do':
+                self.flags.do_block = True
+            elif current_token.text == 'if':
+                self.flags.if_block = True
+            elif current_token.text == 'import':
+                self.flags.import_block = True
+            elif current_token.text == 'from' and self.flags.import_block:
+                self.flags.import_block = False
+
+
+    def handle_semicolon(self, current_token):
+        if self.start_of_statement(current_token):
+            # The conditional starts the statement if appropriate.
+            # Semicolon can be the start (and end) of a statement
+            self.output.space_before_token = False
+        else:
+            self.handle_whitespace_and_comments(current_token)
+
+        next_token = self.get_token(1)
+        while (self.flags.mode == MODE.Statement and
+                not (self.flags.if_block and next_token and next_token.type == 'TK_RESERVED' and next_token.text == 'else') and
+                not self.flags.do_block):
+            self.restore_mode()
+
+        if self.flags.import_block:
+            self.flags.import_block = False
+
+        self.print_token(current_token)
+
+
+    def handle_string(self, current_token):
+        if self.start_of_statement(current_token):
+            # The conditional starts the statement if appropriate.
+            # One difference - strings want at least a space before
+            self.output.space_before_token = True
+        else:
+            self.handle_whitespace_and_comments(current_token)
+
+            if self.last_type == 'TK_RESERVED' or self.last_type == 'TK_WORD' or self.flags.inline_frame:
+                self.output.space_before_token = True
+            elif self.last_type in ['TK_COMMA', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']:
+                if not self.start_of_object_property():
+                    self.allow_wrap_or_preserved_newline(current_token)
+            else:
+                self.print_newline()
+
+        self.print_token(current_token)
+
+
+    def handle_equals(self, current_token):
+        if self.start_of_statement(current_token):
+            # The conditional starts the statement if appropriate.
+            pass
+        else:
+            self.handle_whitespace_and_comments(current_token)
+
+
+        if self.flags.declaration_statement:
+            # just got an '=' in a var-line, different line breaking rules will apply
+            self.flags.declaration_assignment = True
+
+        self.output.space_before_token = True
+        self.print_token(current_token)
+        self.output.space_before_token = True
+
+
+    def handle_comma(self, current_token):
+        self.handle_whitespace_and_comments(current_token, True)
+
+        self.print_token(current_token)
+        self.output.space_before_token = True
+
+        if self.flags.declaration_statement:
+            if self.is_expression(self.flags.parent.mode):
+                # do not break on comma, for ( var a = 1, b = 2
+                self.flags.declaration_assignment = False
+
+            if self.flags.declaration_assignment:
+                self.flags.declaration_assignment = False
+                self.print_newline(preserve_statement_flags = True)
+            elif self.opts.comma_first:
+                # for comma-first, we want to allow a newline before the comma
+                # to turn into a newline after the comma, which we will fixup later
+                self.allow_wrap_or_preserved_newline(current_token)
+
+        elif self.flags.mode == MODE.ObjectLiteral \
+            or (self.flags.mode == MODE.Statement and self.flags.parent.mode ==  MODE.ObjectLiteral):
+            if self.flags.mode == MODE.Statement:
+                self.restore_mode()
+
+            if not self.flags.inline_frame:
+                self.print_newline()
+        elif self.opts.comma_first:
+            # EXPR or DO_BLOCK
+            # for comma-first, we want to allow a newline before the comma
+            # to turn into a newline after the comma, which we will fixup later
+            self.allow_wrap_or_preserved_newline(current_token)
+
+
+    def handle_operator(self, current_token):
+        isGeneratorAsterisk = current_token.text == '*' and \
+            ((self.last_type == 'TK_RESERVED' and self.flags.last_text in ['function', 'yield']) or
+                (self.last_type in ['TK_START_BLOCK', 'TK_COMMA', 'TK_END_BLOCK', 'TK_SEMICOLON']))
+        isUnary = current_token.text in ['+', '-'] \
+            and (self.last_type in ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR'] \
+            or self.flags.last_text in Tokenizer.line_starters or self.flags.last_text == ',')
+
+        if self.start_of_statement(current_token):
+            # The conditional starts the statement if appropriate.
+            pass
+        else:
+            preserve_statement_flags = not isGeneratorAsterisk
+            self.handle_whitespace_and_comments(current_token, preserve_statement_flags)
+
+        if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text):
+            # return had a special handling in TK_WORD
+            self.output.space_before_token = True
+            self.print_token(current_token)
+            return
+
+        # hack for actionscript's import .*;
+        if current_token.text == '*' and self.last_type == 'TK_DOT':
+            self.print_token(current_token)
+            return
+
+        if current_token.text == '::':
+            # no spaces around the exotic namespacing syntax operator
+            self.print_token(current_token)
+            return
+
+        # Allow line wrapping between operators when operator_position is
+        #   set to before or preserve
+        if self.last_type == 'TK_OPERATOR' and self.opts.operator_position in OPERATOR_POSITION_BEFORE_OR_PRESERVE:
+            self.allow_wrap_or_preserved_newline(current_token)
+
+        if current_token.text == ':' and self.flags.in_case:
+            self.flags.case_body = True
+            self.indent()
+            self.print_token(current_token)
+            self.print_newline()
+            self.flags.in_case = False
+            return
+
+        space_before = True
+        space_after = True
+        in_ternary = False
+
+        if current_token.text == ':':
+            if self.flags.ternary_depth == 0:
+                # Colon is invalid javascript outside of ternary and object, but do our best to guess what was meant.
+                space_before = False
+            else:
+                self.flags.ternary_depth -= 1
+                in_ternary = True
+        elif current_token.text == '?':
+            self.flags.ternary_depth += 1
+
+        # let's handle the operator_position option prior to any conflicting logic
+        if (not isUnary) and (not isGeneratorAsterisk) and \
+            self.opts.preserve_newlines and current_token.text in Tokenizer.positionable_operators:
+
+            isColon = current_token.text == ':'
+            isTernaryColon = isColon and in_ternary
+            isOtherColon = isColon and not in_ternary
+
+            if self.opts.operator_position == OPERATOR_POSITION['before_newline']:
+                # if the current token is : and it's not a ternary statement then we set space_before to false
+                self.output.space_before_token = not isOtherColon
+
+                self.print_token(current_token)
+
+                if (not isColon) or isTernaryColon:
+                    self.allow_wrap_or_preserved_newline(current_token)
+
+                self.output.space_before_token = True
+
+                return
+
+            elif self.opts.operator_position == OPERATOR_POSITION['after_newline']:
+                # if the current token is anything but colon, or (via deduction) it's a colon and in a ternary statement,
+                #   then print a newline.
+                self.output.space_before_token = True
+
+                if (not isColon) or isTernaryColon:
+                    if self.get_token(1).wanted_newline:
+                        self.print_newline(preserve_statement_flags = True)
+                    else:
+                        self.allow_wrap_or_preserved_newline(current_token)
+                else:
+                    self.output.space_before_token = False
+
+                self.print_token(current_token)
+
+                self.output.space_before_token = True
+                return
+
+            elif self.opts.operator_position == OPERATOR_POSITION['preserve_newline']:
+                if not isOtherColon:
+                    self.allow_wrap_or_preserved_newline(current_token)
+
+                # if we just added a newline, or the current token is : and it's not a ternary statement,
+                #   then we set space_before to false
+                self.output.space_before_token = not (self.output.just_added_newline() or isOtherColon)
+
+                self.print_token(current_token)
+
+                self.output.space_before_token = True
+                return
+
+        if isGeneratorAsterisk:
+            self.allow_wrap_or_preserved_newline(current_token)
+            space_before = False
+            next_token = self.get_token(1)
+            space_after = next_token and next_token.type in ['TK_WORD','TK_RESERVED']
+        elif current_token.text == '...':
+            self.allow_wrap_or_preserved_newline(current_token)
+            space_before = self.last_type == 'TK_START_BLOCK'
+            space_after = False
+        elif current_token.text in ['--', '++', '!', '~'] or isUnary:
+            space_before = False
+            space_after = False
+
+            # http://www.ecma-international.org/ecma-262/5.1/#sec-7.9.1
+            # if there is a newline between -- or ++ and anything else we should preserve it.
+            if current_token.wanted_newline and (current_token.text == '--' or current_token.text == '++'):
+                self.print_newline(preserve_statement_flags = True)
+
+            if self.flags.last_text == ';' and self.is_expression(self.flags.mode):
+                # for (;; ++i)
+                #         ^^
+                space_before = True
+
+            if self.last_type == 'TK_RESERVED':
+                space_before = True
+            elif self.last_type == 'TK_END_EXPR':
+                space_before = not (self.flags.last_text == ']' and current_token.text in ['--', '++'])
+            elif self.last_type == 'TK_OPERATOR':
+                # a++ + ++b
+                # a - -b
+                space_before = current_token.text in ['--', '-','++', '+'] and self.flags.last_text in ['--', '-','++', '+']
+                # + and - are not unary when preceeded by -- or ++ operator
+                # a-- + b
+                # a * +b
+                # a - -b
+                if current_token.text in ['-', '+'] and self.flags.last_text in ['--', '++']:
+                    space_after = True
+
+            if (((self.flags.mode == MODE.BlockStatement and not self.flags.inline_frame) or self.flags.mode == MODE.Statement)
+                    and self.flags.last_text in ['{', ';']):
+                # { foo: --i }
+                # foo(): --bar
+                self.print_newline()
+
+        if space_before:
+            self.output.space_before_token = True
+
+        self.print_token(current_token)
+
+        if space_after:
+            self.output.space_before_token = True
+
+
+
+    def handle_block_comment(self, current_token, preserve_statement_flags):
+        if self.output.raw:
+            self.output.add_raw_token(current_token)
+            if current_token.directives and current_token.directives.get('preserve') == 'end':
+                # If we're testing the raw output behavior, do not allow a directive to turn it off.
+                self.output.raw = self.opts.test_output_raw
+            return
+
+        if current_token.directives:
+            self.print_newline(preserve_statement_flags = preserve_statement_flags)
+            self.print_token(current_token)
+            if current_token.directives.get('preserve') == 'start':
+                self.output.raw = True
+
+            self.print_newline(preserve_statement_flags = True)
+            return
+
+        # inline block
+        if not self.acorn.newline.search(current_token.text) and not current_token.wanted_newline:
+            self.output.space_before_token = True
+            self.print_token(current_token)
+            self.output.space_before_token = True
+            return
+
+        lines = self.acorn.allLineBreaks.split(current_token.text)
+        javadoc = False
+        starless = False
+        last_indent = current_token.whitespace_before
+        last_indent_length = len(last_indent)
+
+        # block comment starts with a new line
+        self.print_newline(preserve_statement_flags = preserve_statement_flags)
+        if  len(lines) > 1:
+            javadoc = not any(l for l in lines[1:] if ( l.strip() == '' or (l.lstrip())[0] != '*'))
+            starless = all(l.startswith(last_indent) or l.strip() == '' for l in lines[1:])
+
+        # first line always indented
+        self.print_token(current_token, lines[0])
+        for line in lines[1:]:
+            self.print_newline(preserve_statement_flags = True)
+            if javadoc:
+                # javadoc: reformat and re-indent
+                self.print_token(current_token, ' ' + line.lstrip())
+            elif starless and len(line) > last_indent_length:
+                # starless: re-indent non-empty content, avoiding trim
+                self.print_token(current_token, line[last_indent_length:])
+            else:
+                # normal comments output raw
+                self.output.add_token(line)
+
+        self.print_newline(preserve_statement_flags = preserve_statement_flags)
+
+    def handle_comment(self, current_token, preserve_statement_flags):
+        if current_token.wanted_newline:
+            self.print_newline(preserve_statement_flags = preserve_statement_flags)
+
+        if not current_token.wanted_newline:
+            self.output.trim(True)
+
+        self.output.space_before_token = True
+        self.print_token(current_token)
+        self.print_newline(preserve_statement_flags = preserve_statement_flags)
+
+
+    def handle_dot(self, current_token):
+        if self.start_of_statement(current_token):
+            # The conditional starts the statement if appropriate.
+            pass
+        else:
+            self.handle_whitespace_and_comments(current_token, True)
+
+        if self.last_type == 'TK_RESERVED' and self.is_special_word(self.flags.last_text):
+            self.output.space_before_token = True
+        else:
+            # allow preserved newlines before dots in general
+            # force newlines on dots after close paren when break_chained - for bar().baz()
+            self.allow_wrap_or_preserved_newline(current_token,
+                self.flags.last_text == ')' and self.opts.break_chained_methods)
+
+        self.print_token(current_token)
+
+    def handle_unknown(self, current_token, preserve_statement_flags):
+        self.print_token(current_token)
+        if current_token.text[-1] == '\n':
+            self.print_newline(preserve_statement_flags = preserve_statement_flags)
+
+    def handle_eof(self, current_token):
+        # Unwind any open statements
+        while self.flags.mode == MODE.Statement:
+            self.restore_mode()
+
+        self.handle_whitespace_and_comments(current_token)
diff --git a/python/jsbeautifier/javascript/options.py b/python/jsbeautifier/javascript/options.py
new file mode 100644
index 000000000..d23a82d5b
--- /dev/null
+++ b/python/jsbeautifier/javascript/options.py
@@ -0,0 +1,86 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+class BeautifierOptions:
+    def __init__(self):
+        self.indent_size = 4
+        self.indent_char = ' '
+        self.indent_with_tabs = False
+        self.eol = 'auto'
+        self.preserve_newlines = True
+        self.max_preserve_newlines = 10
+        self.space_in_paren = False
+        self.space_in_empty_paren = False
+        self.e4x = False
+        self.jslint_happy = False
+        self.space_after_anon_function = False
+        self.brace_style = 'collapse'
+        self.keep_array_indentation = False
+        self.keep_function_indentation = False
+        self.eval_code = False
+        self.unescape_strings = False
+        self.wrap_line_length = 0
+        self.break_chained_methods = False
+        self.end_with_newline = False
+        self.comma_first = False
+        self.operator_position = 'before-newline'
+
+        self.css = None
+        self.js = None
+        self.html = None
+
+        # For testing of beautify ignore:start directive
+        self.test_output_raw = False
+        self.editorconfig = False
+
+
+    def __repr__(self):
+        return \
+"""indent_size = %d
+indent_char = [%s]
+preserve_newlines = %s
+max_preserve_newlines = %d
+space_in_paren = %s
+jslint_happy = %s
+space_after_anon_function = %s
+indent_with_tabs = %s
+brace_style = %s
+keep_array_indentation = %s
+eval_code = %s
+wrap_line_length = %s
+unescape_strings = %s
+""" % ( self.indent_size,
+        self.indent_char,
+        self.preserve_newlines,
+        self.max_preserve_newlines,
+        self.space_in_paren,
+        self.jslint_happy,
+        self.space_after_anon_function,
+        self.indent_with_tabs,
+        self.brace_style,
+        self.keep_array_indentation,
+        self.eval_code,
+        self.wrap_line_length,
+        self.unescape_strings,
+        )
diff --git a/python/jsbeautifier/javascript/tokenizer.py b/python/jsbeautifier/javascript/tokenizer.py
new file mode 100644
index 000000000..f0f9c51b8
--- /dev/null
+++ b/python/jsbeautifier/javascript/tokenizer.py
@@ -0,0 +1,520 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import re
+from ..core.inputscanner import InputScanner
+from ..core.token import Token
+
+class Tokenizer:
+
+    whitespace = ["\n", "\r", "\t", " "]
+    digit = re.compile('[0-9]')
+    digit_bin = re.compile('[01]')
+    digit_oct = re.compile('[01234567]')
+    digit_hex = re.compile('[0123456789abcdefABCDEF]')
+
+    positionable_operators = '!= !== % & && * ** + - / : < << <= == === > >= >> >>> ? ^ | ||'.split(' ')
+    punct = (positionable_operators +
+        # non-positionable operators - these do not follow operator position settings
+        '! %= &= *= **= ++ += , -- -= /= :: <<= = => >>= >>>= ^= |= ~ ...'.split(' '))
+
+    # Words which always should start on a new line
+    line_starters = 'continue,try,throw,return,var,let,const,if,switch,case,default,for,while,break,function,import,export'.split(',')
+    reserved_words = line_starters + ['do', 'in', 'of', 'else', 'get', 'set', 'new', 'catch', 'finally', 'typeof', 'yield', 'async', 'await', 'from', 'as']
+
+    def __init__ (self, input_string, opts, indent_string):
+        import jsbeautifier.core.acorn as acorn
+        self.acorn = acorn
+        self.input = InputScanner(input_string)
+        self.opts = opts
+        self.indent_string = indent_string
+        #  /* ... */ comment ends with nearest */ or end of file
+        self.block_comment_pattern = re.compile('([\s\S]*?)((?:\*\/)|$)')
+
+        # comment ends just before nearest linefeed or end of file
+        self.comment_pattern = re.compile(self.acorn.six.u('([^\n\r\u2028\u2029]*)'))
+
+        self.directives_block_pattern = re.compile('\/\* beautify( \w+[:]\w+)+ \*\/')
+        self.directive_pattern = re.compile(' (\w+)[:](\w+)')
+        self.directives_end_ignore_pattern = re.compile('([\s\S]*?)((?:\/\*\sbeautify\signore:end\s\*\/)|$)')
+
+        self.template_pattern = re.compile('((<\?php|<\?=)[\s\S]*?\?>)|(<%[\s\S]*?%>)')
+
+    def tokenize(self):
+        self.in_html_comment = False
+        self.tokens = []
+
+        next = None
+        last = None
+        open = None
+        open_stack = []
+        comments = []
+
+        while not (not last == None and last.type == 'TK_EOF'):
+            token_values = self.__tokenize_next()
+            next = Token(token_values[1], token_values[0], self.n_newlines, self.whitespace_before_token)
+
+            while next.type == 'TK_COMMENT' or next.type == 'TK_BLOCK_COMMENT' or next.type == 'TK_UNKNOWN':
+                if next.type == 'TK_BLOCK_COMMENT':
+                    next.directives = token_values[2]
+
+                comments.append(next)
+                token_values = self.__tokenize_next()
+                next = Token(token_values[1], token_values[0], self.n_newlines, self.whitespace_before_token)
+
+            if len(comments) > 0:
+                next.comments_before = comments
+                comments = []
+
+            if next.type == 'TK_START_BLOCK' or next.type == 'TK_START_EXPR':
+                next.parent = last
+                open_stack.append(open)
+                open = next
+            elif (next.type == 'TK_END_BLOCK' or next.type == 'TK_END_EXPR') and \
+                (not open == None and ( \
+                    (next.text == ']' and open.text == '[') or \
+                    (next.text == ')' and open.text == '(') or \
+                    (next.text == '}' and open.text == '{'))):
+                next.parent = open.parent
+                next.opened = open
+                open = open_stack.pop()
+
+            self.tokens.append(next)
+            last = next
+        return self.tokens
+
+    def get_directives (self, text):
+        if not self.directives_block_pattern.match(text):
+            return None
+
+        directives = {}
+        directive_match = self.directive_pattern.search(text)
+        while directive_match:
+            directives[directive_match.group(1)] = directive_match.group(2)
+            directive_match = self.directive_pattern.search(text, directive_match.end())
+
+        return directives
+
+
+    def __tokenize_next(self):
+
+        whitespace_on_this_line = []
+        self.n_newlines = 0
+        self.whitespace_before_token = ''
+
+        c = self.input.next()
+
+        if c == None:
+            return '', 'TK_EOF'
+
+        if len(self.tokens) > 0:
+            last_token = self.tokens[-1]
+        else:
+            # For the sake of tokenizing we can pretend that there was on open brace to start
+            last_token = Token('TK_START_BLOCK', '{')
+
+        while c in self.whitespace:
+            if self.acorn.newline.match(c):
+                # treat \r\n as one newline
+                if not (c == '\n' and self.input.peek(-2) == '\r'):
+                    self.n_newlines += 1
+                    whitespace_on_this_line = []
+            else:
+                whitespace_on_this_line.append(c)
+
+            c = self.input.next()
+
+            if c == None:
+                return '', 'TK_EOF'
+
+        if len(whitespace_on_this_line) != 0:
+            self.whitespace_before_token = ''.join(whitespace_on_this_line)
+
+        if self.digit.match(c) or (c == '.' and self.input.testChar(self.digit)):
+            allow_decimal = True
+            allow_e = True
+            local_digit = self.digit
+
+            if c == '0' and self.input.testChar(re.compile('[XxOoBb]')):
+                # switch to hex/oct/bin number, no decimal or e, just hex/oct/bin digits
+                allow_decimal = False
+                allow_e = False
+                if self.input.testChar(re.compile('[Bb]')):
+                    local_digit = self.digit_bin
+                elif self.input.testChar(re.compile('[Oo]')):
+                    local_digit = self.digit_oct
+                else:
+                    local_digit = self.digit_hex
+                c += self.input.next()
+            elif c == '.':
+                # Already have a decimal for this literal, don't allow another
+                allow_decimal = False
+            else:
+                # we know this first loop will run.  It keeps the logic simpler.
+                c = ''
+                self.input.back()
+
+            # Add the digits
+            while self.input.testChar(local_digit):
+                c += self.input.next()
+
+                if allow_decimal and self.input.peek() == '.':
+                    c += self.input.next()
+                    allow_decimal = False
+
+                # a = 1.e-7 is valid, so we test for . then e in one loop
+                if allow_e and self.input.testChar(re.compile('[Ee]')):
+                    c += self.input.next()
+
+                    if self.input.testChar(re.compile('[+-]')):
+                        c += self.input.next()
+
+                    allow_e = False
+                    allow_decimal = False
+
+            return c, 'TK_WORD'
+
+        if self.acorn.isIdentifierStart(self.input.peekCharCode(-1)):
+            if self.input.hasNext():
+                while self.acorn.isIdentifierChar(self.input.peekCharCode()):
+                    c += self.input.next()
+                    if not self.input.hasNext():
+                        break
+
+            if not (last_token.type == 'TK_DOT' \
+                        or (last_token.type == 'TK_RESERVED' and last_token.text in ['set', 'get'])) \
+                    and c in self.reserved_words:
+                if c == 'in' or c == 'of': # in and of are operators, need to hack
+                    return c, 'TK_OPERATOR'
+
+                return c, 'TK_RESERVED'
+
+            return c, 'TK_WORD'
+
+        if c in '([':
+            return c, 'TK_START_EXPR'
+
+        if c in ')]':
+            return c, 'TK_END_EXPR'
+
+        if c == '{':
+            return c, 'TK_START_BLOCK'
+
+        if c == '}':
+            return c, 'TK_END_BLOCK'
+
+        if c == ';':
+            return c, 'TK_SEMICOLON'
+
+        if c == '/':
+            comment = ''
+            inline_comment = True
+            if self.input.peek() == '*': # peek /* .. */ comment
+                self.input.next()
+                comment_match = self.input.match(self.block_comment_pattern)
+                comment = '/*' + comment_match.group(0)
+
+                directives = self.get_directives(comment)
+                if directives and directives.get('ignore') == 'start':
+                    comment_match = self.input.match(self.directives_end_ignore_pattern)
+                    comment += comment_match.group(0)
+                comment = re.sub(self.acorn.allLineBreaks, '\n', comment)
+                return comment, 'TK_BLOCK_COMMENT', directives
+
+            if self.input.peek() == '/': # peek // comment
+                self.input.next()
+                comment_match = self.input.match(self.comment_pattern)
+                comment = '//' + comment_match.group(0)
+                return comment, 'TK_COMMENT'
+
+        startXmlRegExp = re.compile('<()([-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*>')
+
+        self.has_char_escapes = False
+
+        if c == '`' or c == "'" or c == '"' or \
+            ( \
+                (c == '/') or \
+                (self.opts.e4x and c == "<" and self.input.test(startXmlRegExp, -1)) \
+            ) and ( \
+                (last_token.type == 'TK_RESERVED' and last_token.text in ['return', 'case', 'throw', 'else', 'do', 'typeof', 'yield']) or \
+                (last_token.type == 'TK_END_EXPR' and last_token.text == ')' and \
+                            last_token.parent and last_token.parent.type == 'TK_RESERVED' and last_token.parent.text in ['if', 'while', 'for']) or \
+                (last_token.type in ['TK_COMMENT', 'TK_START_EXPR', 'TK_START_BLOCK', 'TK_END_BLOCK', 'TK_OPERATOR', \
+                                   'TK_EQUALS', 'TK_EOF', 'TK_SEMICOLON', 'TK_COMMA'])):
+            sep = c
+            esc = False
+            esc1 = 0
+            esc2 = 0
+            resulting_string = c
+            in_char_class = False
+
+            if sep == '/':
+                # handle regexp
+                in_char_class = False
+                while self.input.hasNext() and \
+                        (esc or in_char_class or self.input.peek()!= sep) and \
+                        not self.input.testChar(self.acorn.newline):
+                    resulting_string += self.input.peek()
+                    if not esc:
+                        esc = self.input.peek() == '\\'
+                        if self.input.peek() == '[':
+                            in_char_class = True
+                        elif self.input.peek() == ']':
+                            in_char_class = False
+                    else:
+                        esc = False
+                    self.input.next()
+
+            elif self.opts.e4x and sep == '<':
+                # handle e4x xml literals
+                xmlRegExp = re.compile('[\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*>')
+                self.input.back()
+                xmlStr = ""
+                match = self.input.match(xmlRegExp)
+                if match:
+                    rootTag = match.group(2)
+                    rootTag = re.sub(r'^{\s+', '{', re.sub(r'\s+}$', '}', rootTag))
+                    isCurlyRoot = rootTag.startswith('{')
+                    depth = 0
+                    while (match):
+                        isEndTag = match.group(1)
+                        tagName = match.group(2)
+                        isSingletonTag = (match.groups()[-1] != "") or (match.group(2)[0:8] == "![CDATA[")
+                        if not isSingletonTag and (
+                            tagName == rootTag or (isCurlyRoot and re.sub(r'^{\s+', '{', re.sub(r'\s+}$', '}', tagName)))):
+                            if isEndTag:
+                                depth -= 1
+                            else:
+                                depth += 1
+
+                        xmlStr += match.group(0)
+                        if depth <= 0:
+                            break
+
+                        match = self.input.match(xmlRegExp)
+
+
+                    # if we didn't close correctly, keep unformatted.
+                    if not match:
+                        xmlStr += self.input.match(re.compile('[\s\S]*')).group(0)
+
+                    xmlStr = re.sub(self.acorn.allLineBreaks, '\n', xmlStr)
+                    return xmlStr, 'TK_STRING'
+
+            else:
+
+                # handle string
+                def parse_string(self, resulting_string, delimiter, allow_unescaped_newlines = False, start_sub = None):
+                    esc = False
+                    while self.input.hasNext():
+                        current_char = self.input.peek()
+                        if not (esc or (current_char != delimiter and
+                                (allow_unescaped_newlines or not self.acorn.newline.match(current_char)))):
+                            break
+
+                        # Handle \r\n linebreaks after escapes or in template strings
+                        if (esc or allow_unescaped_newlines) and self.acorn.newline.match(current_char):
+                            if current_char == '\r' and self.input.peek(1) == '\n':
+                                self.input.next()
+                                current_char = self.input.peek()
+
+                            resulting_string += '\n'
+                        else:
+                            resulting_string += current_char
+
+                        if esc:
+                            if current_char == 'x' or current_char == 'u':
+                                self.has_char_escapes = True
+
+                            esc = False
+                        else:
+                            esc = current_char == '\\'
+
+                        self.input.next()
+
+                        if start_sub and resulting_string.endswith(start_sub):
+                            if delimiter == '`':
+                                resulting_string = parse_string(self, resulting_string, '}', allow_unescaped_newlines, '`')
+                            else:
+                                resulting_string = parse_string(self, resulting_string, '`', allow_unescaped_newlines, '${')
+
+                            if self.input.hasNext():
+                                resulting_string += self.input.next()
+
+                    return resulting_string
+
+                if sep == '`':
+                    resulting_string = parse_string(self, resulting_string, '`', True, '${')
+                else:
+                    resulting_string = parse_string(self, resulting_string, sep)
+
+
+            if self.has_char_escapes and self.opts.unescape_strings:
+                resulting_string = self.unescape_string(resulting_string)
+
+            if self.input.peek() == sep:
+                resulting_string += self.input.next()
+
+                if sep == '/':
+                    # regexps may have modifiers /regexp/MOD, so fetch those too
+                    # Only [gim] are valid, but if the user puts in garbage, do what we can to take it.
+                    while self.input.hasNext() and self.acorn.isIdentifierStart(self.input.peekCharCode()):
+                        resulting_string += self.input.next()
+
+            resulting_string = re.sub(self.acorn.allLineBreaks, '\n', resulting_string)
+
+            return resulting_string, 'TK_STRING'
+
+        if c == '#':
+
+            # she-bang
+            if len(self.tokens) == 0 and self.input.peek() == '!':
+                resulting_string = c
+                while self.input.hasNext() and c != '\n':
+                    c = self.input.next()
+                    resulting_string += c
+                return resulting_string.strip() + '\n', 'TK_UNKNOWN'
+
+
+            # Spidermonkey-specific sharp variables for circular references
+            # https://developer.mozilla.org/En/Sharp_variables_in_JavaScript
+            # http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935
+            sharp = '#'
+            if self.input.hasNext() and self.input.testChar(self.digit):
+                while True:
+                    c = self.input.next()
+                    sharp += c
+                    if (not self.input.hasNext()) or c == '#' or c == '=':
+                        break
+            if c == '#':
+                pass
+            elif self.input.peek() == '[' and self.input.peek(1) == ']':
+                sharp += '[]'
+                self.input.next()
+                self.input.next()
+            elif self.input.peek() == '{' and self.input.peek(1) == '}':
+                sharp += '{}'
+                self.input.next()
+                self.input.next()
+            return sharp, 'TK_WORD'
+
+        if c == '<' and self.input.peek() in ['?', '%']:
+            self.input.back()
+            template_match = self.input.match(self.template_pattern)
+            if template_match:
+                c = template_match.group(0)
+                c = re.sub(self.acorn.allLineBreaks, '\n', c)
+                return c, 'TK_STRING'
+
+
+        if c == '<' and self.input.match(re.compile('\!--')):
+            c = '', 'TK_COMMENT'
+
+        if c == '.':
+            if self.input.peek() == '.' and self.input.peek(1) == '.':
+                c += self.input.next() + self.input.next()
+                return c, 'TK_OPERATOR'
+
+            return c, 'TK_DOT'
+
+        if c in self.punct:
+            while self.input.hasNext() and c + self.input.peek() in self.punct:
+                c += self.input.next()
+                if not self.input.hasNext():
+                    break
+
+            if c == ',':
+                return c, 'TK_COMMA'
+            if c == '=':
+                return c, 'TK_EQUALS'
+
+            return c, 'TK_OPERATOR'
+
+        return c, 'TK_UNKNOWN'
+
+    def unescape_string(self, s):
+        # You think that a regex would work for this
+        # return s.replace(/\\x([0-9a-f]{2})/gi, function(match, val) {
+        #         return String.fromCharCode(parseInt(val, 16));
+        #     })
+        # However, dealing with '\xff', '\\xff', '\\\xff' makes this more fun.
+        out = self.acorn.six.u('')
+        escaped = 0
+
+        input_scan = InputScanner(s)
+        matched = None
+
+        while input_scan.hasNext():
+            # Keep any whitespace, non-slash characters
+            # also keep slash pairs.
+            matched = input_scan.match(re.compile(r'([\s]|[^\\]|\\\\)+'))
+
+            if matched:
+                out += matched.group(0)
+
+            if input_scan.peek() != '\\':
+                continue
+
+            input_scan.next()
+            if input_scan.peek() == 'x':
+                matched = input_scan.match(re.compile('x([0-9A-Fa-f]{2})'))
+            elif input_scan.peek() == 'u':
+                matched = input_scan.match(re.compile('u([0-9A-Fa-f]{4})'));
+            else:
+                out += '\\'
+                if input_scan.hasNext():
+                    out += input_scan.next()
+                continue
+
+            # If there's some error decoding, return the original string
+            if not matched:
+                return s
+
+            escaped = int(matched.group(1), 16)
+
+            if escaped > 0x7e and escaped <= 0xff and matched.group(0).startswith('x'):
+                # we bail out on \x7f..\xff,
+                # leaving whole string escaped,
+                # as it's probably completely binary
+                return s
+            elif escaped >= 0x00 and escaped < 0x20:
+                # leave 0x00...0x1f escaped
+                out += '\\' + matched.group(0)
+                continue
+            elif escaped == 0x22 or escaped == 0x27 or escaped == 0x5c:
+                # single-quote, apostrophe, backslash - escape these
+                out += ('\\' + chr(escaped))
+            else:
+                out += self.acorn.six.unichr(escaped)
+
+        return out
diff --git a/tools/build.sh b/tools/build.sh
index 523eabe5a..b691daf66 100755
--- a/tools/build.sh
+++ b/tools/build.sh
@@ -45,8 +45,26 @@ build_js()
     echo Building javascript...
     npm install || exit 1
     generate_tests
+
+    # generate lib files
+    ./node_modules/.bin/webpack
+
+    # Wrap webkit output into an non-breaking form.
+    # In an upcoming verion these will be replaced with standard webpack umd
+    cat ./tools/template/beautify.begin.js > ./js/lib/beautify.js
+    cat ./dist/legacy_beautify_js.js >> ./js/lib/beautify.js
+    cat ./tools/template/beautify.end.js >> ./js/lib/beautify.js
+
+    cat ./tools/template/beautify-css.begin.js > ./js/lib/beautify-css.js
+    cat ./dist/legacy_beautify_css.js >> ./js/lib/beautify-css.js
+    cat ./tools/template/beautify-css.end.js >> ./js/lib/beautify-css.js
+
+    cat ./tools/template/beautify-html.begin.js > ./js/lib/beautify-html.js
+    cat ./dist/legacy_beautify_html.js >> ./js/lib/beautify-html.js
+    cat ./tools/template/beautify-html.end.js >> ./js/lib/beautify-html.js
+
     # jshint
-    $PROJECT_DIR/node_modules/.bin/jshint 'js' 'test' || exit 1
+    $PROJECT_DIR/node_modules/.bin/jshint 'js/src' 'test' || exit 1
 
     # beautify test and data
     $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/test/amd-beautify-tests.js || exit 1
@@ -65,9 +83,12 @@ build_js()
     $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/lib/unpackers/myobfuscate_unpacker.js || exit 1
     $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/lib/unpackers/p_a_c_k_e_r_unpacker.js  || exit 1
     $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/lib/unpackers/urlencode_unpacker.js || exit 1
-    $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/lib/beautify-css.js || exit 1
-    $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/lib/beautify-html.js || exit 1
-    $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/lib/beautify.js || exit 1
+    $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/src/css/index.js || exit 1
+    $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/src/html/index.js || exit 1
+    $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/src/javascript/index.js || exit 1
+    $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/src/javascript/beautifier.js || exit 1
+    $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/src/javascript/tokenizer.js || exit 1
+
     $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/lib/cli.js || exit 1
     $PROJECT_DIR/js/bin/js-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r $PROJECT_DIR/js/index.js || exit 1
 
@@ -76,7 +97,7 @@ build_js()
     # $PROJECT_DIR/js/bin/html-beautify.js --config $PROJECT_DIR/jsbeautifyrc -r index.html
 
     # jshint again to make sure things haven't changed
-    $PROJECT_DIR/node_modules/.bin/jshint 'js' 'test' || exit 1
+    $PROJECT_DIR/node_modules/.bin/jshint 'js/src' 'test' || exit 1
 }
 
 generate_tests()
diff --git a/tools/generate-changelog.sh b/tools/generate-changelog.sh
index bf1ca190a..7fe7661ed 100755
--- a/tools/generate-changelog.sh
+++ b/tools/generate-changelog.sh
@@ -15,6 +15,16 @@ main()
       exit 1
     fi
 
+    jq --version || {
+      echo "Required tool 'jq' missing.  Failed."
+      exit 1
+    }
+
+    gsort --version || {
+      echo "Required tool 'gsort' missing.  Failed."
+      exit 1
+    }
+
     IFS=$'\n'
     echo "# Changelog" > CHANGELOG.md
 
diff --git a/tools/template/beautify-css.begin.js b/tools/template/beautify-css.begin.js
new file mode 100644
index 000000000..610547d8d
--- /dev/null
+++ b/tools/template/beautify-css.begin.js
@@ -0,0 +1,66 @@
+/*jshint curly:false, eqeqeq:true, laxbreak:true, noempty:false */
+/* AUTO-GENERATED. DO NOT MODIFY. */
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+
+
+ CSS Beautifier
+---------------
+
+    Written by Harutyun Amirjanyan, (amirjanyan@gmail.com)
+
+    Based on code initially developed by: Einar Lielmanis, 
+        http://jsbeautifier.org/
+
+    Usage:
+        css_beautify(source_text);
+        css_beautify(source_text, options);
+
+    The options are (default in brackets):
+        indent_size (4)                         — indentation size,
+        indent_char (space)                     — character to indent with,
+        selector_separator_newline (true)       - separate selectors with newline or
+                                                  not (e.g. "a,\nbr" or "a, br")
+        end_with_newline (false)                - end with a newline
+        newline_between_rules (true)            - add a new line after every css rule
+        space_around_selector_separator (false) - ensure space around selector separators:
+                                                  '>', '+', '~' (e.g. "a>b" -> "a > b")
+    e.g
+
+    css_beautify(css_source_text, {
+      'indent_size': 1,
+      'indent_char': '\t',
+      'selector_separator': ' ',
+      'end_with_newline': false,
+      'newline_between_rules': true,
+      'space_around_selector_separator': true
+    });
+*/
+
+// http://www.w3.org/TR/CSS21/syndata.html#tokenization
+// http://www.w3.org/TR/css3-syntax/
+
+(function() {
diff --git a/tools/template/beautify-css.end.js b/tools/template/beautify-css.end.js
new file mode 100644
index 000000000..47a6bced6
--- /dev/null
+++ b/tools/template/beautify-css.end.js
@@ -0,0 +1,23 @@
+
+var css_beautify = legacy_beautify_css;
+/* Footer */
+if (typeof define === "function" && define.amd) {
+    // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
+    define([], function() {
+        return {
+            css_beautify: css_beautify
+        };
+    });
+} else if (typeof exports !== "undefined") {
+    // Add support for CommonJS. Just put this file somewhere on your require.paths
+    // and you will be able to `var html_beautify = require("beautify").html_beautify`.
+    exports.css_beautify = css_beautify;
+} else if (typeof window !== "undefined") {
+    // If we're running a web page and don't have either of the above, add our one global
+    window.css_beautify = css_beautify;
+} else if (typeof global !== "undefined") {
+    // If we don't even have window, try global.
+    global.css_beautify = css_beautify;
+}
+
+}());
diff --git a/tools/template/beautify-html.begin.js b/tools/template/beautify-html.begin.js
new file mode 100644
index 000000000..f98bce191
--- /dev/null
+++ b/tools/template/beautify-html.begin.js
@@ -0,0 +1,75 @@
+/*jshint curly:false, eqeqeq:true, laxbreak:true, noempty:false */
+/* AUTO-GENERATED. DO NOT MODIFY. */
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+
+
+ Style HTML
+---------------
+
+  Written by Nochum Sossonko, (nsossonko@hotmail.com)
+
+  Based on code initially developed by: Einar Lielmanis, 
+    http://jsbeautifier.org/
+
+  Usage:
+    style_html(html_source);
+
+    style_html(html_source, options);
+
+  The options are:
+    indent_inner_html (default false)  — indent  and  sections,
+    indent_size (default 4)          — indentation size,
+    indent_char (default space)      — character to indent with,
+    wrap_line_length (default 250)            -  maximum amount of characters per line (0 = disable)
+    brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "none"
+            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.
+    unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted
+    content_unformatted (defaults to pre tag) - list of tags, that its content shouldn't be reformatted
+    indent_scripts (default normal)  - "keep"|"separate"|"normal"
+    preserve_newlines (default true) - whether existing line breaks before elements should be preserved
+                                        Only works before elements, not inside tags or for text.
+    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk
+    indent_handlebars (default false) - format and indent {{#foo}} and {{/foo}}
+    end_with_newline (false)          - end with a newline
+    extra_liners (default [head,body,/html]) -List of tags that should have an extra newline before them.
+
+    e.g.
+
+    style_html(html_source, {
+      'indent_inner_html': false,
+      'indent_size': 2,
+      'indent_char': ' ',
+      'wrap_line_length': 78,
+      'brace_style': 'expand',
+      'preserve_newlines': true,
+      'max_preserve_newlines': 5,
+      'indent_handlebars': false,
+      'extra_liners': ['/html']
+    });
+*/
+
+(function() {
diff --git a/tools/template/beautify-html.end.js b/tools/template/beautify-html.end.js
new file mode 100644
index 000000000..d460fad31
--- /dev/null
+++ b/tools/template/beautify-html.end.js
@@ -0,0 +1,37 @@
+
+var style_html = legacy_beautify_html;
+/* Footer */
+if (typeof define === "function" && define.amd) {
+    // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
+    define(["require", "./beautify", "./beautify-css"], function(requireamd) {
+        var js_beautify = requireamd("./beautify");
+        var css_beautify = requireamd("./beautify-css");
+
+        return {
+            html_beautify: function(html_source, options) {
+                return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
+            }
+        };
+    });
+} else if (typeof exports !== "undefined") {
+    // Add support for CommonJS. Just put this file somewhere on your require.paths
+    // and you will be able to `var html_beautify = require("beautify").html_beautify`.
+    var js_beautify = require('./beautify.js');
+    var css_beautify = require('./beautify-css.js');
+
+    exports.html_beautify = function(html_source, options) {
+        return style_html(html_source, options, js_beautify.js_beautify, css_beautify.css_beautify);
+    };
+} else if (typeof window !== "undefined") {
+    // If we're running a web page and don't have either of the above, add our one global
+    window.html_beautify = function(html_source, options) {
+        return style_html(html_source, options, window.js_beautify, window.css_beautify);
+    };
+} else if (typeof global !== "undefined") {
+    // If we don't even have window, try global.
+    global.html_beautify = function(html_source, options) {
+        return style_html(html_source, options, global.js_beautify, global.css_beautify);
+    };
+}
+
+}());
diff --git a/tools/template/beautify.begin.js b/tools/template/beautify.begin.js
new file mode 100644
index 000000000..51fdc3524
--- /dev/null
+++ b/tools/template/beautify.begin.js
@@ -0,0 +1,109 @@
+/*jshint curly:false, eqeqeq:true, laxbreak:true, noempty:false */
+/* AUTO-GENERATED. DO NOT MODIFY. */
+/* see js/src/javascript/index.js */
+/*
+
+  The MIT License (MIT)
+
+  Copyright (c) 2007-2017 Einar Lielmanis, Liam Newman, and contributors.
+
+  Permission is hereby granted, free of charge, to any person
+  obtaining a copy of this software and associated documentation files
+  (the "Software"), to deal in the Software without restriction,
+  including without limitation the rights to use, copy, modify, merge,
+  publish, distribute, sublicense, and/or sell copies of the Software,
+  and to permit persons to whom the Software is furnished to do so,
+  subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+  SOFTWARE.
+
+ JS Beautifier
+---------------
+
+
+  Written by Einar Lielmanis, 
+      http://jsbeautifier.org/
+
+  Originally converted to javascript by Vital, 
+  "End braces on own line" added by Chris J. Shull, 
+  Parsing improvements for brace-less statements by Liam Newman 
+
+
+  Usage:
+    js_beautify(js_source_text);
+    js_beautify(js_source_text, options);
+
+  The options are:
+    indent_size (default 4)          - indentation size,
+    indent_char (default space)      - character to indent with,
+    preserve_newlines (default true) - whether existing line breaks should be preserved,
+    max_preserve_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk,
+
+    jslint_happy (default false) - if true, then jslint-stricter mode is enforced.
+
+            jslint_happy        !jslint_happy
+            ---------------------------------
+            function ()         function()
+
+            switch () {         switch() {
+            case 1:               case 1:
+              break;                break;
+            }                   }
+
+    space_after_anon_function (default false) - should the space before an anonymous function's parens be added, "function()" vs "function ()",
+          NOTE: This option is overriden by jslint_happy (i.e. if jslint_happy is true, space_after_anon_function is true by design)
+
+    brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | "none" | any of the former + ",preserve-inline"
+            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.
+            preserve-inline will try to preserve inline blocks of curly braces
+
+    space_before_conditional (default true) - should the space before conditional statement be added, "if(true)" vs "if (true)",
+
+    unescape_strings (default false) - should printable characters in strings encoded in \xNN notation be unescaped, "example" vs "\x65\x78\x61\x6d\x70\x6c\x65"
+
+    wrap_line_length (default unlimited) - lines should wrap at next opportunity after this number of characters.
+          NOTE: This is not a hard limit. Lines will continue until a point where a newline would
+                be preserved if it were present.
+
+    end_with_newline (default false)  - end output with a newline
+
+
+    e.g
+
+    js_beautify(js_source_text, {
+      'indent_size': 1,
+      'indent_char': '\t'
+    });
+
+*/
+
+// Object.values polyfill found here:
+// http://tokenposts.blogspot.com.au/2012/04/javascript-objectkeys-browser.html
+// This is required for early versions of IE.
+if (!Object.values) {
+    Object.values = function(o) {
+        if (o !== Object(o)) {
+            throw new TypeError('Object.values called on a non-object');
+        }
+        var k = [],
+            p;
+        for (p in o) {
+            if (Object.prototype.hasOwnProperty.call(o, p)) {
+                k.push(o[p]);
+            }
+        }
+        return k;
+    };
+}
+
+(function() {
diff --git a/tools/template/beautify.end.js b/tools/template/beautify.end.js
new file mode 100644
index 000000000..c10c2325e
--- /dev/null
+++ b/tools/template/beautify.end.js
@@ -0,0 +1,21 @@
+
+var js_beautify = legacy_beautify_js;
+/* Footer */
+if (typeof define === "function" && define.amd) {
+    // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
+    define([], function() {
+        return { js_beautify: js_beautify };
+    });
+} else if (typeof exports !== "undefined") {
+    // Add support for CommonJS. Just put this file somewhere on your require.paths
+    // and you will be able to `var js_beautify = require("beautify").js_beautify`.
+    exports.js_beautify = js_beautify;
+} else if (typeof window !== "undefined") {
+    // If we're running a web page and don't have either of the above, add our one global
+    window.js_beautify = js_beautify;
+} else if (typeof global !== "undefined") {
+    // If we don't even have window, try global.
+    global.js_beautify = js_beautify;
+}
+
+}());
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 000000000..1399ddcff
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,52 @@
+var path = require('path');
+
+var legacy_js = {
+  entry: './js/src/javascript/index.js',
+  resolve: {
+    modules: [ path.resolve(__dirname, "js/src") ]
+  },
+  output: {
+    library: 'legacy_beautify_js',
+    filename: 'legacy_beautify_js.js',
+    path: path.resolve(__dirname, 'dist')
+  }
+
+};
+
+var legacy_css = {
+  entry: './js/src/css/index.js',
+  resolve: {
+    modules: [ path.resolve(__dirname, "js/src") ]
+  },
+  output: {
+    library: 'legacy_beautify_css',
+    filename: 'legacy_beautify_css.js',
+    path: path.resolve(__dirname, 'dist')
+  }
+};
+
+var legacy_html = {
+  entry: './js/src/html/index.js',
+  resolve: {
+    modules: [ path.resolve(__dirname, "js/src") ]
+  },
+  output: {
+    library: 'legacy_beautify_html',
+    filename: 'legacy_beautify_html.js',
+    path: path.resolve(__dirname, 'dist')
+  }
+};
+
+
+var dist_full = {
+  entry: './js/src/index.js',
+  output: {
+    library: 'beautifier',
+    libraryTarget: 'umd',
+    umdNamedDefine: true,
+    filename: 'beautifier.js',
+    path: path.resolve(__dirname, 'dist')
+  }
+};
+
+module.exports = [dist_full, legacy_js, legacy_css, legacy_html];