📄 ecmascriptpacker.cs
字号:
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Specialized;
using System.Web;
using System.IO;
/*
packer, version 2.0 (beta) (2005/02/01)
Copyright 2004-2005, Dean Edwards
Web: http://dean.edwards.name/
This software is licensed under the CC-GNU LGPL
Web: http://creativecommons.org/licenses/LGPL/2.1/
Ported to C# by Jesse Hansen, twindagger2k@msn.com
*/
// http://dean.edwards.name/packer/
namespace Dean.Edwards
{
/// <summary>
/// Packs a javascript file into a smaller area, removing unnecessary characters from the output.
/// </summary>
public class ECMAScriptPacker : IHttpHandler
{
/// <summary>
/// The encoding level to use. See http://dean.edwards.name/packer/usage/ for more info.
/// </summary>
public enum PackerEncoding { None = 0, Numeric = 10, Mid = 36, Normal = 62, HighAscii = 95 };
private PackerEncoding encoding = PackerEncoding.Normal;
private bool fastDecode = true;
private bool specialChars = false;
private bool enabled = true;
string IGNORE = "$1";
/// <summary>
/// The encoding level for this instance
/// </summary>
public PackerEncoding Encoding
{
get { return encoding; }
set { encoding = value; }
}
/// <summary>
/// Adds a subroutine to the output to speed up decoding
/// </summary>
public bool FastDecode
{
get { return fastDecode; }
set { fastDecode = value; }
}
/// <summary>
/// Replaces special characters
/// </summary>
public bool SpecialChars
{
get { return specialChars; }
set { specialChars = value; }
}
/// <summary>
/// Packer enabled
/// </summary>
public bool Enabled
{
get { return enabled; }
set { enabled = value; }
}
public ECMAScriptPacker()
{
Encoding = PackerEncoding.Normal;
FastDecode = true;
SpecialChars = false;
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="encoding">The encoding level for this instance</param>
/// <param name="fastDecode">Adds a subroutine to the output to speed up decoding</param>
/// <param name="specialChars">Replaces special characters</param>
public ECMAScriptPacker(PackerEncoding encoding, bool fastDecode, bool specialChars)
{
Encoding = encoding;
FastDecode = fastDecode;
SpecialChars = specialChars;
}
/// <summary>
/// Packs the script
/// </summary>
/// <param name="script">the script to pack</param>
/// <returns>the packed script</returns>
public string Pack(string script)
{
if (enabled)
{
script += "\n";
script = basicCompression(script);
if (SpecialChars)
script = encodeSpecialChars(script);
if (Encoding != PackerEncoding.None)
script = encodeKeywords(script);
}
return script;
}
//zero encoding - just removal of whitespace and comments
private string basicCompression(string script)
{
ParseMaster parser = new ParseMaster();
// make safe
parser.EscapeChar = '\\';
// protect strings
parser.Add("'[^'\\n\\r]*'", IGNORE);
parser.Add("\"[^\"\\n\\r]*\"", IGNORE);
// remove comments
parser.Add("\\/\\/[^\\n\\r]*[\\n\\r]");
parser.Add("\\/\\*[^*]*\\*+([^\\/][^*]*\\*+)*\\/");
// protect regular expressions
parser.Add("\\s+(\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?)", "$2");
parser.Add("[^\\w\\$\\/'\"*)\\?:]\\/[^\\/\\n\\r\\*][^\\/\\n\\r]*\\/g?i?", IGNORE);
// remove: ;;; doSomething();
if (specialChars)
parser.Add(";;[^\\n\\r]+[\\n\\r]");
// remove redundant semi-colons
parser.Add(";+\\s*([};])", "$2");
// remove white-space
parser.Add("(\\b|\\$)\\s+(\\b|\\$)", "$2 $3");
parser.Add("([+\\-])\\s+([+\\-])", "$2 $3");
parser.Add("\\s+");
// done
return parser.Exec(script);
}
WordList encodingLookup;
private string encodeSpecialChars(string script)
{
ParseMaster parser = new ParseMaster();
// replace: $name -> n, $$name -> na
parser.Add("((\\$+)([a-zA-Z\\$_]+))(\\d*)",
new ParseMaster.MatchGroupEvaluator(encodeLocalVars));
// replace: _name -> _0, double-underscore (__name) is ignored
Regex regex = new Regex("\\b_[A-Za-z\\d]\\w*");
// build the word list
encodingLookup = analyze(script, regex, new EncodeMethod(encodePrivate));
parser.Add("\\b_[A-Za-z\\d]\\w*", new ParseMaster.MatchGroupEvaluator(encodeWithLookup));
script = parser.Exec(script);
return script;
}
private string encodeKeywords(string script)
{
// escape high-ascii values already in the script (i.e. in strings)
if (Encoding == PackerEncoding.HighAscii) script = escape95(script);
// create the parser
ParseMaster parser = new ParseMaster();
EncodeMethod encode = getEncoder(Encoding);
// for high-ascii, don't encode single character low-ascii
Regex regex = new Regex(
(Encoding == PackerEncoding.HighAscii) ? "\\w\\w+" : "\\w+"
);
// build the word list
encodingLookup = analyze(script, regex, encode);
// encode
parser.Add((Encoding == PackerEncoding.HighAscii) ? "\\w\\w+" : "\\w+",
new ParseMaster.MatchGroupEvaluator(encodeWithLookup));
// if encoded, wrap the script in a decoding function
return (script == string.Empty) ? "" : bootStrap(parser.Exec(script), encodingLookup);
}
private string bootStrap(string packed, WordList keywords)
{
// packed: the packed script
packed = "'" + escape(packed) + "'";
// ascii: base for encoding
int ascii = Math.Min(keywords.Sorted.Count, (int) Encoding);
if (ascii == 0)
ascii = 1;
// count: number of words contained in the script
int count = keywords.Sorted.Count;
// keywords: list of words contained in the script
foreach (object key in keywords.Protected.Keys)
{
keywords.Sorted[(int) key] = "";
}
// convert from a string to an array
StringBuilder sbKeywords = new StringBuilder("'");
foreach (string word in keywords.Sorted)
sbKeywords.Append(word + "|");
sbKeywords.Remove(sbKeywords.Length-1, 1);
string keywordsout = sbKeywords.ToString() + "'.split('|')";
string encode;
string inline = "c";
switch (Encoding)
{
case PackerEncoding.Mid:
encode = "function(c){return c.toString(36)}";
inline += ".toString(a)";
break;
case PackerEncoding.Normal:
encode = "function(c){return(c<a?\"\":e(parseInt(c/a)))+" +
"((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))}";
inline += ".toString(a)";
break;
case PackerEncoding.HighAscii:
encode = "function(c){return(c<a?\"\":e(c/a))+" +
"String.fromCharCode(c%a+161)}";
inline += ".toString(a)";
break;
default:
encode = "function(c){return c}";
break;
}
// decode: code snippet to speed up decoding
string decode = "";
if (fastDecode)
{
decode = "if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\\\w+'};c=1;}";
if (Encoding == PackerEncoding.HighAscii)
decode = decode.Replace("\\\\w", "[\\xa1-\\xff]");
else if (Encoding == PackerEncoding.Numeric)
decode = decode.Replace("e(c)", inline);
if (count == 0)
decode = decode.Replace("c=1", "c=0");
}
// boot function
string unpack = "function(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p;}";
Regex r;
if (fastDecode)
{
//insert the decoder
r = new Regex("\\{");
unpack = r.Replace(unpack, "{" + decode + ";", 1);
}
if (Encoding == PackerEncoding.HighAscii)
{
// get rid of the word-boundries for regexp matches
r = new Regex("'\\\\\\\\b'\\s*\\+|\\+\\s*'\\\\\\\\b'");
unpack = r.Replace(unpack, "");
}
if (Encoding == PackerEncoding.HighAscii || ascii > (int) PackerEncoding.Normal || fastDecode)
{
// insert the encode function
r = new Regex("\\{");
unpack = r.Replace(unpack, "{e=" + encode + ";", 1);
}
else
{
r = new Regex("e\\(c\\)");
unpack = r.Replace(unpack, inline);
}
// no need to pack the boot function since i've already done it
string _params = "" + packed + "," + ascii + "," + count + "," + keywordsout;
if (fastDecode)
{
//insert placeholders for the decoder
_params += ",0,{}";
}
// the whole thing
return "eval(" + unpack + "(" + _params + "))\n";
}
private string escape(string input)
{
Regex r = new Regex("([\\\\'])");
return r.Replace(input, "\\$1");
}
private EncodeMethod getEncoder(PackerEncoding encoding)
{
switch (encoding)
{
case PackerEncoding.Mid:
return new EncodeMethod(encode36);
case PackerEncoding.Normal:
return new EncodeMethod(encode62);
case PackerEncoding.HighAscii:
return new EncodeMethod(encode95);
default:
return new EncodeMethod(encode10);
}
}
private string encode10(int code)
{
return code.ToString();
}
//lookups seemed like the easiest way to do this since
// I don't know of an equivalent to .toString(36)
private static string lookup36 = "0123456789abcdefghijklmnopqrstuvwxyz";
private string encode36(int code)
{
string encoded = "";
int i = 0;
do
{
int digit = (code / (int) Math.Pow(36, i)) % 36;
encoded = lookup36[digit] + encoded;
code -= digit * (int) Math.Pow(36, i++);
} while (code > 0);
return encoded;
}
private static string lookup62 = lookup36 + "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private string encode62(int code)
{
string encoded = "";
int i = 0;
do
{
int digit = (code / (int) Math.Pow(62, i)) % 62;
encoded = lookup62[digit] + encoded;
code -= digit * (int) Math.Pow(62, i++);
} while (code > 0);
return encoded;
}
private static string lookup95 = "、¥ウЖ┆
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -