]> dev.renevier.net Git - syp.git/blob - openlayers/tools/jsmin.py
initial commit
[syp.git] / openlayers / tools / jsmin.py
1 #!/usr/bin/python
2
3 # This code is original from jsmin by Douglas Crockford, it was translated to
4 # Python by Baruch Even. The original code had the following copyright and
5 # license.
6 #
7 # /* jsmin.c
8 #    2007-01-08
9 #
10 # Copyright (c) 2002 Douglas Crockford  (www.crockford.com)
11 #
12 # Permission is hereby granted, free of charge, to any person obtaining a copy of
13 # this software and associated documentation files (the "Software"), to deal in
14 # the Software without restriction, including without limitation the rights to
15 # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
16 # of the Software, and to permit persons to whom the Software is furnished to do
17 # so, subject to the following conditions:
18 #
19 # The above copyright notice and this permission notice shall be included in all
20 # copies or substantial portions of the Software.
21 #
22 # The Software shall be used for Good, not Evil.
23 #
24 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 # SOFTWARE.
31 # */
32
33 from StringIO import StringIO
34
35 def jsmin(js):
36     ins = StringIO(js)
37     outs = StringIO()
38     JavascriptMinify().minify(ins, outs)
39     str = outs.getvalue()
40     if len(str) > 0 and str[0] == '\n':
41         str = str[1:]
42     return str
43
44 def isAlphanum(c):
45     """return true if the character is a letter, digit, underscore,
46            dollar sign, or non-ASCII character.
47     """
48     return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or
49             (c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or (c is not None and ord(c) > 126));
50
51 class UnterminatedComment(Exception):
52     pass
53
54 class UnterminatedStringLiteral(Exception):
55     pass
56
57 class UnterminatedRegularExpression(Exception):
58     pass
59
60 class JavascriptMinify(object):
61
62     def _outA(self):
63         self.outstream.write(self.theA)
64     def _outB(self):
65         self.outstream.write(self.theB)
66
67     def _get(self):
68         """return the next character from stdin. Watch out for lookahead. If
69            the character is a control character, translate it to a space or
70            linefeed.
71         """
72         c = self.theLookahead
73         self.theLookahead = None
74         if c == None:
75             c = self.instream.read(1)
76         if c >= ' ' or c == '\n':
77             return c
78         if c == '': # EOF
79             return '\000'
80         if c == '\r':
81             return '\n'
82         return ' '
83
84     def _peek(self):
85         self.theLookahead = self._get()
86         return self.theLookahead
87
88     def _next(self):
89         """get the next character, excluding comments. peek() is used to see
90            if a '/' is followed by a '/' or '*'.
91         """
92         c = self._get()
93         if c == '/':
94             p = self._peek()
95             if p == '/':
96                 c = self._get()
97                 while c > '\n':
98                     c = self._get()
99                 return c
100             if p == '*':
101                 c = self._get()
102                 while 1:
103                     c = self._get()
104                     if c == '*':
105                         if self._peek() == '/':
106                             self._get()
107                             return ' '
108                     if c == '\000':
109                         raise UnterminatedComment()
110
111         return c
112
113     def _action(self, action):
114         """do something! What you do is determined by the argument:
115            1   Output A. Copy B to A. Get the next B.
116            2   Copy B to A. Get the next B. (Delete A).
117            3   Get the next B. (Delete B).
118            action treats a string as a single character. Wow!
119            action recognizes a regular expression if it is preceded by ( or , or =.
120         """
121         if action <= 1:
122             self._outA()
123
124         if action <= 2:
125             self.theA = self.theB
126             if self.theA == "'" or self.theA == '"':
127                 while 1:
128                     self._outA()
129                     self.theA = self._get()
130                     if self.theA == self.theB:
131                         break
132                     if self.theA <= '\n':
133                         raise UnterminatedStringLiteral()
134                     if self.theA == '\\':
135                         self._outA()
136                         self.theA = self._get()
137
138
139         if action <= 3:
140             self.theB = self._next()
141             if self.theB == '/' and (self.theA == '(' or self.theA == ',' or
142                                      self.theA == '=' or self.theA == ':' or
143                                      self.theA == '[' or self.theA == '?' or
144                                      self.theA == '!' or self.theA == '&' or
145                                      self.theA == '|'):
146                 self._outA()
147                 self._outB()
148                 while 1:
149                     self.theA = self._get()
150                     if self.theA == '/':
151                         break
152                     elif self.theA == '\\':
153                         self._outA()
154                         self.theA = self._get()
155                     elif self.theA <= '\n':
156                         raise UnterminatedRegularExpression()
157                     self._outA()
158                 self.theB = self._next()
159
160
161     def _jsmin(self):
162         """Copy the input to the output, deleting the characters which are
163            insignificant to JavaScript. Comments will be removed. Tabs will be
164            replaced with spaces. Carriage returns will be replaced with linefeeds.
165            Most spaces and linefeeds will be removed.
166         """
167         self.theA = '\n'
168         self._action(3)
169
170         while self.theA != '\000':
171             if self.theA == ' ':
172                 if isAlphanum(self.theB):
173                     self._action(1)
174                 else:
175                     self._action(2)
176             elif self.theA == '\n':
177                 if self.theB in ['{', '[', '(', '+', '-']:
178                     self._action(1)
179                 elif self.theB == ' ':
180                     self._action(3)
181                 else:
182                     if isAlphanum(self.theB):
183                         self._action(1)
184                     else:
185                         self._action(2)
186             else:
187                 if self.theB == ' ':
188                     if isAlphanum(self.theA):
189                         self._action(1)
190                     else:
191                         self._action(3)
192                 elif self.theB == '\n':
193                     if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:
194                         self._action(1)
195                     else:
196                         if isAlphanum(self.theA):
197                             self._action(1)
198                         else:
199                             self._action(3)
200                 else:
201                     self._action(1)
202
203     def minify(self, instream, outstream):
204         self.instream = instream
205         self.outstream = outstream
206         self.theA = None
207         self.thaB = None
208         self.theLookahead = None
209
210         self._jsmin()
211         self.instream.close()
212
213 if __name__ == '__main__':
214     import sys
215     jsm = JavascriptMinify()
216     jsm.minify(sys.stdin, sys.stdout)