Skip to content

Instantly share code, notes, and snippets.

@mqu
Created February 1, 2013 20:26
Show Gist options
  • Save mqu/4693894 to your computer and use it in GitHub Desktop.
Save mqu/4693894 to your computer and use it in GitHub Desktop.
a very simple javascript beautifier writen in php5. Usage : javascript-beautifier some-script.js > beautifull-script.js keywords : javascript, beautify, pretty, php, shell, command-line.
#!/usr/bin/php -q
<?php
/**
* JSBeautifier class
*
* @author Einar Lielmanis
* @author Kaspars Foigts
* @author Igal Alkon
*
* @package JSBeautifier
*
* Naming conventions kept over from python code.
*
* Originally written by Einar Lielmanis et al.,
* Conversion to PHP by Kaspars Foigts, laacz@laacz.lv,
* MIT licence, enjoy.
*
* Performance is dreadful. If you want to improve stuff, do so.
*
* source : https://github.com/einars/js-beautify/tree/master/php
*
* UPDATE (19/06/2012):
* js_beautify() was REMOVED from the global namespace
* it can be found commented below current comment block
* class now don't set any global defines and don't read from golobal constant list
* all constants are now inside JSBeautifier class.
* this change was done to make class to fit modern framework structure better
* by removing any global dependencies, it also was converted to modern PHP OOP style
* and now has only one public interface:
* > beautify($string, BeautifierOptions $options = null)
*
* Just include this into your PHP script, and use it away. Example:
*
* <?php
* require('path/to/jsbeautifier.php');
*
* // Init class
* $jsb = new JSBeautifier();
*
* // Beautify string
* $result = $jsb->beautify('your javascript string');
*
* // Beautify file
* $result = $jsb->beautify(file_get_contents('some_file.js'));
*
* // You may specify some options:
* $opts = new BeautifierOptions();
* $opts->indent_size = 2;
* $result = $jsb->beautify('some javascript', $opts);
*/
/*
function js_beautify($string, $options = null) {
$beautifier = new JSBeautifier();
return $beautifier->beautify($string, $options);
}
*/
class JSBeautifier
{
const TK_EOF = 0;
const TK_START_EXPR = 1;
const TK_END_EXPR = 2;
const TK_START_BLOCK = 3;
const TK_END_BLOCK = 4;
const TK_WORD = 5;
const TK_SEMICOLON = 6;
const TK_STRING = 7;
const TK_EQUALS = 8;
const TK_OPERATOR = 9;
const TK_BLOCK_COMMENT = 10;
const TK_INLINE_COMMENT = 11;
const TK_COMMENT = 12;
const TK_UNKNOWN = 13;
// Brace Styles
const BS_EXPAND = 'expand';
const BS_COLLAPSE = 'collapse';
const BS_END_EXPAND = 'end-expand';
/**
* @var BeautifierOptions
*/
private $options;
/**
* @var BeautifierFlags
*/
private $flags;
/**
* @var array
*/
private $flag_store;
/**
* @var bool
*/
private $wanted_newline;
/**
* @var bool
*/
private $just_added_newline;
/**
* @var bool
*/
private $do_block_just_closed;
/**
* @var string
*/
private $indent_string;
/**
* @var string
*/
private $preindent_string;
/**
* last TK_WORD seen
* @var string
*/
private $last_word;
/**
* last token type
* @var int
*/
private $last_type;
/**
* last token text
* @var string
*/
private $last_text;
/**
* pre-last token text
* @var string
*/
private $last_last_text;
/**
* @var mixed
*/
private $input;
/**
* formatted javascript gets built here
* @var array
*/
private $output;
/**
* @var array
*/
private $whitespace;
/**
* @var string
*/
private $wordchar;
/**
* @var string
*/
private $digits;
/**
* @var string
*/
private $punct;
/**
* @var array
*/
private $line_starters;
/**
* @var int
*/
private $parser_pos;
private $n_newlines;
/**
* @param BeautifierOptions|null $options
*/
public function __construct(BeautifierOptions $options = null)
{
$this->options = $options ?: new BeautifierOptions();
$this->blank_state();
}
/**
* Reset all properties
*/
private function blank_state()
{
$this->flags = new BeautifierFlags('BLOCK');
$this->flag_store = array();
$this->wanted_newline = false;
$this->just_added_newline = false;
$this->do_block_just_closed = false;
$this->indent_string = $this->options->indent_with_tabs
? "\t" : str_repeat($this->options->indent_char, $this->options->indent_size);
$this->preindent_string = '';
$this->last_word = '';
$this->last_type = static::TK_START_EXPR;
$this->last_text = '';
$this->last_last_text = '';
$this->input = null;
$this->output = array();
$this->whitespace = array("\n", "\r", "\t", " ");
$this->wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$';
$this->digits = '0123456789';
$this->punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::';
$this->punct .= ' <?= <? ?> <%= <% %>';
$this->punct = explode(' ', $this->punct);
$this->line_starters = explode(',', 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function');
$this->set_mode('BLOCK');
$this->parser_pos = 0;
}
private function set_mode($mode)
{
$prev = new BeautifierFlags('BLOCK');
if (isset($this->flags))
{
$this->flag_store[] = $this->flags;
$prev = $this->flags;
}
$this->flags = new BeautifierFlags($mode);
if (isset($this->flag_store) && (count($this->flag_store) == 1))
{
$this->flags->indentation_level = 0;
}
else
{
$this->flags->indentation_level = $prev->indentation_level;
if ($prev->var_line && $prev->var_line_reindented)
{
$this->flags->indentation_level++;
}
}
$this->flags->previous_mode = $prev->mode;
}
/**
* Use this to beautify JavaScript
*
* @param $string
* @param null|BeautifierOptions $options
* @return string
* @throws \Exception
*/
public function beautify($string, BeautifierOptions $options = null)
{
if ($options)
{
$this->options = $options;
}
if ( ! in_array($this->options->brace_style, array(static::BS_COLLAPSE, static::BS_EXPAND, static::BS_END_EXPAND)))
{
throw new \Exception(sprintf('opts.brace_style must be %s, %s or %s.',
static::BS_COLLAPSE, static::BS_EXPAND, static::BS_END_EXPAND));
}
$this->blank_state();
while ($string && strpos(" \t", $string[0]) !== false)
{
$this->preindent_string .= $string[0];
$string = substr($string, 1);
}
// @todo: Implement unpackers
// self.input = self.unpack(s, opts.eval_code)
$this->input = $string;
$this->parser_pos = 0;
$handlers = array(
static::TK_START_EXPR => 'handle_start_expr',
static::TK_END_EXPR => 'handle_end_expr',
static::TK_START_BLOCK => 'handle_start_block',
static::TK_END_BLOCK => 'handle_end_block',
static::TK_WORD => 'handle_word',
static::TK_SEMICOLON => 'handle_semicolon',
static::TK_STRING => 'handle_string',
static::TK_EQUALS => 'handle_equals',
static::TK_OPERATOR => 'handle_operator',
static::TK_BLOCK_COMMENT => 'handle_block_comment',
static::TK_INLINE_COMMENT => 'handle_inline_comment',
static::TK_COMMENT => 'handle_comment',
static::TK_UNKNOWN => 'handle_unknown',
);
while (true)
{
list($token_text, $token_type) = $this->get_next_token();
if ($token_type === static::TK_EOF) break;
//echo $token_type_str . "\t" . $token_text . "\n";
$this->$handlers[$token_type]($token_text);
$this->last_last_text = $this->last_text;
$this->last_type = $token_type;
$this->last_text = $token_text;
}
$sweet_code = $this->preindent_string . preg_replace("/[\n ]+$/", '', join('', $this->output));
return $sweet_code;
}
private function handle_equals($token_text)
{
if ($this->flags->var_line)
{
// just got an '=' in a var-line, different line breaking rules will apply
$this->flags->var_line_tainted = true;
}
$this->append(' ');
$this->append($token_text);
$this->append(' ');
}
private function handle_semicolon($token_text)
{
$this->append($token_text);
$this->flags->var_line = false;
$this->flags->var_line_reindented = false;
if ($this->flags->mode == 'OBJECT')
{
# OBJECT mode is weird and doesn't get reset too well.
$this->flags->mode = 'BLOCK';
}
}
private function trim_output($eat_newlines = false)
{
while (count($this->output) && ($this->output[count($this->output)-1] == ' ' ||
$this->output[count($this->output)-1] == $this->indent_string ||
$this->output[count($this->output)-1] == $this->preindent_string ||
($eat_newlines && (strpos("\n\r", $this->output[count($this->output)-1]) !== false))))
{
array_pop($this->output);
}
}
private function get_next_token()
{
$this->n_newlines = 0;
if ($this->parser_pos >= strlen($this->input)) { return array('', static::TK_EOF); }
$this->wanted_newline = false;
$c = $this->input[$this->parser_pos];
$this->parser_pos++;
$keep_whitespace = $this->options->keep_array_indentation && $this->is_array($this->flags->mode);
if ($keep_whitespace)
{
/*
* slight mess to allow nice preservation of array indentation and reindent that correctly
* first time when we get to the arrays:
* var a = [
* ....'something'
* we make note of whitespace_count = 4 into flags.indentation_baseline
* so we know that 4 whitespaces in original source match indent_level of reindented source
*
* and afterwards, when we get to
* 'something,
* .......'something else'
* we know that this should be indented to indent_level + (7 - indentation_baseline) spaces
*/
$whitespace_count = 0;
while (in_array($c, $this->whitespace) !== false)
{
switch ($c)
{
case "\n":
$this->trim_output();
$this->output[] = "\n";
$this->just_added_newline = true;
$whitespace_count = 0;
break;
case "\t":
$whitespace_count += 4;
break;
case "\r":
break;
default:
$whitespace_count++;
break;
}
if ($this->parser_pos >= strlen($this->input))
{
return array('', static::TK_EOF);
}
$c = $this->input[$this->parser_pos];
$this->parser_pos++;
}
if ($this->flags->indentation_baseline == -1)
{
$this->flags->indentation_baseline = $whitespace_count;
}
if ($this->just_added_newline)
{
for ($i = 0; $i <= $this->flags->indentation_level; $i++)
{
$this->output[] = $this->indent_string;
}
if ($this->flags->indentation_baseline != -1)
{
for ($i = 0; $i < $whitespace_count - $this->flags->indentation_baseline; $i++)
{
$this->output[] = ' ';
}
}
}
}
else
{
/*
* not keep_whitespace
*/
while (in_array($c, $this->whitespace))
{
if ($c == "\n")
{
if ($this->options->max_preserve_newlines == 0 || ($this->options->max_preserve_newlines > $this->n_newlines))
{
$this->n_newlines++;
}
}
if ($this->parser_pos >= strlen($this->input))
{
return array('', static::TK_EOF);
}
$c = $this->input[$this->parser_pos];
$this->parser_pos++;
}
if ($this->options->preserve_newlines && $this->n_newlines > 1)
{
for ($i = 0; $i < $this->n_newlines; $i++)
{
$this->append_newline($i == 0);
$this->just_added_newline = true;
}
}
$this->wanted_newline = $this->n_newlines > 0;
}
if (strpos($this->wordchar, $c) !== false)
{
if ($this->parser_pos < strlen($this->input))
{
while (strpos($this->wordchar, $this->input[$this->parser_pos]) !== false)
{
$c .= $this->input[$this->parser_pos];
$this->parser_pos++;
if ($this->parser_pos == strlen($this->input))
{
break;
}
}
}
// small and surprisingly unugly hack for 1E-10 representation
if (($this->parser_pos != strlen($this->input)) &&
(strpos('+-', $this->input[$this->parser_pos]) !== false) && preg_match('/^[0-9]+[Ee]$/', $c))
{
$sign = $this->input[$this->parser_pos];
$this->parser_pos++;
$t = $this->get_next_token();
$c .= $sign . $t[0];
return array($c, static::TK_WORD);
}
// in is an operator, need to hack
if ($c == 'in')
{
return array($c, static::TK_OPERATOR);
}
if ($this->wanted_newline && ($this->last_type != static::TK_OPERATOR) &&
($this->last_type != static::TK_EQUALS) && ! $this->flags->if_line &&
($this->options->preserve_newlines || ($this->last_text != 'var')))
{
$this->append_newline();
}
return array($c, static::TK_WORD);
}
if (strpos('([', $c) !== false) return array($c, static::TK_START_EXPR);
if (strpos(')]', $c) !== false) return array($c, static::TK_END_EXPR);
if ($c == '{') return array($c, static::TK_START_BLOCK);
if ($c == '}') return array($c, static::TK_END_BLOCK);
if ($c == ';') return array($c, static::TK_SEMICOLON);
if ($c == '/')
{
$comment = '';
$comment_mode = static::TK_INLINE_COMMENT;
// peek /* .. */ comment
if ($this->input[$this->parser_pos] == '*')
{
$this->parser_pos++;
if ($this->parser_pos < strlen($this->input))
{
while ( ! (($this->input[$this->parser_pos] == '*') &&
($this->parser_pos + 1 < strlen($this->input)) &&
($this->input[$this->parser_pos + 1] == '/')) &&
($this->parser_pos < strlen($this->input)))
{
$c = $this->input[$this->parser_pos];
$comment .= $c;
if (strpos("\r\n", $c) !== false)
{
$comment_mode = static::TK_BLOCK_COMMENT;
}
$this->parser_pos ++;
if ($this->parser_pos >= strlen($this->input))
{
break;
}
}
}
$this->parser_pos += 2;
return array('/*' . $comment . '*/', $comment_mode);
}
// peek // comment
if ($this->input[$this->parser_pos] == '/')
{
$comment = $c;
while (strpos("\r\n", $this->input[$this->parser_pos]) === false)
{
$comment .= $this->input[$this->parser_pos];
$this->parser_pos++;
if ($this->parser_pos >= strlen($this->input))
{
break;
}
}
$this->parser_pos ++;
if ($this->wanted_newline)
{
$this->append_newline();
}
return array($comment, static::TK_COMMENT);
}
}
if (($c == "'") || ($c == '"') ||
($c == '/' && (($this->last_type == static::TK_WORD && in_array($this->last_text, array('return', 'do'))) ||
(in_array($this->last_type, array(
static::TK_COMMENT, static::TK_START_EXPR, static::TK_START_BLOCK, static::TK_END_BLOCK,
static::TK_OPERATOR, static::TK_EQUALS, static::TK_EOF, static::TK_SEMICOLON))))))
{
$sep = $c;
$esc = false;
$esc1 = 0;
$esc2 = 0;
$resulting_string = $c;
if ($this->parser_pos < strlen($this->input))
{
if ($sep == '/')
{
// handle regexp
$in_char_class = false;
while ($esc || $in_char_class || $this->input[$this->parser_pos] != $sep)
{
$resulting_string .= $this->input[$this->parser_pos];
if ( ! $esc)
{
$esc = $this->input[$this->parser_pos] == '\\';
if ($this->input[$this->parser_pos] == '[')
{
$in_char_class = true;
}
elseif($this->input[$this->parser_pos] == ']')
{
$in_char_class = false;
}
}
else
{
$esc = false;
}
$this->parser_pos++;
if ($this->parser_pos >= strlen($this->input))
{
// incomplete regex when end-of-file reached
// bail out with what has received so far
return array($resulting_string, static::TK_STRING);
}
}
}
else
{
// handle string
while ($esc || $this->input[$this->parser_pos] != $sep) {
$resulting_string .= $this->input[$this->parser_pos];
if ($esc1 && $esc1 >= $esc2) {
$esc1 = hexdec(substr($resulting_string, -$esc2));
if ($esc1 && $esc1 >= 0x20 && $esc1 <= 0x7e) {
$esc1 = chr($esc1);
$resulting_string = substr($resulting_string, 0, -2 - $esc2) . ((($esc1 === $sep) || ($esc1 === '\\')) ? '\\' : '') . $esc1;
}
$esc1 = 0;
}
if ($esc1) {
$esc1++;
} else if (!$esc) {
$esc = $this->input[$this->parser_pos] == '\\';
} else {
$esc = false;
if ($this->options->unescape_strings) {
if ($this->input[$this->parser_pos] === 'x') {
$esc1++;
$esc2 = 2;
} else if ($this->input[$this->parser_pos] === 'u') {
$esc1++;
$esc2 = 4;
}
}
}
$this->parser_pos++;
if ($this->parser_pos >= strlen($this->input)) {
# incomplete string when end-of-file reached
# bail out with what has received so far
return array($resulting_string, static::TK_STRING);
}
}
}
}
$this->parser_pos++;
$resulting_string .= $sep;
if ($sep == '/')
{
// regexps may have modifiers /regexp/MOD, so fetch those too
while ($this->parser_pos < strlen($this->input) && strpos($this->wordchar, $this->input[$this->parser_pos]) !== false)
{
$resulting_string .= $this->input[$this->parser_pos];
$this->parser_pos++;
}
}
return array($resulting_string, static::TK_STRING);
}
if ($c == '#')
{
// she-bang
if (count($this->output) == 0 && strlen($this->input) > 1 && $this->input[$this->parser_pos] == '!')
{
$resulting_string = $c;
while ($this->parser_pos < strlen($this->input) && $c != "\n")
{
$c = $this->input[$this->parser_pos];
$resulting_string .= $c;
$this->parser_pos++;
}
$this->output[] = trim($resulting_string) . "\n";
$this->append_newline();
return $this->get_next_token();
}
// 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 ($this->parser_pos < strlen($this->input) && strpos($this->digits, $this->input[$this->parser_pos]) !== false)
{
while (true)
{
$c = $this->input[$this->parser_pos];
$sharp .= $c;
$this->parser_pos++;
if ($this->parser_pos >= strlen($this->input) || $c == '#' || $c == '=')
{
break;
}
}
}
// if ($c == '#' || $this->parser_pos >= strlen($this->input)) // @todo: Is this needed ?
if ($c == '#' || $this->parser_pos >= strlen($this->input))
{
// @todo: what is this? remove.
;
}
elseif ($this->input[$this->parser_pos] == '[' && $this->input[$this->parser_pos+1] == ']')
{
$sharp .= '[]';
$this->parser_pos += 2;
}
elseif ($this->input[$this->parser_pos] == '{' && $this->input[$this->parser_pos+1] == '}')
{
$sharp .= '{}';
$this->parser_pos += 2;
}
return array($sharp, static::TK_WORD);
}
if ($c == '<' && substr($this->input, $this->parser_pos - 1, 4) == '<!--')
{
$this->parser_pos += 3;
$this->flags->in_html_comment = true;
return array('<!--', static::TK_COMMENT);
}
if ($c == '-' && $this->flags->in_html_comment && substr($this->input, $this->parser_pos - 1, 3) == '-->')
{
$this->flags->in_html_comment = false;
$this->parser_pos += 2;
if ($this->wanted_newline)
{
$this->append_newline();
}
return array('-->', static::TK_COMMENT);
}
if (in_array($c, $this->punct))
{
while ($this->parser_pos < strlen($this->input) && in_array($c . $this->input[$this->parser_pos], $this->punct))
{
$c .= $this->input[$this->parser_pos];
$this->parser_pos++;
if ($this->parser_pos >= strlen($this->input))
{
break;
}
}
return ($c == '=') ? array($c, static::TK_EQUALS) : array($c, static::TK_OPERATOR);
}
return array($c, static::TK_UNKNOWN);
}
private function handle_start_expr($token_text)
{
if ($token_text == '[')
{
if ($this->last_type == static::TK_WORD || $this->last_text == ')')
{
if (in_array($this->last_text, $this->line_starters))
{
$this->append(' ');
}
$this->set_mode('(EXPRESSION)');
$this->append($token_text);
return;
}
if (in_array($this->flags->mode, array('[EXPRESSION]', '[INDENTED-EXPRESSION]')))
{
if ($this->last_last_text == ']' && $this->last_text == ',')
{
// ], [ goers to a new line
if ($this->flags->mode == '[EXPRESSION]')
{
$this->flags->mode = '[INDENTED-EXPRESSION]';
if ( ! $this->options->keep_array_indentation)
{
$this->indent();
}
}
$this->set_mode('[EXPRESSION]');
if ( ! $this->options->keep_array_indentation)
{
$this->append_newline();
}
}
elseif ($this->last_text == '[')
{
if ($this->flags->mode == '[EXPRESSION]')
{
$this->flags->mode = '[INDENTED-EXPRESSION]';
if ( ! $this->options->keep_array_indentation)
{
$this->indent();
}
}
$this->set_mode('[EXPRESSION]');
if ( ! $this->options->keep_array_indentation)
{
$this->append_newline();
}
}
else
{
$this->set_mode('[EXPRESSION]');
}
}
else
{
$this->set_mode('[EXPRESSION]');
}
}
else
{
$this->set_mode('(EXPRESSION)');
}
if ($this->last_text == ';' || $this->last_type == static::TK_START_BLOCK)
{
$this->append_newline();
}
elseif (in_array($this->last_type, array(static::TK_END_EXPR, static::TK_START_EXPR, static::TK_END_BLOCK)) || $this->last_text == '.')
{
# do nothing on (( and )( and ][ and ]( and .(
}
elseif (!in_array($this->last_type, array(static::TK_WORD, static::TK_OPERATOR)))
{
$this->append(' ');
}
elseif ($this->last_word == 'function' || $this->last_word == 'typeof')
{
# function() vs function (), typeof() vs typeof ()
if ($this->options->jslint_happy)
{
$this->append(' ');
}
}
elseif (in_array($this->last_text, $this->line_starters) || $this->last_text == 'catch')
{
$this->append(' ');
}
$this->append($token_text);
}
private function handle_end_expr($token_text)
{
if ($token_text == ']')
{
if ($this->options->keep_array_indentation)
{
if ($this->last_text == '}')
{
$this->remove_indent();
$this->append($token_text);
$this->restore_mode();
return;
}
}
else
{
if ($this->flags->mode == '[INDENTED-EXPRESSION]')
{
if ($this->last_text == ']')
{
$this->restore_mode();
$this->append_newline();
$this->append($token_text);
return;
}
}
}
}
$this->restore_mode();
$this->append($token_text);
}
private function handle_start_block($token_text)
{
($this->last_word == 'do') ? $this->set_mode('DO_BLOCK') : $this->set_mode('BLOCK');
if ($this->options->brace_style == static::BS_EXPAND)
{
if ($this->last_type != static::TK_OPERATOR)
{
if (in_array($this->last_text, array('return', '=')))
{
$this->append(' ');
}
else
{
$this->append_newline(true);
}
}
$this->append($token_text);
$this->indent();
}
else
{
if ( ! in_array($this->last_type, array(static::TK_OPERATOR, static::TK_START_EXPR)))
{
if ($this->last_type == static::TK_START_BLOCK)
{
$this->append_newline();
}
else
{
$this->append(' ');
}
}
else
{
// if TK_OPERATOR or TK_START_EXPR
if ($this->is_array($this->flags->previous_mode) && $this->last_text == ',')
{
if ($this->last_last_text == '}')
{
$this->append(' ');
}
else
{
$this->append_newline();
}
}
}
$this->indent();
$this->append($token_text);
}
}
private function handle_end_block($token_text)
{
$this->restore_mode();
if ($this->options->brace_style == static::BS_EXPAND)
{
if ($this->last_text != '{')
{
$this->append_newline();
}
}
else
{
if ($this->last_type == static::TK_START_BLOCK)
{
if ($this->just_added_newline)
{
$this->remove_indent();
}
else
{
// {}
$this->trim_output();
}
}
else
{
if ($this->is_array($this->flags->mode) && $this->options->keep_array_indentation)
{
$this->options->keep_array_indentation = false;
$this->append_newline();
$this->options->keep_array_indentation = true;
}
else
{
$this->append_newline();
}
}
}
$this->append($token_text);
}
private function handle_word($token_text)
{
if ($this->do_block_just_closed)
{
$this->append(' ');
$this->append($token_text);
$this->append(' ');
$this->do_block_just_closed = false;
return;
}
if ($token_text == 'function')
{
if ($this->flags->var_line)
{
$this->flags->var_line_reindented = !$this->options->keep_function_indentation;
}
if (($this->just_added_newline || $this->last_text == ';') && $this->last_text != '{')
{
// make sure there is a nice clean space of at least one blank line
// before a new function definition
$have_newlines = $this->n_newlines;
if ( ! $this->just_added_newline)
{
$have_newlines = 0;
}
if ( ! $this->options->preserve_newlines)
{
$have_newlines = 1;
}
if ($have_newlines < 2)
{
for ($i = 0; $i < 2 - $have_newlines; $i++)
{
$this->append_newline(false);
}
}
}
}
if (in_array($token_text, array('case', 'default')))
{
if ($this->last_text == ':')
{
$this->remove_indent();
}
else
{
$this->flags->indentation_level--;
$this->append_newline();
$this->flags->indentation_level++;
}
$this->append($token_text);
$this->flags->in_case = true;
return;
}
$prefix = 'NONE';
if ($this->last_type == static::TK_END_BLOCK)
{
if ( ! in_array($token_text, array('else', 'catch', 'finally')))
{
$prefix = 'NEWLINE';
}
else
{
if (in_array($this->options->brace_style, array(static::BS_EXPAND, static::BS_END_EXPAND)))
{
$prefix = 'NEWLINE';
}
else
{
$prefix = 'SPACE';
$this->append(' ');
}
}
}
elseif ($this->last_type == static::TK_SEMICOLON && in_array($this->flags->mode, array('BLOCK', 'DO_BLOCK')))
{
$prefix = 'NEWLINE';
}
elseif ($this->last_type == static::TK_SEMICOLON && $this->is_expression($this->flags->mode))
{
$prefix = 'SPACE';
}
elseif ($this->last_type == static::TK_STRING)
{
$prefix = 'NEWLINE';
}
elseif ($this->last_type == static::TK_WORD)
{
if ($this->last_text == 'else')
{
# eat newlines between ...else *** some_op...
# won't preserve extra newlines in this place (if any), but don't care that much
$this->trim_output(true);
}
$prefix = 'SPACE';
}
elseif ($this->last_type == static::TK_START_BLOCK)
{
$prefix = 'NEWLINE';
}
elseif ($this->last_type == static::TK_END_EXPR)
{
$this->append(' ');
$prefix = 'NEWLINE';
}
if ($this->flags->if_line && $this->last_type == static::TK_END_EXPR)
{
$this->flags->if_line = false;
}
if (in_array($token_text, $this->line_starters))
{
if ($this->last_text == 'else')
{
$prefix = 'SPACE';
}
else
{
$prefix = 'NEWLINE';
}
}
if (in_array($token_text, array('else', 'catch', 'finally')))
{
if ($this->last_type != static::TK_END_BLOCK ||
$this->options->brace_style == static::BS_EXPAND ||
$this->options->brace_style == static::BS_END_EXPAND)
{
$this->append_newline();
}
else
{
$this->trim_output(true);
$this->append(' ');
}
}
elseif ($prefix == 'NEWLINE')
{
if ($token_text == 'function' && ($this->last_type == static::TK_START_EXPR || in_array($this->last_text, array('=',','))))
{
// no need to force newline on "function" -
// (function...
}
elseif ($token_text == 'function' && $this->last_text == 'new')
{
$this->append(' ');
}
elseif (in_array($this->last_text, array('return', 'throw')))
{
// no newline between return nnn
$this->append(' ');
}
elseif ($this->last_type != static::TK_END_EXPR)
{
if (($this->last_type != static::TK_START_EXPR || $token_text != 'var') && $this->last_text != ':')
{
// no need to force newline on VAR -
// for (var x = 0...
if ($token_text == 'if' && $this->last_word == 'else' && $this->last_text != '{')
{
$this->append(' ');
}
else
{
$this->flags->var_line = false;
$this->flags->var_line_reindented = false;
$this->append_newline();
}
}
} elseif (in_array($token_text, $this->line_starters) && $this->last_text != ')')
{
$this->flags->var_line = false;
$this->flags->var_line_reindented = false;
$this->append_newline();
}
}
elseif ($this->is_array($this->flags->mode) && $this->last_text == ',' && $this->last_last_text == '}')
{
$this->append_newline(); # }, in lists get a newline
}
elseif ($prefix == 'SPACE')
{
$this->append(' ');
}
$this->append($token_text);
$this->last_word = $token_text;
if ($token_text == 'var')
{
$this->flags->var_line = true;
$this->flags->var_line_reindented = false;
$this->flags->var_line_tainted = false;
}
if ($token_text == 'if')
{
$this->flags->if_line = true;
}
if ($token_text == 'else')
{
$this->flags->if_line = false;
}
}
private function restore_mode()
{
$this->do_block_just_closed = $this->flags->mode == 'DO_BLOCK';
if (count($this->flag_store))
{
$this->flags = array_pop($this->flag_store);
}
}
private function is_array($mode)
{
return in_array($mode, array('[EXPRESSION]', '[INDENTED-EXPRESSION]'));
}
private function is_expression($mode)
{
return in_array($mode, array('[EXPRESSION]', '[INDENTED-EXPRESSION]', '(EXPRESSION)'));
}
private function append_newline($ignore_repeated = true)
{
$this->flags->eat_next_space = false;
if ($this->options->keep_array_indentation && $this->is_array($this->flags->mode))
{
return;
}
$this->flags->if_line = false;
$this->trim_output();
// retirn if no newline on start of file
if (count($this->output) == 0)
{
return;
}
if ($this->output[count($this->output)-1] != "\n" || ! $ignore_repeated)
{
$this->just_added_newline = true;
$this->output[] = "\n";
}
if ($this->preindent_string)
{
$this->output[] = $this->preindent_string;
}
for ($i = 0; $i < $this->flags->indentation_level; $i++)
{
$this->output[] = $this->indent_string;
}
if ($this->flags->var_line && $this->flags->var_line_reindented)
{
$this->output[] = $this->indent_string;
}
}
private function handle_string($token_text)
{
if (in_array($this->last_type, array(static::TK_START_BLOCK, static::TK_END_BLOCK, static::TK_SEMICOLON)))
{
$this->append_newline();
}
elseif ($this->last_type == static::TK_WORD)
{
$this->append(' ');
}
$this->append($token_text);
}
private function handle_unknown($token_text)
{
if (in_array($this->last_text, array('return', 'throw')))
{
$this->append(' ');
}
$this->append($token_text);
}
private function handle_operator($token_text)
{
$space_before = true;
$space_after = true;
if ($this->flags->var_line && $token_text == ',' && $this->is_expression($this->flags->mode))
{
// do not break on comma, for ( var a = 1, b = 2
$this->flags->var_line_tainted = false;
}
if ($this->flags->var_line && $token_text == ',')
{
if ($this->flags->var_line_tainted)
{
$this->append($token_text);
$this->flags->var_line_reindented = true;
$this->flags->var_line_tainted = false;
$this->append_newline();
return;
}
else
{
$this->flags->var_line_tainted = false;
}
}
if (in_array($this->last_text, array('return', 'throw')))
{
// return had a special handling in TK_WORD
$this->append(' ');
$this->append($token_text);
return;
}
if ($token_text == ':' && $this->flags->in_case)
{
$this->append($token_text);
$this->append_newline();
$this->flags->in_case = false;
return;
}
if ($token_text == '::')
{
// no spaces around the exotic namespacing syntax operator
$this->append($token_text);
return;
}
if ($token_text == ',')
{
if ($this->flags->var_line)
{
if ( ! $this->flags->var_line_tainted)
{
$this->append($token_text);
$this->append(' ');
}
}
elseif ($this->last_type == static::TK_END_BLOCK && $this->flags->mode != '(EXPRESSION)')
{
$this->append($token_text);
if ($this->flags->mode == 'OBJECT' && $this->last_text == '}')
{
$this->append_newline();
}
else
{
$this->append(' ');
}
}
else
{
if ($this->flags->mode == 'OBJECT')
{
$this->append($token_text);
$this->append_newline();
}
else
{
// EXPR or DO_BLOCK
$this->append($token_text);
$this->append(' ');
}
}
// comma handled
return;
}
// @todo: make htis normal
elseif (in_array($token_text, array('--', '++', '!')) ||
(in_array($token_text, array('+', '-'))
&& in_array($this->last_type, array(static::TK_START_BLOCK, static::TK_START_EXPR, static::TK_EQUALS, static::TK_OPERATOR))) ||
in_array($this->last_text, $this->line_starters))
{
$space_before = false;
$space_after = false;
if ($this->last_text == ';' && $this->is_expression($this->flags->mode))
{
// for (;; ++i)
// ^^
$space_before = true;
}
if ($this->last_type == static::TK_WORD && in_array($this->last_text, $this->line_starters))
{
$space_before = true;
}
if ($this->flags->mode == 'BLOCK' && in_array($this->last_text, array('{', ';')))
{
// { foo: --i }
// foo(): --bar
$this->append_newline();
}
}
elseif ($token_text == '.')
{
# decimal digits or object.property
$space_before = false;
}
elseif ($token_text == ':')
{
if ($this->flags->ternary_depth == 0)
{
$this->flags->mode = 'OBJECT';
$space_before = false;
}
else
{
$this->flags->ternary_depth--;
}
}
elseif ($token_text == '?')
{
$this->flags->ternary_depth++;
}
if ($space_before)
{
$this->append(' ');
}
$this->append($token_text);
if ($space_after)
{
$this->append(' ');
}
}
private function handle_inline_comment($token_text)
{
$this->append(' ');
$this->append($token_text);
($this->is_expression($this->flags->mode))
? $this->append(' ') : $this->append_newline_forced();
}
private function handle_block_comment($token_text)
{
$lines = explode("\x0a", str_replace("\x0d", '', $token_text));
// all lines start with an asterisk? that's a proper box comment
$all_lines_start_with_asterisk = true;
for ($i = 1; $i < count($lines); $i++)
{
if (trim($lines[$i]) == '' || substr(trim($lines[$i]), 0, 1) != '*')
{
$all_lines_start_with_asterisk = false;
}
}
if ($all_lines_start_with_asterisk)
{
$this->append_newline();
$this->append($lines[0]);
for ($i = 1; $i < count($lines); $i++)
{
$this->append_newline();
$this->append(' ' . trim($lines[$i]));
}
}
else
{
// simple block comment: leave intact
if (count($lines) > 1)
{
# multiline comment starts on a new line
$this->append_newline();
$this->trim_output();
}
else
{
// single line /* ... */ comment stays on the same line
$this->append(' ');
}
foreach ($lines as $line)
{
$this->append($line);
$this->append("\n");
}
}
$this->append_newline();
}
private function handle_comment($token_text)
{
($this->wanted_newline) ? $this->append_newline() : $this->append(' ');
$this->append($token_text);
$this->append_newline_forced();
}
private function append($s)
{
if ($s == ' ')
{
# make sure only single space gets drawn
if ($this->flags->eat_next_space)
{
$this->flags->eat_next_space = false;
}
elseif (count($this->output) && !in_array($this->output[count($this->output)-1], array(' ', "\n", $this->indent_string)))
{
$this->output[] = ' ';
}
}
else
{
$this->just_added_newline = false;
$this->flags->eat_next_space = false;
$this->output[] = $s;
}
}
private function append_newline_forced()
{
$old_array_indentation = $this->options->keep_array_indentation;
$this->options->keep_array_indentation = false;
$this->append_newline();
$this->options->keep_array_indentation = $old_array_indentation;
}
private function indent()
{
$this->flags->indentation_level++;
}
private function remove_indent()
{
if (count($this->output) && in_array($this->output[count($this->output)-1], array($this->indent_string, $this->preindent_string)))
{
array_pop($this->output);
}
}
}
/**
* BeautifierFlags Class
* @package JSBeautifier
*/
class BeautifierFlags
{
public $previous_mode = 'BLOCK';
public $mode;
public $var_line = false;
public $var_line_tainted = false;
public $var_line_reindented = false;
public $in_html_comment = false;
public $if_line = false;
public $in_case = false;
public $eat_next_space = false;
public $indentation_baseline = -1;
public $indentation_level = 0;
public $ternary_depth = 0;
public function __construct($mode)
{
$this->mode = $mode;
}
}
/**
* BeautifierOptions Class
* @package JSBeautifier
*/
class BeautifierOptions
{
public $indent_size = 4;
public $indent_char = ' ';
public $indent_with_tabs = false;
public $preserve_newlines = true;
public $max_preserve_newlines = 10;
public $jslint_happy = false;
public $brace_style = JSBeautifier::BS_COLLAPSE;
public $keep_array_indentation = false;
public $keep_function_indentation = false;
public $eval_code = false;
public $unescape_strings = false;
}
function get_arg($idx=1, $default=false){
if(isset($_SERVER["argv"][$idx]))
return $_SERVER["argv"][$idx];
return $default;
}
$opts = new BeautifierOptions();
$indent_size = 4;
$indent_char = ' ';
$preserve_newlines = true;
$jslint_happy = false;
$keep_array_indentation = false;
$brace_style = 'collapse';
$file = get_arg(1, false);
if(!$file) exit(0);
$jsbeautifier = new JSBeautifier();
$result = $jsbeautifier->beautify(file_get_contents($file), $opts);
echo $result;
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment