Per bug 22884, using "localhost" probably won't work on Windows. However, it's not...
[lhc/web/wiklou.git] / includes / JSMin.php
1 <?php
2 /**
3 * jsmin.php - PHP implementation of Douglas Crockford's JSMin.
4 *
5 * This is pretty much a direct port of jsmin.c to PHP with just a few
6 * PHP-specific performance tweaks. Also, whereas jsmin.c reads from stdin and
7 * outputs to stdout, this library accepts a string as input and returns another
8 * string as output.
9 *
10 * PHP 5 or higher is required.
11 *
12 * Permission is hereby granted to use this version of the library under the
13 * same terms as jsmin.c, which has the following license:
14 *
15 * --
16 * Copyright (c) 2002 Douglas Crockford (www.crockford.com)
17 *
18 * Permission is hereby granted, free of charge, to any person obtaining a copy of
19 * this software and associated documentation files (the "Software"), to deal in
20 * the Software without restriction, including without limitation the rights to
21 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
22 * of the Software, and to permit persons to whom the Software is furnished to do
23 * so, subject to the following conditions:
24 *
25 * The above copyright notice and this permission notice shall be included in all
26 * copies or substantial portions of the Software.
27 *
28 * The Software shall be used for Good, not Evil.
29 *
30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36 * SOFTWARE.
37 * --
38 *
39 * @file
40 * @author Ryan Grove <ryan@wonko.com>
41 * @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
42 * @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
43 * @license http://opensource.org/licenses/mit-license.php MIT License
44 * @version 1.1.1 (2008-03-02)
45 * @link http://code.google.com/p/jsmin-php/
46 */
47
48 class JSMin {
49 const ORD_LF = 10;
50 const ORD_SPACE = 32;
51
52 protected $a = '';
53 protected $b = '';
54 protected $input = '';
55 protected $inputIndex = 0;
56 protected $inputLength = 0;
57 protected $lookAhead = null;
58 protected $output = '';
59
60 // -- Public Static Methods --------------------------------------------------
61
62 public static function minify( $js ) {
63 wfProfileIn( __METHOD__ );
64 $jsmin = new JSMin( $js );
65 $ret = $jsmin->min();
66 wfProfileOut( __METHOD__ );
67 return $ret;
68 }
69
70 // -- Public Instance Methods ------------------------------------------------
71
72 public function __construct( $input ) {
73 $this->input = str_replace( "\r\n", "\n", $input );
74 $this->inputLength = strlen( $this->input );
75 }
76
77 // -- Protected Instance Methods ---------------------------------------------
78
79 protected function action( $d ) {
80 switch( $d ) {
81 case 1:
82 $this->output .= $this->a;
83
84 case 2:
85 $this->a = $this->b;
86
87 if ( $this->a === "'" || $this->a === '"' ) {
88 for ( ; ; ) {
89 $this->output .= $this->a;
90 $this->a = $this->get();
91
92 if ( $this->a === $this->b ) {
93 break;
94 }
95
96 if ( ord( $this->a ) <= self::ORD_LF ) {
97 throw new JSMinException( 'Unterminated string literal.' );
98 }
99
100 if ( $this->a === '\\' ) {
101 $this->output .= $this->a;
102 $this->a = $this->get();
103 }
104 }
105 }
106
107 case 3:
108 $this->b = $this->next();
109
110 if ( $this->b === '/' && (
111 $this->a === '(' || $this->a === ',' || $this->a === '=' ||
112 $this->a === ':' || $this->a === '[' || $this->a === '!' ||
113 $this->a === '&' || $this->a === '|' || $this->a === '?' ) ) {
114
115 $this->output .= $this->a . $this->b;
116
117 for ( ; ; ) {
118 $this->a = $this->get();
119
120 if ( $this->a === '/' ) {
121 break;
122 } elseif ( $this->a === '\\' ) {
123 $this->output .= $this->a;
124 $this->a = $this->get();
125 } elseif ( ord( $this->a ) <= self::ORD_LF ) {
126 throw new JSMinException( 'Unterminated regular expression ' .
127 'literal.' );
128 }
129
130 $this->output .= $this->a;
131 }
132
133 $this->b = $this->next();
134 }
135 }
136 }
137
138 protected function get() {
139 $c = $this->lookAhead;
140 $this->lookAhead = null;
141
142 if ( $c === null ) {
143 if ( $this->inputIndex < $this->inputLength ) {
144 $c = substr( $this->input, $this->inputIndex, 1 );
145 $this->inputIndex += 1;
146 } else {
147 $c = null;
148 }
149 }
150
151 if ( $c === "\r" ) {
152 return "\n";
153 }
154
155 if ( $c === null || $c === "\n" || ord( $c ) >= self::ORD_SPACE ) {
156 return $c;
157 }
158
159 return ' ';
160 }
161
162 protected function isAlphaNum( $c ) {
163 return ord( $c ) > 126 || $c === '\\' || preg_match( '/^[\w\$]$/', $c ) === 1;
164 }
165
166 protected function min() {
167 $this->a = "\n";
168 $this->action( 3 );
169
170 while ( $this->a !== null ) {
171 switch ( $this->a ) {
172 case ' ':
173 if ( $this->isAlphaNum( $this->b ) ) {
174 $this->action( 1 );
175 } else {
176 $this->action( 2 );
177 }
178 break;
179
180 case "\n":
181 switch ( $this->b ) {
182 case '{':
183 case '[':
184 case '(':
185 case '+':
186 case '-':
187 $this->action( 1 );
188 break;
189
190 case ' ':
191 $this->action( 3 );
192 break;
193
194 default:
195 if ( $this->isAlphaNum( $this->b ) ) {
196 $this->action( 1 );
197 }
198 else {
199 $this->action( 2 );
200 }
201 }
202 break;
203
204 default:
205 switch ( $this->b ) {
206 case ' ':
207 if ( $this->isAlphaNum( $this->a ) ) {
208 $this->action( 1 );
209 break;
210 }
211
212 $this->action( 3 );
213 break;
214
215 case "\n":
216 switch ( $this->a ) {
217 case '}':
218 case ']':
219 case ')':
220 case '+':
221 case '-':
222 case '"':
223 case "'":
224 $this->action( 1 );
225 break;
226
227 default:
228 if ( $this->isAlphaNum( $this->a ) ) {
229 $this->action( 1 );
230 }
231 else {
232 $this->action( 3 );
233 }
234 }
235 break;
236
237 default:
238 $this->action( 1 );
239 break;
240 }
241 }
242 }
243
244 return $this->output;
245 }
246
247 protected function next() {
248 $c = $this->get();
249
250 if ( $c === '/' ) {
251 switch( $this->peek() ) {
252 case '/':
253 for ( ; ; ) {
254 $c = $this->get();
255
256 if ( ord( $c ) <= self::ORD_LF ) {
257 return $c;
258 }
259 }
260
261 case '*':
262 $this->get();
263
264 for ( ; ; ) {
265 switch( $this->get() ) {
266 case '*':
267 if ( $this->peek() === '/' ) {
268 $this->get();
269 return ' ';
270 }
271 break;
272
273 case null:
274 throw new JSMinException( 'Unterminated comment.' );
275 }
276 }
277
278 default:
279 return $c;
280 }
281 }
282
283 return $c;
284 }
285
286 protected function peek() {
287 $this->lookAhead = $this->get();
288 return $this->lookAhead;
289 }
290 }
291
292 // -- Exceptions ---------------------------------------------------------------
293 class JSMinException extends Exception {}