1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340 |
<?php // $Id$
/**
* Javascript Combiner, by Nick Fisher. http://www.spadgos.com/
* Uses Nicolas Martin's port of Dean Edward's Javascript Packer
* http://dean.edwards.name/download/#packer
*
* Command line tool to combine all the javascript files in a directory into
* one file which is then packed to reduce download size and HTTP requests.
*
* Usage:
* php c:\path\to\combine.php c:\path\to\JSFolder\
* or:
* php /path/to/combine.php /path/to/JSFolder/
*
* Relative paths are OK, too.
*
* If there is a file in the given folder with the name "packer.ini", this file
* will be read and used to create custom packages. By default, this file is
* required. To just pack all the files in the folder using default settings,
* then use an extra parameter in the call to this file:
* php c:\path\to\combine.php c:\path\to\JSFolder\ 1
*
* If this extra flag is left out, or set to a falsey value (eg: 0), then the
* combiner will require a packer.ini file.
*
* Format of the packer.ini file:
* [outputFile.js]
* input_one.js
* input_two.js
* input_three.js
*
* [outputFileTwo.js]
* input_four.js
* input_five.js
*
* Files not mentioned in the packer.ini file won't be packed. If no [headings]
* are given (ie: packer.ini is just a list of input filenames), they will all
* be packed into "combined.pack.js"
*
* Files from multiple folders can be combined this way. Just specify the path
* as relative to the .ini file. eg:
* [myFile.js]
* src/myFile.js
* [packed/myFile.js] ; the output can be in a different folder.
* src/myFile.js
*
* Additional options can be specified per output file. Options are written as:
* variable = value
* With one variable per line, and whitespace on either side of the = being ignored.
* A value of "no", "false", "null" or "off" will be converted to false if the
* option is a flag (ie: a boolean).
*
* Option name Type Default Description
* ----------------------------------------------------------------------------
* include_list flag false Include a list of the files which were
* combined to make this output file.
* include_versions flag true If include_list is on, this will scan
* the first few lines of each input file
* for an SVN Id keyword and insert the
* revision number after the filename in
* the include_list. eg: "myFile.js (r133)"
* compress value 95 0,10,62,95 or 'no', 'None', 'Numeric',
* 'Normal', 'High ASCII'. This value will
* be passed through to the JSPacker class.
* See that for more information.
* firebug_check flag false Searches JS files for a reference to the
* Firebug 'console' object. A warning will
* be printed if it is found. This would be
* used to make sure you haven't left it in
* before deployment to the general public.
* How cool are the same size line lengths?
*
* If a file has the string /*nopack* / (but without the space between the last
* two chars which was added so this comment wouldn't be broken) then that file
* is merely appended to the other, packed files at the end. This is used if a
* particular file is already packed and causing the packer to error (usually a
* "Array to string" error). Note that nopack files are placed AFTER packed the
* packed files - which could lead to strange errors if you were relying on the
* order of the included files. Alternatively, change your compression level
* down to 62 or even 10.
* The best option in these cases though is to not pack any source files in the
* first place, and only use the full, non-minified versions.
*/
require 'class.JavaScriptPacker.php';
define("NL", "\r\n");
if ($argc >= 2) {
$folder = $argv[1];
if (!is_dir($folder)) die ("You must specify a folder name.");
if (substr($folder, -1) != "\\" && substr($folder, -1) != "/" ) $folder .= "/";
$optionalINI = !empty($argv[2]); // if this is false, then packer.ini is required.
} else {
echo 'You must specify a folder name.' . "\n";
die();
}
if (function_exists('mb_detect_encoding')) {
function is_utf8($str) {
return mb_detect_encoding($str,"UTF-8, ISO-8859-1, GBK") !== "UTF-8";
}
} else {
function is_utf8($str) {
$c=0; $b=0;
$bits=0;
$len=strlen($str);
for($i=0; $i<$len; $i++){
$c=ord($str[$i]);
if($c > 128){
if(($c >= 254)) return false;
elseif($c >= 252) $bits=6;
elseif($c >= 248) $bits=5;
elseif($c >= 240) $bits=4;
elseif($c >= 224) $bits=3;
elseif($c >= 192) $bits=2;
else return false;
if(($i+$bits) > $len) return false;
while($bits > 1){
$i++;
$b=ord($str[$i]);
if($b < 128 || $b > 191) return false;
$bits--;
}
}
}
return true;
}
}
$files = array();
$options = array();
$currFile = "combined.pack.js";
if (file_exists($folder . 'packer.ini')) {
$lines = file($folder . "packer.ini");
foreach ($lines as $line) {
// strip leading/trailing whitespace and everything after a semicolon (the comment character)
$line = preg_replace("/;.*/", "", $line);
$line = trim(preg_replace("/^\\s*([^;\\s]*)\\s*(;.*)?$/", "\\1", $line));
if (!$line) continue;
if (preg_match("/^\\[([^\\]]+)\\]\$/", $line, $matches)) {
// headings are defined [like this]
$currFile = $matches[1];
$files[$currFile] = array();
$options[$currFile] = array();
} else if (preg_match("/^(.*?)=(.*)$/", $line, $matches)) {
// options are defined like=this
$var = trim($matches[1]);
$val = trim($matches[2]);
$options[$currFile][$var] = $val;
} else if (strpos($line, "*") !== false || strpos($line, "?") !== false) {
// a wildcard has been specified
$matches = glob($line);
foreach ($matches as $m) {
if (is_file($m)) {
$files[$currFile][] = $m;
}
}
} else {
$files[$currFile][] = $line;
}
}
} else if (!$optionalINI) {
echo "Can't find packer.ini at " . $folder . "packer.ini";
die();
} else {
$temp = glob($folder . "*.js");
$files[$currFile] = $options[$currFile] = array();
foreach ($temp as $src) {
if ((strpos($src, ".pack.js") === false) && (substr($src, -3) == ".js")) {
$files[$currFile][] = $src;
}
}
}
if (count($files, COUNT_RECURSIVE) == 0) die("No files found.");
$t1 = microtime(true);
foreach ($files as $outputFile => $inputFiles) {
echo "Output file: $outputFile\n";
$myOptions = $options[$outputFile];
$inputFiles = array_unique($inputFiles);
$compression = getOptionValue($myOptions, 'compress', 95);
echo "Compression: " . $compression . "\n";
$firebugCheck = getOptionFlag($myOptions, 'firebug_check', false);
$combined = ''; // scripts which are going to be packed.
$nopack = ''; // scripts which are not to be packed. they get appended **after** the other scripts are packed.
foreach ($inputFiles as $src) {
if (!file_exists($src)) {
echo "Can't find file: $src\n";
} else {
if (preg_match("/\\.css\\.php$/i", $src)) {
// we have a CSS+PHP file.
$script = parseCSS($src);
} else {
$script = file_get_contents($src);
}
//$script = convToUtf8($script);
$addToVar = 'combined';
echo "Adding " . basename($src);
if (strpos($script, "/"."*nopack*"."/") !== false) {
echo " *nopack*";
$addToVar = 'nopack';
}
echo " (" . strlen($script) . " bytes)\n";
if ($firebugCheck) {
if (($pos = strpos($script, 'console.')) !== false) {
echo "WARNING: reference to console found:\n"
. substr($script, $pos - 20, $pos + 40)
. "\n\n";
}
}
$$addToVar .= NL . $script;
}
}
$orig = strlen($combined) + strlen($nopack);
if ($orig == 0) {
echo "No content.\n";
continue;
}
if ($compression != "0" && !asBool($compression)) {
$packed = $combined . $nopack;
} else if (preg_match("/\\.css$/i", $outputFile)) {
// output file is CSS. we obviously don't want to do the JSPacker.
$packed = compressCSS($combined) . $nopack;
} else {
echo "Compressing at rate: " . $compression;
$packer = new JavaScriptPacker($combined, $compression, true, false);
$packed = $packer->pack() . $nopack;
$packed = str_replace(array("\r\n", "\r", "\n"), NL, $packed);
}
if (getOptionFlag($myOptions, 'include_list', false)) {
$fileList = $inputFiles;
if (getOptionFlag($myOptions, 'include_versions', false)) {
$temp = $fileList;
foreach ($temp as $i => $filename) {
$version = getSVNVersion($filename);
if ($version) {
$temp[$i] = $temp[$i] . " (r$version)";
}
}
$fileList = $temp;
}
$packed = "/* Included files:" . NL . implode(NL, $fileList) . NL ."*/" . NL . $packed;
}
file_put_contents($outputFile, $packed);
$packedSize = strlen($packed);
echo "\n" . basename($outputFile) . ': ' . $orig . " bytes => " . $packedSize . " bytes (" . number_format(100 - ($packedSize / $orig) * 100, 1) . "% saving)\n\n";
}
$t2 = microtime(true);
$time = sprintf('%.4f', ($t2 - $t1));
$numFiles = (count($files, COUNT_RECURSIVE) - count($files));
echo "==========\nCompleted in {$time}s. Packed " . $numFiles . " file" . ($numFiles == 1 ? "" : "s");
//////////////////////////////
//////////////////////////////
//////////////////////////////
function asBool($val) {
$val = strtolower($val);
if (in_array($val, array("no", "false", "off", "null"))) return false;
else return !!$val;
}
function getOptionValue($options, $var, $default = false) {
if (isset($options[$var])) {
return $options[$var];
} else {
return $default;
}
}
function getOptionFlag($options, $var, $default = false) {
return asBool(getOptionValue($options, $var, $default));
}
// search the first 10 lines of a
function getSVNVersion($file) {
if ($fh = @fopen($file, 'r')) {
for ($n = 0; !feof($fh) && $n < 10; ++$n) {
$line = fgets($fh);
if (preg_match("@\\\$".'I'.'d:' . "\\s\\S+\\s(\\d+)\\s\\S+\\s\\S+\\s\\S+\\s\\\$@", $line, $matches)) {
fclose($fh);
return intval($matches[1]);
}
}
fclose($fh);
} else {
echo "Can't open file $file.\n";
}
return false;
}
function compressCSS($css) {
return
preg_replace(
array('@\s\s+@','@(\w+:)\s*([\w\s,#]+;?)@'),
array(' ','$1$2'),
str_replace(
array("\r","\n","\t",' {','} ',';}'),
array('','','','{','}','}'),
preg_replace('@/\*[^*]*\*+([^/][^*]*\*+)*/@', '', $css)
)
)
;
}
// converts all the $variablenames strewn throughout the .css.php file to their equivalent
// this saves you having to type <?php echo $myColour; ? > in your css.
function parseCSS($filename) {
ob_start();
include($filename);
$newVars = get_defined_vars();
$file = compressCSS(ob_get_clean());
return eval('return "' . addslashes($file) . '";');
}
function convToUtf8($str) {
if(!is_utf8($str)) {
return iconv("gbk","utf-8",$str);
} else {
return $str;
}
} |