Пример #1
0
def csscombine(
    path=None,
    url=None,
    cssText=None,
    href=None,
    sourceencoding=None,
    targetencoding=None,
    minify=True,
    resolveVariables=True,
):
    """Combine sheets referred to by @import rules in given CSS proxy sheet
    into a single new sheet.

    :returns: combined cssText, normal or minified
    :Parameters:
        `path` or `url` or `cssText` + `href`
            path or URL to a CSSStyleSheet or a cssText of a sheet which imports
            other sheets which are then combined into one sheet.
            `cssText` normally needs `href` to be able to resolve relative
            imports.
        `sourceencoding` = 'utf-8'
            explicit encoding of the source proxysheet
        `targetencoding`
            encoding of the combined stylesheet
        `minify` = True
            defines if the combined sheet should be minified, in this case
            comments are not parsed at all!
        `resolveVariables` = True
            defines if variables in combined sheet should be resolved
    """
    cssutils.log.info('Combining files from %r' % url, neverraise=True)
    if sourceencoding is not None:
        cssutils.log.info('Using source encoding %r' % sourceencoding,
                          neverraise=True)

    parser = cssutils.CSSParser(parseComments=not minify)

    if path and not cssText:
        src = parser.parseFile(path, encoding=sourceencoding)
    elif url:
        src = parser.parseUrl(url, encoding=sourceencoding)
    elif cssText:
        src = parser.parseString(cssText, href=href, encoding=sourceencoding)
    else:
        sys.exit('Path or URL must be given')

    result = cssutils.resolveImports(src)
    result.encoding = targetencoding
    cssutils.log.info('Using target encoding: %r' % targetencoding,
                      neverraise=True)

    oldser = cssutils.ser
    cssutils.setSerializer(cssutils.serialize.CSSSerializer())
    if minify:
        cssutils.ser.prefs.useMinified()
    cssutils.ser.prefs.resolveVariables = resolveVariables
    cssText = result.cssText
    cssutils.setSerializer(oldser)

    return cssText
Пример #2
0
def csscombine(
    path=None,
    url=None,
    cssText=None,
    href=None,
    sourceencoding=None,
    targetencoding=None,
    minify=True,
    resolveVariables=True,
):
    """Combine sheets referred to by @import rules in given CSS proxy sheet
    into a single new sheet.

    :returns: combined cssText, normal or minified
    :Parameters:
        `path` or `url` or `cssText` + `href`
            path or URL to a CSSStyleSheet or a cssText of a sheet which imports
            other sheets which are then combined into one sheet.
            `cssText` normally needs `href` to be able to resolve relative
            imports.
        `sourceencoding` = 'utf-8'
            explicit encoding of the source proxysheet
        `targetencoding`
            encoding of the combined stylesheet
        `minify` = True
            defines if the combined sheet should be minified, in this case
            comments are not parsed at all!
        `resolveVariables` = True
            defines if variables in combined sheet should be resolved
    """
    cssutils.log.info(u"Combining files from %r" % url, neverraise=True)
    if sourceencoding is not None:
        cssutils.log.info(u"Using source encoding %r" % sourceencoding, neverraise=True)

    parser = cssutils.CSSParser(parseComments=not minify)

    if path and not cssText:
        src = parser.parseFile(path, encoding=sourceencoding)
    elif url:
        src = parser.parseUrl(url, encoding=sourceencoding)
    elif cssText:
        src = parser.parseString(cssText, href=href, encoding=sourceencoding)
    else:
        sys.exit("Path or URL must be given")

    result = cssutils.resolveImports(src)
    result.encoding = targetencoding
    cssutils.log.info(u"Using target encoding: %r" % targetencoding, neverraise=True)

    oldser = cssutils.ser
    cssutils.setSerializer(cssutils.serialize.CSSSerializer())
    if minify:
        cssutils.ser.prefs.useMinified()
    cssutils.ser.prefs.resolveVariables = resolveVariables
    cssText = result.cssText
    cssutils.setSerializer(oldser)

    return cssText
