Тема: JScript: Удаление неиспользуемых функций из кода JScript
Приветствую, форумчане! Хочу поделиться моей наработкой по удалению неиспользуемых функций из тела скрипта. Разумеется, результат еще нельзя назвать универсальным инструментом, но мне уже здорово упростил жизнь. Почему бы не поделиться?
Итак, на входе файл, содержащий код яваскрипта, который необходимо оптимизировать. На выходе получим тот же код, но очищенный от комментариев и неиспользуемых глобальных функций. Анализ неиспользуемых блоков кода, переменных, функций внутри функций не производится.
Кстати, указанный проект тесно связан с параллельной работой по созданию фреймворка. Таким образом, пару строк функций фреймворка не будут обязывать кодера тащить за собой всю библиотеку. Бросили файл на скрипт - получили код.
Примечание: 1) функции вида new Function() обработчик не затрагивает.
2) Расширение прототипа также учитывается.
Код немного жестокий для понимания. Если нужно, расскажу по алгоритму.
if (WScript.Arguments.length == 0) WScript.Quit();
var FileName = WScript.Arguments(0);
var FileNameOut = /\./.test(FileName) ? FileName.replace(/\.[^\.]+$/, ".out.js") : (FileName + ".out.js");
Array.prototype.getUnique = function () {
var u = {},
a = [];
for (var i = 0, l = this.length; i < l; ++i) {
if (u.hasOwnProperty(this[i])) {
continue;
}
a.push(this[i]);
u[this[i]] = 1;
}
return a;
}
var FSO = new ActiveXObject("Scripting.FileSystemObject");
var ForReading = 1,
ForWriting = 2,
ForAppending = 8;
// Открываем файл и читаем код
var f = FSO.OpenTextFile(FileName, 1);
var source = f.ReadAll();
f.Close();
var text = cleanCodeFromUnusedFunctions(source);
var resFile = FSO.OpenTextFile(FileNameOut, 2, 1);
resFile.Write(js_beautify(text, {indent_size: 1, indent_char: '\t'}));
resFile.Close();
WScript.Echo("Результаты сохранены в файле: " + FileNameOut);
WScript.Quit();
function cleanCodeFromUnusedFunctions(source)
{
var tmp = cleanCodeFromValues(source),
text = tmp[0],
skip = tmp[1];
var Funcs = [];
var globalFuncs = findFunctionDeclaration(text);
var globalCalls = findCalls(globalFuncs[0]);
var GlobalFunctions = globalFuncs[1];
// разворачиваем цепочку функций
(function (tmp, calls) {
if (calls == []) return;
// перебираем массив с вызовами функций, ищем их в коде
for (var i = 0, l = calls.length; i < l; i++) {
for (var j = 0, k = GlobalFunctions.length; j < k; j++) {
if (calls[i] == GlobalFunctions[j][1]) {
var flag = true;
for (var g = 0; g < Funcs.length; g++) {
if (GlobalFunctions[j][1][2] == Funcs[g][2]) flag = false;
}
if (flag) {
Funcs.push(GlobalFunctions[j]);
var t = tmp.slice(GlobalFunctions[j][3], GlobalFunctions[j][4]);
arguments.callee(tmp, findCalls(t));
}
}
}
}
})(text, globalCalls);
text = restoreFunction(globalFuncs[0], text, Funcs);
var last = 0;
for (var i = 0, l = GlobalFunctions.length; i < l; i++) {
if (last != GlobalFunctions[i][2])
text = restoreCode(text, source, skip, /str|reg|res/, last, GlobalFunctions[i][2]);
last = GlobalFunctions[i][4];
}
text = restoreCode(text, source, skip, /str|reg|res/, last, text.length);
for (var i = 0, l = Funcs.length; i < l; i++) {
text = restoreCode(text, source, skip, /str|reg|res/, Funcs[i][2], Funcs[i][4])
}
text = text.replace(/^\s+$/gm, "\r\n").replace(/(\r?\n)+/g, "\r\n").replace(/^\s*;\s*$/gm, ";");
return text;
}
// воcстановит кусок кода от ранних изменений. например, восстановит строки или регулярные выражения
function restoreCode(tmp, code, skipInfo, what, start, end) {
!skipInfo ? skipInfo = [] : 0;
!start ? start = 0 : 0;
!end ? end = code.length : 0;
for (var i = 0, l = skipInfo.length; i < l; i++) {
if ((start <= skipInfo[i][1]) && (end >= skipInfo[i][2]))
if (what.test(skipInfo[i][0])) tmp = tmp.slice(0, skipInfo[i][1]) + code.slice(skipInfo[i][1], skipInfo[i][2]) + tmp.slice(skipInfo[i][2]);
}
return tmp;
}
function restoreFunction(tmp, code, func_arr) {
for (var i = 0; i < func_arr.length; i++) {
tmp = tmp.slice(0, func_arr[i][2]) + code.slice(func_arr[i][2], func_arr[i][4] + 1) + tmp.slice(func_arr[i][4] + 1);
}
return tmp;
}
// вернет массив с информацией о вызовах функций
function findCalls(s) {
if (!s) return [];
var ar = s.match(/(function\s+)?\.?[_$\w\d]+\s*\(|\b[\w$_][\.\w\d_]\.call|\b[\w$_][\.\w\d_]\.apply/g);
if (ar) {
ar = ar.join("|").replace(/\b(function\s+[^\|]+|\b(if|for|while|eval|catch|do|break|case|else|in|return|switch|this|throw|typeof|var|void|with|ActiveXObject|GetObject|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|escape|isFinite|isNaN|Number|parseFloat|Error|parseInt|String|unescape|Array|Date|[Ff]unction|VBArray)\b|RegExp|Enumerator)\s*\|?|\s*\(|/g, "").replace(/\|/g, ",").replace(/\.,/g, ',').replace(/,(,)+/g, ',').replace(/^,|,$/g, '');
ar = ar.split(",").getUnique();
return ar;
} else return [];
}
// вернет [очищенный code от глобальных функций, информация_о_заменах]
function findFunctionDeclaration(code) {
var flag = false,
Result = [],
delta = 0,
tmp = code;
function processFunction(m) {
flag = true;
var pos = arguments[arguments.length-2];
if (/new\s+Function\b/.test(m)) return;
/*
if (/new\s+Function\b/.test(m)) {
Result.push(["function", m.replace(/\s*=.+/, ""), delta + pos, delta + pos, delta + pos + m.length + 1, []]);
code = code.slice(0, delta + pos) + new Array(m.length + 1).join(" ") + code.slice(delta + pos + m.length + 1);
delta += pos + m.length;
tmp = tmp.slice(pos + m.length + 1);
return;
}
*/
var tmp2 = tmp.slice(pos + m.length + 1);
var start = end = pos + m.length + 1;
var index = 0;
function searchLimits(m1) {
var pos1 = arguments[arguments.length-2];
if (m1 == "{") index++;
if (m1 == "}") index--;
tmp2 = tmp2.slice(pos1 + 1);
end += pos1 + 1;
}
while (index != -1 && /[{}]/.test(tmp2)) tmp2.replace(/[{}]/, searchLimits);
var ar = findCalls(tmp.slice(start, end));
Result.push(["function", m.replace(/\(([\r\n]|.)*/, "").replace(/\bfunction\b|\bnew\b|[^\w\d_\.]/g, "").match(/\.?[\w\d_$]+$/), delta + pos, delta + start, delta + end, ar]);
code = code.slice(0, delta + pos) + new Array(end - pos + 1).join(" ") + code.slice(delta + end);
tmp = tmp.slice(end);
delta += end;
}
do {
flag = false;
if (tmp)
tmp.replace(/(?!=\s*)function\s+[\w$_][\.\w\d_]*\s*\([^\)]*\)[^\{]*{|[\w$_][\.\w\d_]*\s*=\s*function\s*\([^\)]*\)[^\{]*{/, processFunction);
}
while (flag);
return [code, Result];
}
// Эта функция вернет массив [очищенный_код, информация_о_заменах_в_коде] код без строковых значений, регулярных выражений, комментариев и ресурсов. Вместо них будут пробелы так, чтобы не нарушилась длина кода.
function cleanCodeFromValues(code) {
var tmp = code,
found = "",
startpos = 0,
delta = 0,
skip = [];
//var searchValues = /\/\*\[?|["']|\/\/|(?:(^|[;\}\{\(])\s*)\/(?![\*\/])/m;
var searchValues = /\/\*\[?|[@#]*["']|\/\/|(?:(^|[;\}\{\(])\s*)\/(?![\*\/])/m;
var types = {
'/*': "comment",
'/*[': "resource",
'"': "string",
"'": "string",
'//': "comment",
"/": "regexp"
};
function getStartPointOfValues(m) {
found = m;
var pos = arguments[arguments.length-2];
if (!/[^\/]\/$/.test(m)) {
startpos = delta + pos;
} else {
found = "/";
//WScript.Echo(m);
startpos = delta + pos + m.length - 1;
}
tmp = tmp.slice(pos + m.length);
delta += pos + m.length;
}
function getEndPointOfValues(m) {
var pos = arguments[arguments.length-2];
if (found == "/") {
pos++;
m = "/";
}
if (/.["']$/.test(found)) {
m = found;
pos++;
}
if (found == "//") pos--;
//WScript.Echo(["Найдено ранее: "+found, "Его длина: "+found.length, "Найдено в текущий момент: "+m, "Позиция: "+pos, "Что внутри: "+tmp.slice(0, pos)].join("\r\n"));
var content = tmp.slice(0, pos);
tmp = tmp.slice(pos + m.length);
delta += pos + m.length;
skip.push([types[found], startpos, delta]);
code = code.slice(0, startpos) + new Array(delta - startpos + 1).join(" ") + code.slice(delta);
}
function findValues() {
tmp.replace(searchValues, getStartPointOfValues);
if (found == '"') tmp.replace(/(?:^"|(?:\\\\|[^\\])")/, getEndPointOfValues);
if (found == "'") tmp.replace(/(?:^'|(?:\\\\|[^\\])')/, getEndPointOfValues);
if (found == '//') tmp.replace(/[\r\n]/, getEndPointOfValues);
if (found == '/*' || found == '/*[') tmp.replace(/\*\//, getEndPointOfValues);
if (found == "/") tmp.replace(/(?!\\).\//, getEndPointOfValues);
found = "";
return tmp;
}
//Перебираем содержание.
while (tmp != findValues()) {}
tmp = "";
return [code, skip];
}
/*jslint onevar: false, plusplus: false */
/*
JS Beautifier
---------------
Written by Einar Lielmanis, <einars@gmail.com>
http://jsbeautifier.org/
Originally converted to javascript by Vital, <vital76@gmail.com>
You are free to use this in any way you want, in case you find this useful or working for you.
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,
indent_level (default 0) — initial indentation level, you probably won't need this ever,
space_after_anon_function (default false) — if true, then space is added between "function ()"
(jslint is happy about this); if false, then the common "function()" output is used.
e.g
js_beautify(js_source_text, {indent_size: 1, indent_char: '\t'});
*/
function js_beautify(js_source_text, options) {
var input, output, token_text, last_type, last_text, last_last_text, last_word, flags, flag_store, indent_string;
var whitespace, wordchar, punct, parser_pos, line_starters, digits;
var prefix, token_type, do_block_just_closed;
var indent_level, wanted_newline, just_added_newline, n_newlines;
options = options || {};
var opt_indent_size = options.indent_size || 4;
var opt_indent_char = options.indent_char || ' ';
var opt_preserve_newlines = typeof options.preserve_newlines === 'undefined' ? true : options.preserve_newlines;
var opt_indent_level = options.indent_level || 0;
var opt_space_after_anon_function = options.space_after_anon_function === 'undefined' ? false : options.space_after_anon_function;
var opt_keep_array_indentation = typeof options.keep_array_indentation === 'undefined' ? true : options.keep_array_indentation;
just_added_newline = false;
function trim_output() {
while (output.length && (output[output.length - 1] === ' ' || output[output.length - 1] === indent_string)) {
output.pop();
}
}
function print_newline(ignore_repeated) {
if (opt_keep_array_indentation && is_array(flags.mode)) {
return;
}
ignore_repeated = typeof ignore_repeated === 'undefined' ? true : ignore_repeated;
flags.if_line = false;
trim_output();
if (!output.length) {
return;
}
if (output[output.length - 1] !== "\n" || !ignore_repeated) {
just_added_newline = true;
output.push("\n");
}
for (var i = 0; i < indent_level; i += 1) {
output.push(indent_string);
}
}
function print_single_space() {
var last_output = ' ';
if (output.length) {
last_output = output[output.length - 1];
}
if (last_output !== ' ' && last_output !== '\n' && last_output !== indent_string) {
output.push(' ');
}
}
function print_token() {
just_added_newline = false;
output.push(token_text);
}
function indent() {
indent_level += 1;
}
function unindent() {
if (indent_level) {
indent_level -= 1;
}
}
function remove_indent() {
if (output.length && output[output.length - 1] === indent_string) {
output.pop();
}
}
function print_javadoc_comment() {
var lines = token_text.split('\n');
output.push(lines[0]);
for (var i = 1; i < lines.length; i++) {
print_newline();
output.push(' ');
output.push(lines[i].replace(/^\s+/, ''));
}
}
function set_mode(mode) {
if (flags) {
flag_store.push(flags);
}
flags = {
mode: mode,
var_line: false,
var_line_tainted: false,
if_line: false,
in_case: false,
indentation_baseline: -1
};
}
function is_expression(mode) {
return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]' || mode === '(EXPRESSION)';
}
function is_array(mode) {
return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]';
}
function restore_mode() {
do_block_just_closed = flags.mode === 'DO_BLOCK';
flags = flag_store.pop();
}
function in_array(what, arr) {
for (var i = 0; i < arr.length; i += 1) {
if (arr[i] === what) {
return true;
}
}
return false;
}
function is_ternary_op() {
var level = 0,
colon_count = 0;
for (var i = output.length - 1; i >= 0; i--) {
switch (output[i]) {
case ':':
if (level === 0) {
colon_count++;
}
break;
case '?':
if (level === 0) {
if (colon_count === 0) {
return true;
} else {
colon_count--;
}
}
break;
case '{':
if (level === 0) {
return false;
}
level--;
break;
case '(':
case '[':
level--;
break;
case ')':
case ']':
case '}':
level++;
break;
}
}
}
function get_next_token() {
n_newlines = 0;
if (parser_pos >= input.length) {
return ['', 'TK_EOF'];
}
var c = input.charAt(parser_pos);
parser_pos += 1;
var keep_whitespace = opt_keep_array_indentation && is_array(flags.mode);
wanted_newline = false;
if (keep_whitespace) {
var whitespace_count = 0;
while (in_array(c, whitespace)) {
if (c === "\n") {
trim_output();
output.push("\n");
just_added_newline = true;
whitespace_count = 0;
} else {
if (c === '\t') {
whitespace_count += 4;
} else {
whitespace_count += 1;
}
}
if (parser_pos >= input.length) {
return ['', 'TK_EOF'];
}
c = input.charAt(parser_pos);
parser_pos += 1;
}
if (flags.indentation_baseline === -1) {
flags.indentation_baseline = whitespace_count;
}
if (just_added_newline) {
for (var i = 0; i < indent_level + 1; i += 1) {
output.push(indent_string);
}
if (flags.indentation_baseline !== -1) {
for (var i = 0; i < whitespace_count - flags.indentation_baseline; i++) {
output.push(' ');
}
}
}
} else {
while (in_array(c, whitespace)) {
if (c === "\n") {
n_newlines += 1;
}
if (parser_pos >= input.length) {
return ['', 'TK_EOF'];
}
c = input.charAt(parser_pos);
parser_pos += 1;
}
if (opt_preserve_newlines) {
if (n_newlines > 1) {
for (var i = 0; i < n_newlines; i += 1) {
print_newline(i === 0);
just_added_newline = true;
}
}
}
wanted_newline = n_newlines > 0;
}
if (in_array(c, wordchar)) {
if (parser_pos < input.length) {
while (in_array(input.charAt(parser_pos), wordchar)) {
c += input.charAt(parser_pos);
parser_pos += 1;
if (parser_pos === input.length) {
break;
}
}
}
if (parser_pos !== input.length && c.match(/^[0-9]+[Ee]$/) && (input.charAt(parser_pos) === '-' || input.charAt(parser_pos) === '+')) {
var sign = input.charAt(parser_pos);
parser_pos += 1;
var t = get_next_token(parser_pos);
c += sign + t[0];
return [c, 'TK_WORD'];
}
if (c === 'in') {
return [c, 'TK_OPERATOR'];
}
if (wanted_newline && last_type !== 'TK_OPERATOR' && !flags.if_line && (opt_preserve_newlines || last_text !== 'var')) {
print_newline();
}
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 = '';
if (input.charAt(parser_pos) === '*') {
parser_pos += 1;
if (parser_pos < input.length) {
while (! (input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/') && parser_pos < input.length) {
comment += input.charAt(parser_pos);
parser_pos += 1;
if (parser_pos >= input.length) {
break;
}
}
}
parser_pos += 2;
return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT'];
}
if (input.charAt(parser_pos) === '/') {
comment = c;
while (input.charAt(parser_pos) !== "\x0d" && input.charAt(parser_pos) !== "\x0a") {
comment += input.charAt(parser_pos);
parser_pos += 1;
if (parser_pos >= input.length) {
break;
}
}
parser_pos += 1;
if (wanted_newline) {
print_newline();
}
return [comment, 'TK_COMMENT'];
}
}
if (c === "'" || c === '"' || (c === '/' && ((last_type === 'TK_WORD' && in_array(last_text, ['return', 'do'])) || (last_type === 'TK_START_EXPR' || last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_OPERATOR' || last_type === 'TK_EOF' || last_type === 'TK_SEMICOLON')))) {
var sep = c;
var esc = false;
var resulting_string = c;
if (parser_pos < input.length) {
if (sep === '/') {
var in_char_class = false;
while (esc || in_char_class || input.charAt(parser_pos) !== sep) {
resulting_string += input.charAt(parser_pos);
if (!esc) {
esc = input.charAt(parser_pos) === '\\';
if (input.charAt(parser_pos) === '[') {
in_char_class = true;
} else if (input.charAt(parser_pos) === ']') {
in_char_class = false;
}
} else {
esc = false;
}
parser_pos += 1;
if (parser_pos >= input.length) {
return [resulting_string, 'TK_STRING'];
}
}
} else {
while (esc || input.charAt(parser_pos) !== sep) {
resulting_string += input.charAt(parser_pos);
if (!esc) {
esc = input.charAt(parser_pos) === '\\';
} else {
esc = false;
}
parser_pos += 1;
if (parser_pos >= input.length) {
return [resulting_string, 'TK_STRING'];
}
}
}
}
parser_pos += 1;
resulting_string += sep;
if (sep === '/') {
while (parser_pos < input.length && in_array(input.charAt(parser_pos), wordchar)) {
resulting_string += input.charAt(parser_pos);
parser_pos += 1;
}
}
return [resulting_string, 'TK_STRING'];
}
if (c === '#') {
var sharp = '#';
if (parser_pos < input.length && in_array(input.charAt(parser_pos), digits)) {
do {
c = input.charAt(parser_pos);
sharp += c;
parser_pos += 1;
} while (parser_pos < input.length && c !== '#' && c !== '=');
if (c === '#') {
return [sharp, 'TK_WORD'];
} else {
return [sharp, 'TK_OPERATOR'];
}
}
}
if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') {
parser_pos += 3;
return ['<!--', 'TK_COMMENT'];
}
if (c === '-' && input.substring(parser_pos - 1, parser_pos + 2) === '-->') {
parser_pos += 2;
if (wanted_newline) {
print_newline();
}
return ['-->', 'TK_COMMENT'];
}
if (in_array(c, punct)) {
while (parser_pos < input.length && in_array(c + input.charAt(parser_pos), punct)) {
c += input.charAt(parser_pos);
parser_pos += 1;
if (parser_pos >= input.length) {
break;
}
}
return [c, 'TK_OPERATOR'];
}
return [c, 'TK_UNKNOWN'];
}
indent_string = '';
while (opt_indent_size > 0) {
indent_string += opt_indent_char;
opt_indent_size -= 1;
}
indent_level = opt_indent_level;
input = js_source_text;
last_word = '';
last_type = 'TK_START_EXPR';
last_text = '';
last_last_text = '';
output = [];
do_block_just_closed = false;
whitespace = "\n\r\t ".split('');
wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split('');
digits = '0123456789'.split('');
punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::'.split(' ');
line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(',');
flag_store = [];
set_mode('BLOCK');
parser_pos = 0;
while (true) {
var t = get_next_token(parser_pos);
token_text = t[0];
token_type = t[1];
if (token_type === 'TK_EOF') {
break;
}
switch (token_type) {
case 'TK_START_EXPR':
if (token_text === '[') {
if (last_type === 'TK_WORD' || last_text === ')') {
if (last_word === 'return' || last_word === 'throw') {
print_single_space();
}
set_mode('(EXPRESSION)');
print_token();
break;
}
if (flags.mode === '[EXPRESSION]' || flags.mode === '[INDENTED-EXPRESSION]') {
if (last_last_text === ']' && last_text === ',') {
if (!opt_keep_array_indentation) {
indent();
print_newline();
}
set_mode('[INDENTED-EXPRESSION]');
} else if (last_text === '[') {
if (!opt_keep_array_indentation) {
indent();
print_newline();
}
set_mode('[INDENTED-EXPRESSION]');
} else {
set_mode('[EXPRESSION]');
}
} else {
set_mode('[EXPRESSION]');
}
} else {
set_mode('(EXPRESSION)');
}
if (last_text === ';' || last_type === 'TK_START_BLOCK') {
print_newline();
} else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR') {} else if (last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') {
print_single_space();
} else if (last_word === 'function') {
if (opt_space_after_anon_function) {
print_single_space();
}
} else if (in_array(last_word, line_starters)) {
print_single_space();
}
print_token();
break;
case 'TK_END_EXPR':
if (token_text === ']' && flags.mode === '[INDENTED-EXPRESSION]') {
unindent();
}
restore_mode();
print_token();
break;
case 'TK_START_BLOCK':
if (last_word === 'do') {
set_mode('DO_BLOCK');
} else {
set_mode('BLOCK');
}
if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') {
if (last_type === 'TK_START_BLOCK') {
print_newline();
} else {
print_single_space();
}
}
print_token();
indent();
break;
case 'TK_END_BLOCK':
if (last_type === 'TK_START_BLOCK') {
if (just_added_newline) {
remove_indent();
} else {
trim_output();
}
unindent();
} else {
unindent();
print_newline();
}
print_token();
restore_mode();
break;
case 'TK_WORD':
if (do_block_just_closed) {
print_single_space();
print_token();
print_single_space();
do_block_just_closed = false;
break;
}
if (token_text === 'function') {
if ((just_added_newline || last_text == ';') && last_text !== '{') {
n_newlines = just_added_newline ? n_newlines : 0;
for (var i = 0; i < 2 - n_newlines; i++) {
print_newline(false);
}
}
}
if (token_text === 'case' || token_text === 'default') {
if (last_text === ':') {
remove_indent();
} else {
unindent();
print_newline();
indent();
}
print_token();
flags.in_case = true;
break;
}
prefix = 'NONE';
if (last_type === 'TK_END_BLOCK') {
if (!in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) {
prefix = 'NEWLINE';
} else {
prefix = 'SPACE';
print_single_space();
}
} else if (last_type === 'TK_SEMICOLON' && (flags.mode === 'BLOCK' || flags.mode === 'DO_BLOCK')) {
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_WORD') {
prefix = 'SPACE';
} else if (last_type === 'TK_START_BLOCK') {
prefix = 'NEWLINE';
} else if (last_type === 'TK_END_EXPR') {
print_single_space();
prefix = 'NEWLINE';
}
if (last_type !== 'TK_END_BLOCK' && in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) {
print_newline();
} else if (in_array(token_text, line_starters) || prefix === 'NEWLINE') {
if (last_text === 'else') {
print_single_space();
} else if ((last_type === 'TK_START_EXPR' || last_text === '=' || last_text === ',') && token_text === 'function') {} else if (last_text === 'return' || last_text === 'throw') {
print_single_space();
} else if (last_type !== 'TK_END_EXPR') {
if ((last_type !== 'TK_START_EXPR' || token_text !== 'var') && last_text !== ':') {
if (token_text === 'if' && last_word === 'else' && last_text !== '{') {
print_single_space();
} else {
print_newline();
}
}
} else {
if (in_array(token_text, line_starters) && last_text !== ')') {
print_newline();
}
}
} else if (prefix === 'SPACE') {
print_single_space();
}
print_token();
last_word = token_text;
if (token_text === 'var') {
flags.var_line = true;
flags.var_line_tainted = false;
}
if (token_text === 'if' || token_text === 'else') {
flags.if_line = true;
}
break;
case 'TK_SEMICOLON':
print_token();
flags.var_line = false;
break;
case 'TK_STRING':
if (last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_SEMICOLON') {
print_newline();
} else if (last_type === 'TK_WORD') {
print_single_space();
}
print_token();
break;
case 'TK_OPERATOR':
var start_delim = true;
var end_delim = true;
if (flags.var_line && token_text === ',' && (is_expression(flags.mode))) {
flags.var_line_tainted = false;
}
if (flags.var_line) {
if (token_text === ',') {
if (flags.var_line_tainted) {
print_token();
print_newline();
output.push(indent_string);
flags.var_line_tainted = false;
break;
} else {
flags.var_line_tainted = false;
}
} else {
flags.var_line_tainted = true;
if (token_text === ':') {
flags.var_line = false;
}
}
}
if (last_text === 'return' || last_text === 'throw') {
print_single_space();
print_token();
break;
}
if (token_text === ':' && flags.in_case) {
print_token();
print_newline();
flags.in_case = false;
break;
}
if (token_text === '::') {
print_token();
break;
}
if (token_text === ',') {
if (flags.var_line) {
if (flags.var_line_tainted) {
print_token();
print_newline();
flags.var_line_tainted = false;
} else {
print_token();
print_single_space();
}
} else if (last_type === 'TK_END_BLOCK') {
print_token();
print_newline();
} else {
if (flags.mode === 'BLOCK') {
print_token();
print_newline();
} else {
print_token();
print_single_space();
}
}
break;
} else if (token_text === '--' || token_text === '++') {
if (last_text === ';') {
if (flags.mode === 'BLOCK') {
print_newline();
start_delim = true;
end_delim = false;
} else {
start_delim = true;
end_delim = false;
}
} else {
if (last_text === '{') {
print_newline();
}
start_delim = false;
end_delim = false;
}
} else if ((token_text === '!' || token_text === '+' || token_text === '-') && (last_text === 'return' || last_text === 'case')) {
start_delim = true;
end_delim = false;
} else if ((token_text === '!' || token_text === '+' || token_text === '-') && last_type === 'TK_START_EXPR') {
start_delim = false;
end_delim = false;
} else if (last_type === 'TK_OPERATOR') {
start_delim = false;
end_delim = false;
} else if (last_type === 'TK_END_EXPR') {
start_delim = true;
end_delim = true;
} else if (token_text === '.') {
start_delim = false;
end_delim = false;
} else if (token_text === ':') {
if (is_ternary_op()) {
start_delim = true;
} else {
start_delim = false;
}
}
if (start_delim) {
print_single_space();
}
print_token();
if (end_delim) {
print_single_space();
}
break;
case 'TK_BLOCK_COMMENT':
print_newline();
if (token_text.substring(0, 3) == '/**') {
print_javadoc_comment();
} else {
print_token();
}
print_newline();
break;
case 'TK_COMMENT':
if (wanted_newline) {
print_newline();
} else {
print_single_space();
}
print_token();
print_newline();
break;
case 'TK_UNKNOWN':
print_token();
break;
}
last_last_text = last_text;
last_type = token_type;
last_text = token_text;
}
return output.join('').replace(/\n+$/, '');
}
