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...