Пример #3
0
def minify_sources(sources, ext, fs_root='', timestamp=False):
    """Use utilities to minify javascript or css.

    :param sources: Paths of source files
    :param ext: Type of files
    :param fs_root: root of file (normally public dir)
    :type sources: string
    :type ext: js or css
    :type fs_root: string

    :returns: List of paths to minified sources
    """
    if 'js' in ext:
        js_minify = JavascriptMinify()
    minified_sources = []

    for source in sources:
        # generate full path to source
        no_ext_source = path.splitext(source)[0]
        full_source = path.join(fs_root, (no_ext_source + ext).lstrip('/'))

        # generate minified source path
        full_source = path.join(fs_root, (source).lstrip('/'))
        no_ext_full_source = path.splitext(full_source)[0]
        minified = no_ext_full_source + ext

        f_minified_source = open(minified, 'w')

        try:
            # minify js source (read stream is auto-closed inside)
            if 'js' in ext:
                js_minify.minify(open(full_source, 'r'), f_minified_source)

            # minify css source
            if 'css' in ext:
                sheet = cssutils.parseFile(full_source)
                cssutils.setSerializer(CSSUtilsMinificationSerializer())
                cssutils.ser.prefs.useMinified()
                f_minified_source.write(sheet.cssText)
        finally:
            f_minified_source.close()

        if no_ext_source.endswith('COMBINED'):
            minified_sources.append(no_ext_source + ext)
        else:
            minified_sources.append(no_ext_source + generate_timestamp(timestamp) + ext)

    return minified_sources
    def test_setCSSSerializer(self):
        "cssutils.setSerializer() and cssutils.ser"
        s = cssutils.parseString('a { left: 0 }')
        exp4 = '''a {
    left: 0
    }'''
        exp1 = '''a {
 left: 0
 }'''
        self.assertEqual(exp4, s.cssText)
        newser = cssutils.CSSSerializer(cssutils.serialize.Preferences(indent=' '))
        cssutils.setSerializer(newser)
        self.assertEqual(exp1, s.cssText)
        newser = cssutils.CSSSerializer(cssutils.serialize.Preferences(indent='    '))
        cssutils.ser = newser
        self.assertEqual(exp4, s.cssText)
Пример #5
0
    def test_setCSSSerializer(self):
        "cssutils.setSerializer() and cssutils.ser"
        s = cssutils.parseString('a { left: 0 }')
        exp4 = '''a {
    left: 0
    }'''
        exp1 = '''a {
 left: 0
 }'''
        self.assertEqual(exp4.encode(), s.cssText)
        newser = cssutils.CSSSerializer(cssutils.serialize.Preferences(indent=' '))
        cssutils.setSerializer(newser)
        self.assertEqual(exp1.encode(), s.cssText)
        newser = cssutils.CSSSerializer(cssutils.serialize.Preferences(indent='    '))
        cssutils.ser = newser
        self.assertEqual(exp4.encode(), s.cssText)
Пример #6
0
def csscombine(path=None,
               url=None,
               sourceencoding=None,
               targetencoding=None,
               minify=True):
    """Combine sheets referred to by @import rules in given CSS proxy sheet
    into a single new sheet.

    :returns: combined cssText, normal or minified
    :Parameters:
        `path` or `url`
            path  or URL to a CSSStyleSheet which imports other sheets which
            are then combined into one sheet
        `targetencoding`
            encoding of the combined stylesheet, default 'utf-8'
        `minify`
            defines if the combined sheet should be minified, default True
    """
    cssutils.log.info(u'Combining files from %r' % url, neverraise=True)
    if sourceencoding is not None:
        cssutils.log.info(u'Using source encoding %r' % sourceencoding,
                          neverraise=True)
    if path:
        src = cssutils.parseFile(path, encoding=sourceencoding)
    elif url:
        src = cssutils.parseUrl(url, encoding=sourceencoding)
    else:
        sys.exit('Path or URL must be given')

    result = cssutils.resolveImports(src)
    result.encoding = targetencoding
    cssutils.log.info(u'Using target encoding: %r' % targetencoding,
                      neverraise=True)

    if minify:
        # save old setting and use own serializer
        oldser = cssutils.ser
        cssutils.setSerializer(cssutils.serialize.CSSSerializer())
        cssutils.ser.prefs.useMinified()
        cssText = result.cssText
        cssutils.setSerializer(oldser)
    else:
        cssText = result.cssText

    return cssText
