Replace some french comments by english ones.
[euphorik.git] / tools / jsmin.js
1 #!/usr/bin/env rhino
2
3 /* jsmin.js - 2006-08-31
4 Author: Franck Marcia
5 This work is an adaptation of jsminc.c published by Douglas Crockford.
6 Permission is hereby granted to use the Javascript version under the same
7 conditions as the jsmin.c on which it is based.
8
9 jsmin.c
10 2006-05-04
11
12 Copyright (c) 2002 Douglas Crockford (www.crockford.com)
13
14 Permission is hereby granted, free of charge, to any person obtaining a copy of
15 this software and associated documentation files (the "Software"), to deal in
16 the Software without restriction, including without limitation the rights to
17 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
18 of the Software, and to permit persons to whom the Software is furnished to do
19 so, subject to the following conditions:
20
21 The above copyright notice and this permission notice shall be included in all
22 copies or substantial portions of the Software.
23
24 The Software shall be used for Good, not Evil.
25
26 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 SOFTWARE.
33
34 Update:
35 add level:
36 1: minimal, keep linefeeds if single
37 2: normal, the standard algorithm
38 3: agressive, remove any linefeed and doesn't take care of potential
39 missing semicolons (can be regressive)
40 store stats
41 jsmin.oldSize
42 jsmin.newSize
43 */
44
45 String.prototype.has = function(c) {
46 return this.indexOf(c) > -1;
47 };
48
49 function jsmin(comment, input, level) {
50
51 if (input === undefined) {
52 input = comment;
53 comment = '';
54 level = 2;
55 } else if (level === undefined || level < 1 || level > 3) {
56 level = 2;
57 }
58
59 if (comment.length > 0) {
60 comment += '\n';
61 }
62
63 var a = '',
64 b = '',
65 EOF = -1,
66 LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
67 DIGITS = '0123456789',
68 ALNUM = LETTERS + DIGITS + '_$\\',
69 theLookahead = EOF;
70
71
72 /* isAlphanum -- return true if the character is a letter, digit, underscore,
73 dollar sign, or non-ASCII character.
74 */
75
76 function isAlphanum(c) {
77 return c != EOF && (ALNUM.has(c) || c.charCodeAt(0) > 126);
78 }
79
80
81 /* get -- return the next character. Watch out for lookahead. If the
82 character is a control character, translate it to a space or
83 linefeed.
84 */
85
86 function get() {
87
88 var c = theLookahead;
89 if (get.i == get.l) {
90 return EOF;
91 }
92 theLookahead = EOF;
93 if (c == EOF) {
94 c = input.charAt(get.i);
95 ++get.i;
96 }
97 if (c >= ' ' || c == '\n') {
98 return c;
99 }
100 if (c == '\r') {
101 return '\n';
102 }
103 return ' ';
104 }
105
106 get.i = 0;
107 get.l = input.length;
108
109
110 /* peek -- get the next character without getting it.
111 */
112
113 function peek() {
114 theLookahead = get();
115 return theLookahead;
116 }
117
118
119 /* next -- get the next character, excluding comments. peek() is used to see
120 if a '/' is followed by a '/' or '*'.
121 */
122
123 function next() {
124
125 var c = get();
126
127 if (c === ";" && peek() === ";") {
128 for (;;) {
129 c = get();
130 if (c <= '\n') {
131 return c;
132 }
133 }
134 }
135
136 if (c == '/') {
137 switch (peek()) {
138 case '/':
139 for (;;) {
140 c = get();
141 if (c <= '\n') {
142 return c;
143 }
144 }
145 break;
146 case '*':
147 get();
148 for (;;) {
149 switch (get()) {
150 case '*':
151 if (peek() == '/') {
152 get();
153 return ' ';
154 }
155 break;
156 case EOF:
157 throw 'Error: Unterminated comment.';
158 }
159 }
160 break;
161 default:
162 return c;
163 }
164 }
165 return c;
166 }
167
168
169 /* action -- do something! What you do is determined by the argument:
170 1 Output A. Copy B to A. Get the next B.
171 2 Copy B to A. Get the next B. (Delete A).
172 3 Get the next B. (Delete B).
173 action treats a string as a single character. Wow!
174 action recognizes a regular expression if it is preceded by ( or , or =.
175 */
176
177 function action(d) {
178
179 var r = [];
180
181 if (d == 1) {
182 r.push(a);
183 }
184
185 if (d < 3) {
186 a = b;
187 if (a == '\'' || a == '"') {
188 for (;;) {
189 r.push(a);
190 a = get();
191 if (a == b) {
192 break;
193 }
194 if (a <= '\n') {
195 throw 'Error: unterminated string literal: ' + a;
196 }
197 if (a == '\\') {
198 r.push(a);
199 a = get();
200 }
201 }
202 }
203 }
204
205 b = next();
206
207 if (b == '/' && '(,=:[!&|'.has(a)) {
208 r.push(a);
209 r.push(b);
210 for (;;) {
211 a = get();
212 if (a == '/') {
213 break;
214 } else if (a =='\\') {
215 r.push(a);
216 a = get();
217 } else if (a <= '\n') {
218 throw 'Error: unterminated Regular Expression literal';
219 }
220 r.push(a);
221 }
222 b = next();
223 }
224
225 return r.join('');
226 }
227
228
229 /* m -- Copy the input to the output, deleting the characters which are
230 insignificant to JavaScript. Comments will be removed. Tabs will be
231 replaced with spaces. Carriage returns will be replaced with
232 linefeeds.
233 Most spaces and linefeeds will be removed.
234 */
235
236 function m() {
237
238 var r = [];
239 a = '\n';
240
241 r.push(action(3));
242
243 while (a != EOF) {
244 switch (a) {
245 case ' ':
246 if (isAlphanum(b)) {
247 r.push(action(1));
248 } else {
249 r.push(action(2));
250 }
251 break;
252 case '\n':
253 switch (b) {
254 case '{':
255 case '[':
256 case '(':
257 case '+':
258 case '-':
259 r.push(action(1));
260 break;
261 case ' ':
262 r.push(action(3));
263 break;
264 default:
265 if (isAlphanum(b)) {
266 r.push(action(1));
267 } else {
268 if (level == 1 && b != '\n') {
269 r.push(action(1));
270 } else {
271 r.push(action(2));
272 }
273 }
274 }
275 break;
276 default:
277 switch (b) {
278 case ' ':
279 if (isAlphanum(a)) {
280 r.push(action(1));
281 break;
282 }
283 r.push(action(3));
284 break;
285 case '\n':
286 if (level == 1 && a != '\n') {
287 r.push(action(1));
288 } else {
289 switch (a) {
290 case '}':
291 case ']':
292 case ')':
293 case '+':
294 case '-':
295 case '"':
296 case '\'':
297 if (level == 3) {
298 r.push(action(3));
299 } else {
300 r.push(action(1));
301 }
302 break;
303 default:
304 if (isAlphanum(a)) {
305 r.push(action(1));
306 } else {
307 r.push(action(3));
308 }
309 }
310 }
311 break;
312 default:
313 r.push(action(1));
314 break;
315 }
316 }
317 }
318
319 return r.join('');
320 }
321
322 jsmin.oldSize = input.length;
323 ret = m(input);
324 jsmin.newSize = ret.length;
325
326 return comment + ret;
327
328 }
329
330 importPackage(java.io);
331 (function (a) {
332 // in is a reserved javascript word, so we need to use [] for access
333 var readingIn = new BufferedReader(new InputStreamReader(java.lang.System["in"]));
334 var sInput = "";
335 var str = "";
336 while(str != null) {
337 sInput += str + '\n';
338
339 str = readingIn.readLine();
340 }
341 print(jsmin(sInput));
342 })(arguments);