|  | #!/usr/bin/env python | 
|  | # | 
|  | # Copyright 2011 - 2013 | 
|  | # Andr\xe9 Malo or his licensors, as applicable | 
|  | # | 
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | # you may not use this file except in compliance with the License. | 
|  | # You may obtain a copy of the License at | 
|  | # | 
|  | #     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | # | 
|  | # Unless required by applicable law or agreed to in writing, software | 
|  | # distributed under the License is distributed on an "AS IS" BASIS, | 
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | # See the License for the specific language governing permissions and | 
|  | # limitations under the License. | 
|  | r""" | 
|  | ===================== | 
|  | Javascript Minifier | 
|  | ===================== | 
|  |  | 
|  | rJSmin is a javascript minifier written in python. | 
|  |  | 
|  | The minifier is based on the semantics of `jsmin.c by Douglas Crockford`_\. | 
|  |  | 
|  | The module is a re-implementation aiming for speed, so it can be used at | 
|  | runtime (rather than during a preprocessing step). Usually it produces the | 
|  | same results as the original ``jsmin.c``. It differs in the following ways: | 
|  |  | 
|  | - there is no error detection: unterminated string, regex and comment | 
|  | literals are treated as regular javascript code and minified as such. | 
|  | - Control characters inside string and regex literals are left untouched; they | 
|  | are not converted to spaces (nor to \n) | 
|  | - Newline characters are not allowed inside string and regex literals, except | 
|  | for line continuations in string literals (ECMA-5). | 
|  | - "return /regex/" is recognized correctly. | 
|  | - "+ +" and "- -" sequences are not collapsed to '++' or '--' | 
|  | - Newlines before ! operators are removed more sensibly | 
|  | - rJSmin does not handle streams, but only complete strings. (However, the | 
|  | module provides a "streamy" interface). | 
|  |  | 
|  | Since most parts of the logic are handled by the regex engine it's way | 
|  | faster than the original python port of ``jsmin.c`` by Baruch Even. The speed | 
|  | factor varies between about 6 and 55 depending on input and python version | 
|  | (it gets faster the more compressed the input already is). Compared to the | 
|  | speed-refactored python port by Dave St.Germain the performance gain is less | 
|  | dramatic but still between 1.2 and 7. See the docs/BENCHMARKS file for | 
|  | details. | 
|  |  | 
|  | rjsmin.c is a reimplementation of rjsmin.py in C and speeds it up even more. | 
|  |  | 
|  | Both python 2 and python 3 are supported. | 
|  |  | 
|  | .. _jsmin.c by Douglas Crockford: | 
|  | http://www.crockford.com/javascript/jsmin.c | 
|  | """ | 
|  | __author__ = "Andr\xe9 Malo" | 
|  | __author__ = getattr(__author__, 'decode', lambda x: __author__)('latin-1') | 
|  | __docformat__ = "restructuredtext en" | 
|  | __license__ = "Apache License, Version 2.0" | 
|  | __version__ = '1.0.7' | 
|  | __all__ = ['jsmin'] | 
|  |  | 
|  | import re as _re | 
|  |  | 
|  |  | 
|  | def _make_jsmin(python_only=False): | 
|  | """ | 
|  | Generate JS minifier based on `jsmin.c by Douglas Crockford`_ | 
|  |  | 
|  | .. _jsmin.c by Douglas Crockford: | 
|  | http://www.crockford.com/javascript/jsmin.c | 
|  |  | 
|  | :Parameters: | 
|  | `python_only` : ``bool`` | 
|  | Use only the python variant. If true, the c extension is not even | 
|  | tried to be loaded. | 
|  |  | 
|  | :Return: Minifier | 
|  | :Rtype: ``callable`` | 
|  | """ | 
|  | # pylint: disable = R0912, R0914, W0612 | 
|  | if not python_only: | 
|  | try: | 
|  | import _rjsmin | 
|  | except ImportError: | 
|  | pass | 
|  | else: | 
|  | return _rjsmin.jsmin | 
|  | try: | 
|  | xrange | 
|  | except NameError: | 
|  | xrange = range  # pylint: disable = W0622 | 
|  |  | 
|  | space_chars = r'[\000-\011\013\014\016-\040]' | 
|  |  | 
|  | line_comment = r'(?://[^\r\n]*)' | 
|  | space_comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)' | 
|  | string1 = \ | 
|  | r'(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^\047\\\r\n]*)*\047)' | 
|  | string2 = r'(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^"\\\r\n]*)*")' | 
|  | strings = r'(?:%s|%s)' % (string1, string2) | 
|  |  | 
|  | charclass = r'(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\])' | 
|  | nospecial = r'[^/\\\[\r\n]' | 
|  | regex = r'(?:/(?![\r\n/*])%s*(?:(?:\\[^\r\n]|%s)%s*)*/)' % ( | 
|  | nospecial, charclass, nospecial) | 
|  | space = r'(?:%s|%s)' % (space_chars, space_comment) | 
|  | newline = r'(?:%s?[\r\n])' % line_comment | 
|  |  | 
|  | def fix_charclass(result): | 
|  | """ Fixup string of chars to fit into a regex char class """ | 
|  | pos = result.find('-') | 
|  | if pos >= 0: | 
|  | result = r'%s%s-' % (result[:pos], result[pos + 1:]) | 
|  |  | 
|  | def sequentize(string): | 
|  | """ | 
|  | Notate consecutive characters as sequence | 
|  |  | 
|  | (1-4 instead of 1234) | 
|  | """ | 
|  | first, last, result = None, None, [] | 
|  | for char in map(ord, string): | 
|  | if last is None: | 
|  | first = last = char | 
|  | elif last + 1 == char: | 
|  | last = char | 
|  | else: | 
|  | result.append((first, last)) | 
|  | first = last = char | 
|  | if last is not None: | 
|  | result.append((first, last)) | 
|  | return ''.join(['%s%s%s' % ( | 
|  | chr(first), | 
|  | last > first + 1 and '-' or '', | 
|  | last != first and chr(last) or '') for first, last in result]) | 
|  |  | 
|  | return _re.sub(r'([\000-\040\047])',  # for better portability | 
|  | lambda m: '\\%03o' % ord(m.group(1)), (sequentize(result) | 
|  | .replace('\\', '\\\\') | 
|  | .replace('[', '\\[') | 
|  | .replace(']', '\\]'))) | 
|  |  | 
|  | def id_literal_(what): | 
|  | """ Make id_literal like char class """ | 
|  | match = _re.compile(what).match | 
|  | result = ''.join([chr(c) for c in xrange(127) if not match(chr(c))]) | 
|  | return '[^%s]' % fix_charclass(result) | 
|  |  | 
|  | def not_id_literal_(keep): | 
|  | """ Make negated id_literal like char class """ | 
|  | match = _re.compile(id_literal_(keep)).match | 
|  | result = ''.join([chr(c) for c in xrange(127) if not match(chr(c))]) | 
|  | return r'[%s]' % fix_charclass(result) | 
|  |  | 
|  | not_id_literal = not_id_literal_(r'[a-zA-Z0-9_$]') | 
|  | preregex1 = r'[(,=:\[!&|?{};\r\n]' | 
|  | preregex2 = r'%(not_id_literal)sreturn' % locals() | 
|  |  | 
|  | id_literal = id_literal_(r'[a-zA-Z0-9_$]') | 
|  | id_literal_open = id_literal_(r'[a-zA-Z0-9_${\[(!+-]') | 
|  | id_literal_close = id_literal_(r'[a-zA-Z0-9_$}\])"\047+-]') | 
|  |  | 
|  | dull = r'[^\047"/\000-\040]' | 
|  |  | 
|  | space_sub = _re.compile(( | 
|  | r'(%(dull)s+)' | 
|  | r'|(%(strings)s%(dull)s*)' | 
|  | r'|(?<=%(preregex1)s)' | 
|  | r'%(space)s*(?:%(newline)s%(space)s*)*' | 
|  | r'(%(regex)s%(dull)s*)' | 
|  | r'|(?<=%(preregex2)s)' | 
|  | r'%(space)s*(?:%(newline)s%(space)s)*' | 
|  | r'(%(regex)s%(dull)s*)' | 
|  | r'|(?<=%(id_literal_close)s)' | 
|  | r'%(space)s*(?:(%(newline)s)%(space)s*)+' | 
|  | r'(?=%(id_literal_open)s)' | 
|  | r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)' | 
|  | r'|(?<=\+)(%(space)s)+(?=\+)' | 
|  | r'|(?<=-)(%(space)s)+(?=-)' | 
|  | r'|%(space)s+' | 
|  | r'|(?:%(newline)s%(space)s*)+') % locals()).sub | 
|  | #print space_sub.__self__.pattern | 
|  |  | 
|  | def space_subber(match): | 
|  | """ Substitution callback """ | 
|  | # pylint: disable = C0321, R0911 | 
|  | groups = match.groups() | 
|  | if groups[0]: | 
|  | return groups[0] | 
|  | elif groups[1]: | 
|  | return groups[1] | 
|  | elif groups[2]: | 
|  | return groups[2] | 
|  | elif groups[3]: | 
|  | return groups[3] | 
|  | elif groups[4]: | 
|  | return '\n' | 
|  | elif groups[5] or groups[6] or groups[7]: | 
|  | return ' ' | 
|  | else: | 
|  | return '' | 
|  |  | 
|  | def jsmin(script):  # pylint: disable = W0621 | 
|  | r""" | 
|  | Minify javascript based on `jsmin.c by Douglas Crockford`_\. | 
|  |  | 
|  | Instead of parsing the stream char by char, it uses a regular | 
|  | expression approach which minifies the whole script with one big | 
|  | substitution regex. | 
|  |  | 
|  | .. _jsmin.c by Douglas Crockford: | 
|  | http://www.crockford.com/javascript/jsmin.c | 
|  |  | 
|  | :Parameters: | 
|  | `script` : ``str`` | 
|  | Script to minify | 
|  |  | 
|  | :Return: Minified script | 
|  | :Rtype: ``str`` | 
|  | """ | 
|  | return space_sub(space_subber, '\n%s\n' % script).strip() | 
|  |  | 
|  | return jsmin | 
|  |  | 
|  | jsmin = _make_jsmin() | 
|  |  | 
|  |  | 
|  | def jsmin_for_posers(script): | 
|  | r""" | 
|  | Minify javascript based on `jsmin.c by Douglas Crockford`_\. | 
|  |  | 
|  | Instead of parsing the stream char by char, it uses a regular | 
|  | expression approach which minifies the whole script with one big | 
|  | substitution regex. | 
|  |  | 
|  | .. _jsmin.c by Douglas Crockford: | 
|  | http://www.crockford.com/javascript/jsmin.c | 
|  |  | 
|  | :Warning: This function is the digest of a _make_jsmin() call. It just | 
|  | utilizes the resulting regex. It's just for fun here and may | 
|  | vanish any time. Use the `jsmin` function instead. | 
|  |  | 
|  | :Parameters: | 
|  | `script` : ``str`` | 
|  | Script to minify | 
|  |  | 
|  | :Return: Minified script | 
|  | :Rtype: ``str`` | 
|  | """ | 
|  | def subber(match): | 
|  | """ Substitution callback """ | 
|  | groups = match.groups() | 
|  | return ( | 
|  | groups[0] or | 
|  | groups[1] or | 
|  | groups[2] or | 
|  | groups[3] or | 
|  | (groups[4] and '\n') or | 
|  | (groups[5] and ' ') or | 
|  | (groups[6] and ' ') or | 
|  | (groups[7] and ' ') or | 
|  | '') | 
|  |  | 
|  | return _re.sub( | 
|  | r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?' | 
|  | r'\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|' | 
|  | r'\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?{};\r\n])(?' | 
|  | r':[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*' | 
|  | r'(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*' | 
|  | r'[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(' | 
|  | r'?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[' | 
|  | r'\r\n]*)*/)[^\047"/\000-\040]*)|(?<=[\000-#%-,./:-@\[-^`{-~-]return' | 
|  | r')(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/' | 
|  | r'))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:' | 
|  | r'/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?' | 
|  | r':(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/' | 
|  | r'\\\[\r\n]*)*/)[^\047"/\000-\040]*)|(?<=[^\000-!#%&(*,./:-@\[\\^`{|' | 
|  | r'~])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)' | 
|  | r'*/))*(?:((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011\013\014\016-\040]' | 
|  | r'|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#%-\047)*,./' | 
|  | r':-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\013\01' | 
|  | r'4\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^\000-#%-,./:' | 
|  | r'-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*' | 
|  | r'\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-\011\013\014\016-' | 
|  | r'\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=-)|(?:[\000-\011\013' | 
|  | r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+|(?:(?:(?://[^' | 
|  | r'\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^' | 
|  | r'/*][^*]*\*+)*/))*)+', subber, '\n%s\n' % script).strip() | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | import sys as _sys | 
|  | _sys.stdout.write(jsmin(_sys.stdin.read())) |