Added JavaScript minification feature (new function t3lib_div::minifyJavaScript)
[Packages/TYPO3.CMS.git] / typo3 / contrib / jsmin / 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 * @package JSMin
40 * @author Ryan Grove <ryan@wonko.com>
41 * @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
42 * @copyright 2007 Ryan Grove <ryan@wonko.com> (PHP port)
43 * @license http://opensource.org/licenses/mit-license.php MIT License
44 * @version 1.1.0 (2007-06-01)
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 = array();
59
60 // -- Public Static Methods --------------------------------------------------
61
62 public static function minify($js) {
63 $jsmin = new JSMin($js);
64 return $jsmin->min();
65 }
66
67 // -- Public Instance Methods ------------------------------------------------
68
69 public function __construct($input) {
70 $this->input = str_replace("\r\n", "\n", $input);
71 $this->inputLength = strlen($this->input);
72 }
73
74 // -- Protected Instance Methods ---------------------------------------------
75
76 protected function action($d) {
77 switch($d) {
78 case 1:
79 $this->output[] = $this->a;
80
81 case 2:
82 $this->a = $this->b;
83
84 if ($this->a === "'" || $this->a === '"') {
85 for (;;) {
86 $this->output[] = $this->a;
87 $this->a = $this->get();
88
89 if ($this->a === $this->b) {
90 break;
91 }
92
93 if (ord($this->a) <= self::ORD_LF) {
94 throw new JSMinException('Unterminated string literal.');
95 }
96
97 if ($this->a === '\\') {
98 $this->output[] = $this->a;
99 $this->a = $this->get();
100 }
101 }
102 }
103
104 case 3:
105 $this->b = $this->next();
106
107 if ($this->b === '/' && (
108 $this->a === '(' || $this->a === ',' || $this->a === '=' ||
109 $this->a === ':' || $this->a === '[' || $this->a === '!' ||
110 $this->a === '&' || $this->a === '|' || $this->a === '?')) {
111
112 $this->output[] = $this->a;
113 $this->output[] = $this->b;
114
115 for (;;) {
116 $this->a = $this->get();
117
118 if ($this->a === '/') {
119 break;
120 }
121 elseif ($this->a === '\\') {
122 $this->output[] = $this->a;
123 $this->a = $this->get();
124 }
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 = $this->input[$this->inputIndex];
145 $this->inputIndex += 1;
146 }
147 else {
148 $c = null;
149 }
150 }
151
152 if ($c === "\r") {
153 return "\n";
154 }
155
156 if ($c === null || $c === "\n" || ord($c) >= self::ORD_SPACE) {
157 return $c;
158 }
159
160 return ' ';
161 }
162
163 protected function isAlphaNum($c) {
164 return ord($c) > 126 || $c === '\\' || preg_match('/^[\w\$]$/', $c) === 1;
165 }
166
167 protected function min() {
168 $this->a = "\n";
169 $this->action(3);
170
171 while ($this->a !== null) {
172 switch ($this->a) {
173 case ' ':
174 if ($this->isAlphaNum($this->b)) {
175 $this->action(1);
176 }
177 else {
178 $this->action(2);
179 }
180 break;
181
182 case "\n":
183 switch ($this->b) {
184 case '{':
185 case '[':
186 case '(':
187 case '+':
188 case '-':
189 $this->action(1);
190 break;
191
192 case ' ':
193 $this->action(3);
194 break;
195
196 default:
197 if ($this->isAlphaNum($this->b)) {
198 $this->action(1);
199 }
200 else {
201 $this->action(2);
202 }
203 }
204 break;
205
206 default:
207 switch ($this->b) {
208 case ' ':
209 if ($this->isAlphaNum($this->a)) {
210 $this->action(1);
211 break;
212 }
213
214 $this->action(3);
215 break;
216
217 case "\n":
218 switch ($this->a) {
219 case '}':
220 case ']':
221 case ')':
222 case '+':
223 case '-':
224 case '"':
225 case "'":
226 $this->action(1);
227 break;
228
229 default:
230 if ($this->isAlphaNum($this->a)) {
231 $this->action(1);
232 }
233 else {
234 $this->action(3);
235 }
236 }
237 break;
238
239 default:
240 $this->action(1);
241 break;
242 }
243 }
244 }
245
246 return implode('', $this->output);
247 }
248
249 protected function next() {
250 $c = $this->get();
251
252 if ($c === '/') {
253 switch($this->peek()) {
254 case '/':
255 for (;;) {
256 $c = $this->get();
257
258 if (ord($c) <= self::ORD_LF) {
259 return $c;
260 }
261 }
262
263 case '*':
264 $this->get();
265
266 for (;;) {
267 switch($this->get()) {
268 case '*':
269 if ($this->peek() === '/') {
270 $this->get();
271 return ' ';
272 }
273 break;
274
275 case null:
276 throw new JSMinException('Unterminated comment.');
277 }
278 }
279
280 default:
281 return $c;
282 }
283 }
284
285 return $c;
286 }
287
288 protected function peek() {
289 $this->lookAhead = $this->get();
290 return $this->lookAhead;
291 }
292 }
293
294 // -- Exceptions ---------------------------------------------------------------
295 class JSMinException extends Exception {}
296 ?>