示例#1
0
 def wrapper(tokens, *args):
     results = []
     for part in split_on_comma(tokens):
         result = function(remove_whitespace(part), *args)
         if result is None:
             return None
         results.append(result)
     return results
示例#2
0
def background_image(token, base_url):
    if token.type != 'FUNCTION':
        return image_url([token], base_url)
    name = token.function_name.lower()
    if name in ('linear-gradient', 'repeating-linear-gradient'):
        arguments = split_on_comma(t for t in token.content if t.type != 'S')
        direction, color_stops = parse_linear_gradient_parameters(arguments)
        if color_stops:
            return 'gradient', LinearGradient(
                [parse_color_stop(stop) for stop in color_stops],
                direction, 'repeating' in name)
示例#3
0
    def _selector_as_string(self, selector):
        """
        Returns a selector as a CSS string

        :param selector: A list of tinycss Tokens
        :type selector: list
        :returns: The CSS string for the selector
        :rtype: str
        """
        return ','.join(''.join(token.as_css()
                                for token in strip_whitespace(token_list))
                        for token_list in split_on_comma(selector))
示例#4
0
def font_family(tokens):
    """``font-family`` property validation."""
    parts = split_on_comma(tokens)
    families = []
    for part in parts:
        if len(part) == 1 and part[0].type == 'STRING':
            families.append(part[0].value)
        elif part and all(token.type == 'IDENT' for token in part):
            families.append(' '.join(token.value for token in part))
        else:
            break
    else:
        return families
示例#5
0
def font_family(tokens):
    """``font-family`` property validation."""
    parts = split_on_comma(tokens)
    families = []
    for part in parts:
        if len(part) == 1 and part[0].type == 'STRING':
            families.append(part[0].value)
        elif part and all(token.type == 'IDENT' for token in part):
            families.append(' '.join(token.value for token in part))
        else:
            break
    else:
        return families
示例#6
0
    def _clean_rule(self, rule):
        """
        Cleans a css Rule by removing Selectors without matches on the tree
        Returns None if the whole rule do not match

        :param rule: CSS Rule to check
        :type rule: A tinycss Rule object
        :returns: A cleaned tinycss Rule with only Selectors matching the tree or None
        :rtype: tinycss Rule or None
        """
        # Always match @ rules
        if rule.at_keyword is not None:
            return rule

        # Clean selectors
        cleaned_token_list = []

        for token_list in split_on_comma(rule.selector):

            # If the token list matches the tree
            if self._token_list_matches_tree(token_list):

                # Add a Comma if multiple token lists matched
                if len(cleaned_token_list) > 0:
                    cleaned_token_list.append(
                        cssselect.parser.Token('DELIM', ',',
                                               len(cleaned_token_list) + 1))

                # Append it to the list of cleaned token list
                cleaned_token_list += token_list

        # Return None if selectors list is empty
        if not cleaned_token_list:
            return None

        # Update rule token list
        rule.selector = cleaned_token_list

        # Return cleaned rule
        return rule
示例#7
0
    def _clean_rule(self, rule):
        """
        Cleans a css Rule by removing Selectors without matches on the tree
        Returns None if the whole rule do not match

        :param rule: CSS Rule to check
        :type rule: A tinycss Rule object
        :returns: A cleaned tinycss Rule with only Selectors matching the tree or None
        :rtype: tinycss Rule or None
        """
        # Always match @ rules
        if rule.at_keyword is not None:
            return rule

        # Clean selectors
        cleaned_token_list = []

        for token_list in split_on_comma(rule.selector):

            # If the token list matches the tree
            if self._token_list_matches_tree(token_list):

                # Add a Comma if multiple token lists matched
                if len(cleaned_token_list) > 0:
                    cleaned_token_list.append(cssselect.parser.Token("DELIM", ",", len(cleaned_token_list) + 1))

                # Append it to the list of cleaned token list
                cleaned_token_list += token_list

        # Return None if selectors list is empty
        if not cleaned_token_list:
            return None

        # Update rule token list
        rule.selector = cleaned_token_list

        # Return cleaned rule
        return rule
示例#8
0
    def parse_media(self, tokens, errors):
        """For CSS 2.1, parse a list of media types.

        Media Queries are expected to override this.

        :param tokens:
            A list of tokens
        :raises:
            :class:`~.parsing.ParseError` on invalid media types/queries
        :returns:
            For CSS 2.1, a list of media types as strings
        """
        if not tokens:
            return ['all']
        media_types = []
        for part in split_on_comma(remove_whitespace(tokens)):
            types = [token.type for token in part]
            if types == ['IDENT']:
                media_types.append(part[0].value)
            else:
                raise ParseError(tokens[0], 'expected a media type'
                    + ((', got ' + ', '.join(types)) if types else ''))
        return media_types
