2 Packer version 3.0 (beta 8) - copyright 2004-2007, Dean Edwards
\r
3 http://www.opensource.org/licenses/mit-license
\r
6 eval(base2.namespace);
\r
8 var IGNORE = RegGrp.IGNORE;
\r
13 var Packer = Base.extend({
\r
14 minify: function(script) {
\r
15 script = script.replace(Packer.CONTINUE, "");
\r
16 script = Packer.data.exec(script);
\r
17 script = Packer.whitespace.exec(script);
\r
18 script = Packer.clean.exec(script);
\r
22 pack: function(script, base62, shrink) {
\r
23 script = this.minify(script + "\n");
\r
24 if (shrink) script = this._shrinkVariables(script);
\r
25 if (base62) script = this._base62Encode(script);
\r
29 _base62Encode: function(script) {
\r
30 var words = new Words(script);
\r
31 var encode = function(word) {
\r
32 return words.fetch(word).encoded;
\r
35 /* build the packed script */
\r
37 var p = this._escape(script.replace(WORDS, encode));
\r
38 var a = Math.min(Math.max(words.count(), 2), 62);
\r
39 var c = words.count();
\r
41 var e = Packer["ENCODE" + (a > 10 ? a > 36 ? 62 : 36 : 10)];
\r
42 var r = a > 10 ? "e(c)" : "c";
\r
45 return format(Packer.UNPACK, p,a,c,k,e,r);
\r
48 _escape: function(script) {
\r
49 // single quotes wrap the final string so escape them
\r
50 // also escape new lines required by conditional comments
\r
51 return script.replace(/([\\'])/g, "\\$1").replace(/[\r\n]+/g, "\\n");
\r
54 _shrinkVariables: function(script) {
\r
55 // Windows Scripting Host cannot do regexp.test() on global regexps.
\r
56 var global = function(regexp) {
\r
57 // This function creates a global version of the passed regexp.
\r
58 return new RegExp(regexp.source, "g");
\r
61 var data = []; // encoded strings and regular expressions
\r
62 var REGEXP = /^[^'"]\//;
\r
63 var store = function(string) {
\r
64 var replacement = "#" + data.length;
\r
65 if (REGEXP.test(string)) {
\r
66 replacement = string.charAt(0) + replacement;
\r
67 string = string.slice(1);
\r
73 // Base52 encoding (a-Z)
\r
74 var encode52 = function(c) {
\r
75 return (c < 52 ? '' : arguments.callee(parseInt(c / 52))) +
\r
76 ((c = c % 52) > 25 ? String.fromCharCode(c + 39) : String.fromCharCode(c + 97));
\r
79 // identify blocks, particularly identify function blocks (which define scope)
\r
80 var BLOCK = /(function\s*[\w$]*\s*\(\s*([^\)]*)\s*\)\s*)?(\{([^{}]*)\})/;
\r
81 var VAR_ = /var\s+/g;
\r
82 var VAR_NAME = /var\s+[\w$]+/g;
\r
83 var COMMA = /\s*,\s*/;
\r
84 var blocks = []; // store program blocks (anything between braces {})
\r
85 // encoder for program blocks
\r
86 var encode = function(block, func, args) {
\r
87 if (func) { // the block is a function block
\r
89 // decode the function block (THIS IS THE IMPORTANT BIT)
\r
90 // We are retrieving all sub-blocks and will re-parse them in light
\r
91 // of newly shrunk variables
\r
92 block = decode(block);
\r
94 // create the list of variable and argument names
\r
95 var vars = match(block, VAR_NAME).join(",").replace(VAR_, "");
\r
96 var ids = Array2.combine(args.split(COMMA).concat(vars.split(COMMA)));
\r
98 // process each identifier
\r
99 var count = 0, shortId;
\r
100 forEach (ids, function(id) {
\r
102 if (id && id.length > 1) { // > 1 char
\r
104 // find the next free short name (check everything in the current scope)
\r
105 do shortId = encode52(count++);
\r
106 while (new RegExp("[^\\w$.]" + shortId + "[^\\w$:]").test(block));
\r
107 // replace the long name with the short name
\r
108 var reg = new RegExp("([^\\w$.])" + id + "([^\\w$:])");
\r
109 while (reg.test(block)) block = block.replace(global(reg), "$1" + shortId + "$2");
\r
110 var reg = new RegExp("([^{,])" + id + ":", "g");
\r
111 block = block.replace(reg, "$1" + shortId + ":");
\r
115 var replacement = "~" + blocks.length + "~";
\r
116 blocks.push(block);
\r
117 return replacement;
\r
120 // decoder for program blocks
\r
121 var ENCODED = /~(\d+)~/;
\r
122 var decode = function(script) {
\r
123 while (ENCODED.test(script)) {
\r
124 script = script.replace(global(ENCODED), function(match, index) {
\r
125 return blocks[index];
\r
131 // encode strings and regular expressions
\r
132 script = Packer.data.exec(script, store);
\r
134 // remove closures (this is for base2 namespaces only)
\r
135 script = script.replace(/new function\(_\)\s*\{/g, "{;#;");
\r
137 // encode blocks, as we encode we replace variable and argument names
\r
138 while (BLOCK.test(script)) {
\r
139 script = script.replace(global(BLOCK), encode);
\r
142 // put the blocks back
\r
143 script = decode(script);
\r
145 // put back the closure (for base2 namespaces only)
\r
146 script = script.replace(/\{;#;/g, "new function(_){");
\r
148 // put strings and regular expressions back
\r
149 script = script.replace(/#(\d+)/g, function(match, index) {
\r
150 return data[index];
\r
156 CONTINUE: /\\\r?\n/g,
\r
158 ENCODE10: "String",
\r
159 ENCODE36: "function(c){return c.toString(a)}",
\r
160 ENCODE62: "function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))}",
\r
162 UNPACK: "eval(function(p,a,c,k,e,r){e=%5;if(!''.replace(/^/,String)){while(c--)r[%6]=k[c]" +
\r
163 "||%6;k=[function(e){return r[e]}];e=function(){return'\\\\w+'};c=1};while(c--)if(k[c])p=p." +
\r
164 "replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p}('%1',%2,%3,'%4'.split('|'),0,{}))",
\r
167 this.data = reduce(this.data, function(data, replacement, expression) {
\r
168 data.store(this.javascript.exec(expression), replacement);
\r
170 }, new RegGrp, this);
\r
171 this.clean = this.data.union(this.clean);
\r
172 this.whitespace = this.data.union(this.whitespace);
\r
176 "\\(\\s*;\\s*;\\s*\\)": "(;;)", // for (;;) loops
\r
177 "throw[^};]+[};]": IGNORE, // a safari 1.3 bug
\r
178 ";+\\s*([};])": "$1"
\r
185 "CONDITIONAL": IGNORE, // conditional comments
\r
186 "(COMMENT1)\\n\\s*(REGEXP)?": "\n$3",
\r
187 "(COMMENT2)\\s*(REGEXP)?": " $3",
\r
188 "([\\[(\\^=,{}:;&|!*?])\\s*(REGEXP)": "$1$2"
\r
191 javascript: new RegGrp({
\r
192 COMMENT1: /(\/\/|;;;)[^\n]*/.source,
\r
193 COMMENT2: /\/\*[^*]*\*+([^\/][^*]*\*+)*\//.source,
\r
194 CONDITIONAL: /\/\*@|@\*\/|\/\/@[^\n]*\n/.source,
\r
195 REGEXP: /\/(\\[\/\\]|[^*\/])(\\.|[^\/\n\\])*\/[gim]*/.source,
\r
196 STRING1: /'(\\.|[^'\\])*'/.source,
\r
197 STRING2: /"(\\.|[^"\\])*"/.source
\r
201 "(\\d)\\s+(\\.\\s*[a-z\\$_\\[(])": "$1 $2", // http://dean.edwards.name/weblog/2007/04/packer3/#comment84066
\r
202 "([+-])\\s+([+-])": "$1 $2", // c = a++ +b;
\r
203 "\\b\\s+\\$\\s+\\b": " $ ", // var $ in
\r
204 "\\$\\s+\\b": "$ ", // object$ in
\r
205 "\\b\\s+\\$": " $", // return $object
\r
206 "\\b\\s+\\b": SPACE,
\r