| Index: tools/jsmin.py
|
| diff --git a/tools/jsmin.py b/tools/jsmin.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ae7581413a9bd5932e08bd1501b7b222d6799702
|
| --- /dev/null
|
| +++ b/tools/jsmin.py
|
| @@ -0,0 +1,218 @@
|
| +#!/usr/bin/python
|
| +
|
| +# This code is original from jsmin by Douglas Crockford, it was translated to
|
| +# Python by Baruch Even. The original code had the following copyright and
|
| +# license.
|
| +#
|
| +# /* jsmin.c
|
| +# 2007-05-22
|
| +#
|
| +# Copyright (c) 2002 Douglas Crockford (www.crockford.com)
|
| +#
|
| +# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
| +# this software and associated documentation files (the "Software"), to deal in
|
| +# the Software without restriction, including without limitation the rights to
|
| +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
| +# of the Software, and to permit persons to whom the Software is furnished to do
|
| +# so, subject to the following conditions:
|
| +#
|
| +# The above copyright notice and this permission notice shall be included in all
|
| +# copies or substantial portions of the Software.
|
| +#
|
| +# The Software shall be used for Good, not Evil.
|
| +#
|
| +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| +# SOFTWARE.
|
| +# */
|
| +
|
| +from StringIO import StringIO
|
| +
|
| +def jsmin(js):
|
| + ins = StringIO(js)
|
| + outs = StringIO()
|
| + JavascriptMinify().minify(ins, outs)
|
| + str = outs.getvalue()
|
| + if len(str) > 0 and str[0] == '\n':
|
| + str = str[1:]
|
| + return str
|
| +
|
| +def isAlphanum(c):
|
| + """return true if the character is a letter, digit, underscore,
|
| + dollar sign, or non-ASCII character.
|
| + """
|
| + return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or
|
| + (c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or (c is not None and ord(c) > 126));
|
| +
|
| +class UnterminatedComment(Exception):
|
| + pass
|
| +
|
| +class UnterminatedStringLiteral(Exception):
|
| + pass
|
| +
|
| +class UnterminatedRegularExpression(Exception):
|
| + pass
|
| +
|
| +class JavascriptMinify(object):
|
| +
|
| + def _outA(self):
|
| + self.outstream.write(self.theA)
|
| + def _outB(self):
|
| + self.outstream.write(self.theB)
|
| +
|
| + def _get(self):
|
| + """return the next character from stdin. Watch out for lookahead. If
|
| + the character is a control character, translate it to a space or
|
| + linefeed.
|
| + """
|
| + c = self.theLookahead
|
| + self.theLookahead = None
|
| + if c == None:
|
| + c = self.instream.read(1)
|
| + if c >= ' ' or c == '\n':
|
| + return c
|
| + if c == '': # EOF
|
| + return '\000'
|
| + if c == '\r':
|
| + return '\n'
|
| + return ' '
|
| +
|
| + def _peek(self):
|
| + self.theLookahead = self._get()
|
| + return self.theLookahead
|
| +
|
| + def _next(self):
|
| + """get the next character, excluding comments. peek() is used to see
|
| + if an unescaped '/' is followed by a '/' or '*'.
|
| + """
|
| + c = self._get()
|
| + if c == '/' and self.theA != '\\':
|
| + p = self._peek()
|
| + if p == '/':
|
| + c = self._get()
|
| + while c > '\n':
|
| + c = self._get()
|
| + return c
|
| + if p == '*':
|
| + c = self._get()
|
| + while 1:
|
| + c = self._get()
|
| + if c == '*':
|
| + if self._peek() == '/':
|
| + self._get()
|
| + return ' '
|
| + if c == '\000':
|
| + raise UnterminatedComment()
|
| +
|
| + return c
|
| +
|
| + def _action(self, action):
|
| + """do something! What you do is determined by the argument:
|
| + 1 Output A. Copy B to A. Get the next B.
|
| + 2 Copy B to A. Get the next B. (Delete A).
|
| + 3 Get the next B. (Delete B).
|
| + action treats a string as a single character. Wow!
|
| + action recognizes a regular expression if it is preceded by ( or , or =.
|
| + """
|
| + if action <= 1:
|
| + self._outA()
|
| +
|
| + if action <= 2:
|
| + self.theA = self.theB
|
| + if self.theA == "'" or self.theA == '"':
|
| + while 1:
|
| + self._outA()
|
| + self.theA = self._get()
|
| + if self.theA == self.theB:
|
| + break
|
| + if self.theA <= '\n':
|
| + raise UnterminatedStringLiteral()
|
| + if self.theA == '\\':
|
| + self._outA()
|
| + self.theA = self._get()
|
| +
|
| +
|
| + if action <= 3:
|
| + self.theB = self._next()
|
| + if self.theB == '/' and (self.theA == '(' or self.theA == ',' or
|
| + self.theA == '=' or self.theA == ':' or
|
| + self.theA == '[' or self.theA == '?' or
|
| + self.theA == '!' or self.theA == '&' or
|
| + self.theA == '|' or self.theA == ';' or
|
| + self.theA == '{' or self.theA == '}' or
|
| + self.theA == '\n'):
|
| + self._outA()
|
| + self._outB()
|
| + while 1:
|
| + self.theA = self._get()
|
| + if self.theA == '/':
|
| + break
|
| + elif self.theA == '\\':
|
| + self._outA()
|
| + self.theA = self._get()
|
| + elif self.theA <= '\n':
|
| + raise UnterminatedRegularExpression()
|
| + self._outA()
|
| + self.theB = self._next()
|
| +
|
| +
|
| + def _jsmin(self):
|
| + """Copy the input to the output, deleting the characters which are
|
| + insignificant to JavaScript. Comments will be removed. Tabs will be
|
| + replaced with spaces. Carriage returns will be replaced with linefeeds.
|
| + Most spaces and linefeeds will be removed.
|
| + """
|
| + self.theA = '\n'
|
| + self._action(3)
|
| +
|
| + while self.theA != '\000':
|
| + if self.theA == ' ':
|
| + if isAlphanum(self.theB):
|
| + self._action(1)
|
| + else:
|
| + self._action(2)
|
| + elif self.theA == '\n':
|
| + if self.theB in ['{', '[', '(', '+', '-']:
|
| + self._action(1)
|
| + elif self.theB == ' ':
|
| + self._action(3)
|
| + else:
|
| + if isAlphanum(self.theB):
|
| + self._action(1)
|
| + else:
|
| + self._action(2)
|
| + else:
|
| + if self.theB == ' ':
|
| + if isAlphanum(self.theA):
|
| + self._action(1)
|
| + else:
|
| + self._action(3)
|
| + elif self.theB == '\n':
|
| + if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:
|
| + self._action(1)
|
| + else:
|
| + if isAlphanum(self.theA):
|
| + self._action(1)
|
| + else:
|
| + self._action(3)
|
| + else:
|
| + self._action(1)
|
| +
|
| + def minify(self, instream, outstream):
|
| + self.instream = instream
|
| + self.outstream = outstream
|
| + self.theA = '\n'
|
| + self.theB = None
|
| + self.theLookahead = None
|
| +
|
| + self._jsmin()
|
| + self.instream.close()
|
| +
|
| +if __name__ == '__main__':
|
| + import sys
|
| + jsm = JavascriptMinify()
|
| + jsm.minify(sys.stdin, sys.stdout)
|
|
|