Пример #7
0
    def filter_file_content(self, file_content, path):
        """URLs are made relative to the target and the CSS is minified."""
        if self.rewrite_urls:
            src_dir = os.path.dirname(path)
            relative_src_dir = relative_path(self.target_file, src_dir)
            if relative_src_dir == ".":
                relative_parts = []
            else:
                relative_parts = relative_src_dir.split(os.path.sep)

            def fix_relative_url(match):
                url = match.group(1)
                # Don't modify absolute URLs or 'data:' urls.
                if (url.startswith("http") or url.startswith("/")
                        or url.startswith("data:")):
                    return match.group(0)
                parts = relative_parts + url.split("/")
                result = []
                for part in parts:
                    if part == ".." and result and result[-1] != "..":
                        result.pop(-1)
                        continue
                    result.append(part)
                return "url(%s)" % "/".join(
                    filter(None, [self.resource_prefix] + result))

            file_content = URL_RE.sub(fix_relative_url, file_content)

        if self.minify:
            old_serializer = cssutils.ser
            cssutils.setSerializer(cssutils.serialize.CSSSerializer())
            try:
                cssutils.ser.prefs.useMinified()

                stylesheet = ESCAPE_STAR_PROPERTY_RE.sub(
                    r'\1_ie_star_hack:', file_content)
                settings.set('DXImageTransform.Microsoft', True)
                parser = cssutils.CSSParser(raiseExceptions=True)
                css = parser.parseString(stylesheet)
                stylesheet = UNESCAPE_STAR_PROPERTY_RE.sub(
                    r'*\1:', css.cssText)
                return stylesheet + "\n"
            finally:
                cssutils.setSerializer(old_serializer)
        return file_content + "\n"
Пример #8
0
    def filter_file_content(self, file_content, path):
        """URLs are made relative to the target and the CSS is minified."""
        if self.rewrite_urls:
            src_dir = os.path.dirname(path)
            relative_parts = relative_path(self.target_file, src_dir).split(
                os.path.sep)

            def fix_relative_url(match):
                url = match.group(1)
                # Don't modify absolute URLs or 'data:' urls.
                if (url.startswith("http") or
                    url.startswith("/") or
                    url.startswith("data:")):
                    return match.group(0)
                parts = relative_parts + url.split("/")
                result = []
                for part in parts:
                    if part == ".." and result and result[-1] != "..":
                        result.pop(-1)
                        continue
                    result.append(part)
                return "url(%s)" % "/".join(
                    filter(None, [self.resource_prefix] + result))
            file_content = URL_RE.sub(fix_relative_url, file_content)

        if self.minify:
            old_serializer = cssutils.ser
            cssutils.setSerializer(cssutils.serialize.CSSSerializer())
            try:
                cssutils.ser.prefs.useMinified()

                stylesheet = ESCAPE_STAR_PROPERTY_RE.sub(
                    r'\1_ie_star_hack:', file_content)
                settings.set('DXImageTransform.Microsoft', True)
                parser = cssutils.CSSParser(raiseExceptions=True)
                css = parser.parseString(stylesheet)
                stylesheet = UNESCAPE_STAR_PROPERTY_RE.sub(
                    r'*\1:', css.cssText)
                return stylesheet + "\n"
            finally:
                cssutils.setSerializer(old_serializer)
        return file_content + "\n"
