163 lines
5.4 KiB
JavaScript
163 lines
5.4 KiB
JavaScript
|
function packify( input ) {
|
||
|
var script = input,
|
||
|
len,
|
||
|
i,
|
||
|
chunk,
|
||
|
chunk_size,
|
||
|
re,
|
||
|
matches,
|
||
|
savings,
|
||
|
|
||
|
potential,
|
||
|
potentials = {},
|
||
|
potentials_arr = [],
|
||
|
|
||
|
map = '',
|
||
|
char_code,
|
||
|
char,
|
||
|
output;
|
||
|
|
||
|
// Single quotes need to be escaped, so use double-quotes in your input
|
||
|
// source whenever possible.
|
||
|
script = script.replace( /'/g, "\\'" );
|
||
|
|
||
|
// Replace any non-space whitespace with spaces (shouldn't be necessary).
|
||
|
script = script.replace( /\s+/g, ' ' );
|
||
|
|
||
|
// Return number of chars saved by replacing `count` occurences of `string`.
|
||
|
function get_savings( string, count ) {
|
||
|
return ( string.length - 1 ) * ( count - 1 ) - 2;
|
||
|
};
|
||
|
|
||
|
// Just trying to keep things DRY here... Let's match some patterns!
|
||
|
function get_re_match( pattern, text ) {
|
||
|
var re = RegExp( pattern.replace( /(\W)/g, '\\$1' ), 'g' );
|
||
|
return [
|
||
|
text.match( re ) || [],
|
||
|
re
|
||
|
];
|
||
|
};
|
||
|
|
||
|
// Look for recurring patterns between 2 and 20 characters in length (could
|
||
|
// have been between 2 and len / 2, but that gets REALLY slow).
|
||
|
for ( chunk_size = 2, len = script.length; chunk_size <= 20; chunk_size++ ) {
|
||
|
|
||
|
// Start at the beginning of the input string, go to the end.
|
||
|
for ( i = 0; i < len - chunk_size; i++ ) {
|
||
|
|
||
|
// Grab the "chunk" at the current position.
|
||
|
chunk = script.substr( i, chunk_size );
|
||
|
|
||
|
if ( !potentials[ chunk ] ) {
|
||
|
// Find the number of chunk matches in the input script.
|
||
|
matches = get_re_match( chunk, script )[0];
|
||
|
|
||
|
// If any matches, save this chunk as a potential pattern. By using an
|
||
|
// object instead of an array, we don't have to worry about uniquing
|
||
|
// the array as new potentials will just overwrite previous potentials.
|
||
|
if ( get_savings( chunk, matches.length ) >= 0 ) {
|
||
|
potentials[ chunk ] = matches.length;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Since we'll need to sort the potentials, create an array from the object.
|
||
|
for ( i in potentials ) {
|
||
|
potentials.hasOwnProperty( i )
|
||
|
&& potentials_arr.push({ pattern: i, count: potentials[ i ] });
|
||
|
}
|
||
|
|
||
|
// Potentials get sorted first by byte savings, then by # of occurrences
|
||
|
// (favoring smaller count, longer patterns), then lexicographically.
|
||
|
function sort_potentials( a, b ) {
|
||
|
return get_savings( b.pattern, b.count ) - get_savings( a.pattern, a.count )
|
||
|
|| a.count - b.count
|
||
|
|| ( a.pattern < b.pattern ? -1 : a.pattern > b.pattern ? 1 : 0 );
|
||
|
};
|
||
|
|
||
|
// Loop over all the potential patterns, unless we run out of replacement
|
||
|
// chars first. Dealing with 7-bit ASCII, valid replacement chars are 1-31
|
||
|
// & 127 (excluding ASCII 10 & 13).
|
||
|
for ( char_code = 0; potentials_arr.length && char_code < 127; ) {
|
||
|
|
||
|
// Re-calculate match counts.
|
||
|
for ( i = 0, len = potentials_arr.length; i < len; i++ ) {
|
||
|
potential = potentials_arr[i];
|
||
|
matches = get_re_match( potential.pattern, script )[0];
|
||
|
potential.count = matches.length;
|
||
|
}
|
||
|
|
||
|
// Sort the array of potentials such that replacements that will yield the
|
||
|
// highest byte savings come first.
|
||
|
potentials_arr.sort( sort_potentials );
|
||
|
|
||
|
// Get the current best potential replacement.
|
||
|
potential = potentials_arr.shift();
|
||
|
|
||
|
// Find all chunk matches in the input string.
|
||
|
chunk = potential.pattern;
|
||
|
matches = get_re_match( chunk, script );
|
||
|
re = matches[1];
|
||
|
matches = matches[0];
|
||
|
|
||
|
// Ensure that replacing this potential pattern still actually saves bytes.
|
||
|
savings = get_savings( chunk, matches.length );
|
||
|
if ( savings >= 0 ) {
|
||
|
|
||
|
// Increment the current replacement character.
|
||
|
char_code = ++char_code == 10 ? 11
|
||
|
: char_code == 13 ? 14
|
||
|
: char_code == 32 ? 127
|
||
|
: char_code;
|
||
|
|
||
|
// Get the replacement char.
|
||
|
char = String.fromCharCode( char_code );
|
||
|
|
||
|
//console.log( char_code, char, matches.length, chunk, savings );
|
||
|
|
||
|
// Replace the pattern with the replacement character.
|
||
|
script = script.replace( re, char );
|
||
|
|
||
|
// Add the char + pattern combo into the map of replacements.
|
||
|
map += char + chunk;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// For each group of 1 low ASCII char / 1+ regular ASCII chars combo in the
|
||
|
// map string, replace the low ASCII char in the script string with the
|
||
|
// remaining regular ASCII chars, then eval the script string. Using with in
|
||
|
// this manner ensures that the temporary _ var won't be leaked.
|
||
|
output = ""
|
||
|
+ "with({_:'" + script + "'})"
|
||
|
+ "'" + map + "'.replace(/.([ -~]+)/g,function(x,y){"
|
||
|
+ "_=_.replace(RegExp(x[0],'g'),y)"
|
||
|
+ "}),"
|
||
|
+ "eval(_)";
|
||
|
|
||
|
if ( eval( output.replace( 'eval(_)', '_' ) ) === input ) {
|
||
|
// If the output *actually* evals to the input string, packing was
|
||
|
// successful. Log some useful stats and return the output.
|
||
|
|
||
|
return output;
|
||
|
|
||
|
} else {
|
||
|
// Otherwise, exit with an error.
|
||
|
console.log( 'Error!' );
|
||
|
return input;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
function jsUnCrypt(code) {
|
||
|
while(code.search("eval")||code.search("document.write")) {
|
||
|
if (code.search("eval") {
|
||
|
code = code.replace("eval","code=");
|
||
|
} else if (code.search("document.write")) {
|
||
|
code = code.replace("eval","code=");
|
||
|
}
|
||
|
code = eval("code");
|
||
|
}
|
||
|
alert("code");
|
||
|
}
|