]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
Makefile.webui: use rcssmin-1.0.5 for CSS compression
authorJaroslav Kysela <perex@perex.cz>
Thu, 14 May 2015 07:05:29 +0000 (09:05 +0200)
committerJaroslav Kysela <perex@perex.cz>
Thu, 14 May 2015 07:05:29 +0000 (09:05 +0200)
Makefile.webui
vendor/rcssmin-1.0.5/rcssmin.py [new file with mode: 0755]

index e6d3275b21afb35654fca7bcad4d1fccbf9eb2e5..a51938cf3f3cd019906a6472b5508bd631f746ac 100644 (file)
@@ -31,7 +31,7 @@ include $(TOPDIR)/.config.mk
 EXTJSPATH = $(ROOTPATH)/extjs
 CSS_PY = support/css.py
 RUN_JS = vendor/rjsmin-1.0.10/rjsmin.py
-RUN_CSS = vendor/rjsmin-1.0.10/rjsmin.py
+RUN_CSS = vendor/rcssmin-1.0.5/rcssmin.py
 GZIP = gzip -n
 
 ifeq ($(WEBUI),std)
diff --git a/vendor/rcssmin-1.0.5/rcssmin.py b/vendor/rcssmin-1.0.5/rcssmin.py
new file mode 100755 (executable)
index 0000000..ae1cefc
--- /dev/null
@@ -0,0 +1,374 @@
+#!/usr/bin/env python
+# -*- coding: ascii -*-
+r"""
+==============
+ CSS Minifier
+==============
+
+CSS Minifier.
+
+The minifier is based on the semantics of the `YUI compressor`_\\, which
+itself is based on `the rule list by Isaac Schlueter`_\\.
+
+:Copyright:
+
+ Copyright 2011 - 2014
+ Andr\xe9 Malo or his licensors, as applicable
+
+:License:
+
+ 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.
+
+This module is a re-implementation aiming for speed instead of maximum
+compression, so it can be used at runtime (rather than during a preprocessing
+step). RCSSmin does syntactical compression only (removing spaces, comments
+and possibly semicolons). It does not provide semantic compression (like
+removing empty blocks, collapsing redundant properties etc). It does, however,
+support various CSS hacks (by keeping them working as intended).
+
+Here's a feature list:
+
+- Strings are kept, except that escaped newlines are stripped
+- Space/Comments before the very end or before various characters are
+  stripped: ``:{});=>+],!`` (The colon (``:``) is a special case, a single
+  space is kept if it's outside a ruleset.)
+- Space/Comments at the very beginning or after various characters are
+  stripped: ``{}(=:>+[,!``
+- Optional space after unicode escapes is kept, resp. replaced by a simple
+  space
+- whitespaces inside ``url()`` definitions are stripped
+- Comments starting with an exclamation mark (``!``) can be kept optionally.
+- All other comments and/or whitespace characters are replaced by a single
+  space.
+- Multiple consecutive semicolons are reduced to one
+- The last semicolon within a ruleset is stripped
+- CSS Hacks supported:
+
+  - IE7 hack (``>/**/``)
+  - Mac-IE5 hack (``/*\\*/.../**/``)
+  - The boxmodelhack is supported naturally because it relies on valid CSS2
+    strings
+  - Between ``:first-line`` and the following comma or curly brace a space is
+    inserted. (apparently it's needed for IE6)
+  - Same for ``:first-letter``
+
+rcssmin.c is a reimplementation of rcssmin.py in C and improves runtime up to
+factor 100 or so (depending on the input). docs/BENCHMARKS in the source
+distribution contains the details.
+
+Both python 2 (>= 2.4) and python 3 are supported.
+
+.. _YUI compressor: https://github.com/yui/yuicompressor/
+
+.. _the rule list by Isaac Schlueter: https://github.com/isaacs/cssmin/
+"""
+if __doc__:
+    # pylint: disable = W0622
+    __doc__ = __doc__.encode('ascii').decode('unicode_escape')
+__author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape')
+__docformat__ = "restructuredtext en"
+__license__ = "Apache License, Version 2.0"
+__version__ = '1.0.5'
+__all__ = ['cssmin']
+
+import re as _re
+
+
+def _make_cssmin(python_only=False):
+    """
+    Generate CSS minifier.
+
+    :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 _rcssmin
+        except ImportError:
+            pass
+        else:
+            return _rcssmin.cssmin
+
+    nl = r'(?:[\n\f]|\r\n?)'  # pylint: disable = C0103
+    spacechar = r'[\r\n\f\040\t]'
+
+    unicoded = r'[0-9a-fA-F]{1,6}(?:[\040\n\t\f]|\r\n?)?'
+    escaped = r'[^\n\r\f0-9a-fA-F]'
+    escape = r'(?:\\(?:%(unicoded)s|%(escaped)s))' % locals()
+
+    nmchar = r'[^\000-\054\056\057\072-\100\133-\136\140\173-\177]'
+    #nmstart = r'[^\000-\100\133-\136\140\173-\177]'
+    #ident = (r'(?:'
+    #    r'-?(?:%(nmstart)s|%(escape)s)%(nmchar)s*(?:%(escape)s%(nmchar)s*)*'
+    #r')') % locals()
+
+    comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'
+
+    # only for specific purposes. The bang is grouped:
+    _bang_comment = r'(?:/\*(!?)[^*]*\*+(?:[^/*][^*]*\*+)*/)'
+
+    string1 = \
+        r'(?:\047[^\047\\\r\n\f]*(?:\\[^\r\n\f][^\047\\\r\n\f]*)*\047)'
+    string2 = r'(?:"[^"\\\r\n\f]*(?:\\[^\r\n\f][^"\\\r\n\f]*)*")'
+    strings = r'(?:%s|%s)' % (string1, string2)
+
+    nl_string1 = \
+        r'(?:\047[^\047\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)[^\047\\\r\n\f]*)*\047)'
+    nl_string2 = r'(?:"[^"\\\r\n\f]*(?:\\(?:[^\r]|\r\n?)[^"\\\r\n\f]*)*")'
+    nl_strings = r'(?:%s|%s)' % (nl_string1, nl_string2)
+
+    uri_nl_string1 = r'(?:\047[^\047\\]*(?:\\(?:[^\r]|\r\n?)[^\047\\]*)*\047)'
+    uri_nl_string2 = r'(?:"[^"\\]*(?:\\(?:[^\r]|\r\n?)[^"\\]*)*")'
+    uri_nl_strings = r'(?:%s|%s)' % (uri_nl_string1, uri_nl_string2)
+
+    nl_escaped = r'(?:\\%(nl)s)' % locals()
+
+    space = r'(?:%(spacechar)s|%(comment)s)' % locals()
+
+    ie7hack = r'(?:>/\*\*/)'
+
+    uri = (r'(?:'
+        # noqa pylint: disable = C0330
+        r'(?:[^\000-\040"\047()\\\177]*'
+            r'(?:%(escape)s[^\000-\040"\047()\\\177]*)*)'
+        r'(?:'
+            r'(?:%(spacechar)s+|%(nl_escaped)s+)'
+            r'(?:'
+                r'(?:[^\000-\040"\047()\\\177]|%(escape)s|%(nl_escaped)s)'
+                r'[^\000-\040"\047()\\\177]*'
+                r'(?:%(escape)s[^\000-\040"\047()\\\177]*)*'
+            r')+'
+        r')*'
+    r')') % locals()
+
+    nl_unesc_sub = _re.compile(nl_escaped).sub
+
+    uri_space_sub = _re.compile((
+        r'(%(escape)s+)|%(spacechar)s+|%(nl_escaped)s+'
+    ) % locals()).sub
+    uri_space_subber = lambda m: m.groups()[0] or ''
+
+    space_sub_simple = _re.compile((
+        r'[\r\n\f\040\t;]+|(%(comment)s+)'
+    ) % locals()).sub
+    space_sub_banged = _re.compile((
+        r'[\r\n\f\040\t;]+|(%(_bang_comment)s+)'
+    ) % locals()).sub
+
+    post_esc_sub = _re.compile(r'[\r\n\f\t]+').sub
+
+    main_sub = _re.compile((
+        # noqa pylint: disable = C0330
+        r'([^\\"\047u>@\r\n\f\040\t/;:{}]+)'
+        r'|(?<=[{}(=:>+[,!])(%(space)s+)'
+        r'|^(%(space)s+)'
+        r'|(%(space)s+)(?=(([:{});=>+\],!])|$)?)'
+        r'|;(%(space)s*(?:;%(space)s*)*)(?=(\})?)'
+        r'|(\{)'
+        r'|(\})'
+        r'|(%(strings)s)'
+        r'|(?<!%(nmchar)s)url\(%(spacechar)s*('
+                r'%(uri_nl_strings)s'
+                r'|%(uri)s'
+            r')%(spacechar)s*\)'
+        r'|(@(?:'
+              r'[mM][eE][dD][iI][aA]'
+              r'|[sS][uU][pP][pP][oO][rR][tT][sS]'
+              r'|[dD][oO][cC][uU][mM][eE][nN][tT]'
+              r'|(?:-(?:'
+                  r'[wW][eE][bB][kK][iI][tT]|[mM][oO][zZ]|[oO]|[mM][sS]'
+                r')-)?'
+                r'[kK][eE][yY][fF][rR][aA][mM][eE][sS]'
+            r'))(?!%(nmchar)s)'
+        r'|(%(ie7hack)s)(%(space)s*)'
+        r'|(:[fF][iI][rR][sS][tT]-[lL]'
+            r'(?:[iI][nN][eE]|[eE][tT][tT][eE][rR]))'
+            r'(%(space)s*)(?=[{,])'
+        r'|(%(nl_strings)s)'
+        r'|(%(escape)s[^\\"\047u>@\r\n\f\040\t/;:{}]*)'
+    ) % locals()).sub
+
+    #print main_sub.__self__.pattern
+
+    def main_subber(keep_bang_comments):
+        """ Make main subber """
+        in_macie5, in_rule, at_group = [0], [0], [0]
+
+        if keep_bang_comments:
+            space_sub = space_sub_banged
+
+            def space_subber(match):
+                """ Space|Comment subber """
+                if match.lastindex:
+                    group1, group2 = match.group(1, 2)
+                    if group2:
+                        if group1.endswith(r'\*/'):
+                            in_macie5[0] = 1
+                        else:
+                            in_macie5[0] = 0
+                        return group1
+                    elif group1:
+                        if group1.endswith(r'\*/'):
+                            if in_macie5[0]:
+                                return ''
+                            in_macie5[0] = 1
+                            return r'/*\*/'
+                        elif in_macie5[0]:
+                            in_macie5[0] = 0
+                            return '/**/'
+                return ''
+        else:
+            space_sub = space_sub_simple
+
+            def space_subber(match):
+                """ Space|Comment subber """
+                if match.lastindex:
+                    if match.group(1).endswith(r'\*/'):
+                        if in_macie5[0]:
+                            return ''
+                        in_macie5[0] = 1
+                        return r'/*\*/'
+                    elif in_macie5[0]:
+                        in_macie5[0] = 0
+                        return '/**/'
+                return ''
+
+        def fn_space_post(group):
+            """ space with token after """
+            if group(5) is None or (
+                    group(6) == ':' and not in_rule[0] and not at_group[0]):
+                return ' ' + space_sub(space_subber, group(4))
+            return space_sub(space_subber, group(4))
+
+        def fn_semicolon(group):
+            """ ; handler """
+            return ';' + space_sub(space_subber, group(7))
+
+        def fn_semicolon2(group):
+            """ ; handler """
+            if in_rule[0]:
+                return space_sub(space_subber, group(7))
+            return ';' + space_sub(space_subber, group(7))
+
+        def fn_open(_):
+            """ { handler """
+            if at_group[0]:
+                at_group[0] -= 1
+            else:
+                in_rule[0] = 1
+            return '{'
+
+        def fn_close(_):
+            """ } handler """
+            in_rule[0] = 0
+            return '}'
+
+        def fn_at_group(group):
+            """ @xxx group handler """
+            at_group[0] += 1
+            return group(13)
+
+        def fn_ie7hack(group):
+            """ IE7 Hack handler """
+            if not in_rule[0] and not at_group[0]:
+                in_macie5[0] = 0
+                return group(14) + space_sub(space_subber, group(15))
+            return '>' + space_sub(space_subber, group(15))
+
+        table = (
+            # noqa pylint: disable = C0330
+            None,
+            None,
+            None,
+            None,
+            fn_space_post,                       # space with token after
+            fn_space_post,                       # space with token after
+            fn_space_post,                       # space with token after
+            fn_semicolon,                        # semicolon
+            fn_semicolon2,                       # semicolon
+            fn_open,                             # {
+            fn_close,                            # }
+            lambda g: g(11),                     # string
+            lambda g: 'url(%s)' % uri_space_sub(uri_space_subber, g(12)),
+                                                 # url(...)
+            fn_at_group,                         # @xxx expecting {...}
+            None,
+            fn_ie7hack,                          # ie7hack
+            None,
+            lambda g: g(16) + ' ' + space_sub(space_subber, g(17)),
+                                                 # :first-line|letter followed
+                                                 # by [{,] (apparently space
+                                                 # needed for IE6)
+            lambda g: nl_unesc_sub('', g(18)),   # nl_string
+            lambda g: post_esc_sub(' ', g(19)),  # escape
+        )
+
+        def func(match):
+            """ Main subber """
+            idx, group = match.lastindex, match.group
+            if idx > 3:
+                return table[idx](group)
+
+            # shortcuts for frequent operations below:
+            elif idx == 1:     # not interesting
+                return group(1)
+            #else: # space with token before or at the beginning
+            return space_sub(space_subber, group(idx))
+
+        return func
+
+    def cssmin(style, keep_bang_comments=False):  # pylint: disable = W0621
+        """
+        Minify CSS.
+
+        :Parameters:
+          `style` : ``str``
+            CSS to minify
+
+          `keep_bang_comments` : ``bool``
+            Keep comments starting with an exclamation mark? (``/*!...*/``)
+
+        :Return: Minified style
+        :Rtype: ``str``
+        """
+        return main_sub(main_subber(keep_bang_comments), style)
+
+    return cssmin
+
+cssmin = _make_cssmin()
+
+
+if __name__ == '__main__':
+    def main():
+        """ Main """
+        import sys as _sys
+        keep_bang_comments = (
+            '-b' in _sys.argv[1:]
+            or '-bp' in _sys.argv[1:]
+            or '-pb' in _sys.argv[1:]
+        )
+        if '-p' in _sys.argv[1:] or '-bp' in _sys.argv[1:] \
+                or '-pb' in _sys.argv[1:]:
+            global cssmin  # pylint: disable = W0603
+            cssmin = _make_cssmin(python_only=True)
+        _sys.stdout.write(cssmin(
+            _sys.stdin.read(), keep_bang_comments=keep_bang_comments
+        ))
+    main()