Пример #9
0
def csscombine(path=None, url=None, sourceencoding=None, targetencoding=None, minify=True):
    """Combine sheets referred to by @import rules in given CSS proxy sheet
    into a single new sheet.

    :returns: combined cssText, normal or minified
    :Parameters:
        `path` or `url`
            path  or URL to a CSSStyleSheet which imports other sheets which
            are then combined into one sheet
        `targetencoding`
            encoding of the combined stylesheet, default 'utf-8'
        `minify`
            defines if the combined sheet should be minified, default True
    """
    cssutils.log.info(u"Combining files from %r" % url, neverraise=True)
    if sourceencoding is not None:
        cssutils.log.info(u"Using source encoding %r" % sourceencoding, neverraise=True)
    if path:
        src = cssutils.parseFile(path, encoding=sourceencoding)
    elif url:
        src = cssutils.parseUrl(url, encoding=sourceencoding)
    else:
        sys.exit("Path or URL must be given")

    result = cssutils.resolveImports(src)
    result.encoding = targetencoding
    cssutils.log.info(u"Using target encoding: %r" % targetencoding, neverraise=True)

    if minify:
        # save old setting and use own serializer
        oldser = cssutils.ser
        cssutils.setSerializer(cssutils.serialize.CSSSerializer())
        cssutils.ser.prefs.useMinified()
        cssText = result.cssText
        cssutils.setSerializer(oldser)
    else:
        cssText = result.cssText

    return cssText
 def setUp(self):
     cssutils.setSerializer(customcssutils.MyCSSSerializer())
     self.css = cssutils.parseFile(os.path.join(os.path.dirname(__file__),
                                                'resources', 'base.css'))
Пример #11
0
def run(bk):
    # set custom serializer if Sigil version is < 0.9.18 (0.9.18 and higher have the new css-parser module)
    if bk.launcher_version() < 20190826:
        cssutils.setSerializer(customcssutils.MyCSSSerializer())
    prefs = get_prefs(bk)
    xml_parser = etree.XMLParser(resolve_entities=False)
    css_parser = cssutils.CSSParser(raiseExceptions=True, validate=False)
    css_to_skip, css_to_parse, css_warnings = pre_parse_css(bk, css_parser)

    form = InfoDialog(bk, prefs)
    form.parse_errors(bk, css_to_skip, css_to_parse, css_warnings)
    form.mainloop()
    if InfoDialog.stop_plugin:
        return -1

    parseAllXMLFiles = prefs['parseAllXMLFiles']

    # Parse files to create the list of "orphaned selectors"
    orphaned_selectors = []
    for css_id, css_href in bk.css_iter():
        if css_id not in css_to_skip.keys():
            css_string = read_css(bk, css_id)
            parsed_css = css_parser.parseString(css_string)
            namespaces_dict, default_prefix = css_namespaces(parsed_css)
            for rule in style_rules(parsed_css):
                for selector_index, selector in enumerate(rule.selectorList):
                    maintain_selector = False
                    if ignore_selectors(selector.selectorText):
                        continue
                    # If css specifies a default namespace, the default prefix
                    # must be added to every unprefixed type selector.
                    if default_prefix:
                        selector_ns = add_default_prefix(
                            default_prefix, selector.selectorText)
                    else:
                        selector_ns = selector.selectorText
                    selector_ns = clean_generic_prefixes(selector_ns)
                    for file_id, href, mime in bk.manifest_iter():
                        if mime == 'application/xhtml+xml':
                            is_xhtml = True
                        elif parseAllXMLFiles and re.search(
                                r'[/+]xml\b', mime):
                            is_xhtml = False
                        else:
                            continue
                        if selector_exists(
                                etree.HTML(
                                    bk.readfile(file_id).encode('utf-8')),
                                selector_ns, namespaces_dict, is_xhtml):
                            maintain_selector = True
                            break
                        try:
                            if selector_exists(
                                    etree.XML(
                                        bk.readfile(file_id).encode('utf-8'),
                                        xml_parser), selector_ns,
                                    namespaces_dict, is_xhtml):
                                maintain_selector = True
                                break
                        except etree.XMLSyntaxError:
                            form = ErrorDlg(href_to_basename(href))
                            form.mainloop()
                            return 1
                    if not maintain_selector:
                        orphaned_selectors.append(
                            (css_id, rule, rule.selectorList[selector_index],
                             selector_index, parsed_css))

    # Show the list of selectors to the user.
    form = SelectorsDialog(bk, orphaned_selectors)
    form.mainloop()
    if SelectorsDialog.stop_plugin:
        return -1

    # Delete selectors chosen by the user.
    css_to_change = {}
    old_rule, counter = None, 0
    for sel_data, to_delete in SelectorsDialog.orphaned_dict.values():
        if to_delete.get() == 1:
            if sel_data[1] == old_rule:
                counter += 1
            else:
                counter = 0
            del sel_data[1].selectorList[sel_data[3] - counter]
            old_rule = sel_data[1]
            css_to_change[sel_data[0]] = sel_data[4].cssText
    for css_id, css_text in css_to_change.items():
        bk.writefile(css_id, css_text)
    return 0