示例#9
0
文件: css21.py 项目: AEliu/calibre
    def parse_media(self, tokens, errors):
        """For CSS 2.1, parse a list of media types.

        Media Queries are expected to override this.

        :param tokens:
            A list of tokens
        :raises:
            :class:`~.parsing.ParseError` on invalid media types/queries
        :returns:
            For CSS 2.1, a list of media types as strings
        """
        if not tokens:
            return ['all']
        media_types = []
        for part in split_on_comma(remove_whitespace(tokens)):
            types = [token.type for token in part]
            if types == ['IDENT']:
                media_types.append(part[0].value)
            else:
                raise ParseError(tokens[0], 'expected a media type'
                    + ((', got ' + ', '.join(types)) if types else ''))
        return media_types
示例#10
0
文件: media3.py 项目: tokot/calibre
    def parse_media(self, tokens, errors):
        if not tokens:
            return [MediaQuery('all')]
        queries = []

        for part in split_on_comma(remove_whitespace(tokens)):
            negated = False
            media_type = None
            expressions = []
            try:
                for i, tok in enumerate(part):
                    if i == 0 and tok.type == 'IDENT':
                        val = tok.value.lower()
                        if val == 'only':
                            continue  # ignore leading ONLY
                        if val == 'not':
                            negated = True
                            continue
                    if media_type is None and tok.type == 'IDENT':
                        media_type = tok.value
                        continue
                    elif media_type is None:
                        media_type = 'all'

                    if tok.type == 'IDENT' and tok.value.lower() == 'and':
                        continue
                    if not tok.is_container:
                        raise MalformedExpression(
                            tok,
                            'expected a media expression not a %s' % tok.type)
                    if tok.type != '(':
                        raise MalformedExpression(
                            tok,
                            'media expressions must be in parentheses not %s' %
                            tok.type)
                    content = remove_whitespace(tok.content)
                    if len(content) == 0:
                        raise MalformedExpression(
                            tok, 'media expressions cannot be empty')
                    if content[0].type != 'IDENT':
                        raise MalformedExpression(
                            content[0],
                            'expected a media feature not a %s' % tok.type)
                    media_feature, expr = content[0].value, None
                    if len(content) > 1:
                        if len(content) < 3:
                            raise MalformedExpression(
                                content[1],
                                'malformed media feature definition')
                        if content[1].type != ':':
                            raise MalformedExpression(content[1],
                                                      'expected a :')
                        expr = content[2:]
                        if len(expr) == 1:
                            expr = expr[0]
                        elif len(expr) == 3 and (
                                expr[0].type, expr[1].type, expr[1].value,
                                expr[2].type) == ('INTEGER', 'DELIM', '/',
                                                  'INTEGER'):
                            # This should really be moved into token_data, but
                            # since RATIO is not part of CSS 2.1 and does not
                            # occur anywhere else, we special case it here.
                            r = expr[0]
                            r.value = (expr[0].value, expr[2].value)
                            r.type = 'RATIO'
                            r._as_css = expr[0]._as_css + expr[
                                1]._as_css + expr[2]._as_css
                            expr = r
                        else:
                            raise MalformedExpression(
                                expr[0], 'malformed media feature definition')

                    expressions.append((media_feature, expr))
            except MalformedExpression as err:
                errors.extend(ParseError(err.tok, err.message))
                media_type, negated, expressions = 'all', True, ()
            queries.append(
                MediaQuery(media_type or 'all',
                           expressions=tuple(expressions),
                           negated=negated))

        return queries
