jsmin.py

Go to the documentation of this file.
00001 #http://www.crockford.com/javascript/jsmin.html
00002 from StringIO import StringIO
00003 
00004 def jsmin(js):
00005     ins = StringIO(js)
00006     outs = StringIO()
00007     JavascriptMinify().minify(ins, outs)
00008     str = outs.getvalue()
00009     if len(str) > 0 and str[0] == '\n':
00010         str = str[1:]
00011     return str
00012 
00013 ##
00014 # return true if the character is a letter, digit, underscore,
00015 #            dollar sign, or non-ASCII character.
00016 #     
00017 def isAlphanum(c):
00018     return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or
00019             (c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or (c is not None and ord(c) > 126));
00020 
00021 class UnterminatedComment(Exception):
00022     pass
00023 
00024 class UnterminatedStringLiteral(Exception):
00025     pass
00026 
00027 class UnterminatedRegularExpression(Exception):
00028     pass
00029 
00030 class JavascriptMinify(object):
00031 
00032     def _outA(self):
00033         self.outstream.write(self.theA)
00034     def _outB(self):
00035         self.outstream.write(self.theB)
00036 
00037     ##
00038     # return the next character from stdin. Watch out for lookahead. If
00039     #            the character is a control character, translate it to a space or
00040     #            linefeed.
00041     #         
00042     def _get(self):
00043         c = self.theLookahead
00044         self.theLookahead = None
00045         if c == None:
00046             c = self.instream.read(1)
00047         if c >= ' ' or c == '\n':
00048             return c
00049         if c == '': # EOF
00050             return '\000'
00051         if c == '\r':
00052             return '\n'
00053         return ' '
00054 
00055     def _peek(self):
00056         self.theLookahead = self._get()
00057         return self.theLookahead
00058 
00059     ##
00060     # get the next character, excluding comments. peek() is used to see
00061     #            if an unescaped '/' is followed by a '/' or '*'.
00062     #         
00063     def _next(self):
00064         c = self._get()
00065         if c == '/' and self.theA != '\\':
00066             p = self._peek()
00067             if p == '/':
00068                 c = self._get()
00069                 while c > '\n':
00070                     c = self._get()
00071                 return c
00072             if p == '*':
00073                 c = self._get()
00074                 while 1:
00075                     c = self._get()
00076                     if c == '*':
00077                         if self._peek() == '/':
00078                             self._get()
00079                             return ' '
00080                     if c == '\000':
00081                         raise UnterminatedComment()
00082 
00083         return c
00084 
00085     ##
00086     # do something! What you do is determined by the argument:
00087     #            1   Output A. Copy B to A. Get the next B.
00088     #            2   Copy B to A. Get the next B. (Delete A).
00089     #            3   Get the next B. (Delete B).
00090     #            action treats a string as a single character. Wow!
00091     #            action recognizes a regular expression if it is preceded by ( or , or =.
00092     #         
00093     def _action(self, action):
00094         if action <= 1:
00095             self._outA()
00096 
00097         if action <= 2:
00098             self.theA = self.theB
00099             if self.theA == "'" or self.theA == '"':
00100                 while 1:
00101                     self._outA()
00102                     self.theA = self._get()
00103                     if self.theA == self.theB:
00104                         break
00105                     if self.theA <= '\n':
00106                         raise UnterminatedStringLiteral()
00107                     if self.theA == '\\':
00108                         self._outA()
00109                         self.theA = self._get()
00110 
00111 
00112         if action <= 3:
00113             self.theB = self._next()
00114             if self.theB == '/' and (self.theA == '(' or self.theA == ',' or
00115                                      self.theA == '=' or self.theA == ':' or
00116                                      self.theA == '[' or self.theA == '?' or
00117                                      self.theA == '!' or self.theA == '&' or
00118                                      self.theA == '|' or self.theA == ';' or
00119                                      self.theA == '{' or self.theA == '}' or
00120                                      self.theA == '\n'):
00121                 self._outA()
00122                 self._outB()
00123                 while 1:
00124                     self.theA = self._get()
00125                     if self.theA == '/':
00126                         break
00127                     elif self.theA == '\\':
00128                         self._outA()
00129                         self.theA = self._get()
00130                     elif self.theA <= '\n':
00131                         raise UnterminatedRegularExpression()
00132                     self._outA()
00133                 self.theB = self._next()
00134 
00135 
00136     ##
00137     # Copy the input to the output, deleting the characters which are
00138     #            insignificant to JavaScript. Comments will be removed. Tabs will be
00139     #            replaced with spaces. Carriage returns will be replaced with linefeeds.
00140     #            Most spaces and linefeeds will be removed.
00141     #         
00142     def _jsmin(self):
00143         self.theA = '\n'
00144         self._action(3)
00145 
00146         while self.theA != '\000':
00147             if self.theA == ' ':
00148                 if isAlphanum(self.theB):
00149                     self._action(1)
00150                 else:
00151                     self._action(2)
00152             elif self.theA == '\n':
00153                 if self.theB in ['{', '[', '(', '+', '-']:
00154                     self._action(1)
00155                 elif self.theB == ' ':
00156                     self._action(3)
00157                 else:
00158                     if isAlphanum(self.theB):
00159                         self._action(1)
00160                     else:
00161                         self._action(2)
00162             else:
00163                 if self.theB == ' ':
00164                     if isAlphanum(self.theA):
00165                         self._action(1)
00166                     else:
00167                         self._action(3)
00168                 elif self.theB == '\n':
00169                     if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:
00170                         self._action(1)
00171                     else:
00172                         if isAlphanum(self.theA):
00173                             self._action(1)
00174                         else:
00175                             self._action(3)
00176                 else:
00177                     self._action(1)
00178 
00179     def minify(self, instream, outstream):
00180         self.instream = instream
00181         self.outstream = outstream
00182         self.theA = '\n'
00183         self.theB = None
00184         self.theLookahead = None
00185 
00186         self._jsmin()
00187         self.instream.close()
00188 
00189 if (__name__ == '__main__'):
00190     import sys
00191     jsm = JavascriptMinify()
00192     jsm.minify(sys.stdin, sys.stdout)
00193     
00194 

© Copyright 2008-2009 Vyper Logix Corp., All Right Reserved; If you reference this document or any part of this document you must use the citation verbatim (including the link) "© Copyright 2008-2009 Vyper Logix Corp., All Right Reserved."

Notice: This source code contained in this document is NOT open source and is NOT being distributed as open source.

122,241 lines of code and growing...