def run(bk):
    cssutils.setSerializer(customcssutils.MyCSSSerializer())
    prefs = get_prefs(bk)
    xml_parser = etree.XMLParser(resolve_entities=False)
    css_parser = cssutils.CSSParser(raiseExceptions=True, validate=False)
    css_to_skip, css_to_parse, css_warnings = pre_parse_css(bk, css_parser)

    form = InfoDialog(bk, prefs)
    form.parse_errors(bk, css_to_skip, css_to_parse, css_warnings)
    form.mainloop()
    if InfoDialog.stop_plugin:
        return -1

    parseAllXMLFiles = prefs['parseAllXMLFiles']

    # Parse files to create the list of "orphaned selectors"
    orphaned_selectors = []
    for css_id, css_href in bk.css_iter():
        if css_id not in css_to_skip.keys():
            css_string = read_css(bk, css_id)
            parsed_css = css_parser.parseString(css_string)
            namespaces_dict, default_prefix = css_namespaces(parsed_css)
            for rule in style_rules(parsed_css):
                for selector_index, selector in enumerate(rule.selectorList):
                    maintain_selector = False
                    if ignore_selectors(selector.selectorText):
                        continue
                    # If css specifies a default namespace, the default prefix
                    # must be added to every unprefixed type selector.
                    if default_prefix:
                        selector_ns = add_default_prefix(default_prefix,
                                                         selector.selectorText)
                    else:
                        selector_ns = selector.selectorText
                    selector_ns = clean_generic_prefixes(selector_ns)
                    for file_id, href, mime in bk.manifest_iter():
                        if mime == 'application/xhtml+xml':
                            is_xhtml = True
                        elif parseAllXMLFiles and re.search(r'[/+]xml\b', mime):
                            is_xhtml = False
                        else:
                            continue
                        if selector_exists(etree.HTML(bk.readfile(file_id).encode('utf-8')),
                                           selector_ns, namespaces_dict, is_xhtml):
                            maintain_selector = True
                            break
                        try:
                            if selector_exists(etree.XML(bk.readfile(file_id).encode('utf-8'),
                                                         xml_parser),
                                               selector_ns, namespaces_dict, is_xhtml):
                                maintain_selector = True
                                break
                        except etree.XMLSyntaxError:
                            form = ErrorDlg(href_to_basename(href))
                            form.mainloop()
                            return 1
                    if not maintain_selector:
                        orphaned_selectors.append((css_id, rule,
                                                   rule.selectorList[selector_index],
                                                   selector_index,
                                                   parsed_css))

    # Show the list of selectors to the user.
    form = SelectorsDialog(bk, orphaned_selectors)
    form.mainloop()
    if SelectorsDialog.stop_plugin:
        return -1

    # Delete selectors chosen by the user.
    css_to_change = {}
    old_rule, counter = None, 0
    for sel_data, to_delete in SelectorsDialog.orphaned_dict.values():
        if to_delete.get() == 1:
            if sel_data[1] == old_rule:
                counter += 1
            else:
                counter = 0
            del sel_data[1].selectorList[sel_data[3]-counter]
            old_rule = sel_data[1]
            css_to_change[sel_data[0]] = sel_data[4].cssText
    for css_id, css_text in css_to_change.items():
        bk.writefile(css_id, css_text)
    return 0