示例#11
0
文件: media3.py 项目: AEliu/calibre
    def parse_media(self, tokens, errors):
        if not tokens:
            return [MediaQuery('all')]
        queries = []

        for part in split_on_comma(remove_whitespace(tokens)):
            negated = False
            media_type = None
            expressions = []
            try:
                for i, tok in enumerate(part):
                    if i == 0 and tok.type == 'IDENT':
                        val = tok.value.lower()
                        if val == 'only':
                            continue  # ignore leading ONLY
                        if val == 'not':
                            negated = True
                            continue
                    if media_type is None and tok.type == 'IDENT':
                        media_type = tok.value
                        continue
                    elif media_type is None:
                        media_type = 'all'

                    if tok.type == 'IDENT' and tok.value.lower() == 'and':
                        continue
                    if not tok.is_container:
                        raise MalformedExpression(tok, 'expected a media expression not a %s' % tok.type)
                    if tok.type != '(':
                        raise MalformedExpression(tok, 'media expressions must be in parentheses not %s' % tok.type)
                    content = remove_whitespace(tok.content)
                    if len(content) == 0:
                        raise MalformedExpression(tok, 'media expressions cannot be empty')
                    if content[0].type != 'IDENT':
                        raise MalformedExpression(content[0], 'expected a media feature not a %s' % tok.type)
                    media_feature, expr = content[0].value, None
                    if len(content) > 1:
                        if len(content) < 3:
                            raise MalformedExpression(content[1], 'malformed media feature definition')
                        if content[1].type != ':':
                            raise MalformedExpression(content[1], 'expected a :')
                        expr = content[2:]
                        if len(expr) == 1:
                            expr = expr[0]
                        elif len(expr) == 3 and (expr[0].type, expr[1].type, expr[1].value, expr[2].type) == (
                            'INTEGER', 'DELIM', '/', 'INTEGER'):
                            # This should really be moved into token_data, but
                            # since RATIO is not part of CSS 2.1 and does not
                            # occur anywhere else, we special case it here.
                            r = expr[0]
                            r.value = (expr[0].value, expr[2].value)
                            r.type = 'RATIO'
                            r._as_css = expr[0]._as_css + expr[1]._as_css + expr[2]._as_css
                            expr = r
                        else:
                            raise MalformedExpression(expr[0], 'malformed media feature definition')

                    expressions.append((media_feature, expr))
            except MalformedExpression as err:
                errors.extend(ParseError(err.tok, err.message))
                media_type, negated, expressions = 'all', True, ()
            queries.append(MediaQuery(media_type or 'all', expressions=tuple(expressions), negated=negated))

        return queries
示例#12
0
def expand_background(base_url, name, tokens):
    """Expand the ``background`` shorthand property.

    See http://dev.w3.org/csswg/css3-background/#the-background

    """
    properties = [
        'background_color', 'background_image', 'background_repeat',
        'background_attachment', 'background_position', 'background_size',
        'background_clip', 'background_origin']
    keyword = get_single_keyword(tokens)
    if keyword in ('initial', 'inherit'):
        for name in properties:
            yield name, keyword
        return

    def parse_layer(tokens, final_layer=False):
        results = {}
        def add(name, value):
            if value is None:
                return False
            name = 'background_' + name
            if name in results:
                raise InvalidValues
            results[name] = value
            return True

        # Make `tokens` a stack
        tokens = tokens[::-1]
        while tokens:
            if add('repeat', background_repeat.single_value(tokens[-2:][::-1])):
                del tokens[-2:]
                continue
            token = tokens[-1:]
            if (
                (final_layer and add('color', other_colors(token)))
                or add('image', image.single_value(token, base_url))
                or add('repeat', background_repeat.single_value(token))
                or add('attachment', background_attachment.single_value(token))
            ):
                tokens.pop()
                continue
            for n in (4, 3, 2, 1)[-len(tokens):]:
                n_tokens = tokens[-n:][::-1]
                position = background_position.single_value(n_tokens)
                if position is not None:
                    assert add('position', position)
                    del tokens[-n:]
                    if (tokens and tokens[-1].type == 'DELIM'
                            and tokens[-1].value == '/'):
                        for n in (3, 2)[-len(tokens):]:
                            # n includes the '/' delimiter.
                            n_tokens = tokens[-n:-1][::-1]
                            size = background_size.single_value(n_tokens)
                            if size is not None:
                                assert add('size', size)
                                del tokens[-n:]
                    break
            if position is not None:
                continue
            if add('origin', box.single_value(token)):
                tokens.pop()
                next_token = tokens[-1:]
                if add('clip', box.single_value(next_token)):
                    tokens.pop()
                else:
                    # The same keyword sets both:
                    assert add('clip', box.single_value(token))
                continue
            raise InvalidValues

        color = results.pop(
            'background_color', INITIAL_VALUES['background_color'])
        for name in properties:
            if name not in results:
                results[name] = INITIAL_VALUES[name][0]
        return color, results

    layers = reversed(split_on_comma(tokens))
    color, last_layer = parse_layer(next(layers), final_layer=True)
    results = dict((k, [v]) for k, v in last_layer.items())
    for tokens in layers:
        _, layer = parse_layer(tokens)
        for name, value in layer.items():
            results[name].append(value)
    for name, values in results.items():
        yield name, values[::-1]  # "Un-reverse"
    yield 'background-color', color
示例#13
0
    def _selector_as_string(self, selector):
        """
        Returns a selector as a CSS string

        :param selector: A list of tinycss Tokens
        :type selector: list
        :returns: The CSS string for the selector
        :rtype: str
        """
        return ",".join(
            "".join(token.as_css() for token in strip_whitespace(token_list)) for token_list in split_on_comma(selector)
        )