예제 #1
0
    def _productions(self):
        """Return definition used for parsing."""
        types = self._prods  # rename!

        itemProd = Choice(
            _ColorProd(self),
            _DimensionProd(self),
            _URIProd(self),
            _ValueProd(self),
            _CalcValueProd(self),
            _CSSVariableProd(self),
            _CSSFunctionProd(self),
        )
        funcProds = Sequence(
            Prod(
                name='FUNCTION',
                match=lambda t, v: t == types.FUNCTION,
                toSeq=lambda t, tokens: (t[0], normalize(t[1])),
            ),
            Choice(
                Sequence(
                    itemProd,
                    Sequence(PreDef.comma(optional=True),
                             itemProd,
                             minmax=lambda: (0, None)),
                    PreDef.funcEnd(stop=True),
                ),
                PreDef.funcEnd(stop=True),
            ),
        )
        return funcProds
예제 #2
0
    def _setCssText(self, cssText):
        self._checkReadonly()

        prods = Sequence(  # PreDef.unary(),
            Choice(
                PreDef.dimension(stop=True),
                PreDef.number(stop=True),
                PreDef.percentage(stop=True),
            ))
        ok, seq, store, unused = ProdParser().parse(cssText, 'DimensionValue',
                                                    prods)
        self.wellformed = ok
        if ok:
            item = seq[0]

            sign, v, d = self.__reUnNumDim.findall(normalize(item.value))[0]
            if '.' in v:
                val = float(sign + v)
            else:
                val = int(sign + v)

            dim = None
            if d:
                dim = d

            self._sign = sign
            self._value = val
            self._dimension = dim
            self._type = item.type

            self._setSeq(seq)
예제 #3
0
    def _setCssText(self, cssText):
        self._checkReadonly()
        types = self._prods  # rename!
        valueProd = Prod(
            name='value',
            match=lambda t, v: t in (types.NUMBER, types.PERCENTAGE),
            toSeq=lambda t, v: (CSSPrimitiveValue, CSSPrimitiveValue(v)),
            toStore='parts',
        )
        # COLOR PRODUCTION
        funccolor = Sequence(
            Prod(
                name='FUNC',
                match=lambda t, v: t == types.FUNCTION and cssutils.helper.
                normalize(v) in ('rgb(', 'rgba(', 'hsl(', 'hsla('),
                toSeq=lambda t, v: (t, v),  # cssutils.helper.normalize(v)),
                toStore='colorType',
            ),
            PreDef.unary(),
            valueProd,
            # 2 or 3 more values starting with Comma
            Sequence(PreDef.comma(),
                     PreDef.unary(),
                     valueProd,
                     minmax=lambda: (2, 3)),
            PreDef.funcEnd(),
        )
        colorprods = Choice(
            funccolor,
            PreDef.hexcolor('colorType'),
            Prod(
                name='named color',
                match=lambda t, v: t == types.IDENT,
                toStore='colorType',
            ),
        )
        # store: colorType, parts
        wellformed, seq, store, unusedtokens = ProdParser().parse(
            cssText, 'RGBColor', colorprods, keepS=True, store={'parts': []})

        if wellformed:
            self.wellformed = True
            if store['colorType'].type == self._prods.HASH:
                self._colorType = 'HEX'
            elif store['colorType'].type == self._prods.IDENT:
                self._colorType = 'Named Color'
            else:
                self._colorType = store['colorType'].value[:-1]
                # self._colorType = \
                # cssutils.helper.normalize(store['colorType'].value)[:-1]

            self._setSeq(seq)
예제 #4
0
    def _productiondefinition(self):
        """Return definition used for parsing."""
        types = self._prods  # rename!

        value = Sequence(
            PreDef.unary(),
            Prod(
                name='PrimitiveValue',
                match=lambda t, v: t in (
                    types.DIMENSION,
                    types.HASH,
                    types.IDENT,
                    types.NUMBER,
                    types.PERCENTAGE,
                    types.STRING,
                ),
                toSeq=lambda t, tokens: (t[0], CSSPrimitiveValue(t[1])),
            ),
        )
        valueOrFunc = Choice(
            value,
            # FUNC is actually not in spec but used in e.g. Prince
            PreDef.function(toSeq=lambda t, tokens: (
                'FUNCTION',
                CSSFunction(cssutils.helper.pushtoken(t, tokens)),
            )),
        )
        funcProds = Sequence(
            Prod(
                name='FUNC',
                match=lambda t, v: t == types.FUNCTION,
                toSeq=lambda t, tokens:
                (t[0], cssutils.helper.normalize(t[1])),
            ),
            Choice(
                Sequence(
                    valueOrFunc,
                    # more values starting with Comma
                    # should use store where colorType is saved to
                    # define min and may, closure?
                    Sequence(PreDef.comma(),
                             valueOrFunc,
                             minmax=lambda: (0, None)),
                    PreDef.funcEnd(stop=True),
                ),
                PreDef.funcEnd(stop=True),
            ),
        )
        return funcProds
예제 #5
0
    def _productiondefinition(self):
        """Return defintion used for parsing."""
        types = self._prods  # rename!

        def toSeq(t, tokens):
            "Do not normalize function name!"
            return t[0], t[1]

        funcProds = Sequence(
            Prod(name='expression',
                 match=lambda t, v: t == types.FUNCTION,
                 toSeq=toSeq),
            Sequence(
                Choice(
                    Prod(
                        name='nested function',
                        match=lambda t, v: t == self._prods.FUNCTION,
                        toSeq=lambda t, tokens: (
                            ExpressionValue._functionName,
                            ExpressionValue(
                                cssutils.helper.pushtoken(t, tokens)),
                        ),
                    ),
                    Prod(
                        name='part',
                        match=lambda t, v: v != ')',
                        toSeq=lambda t, tokens: (t[0], t[1]),
                    ),
                ),
                minmax=lambda: (0, None),
            ),
            PreDef.funcEnd(stop=True),
        )
        return funcProds
예제 #6
0
    def _setCssText(self, cssText):
        self._checkReadonly()

        prods = Choice(
            PreDef.hexcolor(stop=True),
            PreDef.ident(stop=True),
            PreDef.string(stop=True),
            PreDef.unicode_range(stop=True),
        )
        ok, seq, store, unused = ProdParser().parse(cssText, 'Value', prods)
        self.wellformed = ok
        if ok:
            # only 1 value anyway!
            self._type = seq[0].type
            self._value = seq[0].value

            self._setSeq(seq)
예제 #7
0
def _CSSVariableProd(parent, nextSor=False, toStore=None):
    return PreDef.variable(
        nextSor=nextSor,
        toStore=toStore,
        toSeq=lambda t, tokens: (
            CSSVariable._functionName,
            CSSVariable(pushtoken(t, tokens), parent=parent),
        ),
    )
예제 #8
0
    def _setCssText(self, cssText):
        self._checkReadonly()

        types = self._prods  # rename!

        funcProds = Sequence(
            Prod(name='var', match=lambda t, v: t == types.FUNCTION),
            PreDef.ident(toStore='ident'),
            PreDef.funcEnd(stop=True),
        )

        # store: name of variable
        store = {'ident': None}
        wellformed, seq, store, unusedtokens = ProdParser().parse(
            cssText, 'CSSVariable', funcProds, keepS=True)
        if wellformed:
            self._name = store['ident'].value
            self._setSeq(seq)
            self.wellformed = True
예제 #9
0
    def _setCssText(self, cssText):
        self._checkReadonly()

        types = self._prods  # rename!
        prods = Sequence(
            Prod(
                name='var',
                match=lambda t, v: t == types.FUNCTION and normalize(v) ==
                'var(',
            ),
            PreDef.ident(toStore='ident'),
            Sequence(
                PreDef.comma(),
                Choice(
                    _ColorProd(self, toStore='fallback'),
                    _DimensionProd(self, toStore='fallback'),
                    _URIProd(self, toStore='fallback'),
                    _ValueProd(self, toStore='fallback'),
                    _CalcValueProd(self, toStore='fallback'),
                    _CSSVariableProd(self, toStore='fallback'),
                    _CSSFunctionProd(self, toStore='fallback'),
                ),
                minmax=lambda: (0, 1),
            ),
            PreDef.funcEnd(stop=True),
        )

        # store: name of variable
        store = {'ident': None, 'fallback': None}
        ok, seq, store, unused = ProdParser().parse(cssText, 'CSSVariable',
                                                    prods)
        self.wellformed = ok

        if ok:
            self._name = store['ident'].value
            try:
                self._fallback = store['fallback'].value
            except KeyError:
                self._fallback = None

            self._setSeq(seq)
예제 #10
0
    def _setCssText(self, cssText):
        self._checkReadonly()

        prods = Sequence(PreDef.uri(stop=True))

        ok, seq, store, unused = ProdParser().parse(cssText, 'URIValue', prods)
        self.wellformed = ok
        if ok:
            # only 1 value only anyway
            self._type = seq[0].type
            self._value = seq[0].value

            self._setSeq(seq)
예제 #11
0
    def _setCssText(self, cssText):
        self._checkReadonly()

        types = self._prods  # rename!

        _operator = Choice(
            Prod(
                name='Operator */',
                match=lambda t, v: v in '*/',
                toSeq=lambda t, tokens: (t[0], t[1]),
            ),
            Sequence(
                PreDef.S(),
                Choice(
                    Sequence(
                        Prod(
                            name='Operator */',
                            match=lambda t, v: v in '*/',
                            toSeq=lambda t, tokens: (t[0], t[1]),
                        ),
                        PreDef.S(optional=True),
                    ),
                    Sequence(
                        Prod(
                            name='Operator +-',
                            match=lambda t, v: v in '+-',
                            toSeq=lambda t, tokens: (t[0], t[1]),
                        ),
                        PreDef.S(),
                    ),
                    PreDef.funcEnd(stop=True, mayEnd=True),
                ),
            ),
        )

        _operant = lambda: Choice(  # noqa:E731
            _DimensionProd(self), _CalcValueProd(self), _CSSVariableProd(self))

        prods = Sequence(
            Prod(
                name='CALC',
                match=lambda t, v: t == types.FUNCTION and normalize(v) ==
                'calc(',
            ),
            PreDef.S(optional=True),
            _operant(),
            Sequence(_operator, _operant(), minmax=lambda: (0, None)),
            PreDef.funcEnd(stop=True),
        )

        # store: name of variable
        ok, seq, store, unused = ProdParser().parse(cssText,
                                                    'CSSCalc',
                                                    prods,
                                                    checkS=True)
        self.wellformed = ok
        if ok:
            self._setSeq(seq)
예제 #12
0
    def setVariable(self, variableName, value):
        """Used to set a variable value within this variable declaration block.

        :param variableName:
            The name of the CSS variable.
        :param value:
            The new value of the variable, may also be a PropertyValue object.

        :exceptions:
            - :exc:`~xml.dom.SyntaxErr`:
              Raised if the specified value has a syntax error and is
              unparsable.
            - :exc:`~xml.dom.NoModificationAllowedErr`:
              Raised if this declaration is readonly or the property is
              readonly.
        """
        self._checkReadonly()

        # check name
        wellformed, seq, store, unused = ProdParser().parse(
            normalize(variableName), 'variableName', Sequence(PreDef.ident()))
        if not wellformed:
            self._log.error('Invalid variableName: %r: %r' %
                            (variableName, value))
        else:
            # check value
            if isinstance(value, PropertyValue):
                v = value
            else:
                v = PropertyValue(cssText=value, parent=self)

            if not v.wellformed:
                self._log.error('Invalid variable value: %r: %r' %
                                (variableName, value))
            else:
                # update seq
                self.seq._readonly = False

                variableName = normalize(variableName)

                if variableName in self._vars:
                    for i, x in enumerate(self.seq):
                        if x.value[0] == variableName:
                            self.seq.replace(i, [variableName, v], x.type,
                                             x.line, x.col)
                            break
                else:
                    self.seq.append([variableName, v], 'var')
                self.seq._readonly = True
                self._vars[variableName] = v
예제 #13
0
    def _productions(self):
        """Return definition used for parsing."""
        types = self._prods  # rename!

        func = Prod(
            name='MSValue-Sub',
            match=lambda t, v: t == self._prods.FUNCTION,
            toSeq=lambda t, tokens: (
                MSValue._functionName,
                MSValue(pushtoken(t, tokens), parent=self),
            ),
        )

        funcProds = Sequence(
            Prod(
                name='FUNCTION',
                match=lambda t, v: t == types.FUNCTION,
                toSeq=lambda t, tokens: (t[0], t[1]),
            ),
            Sequence(
                Choice(
                    _ColorProd(self),
                    _DimensionProd(self),
                    _URIProd(self),
                    _ValueProd(self),
                    _MSValueProd(self),
                    # _CalcValueProd(self),
                    _CSSVariableProd(self),
                    func,
                    # _CSSFunctionProd(self),
                    Prod(
                        name='MSValuePart',
                        match=lambda t, v: v != ')',
                        toSeq=lambda t, tokens: (t[0], t[1]),
                    ),
                ),
                minmax=lambda: (0, None),
            ),
            PreDef.funcEnd(stop=True),
        )
        return funcProds
예제 #14
0
    def _setMediaText(self, mediaText):
        """
        :param mediaText:
            a single media query string, e.g. ``print and (min-width: 25cm)``

        :exceptions:
            - :exc:`~xml.dom.SyntaxErr`:
              Raised if the specified string value has a syntax error and is
              unparsable.
            - :exc:`~xml.dom.InvalidCharacterErr`:
              Raised if the given mediaType is unknown.
            - :exc:`~xml.dom.NoModificationAllowedErr`:
              Raised if this media query is readonly.

        media_query
         : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*
         | expression [ AND S* expression ]*
         ;
        media_type
         : IDENT
         ;
        expression
         : '(' S* media_feature S* [ ':' S* expr ]? ')' S*
         ;
        media_feature
         : IDENT
         ;

        """
        self._checkReadonly()

        expression = lambda: Sequence(  # noqa
            PreDef.char(name='expression', char='('),
            Prod(name='media_feature', match=lambda t, v: t == PreDef.types.IDENT),
            Sequence(
                PreDef.char(name='colon', char=':'),
                cssutils.css.value.MediaQueryValueProd(self),
                minmax=lambda: (0, 1),  # optional
            ),
            PreDef.char(
                name='expression END', char=')', stopIfNoMoreMatch=self._partof
            ),
        )

        prods = Choice(
            Sequence(
                Prod(
                    name='ONLY|NOT',  # media_query
                    match=lambda t, v: t == PreDef.types.IDENT
                    and normalize(v) in ('only', 'not'),
                    optional=True,
                    toStore='not simple',
                ),
                Prod(
                    name='media_type',
                    match=lambda t, v: t == PreDef.types.IDENT
                    and normalize(v) in self.MEDIA_TYPES,
                    stopIfNoMoreMatch=True,
                    toStore='media_type',
                ),
                Sequence(
                    Prod(
                        name='AND',
                        match=lambda t, v: t == PreDef.types.IDENT
                        and normalize(v) == 'and',
                        toStore='not simple',
                    ),
                    expression(),
                    minmax=lambda: (0, None),
                ),
            ),
            Sequence(
                expression(),
                Sequence(
                    Prod(
                        name='AND',
                        match=lambda t, v: t == PreDef.types.IDENT
                        and normalize(v) == 'and',
                    ),
                    expression(),
                    minmax=lambda: (0, None),
                ),
            ),
        )

        # parse
        ok, seq, store, unused = ProdParser().parse(mediaText, 'MediaQuery', prods)
        self._wellformed = ok
        if ok:
            try:
                media_type = store['media_type']
            except KeyError:
                pass
            else:
                if 'not simple' not in store:
                    self.mediaType = media_type.value

            # TODO: filter doubles!
            self._setSeq(seq)
예제 #15
0
    def _setCssText(self, cssText):  # noqa: C901
        """
        Format::

            unary_operator
              : '-' | '+'
              ;
            operator
              : '/' S* | ',' S* | /* empty */
              ;
            expr
              : term [ operator term ]*
              ;
            term
              : unary_operator?
                [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* |
                  ANGLE S* | TIME S* | FREQ S* ]
              | STRING S* | IDENT S* | URI S* | hexcolor | function
              | UNICODE-RANGE S*
              ;
            function
              : FUNCTION S* expr ')' S*
              ;
            /*
             * There is a constraint on the color that it must
             * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
             * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
             */
            hexcolor
              : HASH S*
              ;

        :exceptions:
            - :exc:`~xml.dom.SyntaxErr`:
              Raised if the specified CSS string value has a syntax error
              (according to the attached property) or is unparsable.
            - :exc:`~xml.dom.InvalidModificationErr`:
              TODO: Raised if the specified CSS string value represents a
              different type of values than the values allowed by the CSS
              property.
            - :exc:`~xml.dom.NoModificationAllowedErr`:
              Raised if this value is readonly.
        """
        self._checkReadonly()

        # used as operator is , / or S
        nextSor = ',/'

        term = Choice(
            Sequence(
                PreDef.unary(),
                Choice(
                    PreDef.number(nextSor=nextSor),
                    PreDef.percentage(nextSor=nextSor),
                    PreDef.dimension(nextSor=nextSor),
                ),
            ),
            PreDef.string(nextSor=nextSor),
            PreDef.ident(nextSor=nextSor),
            PreDef.uri(nextSor=nextSor),
            PreDef.hexcolor(nextSor=nextSor),
            PreDef.unicode_range(nextSor=nextSor),
            # special case IE only expression
            Prod(
                name='expression',
                match=lambda t, v: t == self._prods.FUNCTION and
                (cssutils.helper.normalize(v) in (
                    'expression(',
                    'alpha(',
                    'blur(',
                    'chroma(',
                    'dropshadow(',
                    'fliph(',
                    'flipv(',
                    'glow(',
                    'gray(',
                    'invert(',
                    'mask(',
                    'shadow(',
                    'wave(',
                    'xray(',
                ) or v.startswith('progid:DXImageTransform.Microsoft.')),
                nextSor=nextSor,
                toSeq=lambda t, tokens: (
                    ExpressionValue._functionName,
                    ExpressionValue(cssutils.helper.pushtoken(t, tokens),
                                    parent=self),
                ),
            ),
            # CSS Variable var(
            PreDef.variable(
                nextSor=nextSor,
                toSeq=lambda t, tokens: (
                    'CSSVariable',
                    CSSVariable(cssutils.helper.pushtoken(t, tokens),
                                parent=self),
                ),
            ),
            # calc(
            PreDef.calc(
                nextSor=nextSor,
                toSeq=lambda t, tokens: (
                    CalcValue._functionName,
                    CalcValue(cssutils.helper.pushtoken(t, tokens),
                              parent=self),
                ),
            ),
            # TODO:
            # # rgb/rgba(
            # Prod(name='RGBColor',
            #      match=lambda t, v: t == self._prods.FUNCTION and (
            #         cssutils.helper.normalize(v) in (u'rgb(',
            #                                          u'rgba('
            #                                          )
            #      ),
            #      nextSor=nextSor,
            #             toSeq=lambda t, tokens: (RGBColor._functionName,
            #                                      RGBColor(
            #                   cssutils.helper.pushtoken(t, tokens),
            #                   parent=self)
            #                                      )
            # ),
            # other functions like rgb( etc
            PreDef.function(
                nextSor=nextSor,
                toSeq=lambda t, tokens: (
                    'FUNCTION',
                    CSSFunction(cssutils.helper.pushtoken(t, tokens),
                                parent=self),
                ),
            ),
        )
        operator = Choice(
            PreDef.S(),
            PreDef.char('comma',
                        ',',
                        toSeq=lambda t, tokens: ('operator', t[1])),
            PreDef.char('slash',
                        '/',
                        toSeq=lambda t, tokens: ('operator', t[1])),
            optional=True,
        )
        # CSSValue PRODUCTIONS
        valueprods = Sequence(
            term,
            Sequence(
                operator,  # mayEnd this Sequence if whitespace
                # TODO: only when setting via other class
                # used by variabledeclaration currently
                PreDef.char('END', ';', stopAndKeep=True, optional=True),
                term,
                minmax=lambda: (0, None),
            ),
        )
        # parse
        wellformed, seq, store, notused = ProdParser().parse(cssText,
                                                             'CSSValue',
                                                             valueprods,
                                                             keepS=True)
        if wellformed:
            # - count actual values and set firstvalue which is used later on
            # - combine comma separated list, e.g. font-family to a single item
            # - remove S which should be an operator but is no needed
            count, firstvalue = 0, ()
            newseq = self._tempSeq()
            i, end = 0, len(seq)
            while i < end:
                item = seq[i]
                if item.type == self._prods.S:
                    pass

                elif (item.value, item.type) == (',', 'operator'):
                    # , separared counts as a single STRING for now
                    # URI or STRING value might be a single CHAR too!
                    newseq.appendItem(item)
                    count -= 1
                    if firstvalue:
                        # list of IDENTs is handled as STRING!
                        if firstvalue[1] == self._prods.IDENT:
                            firstvalue = firstvalue[0], 'STRING'

                elif item.value == '/':
                    # / separated items count as one
                    newseq.appendItem(item)

                elif item.value == '-' or item.value == '+':
                    # combine +- and following number or other
                    i += 1
                    try:
                        next = seq[i]
                    except IndexError:
                        firstvalue = ()  # raised later
                        break

                    newval = item.value + next.value
                    newseq.append(newval, next.type, item.line, item.col)
                    if not firstvalue:
                        firstvalue = (newval, next.type)
                    count += 1

                elif item.type != cssutils.css.CSSComment:
                    newseq.appendItem(item)
                    if not firstvalue:
                        firstvalue = (item.value, item.type)
                    count += 1

                else:
                    newseq.appendItem(item)

                i += 1

            if not firstvalue:
                self._log.error('CSSValue: Unknown syntax or no value: %r.' %
                                self._valuestr(cssText))
            else:
                # ok and set
                self._setSeq(newseq)
                self.wellformed = wellformed

                if hasattr(self, '_value'):
                    # only in case of CSSPrimitiveValue, else remove!
                    del self._value

                if count == 1:
                    # inherit, primitive or variable
                    if isinstance(
                            firstvalue[0],
                            str) and 'inherit' == cssutils.helper.normalize(
                                firstvalue[0]):
                        self.__class__ = CSSValue
                        self._cssValueType = CSSValue.CSS_INHERIT
                    elif 'CSSVariable' == firstvalue[1]:
                        self.__class__ = CSSVariable
                        self._value = firstvalue
                        # TODO: remove major hack!
                        self._name = firstvalue[0]._name
                    else:
                        self.__class__ = CSSPrimitiveValue
                        self._value = firstvalue

                elif count > 1:
                    # valuelist
                    self.__class__ = CSSValueList

                    # change items in list to specific type (primitive etc)
                    newseq = self._tempSeq()
                    commalist = []
                    nexttocommalist = False

                    def itemValue(item):
                        "Reserialized simple item.value"
                        if self._prods.STRING == item.type:
                            return cssutils.helper.string(item.value)
                        elif self._prods.URI == item.type:
                            return cssutils.helper.uri(item.value)
                        elif (self._prods.FUNCTION == item.type
                              or 'CSSVariable' == item.type):
                            return item.value.cssText
                        else:
                            return item.value

                    def saveifcommalist(commalist, newseq):
                        """
                        saves items in commalist to seq and items
                        if anything in there
                        """
                        if commalist:
                            newseq.replace(
                                -1,
                                CSSPrimitiveValue(cssText=''.join(commalist)),
                                CSSPrimitiveValue,
                                newseq[-1].line,
                                newseq[-1].col,
                            )
                            del commalist[:]

                    for i, item in enumerate(self._seq):
                        if issubclass(type(item.value), CSSValue):
                            # set parent of CSSValueList items to the lists
                            # parent
                            item.value.parent = self.parent

                        if item.type in (
                                self._prods.DIMENSION,
                                self._prods.FUNCTION,
                                self._prods.HASH,
                                self._prods.IDENT,
                                self._prods.NUMBER,
                                self._prods.PERCENTAGE,
                                self._prods.STRING,
                                self._prods.URI,
                                self._prods.UNICODE_RANGE,
                                'CSSVariable',
                        ):
                            if nexttocommalist:
                                # wait until complete
                                commalist.append(itemValue(item))
                            else:
                                saveifcommalist(commalist, newseq)
                                # append new item
                                if hasattr(item.value, 'cssText'):
                                    newseq.append(
                                        item.value,
                                        item.value.__class__,
                                        item.line,
                                        item.col,
                                    )

                                else:
                                    newseq.append(
                                        CSSPrimitiveValue(itemValue(item)),
                                        CSSPrimitiveValue,
                                        item.line,
                                        item.col,
                                    )

                            nexttocommalist = False

                        elif ',' == item.value:
                            if not commalist:
                                # save last item to commalist
                                commalist.append(itemValue(self._seq[i - 1]))
                            commalist.append(',')
                            nexttocommalist = True

                        else:
                            if nexttocommalist:
                                commalist.append(item.value.cssText)
                            else:
                                newseq.appendItem(item)

                    saveifcommalist(commalist, newseq)
                    self._setSeq(newseq)

                else:
                    # should not happen...
                    self.__class__ = CSSValue
                    self._cssValueType = CSSValue.CSS_CUSTOM
예제 #16
0
    def _setCssText(self, cssText):  # noqa: C901
        self._checkReadonly()
        types = self._prods  # rename!

        component = Choice(
            PreDef.unary(toSeq=lambda t, tokens: (
                t[0],
                DimensionValue(pushtoken(t, tokens), parent=self),
            )),
            PreDef.number(toSeq=lambda t, tokens: (
                t[0],
                DimensionValue(pushtoken(t, tokens), parent=self),
            )),
            PreDef.percentage(toSeq=lambda t, tokens: (
                t[0],
                DimensionValue(pushtoken(t, tokens), parent=self),
            )),
        )
        noalp = Sequence(
            Prod(
                name='FUNCTION',
                match=lambda t, v: t == types.FUNCTION and v in
                ('rgb(', 'hsl('),
                toSeq=lambda t, tokens: (t[0], normalize(t[1])),
            ),
            component,
            Sequence(PreDef.comma(optional=True),
                     component,
                     minmax=lambda: (2, 2)),
            PreDef.funcEnd(stop=True),
        )
        witha = Sequence(
            Prod(
                name='FUNCTION',
                match=lambda t, v: t == types.FUNCTION and v in
                ('rgba(', 'hsla('),
                toSeq=lambda t, tokens: (t[0], normalize(t[1])),
            ),
            component,
            Sequence(PreDef.comma(optional=True),
                     component,
                     minmax=lambda: (3, 3)),
            PreDef.funcEnd(stop=True),
        )
        namedcolor = Prod(
            name='Named Color',
            match=lambda t, v: t == 'IDENT' and
            (normalize(v) in list(self.COLORS.keys())),
            stop=True,
        )

        prods = Choice(PreDef.hexcolor(stop=True), namedcolor, noalp, witha)

        ok, seq, store, unused = ProdParser().parse(cssText, self.type, prods)
        self.wellformed = ok
        if ok:
            t, v = seq[0].type, seq[0].value
            if 'IDENT' == t:
                rgba = self.COLORS[normalize(v)]
            if 'HASH' == t:
                if len(v) == 4:
                    # HASH #rgb
                    rgba = (
                        int(2 * v[1], 16),
                        int(2 * v[2], 16),
                        int(2 * v[3], 16),
                        1.0,
                    )
                else:
                    # HASH #rrggbb
                    rgba = (int(v[1:3], 16), int(v[3:5], 16), int(v[5:7],
                                                                  16), 1.0)

            elif 'FUNCTION' == t:
                functiontype, raw, check = None, [], ''
                HSL = False

                for item in seq:
                    try:
                        type_ = item.value.type
                    except AttributeError:
                        # type of function, e.g. rgb(
                        if item.type == 'FUNCTION':
                            functiontype = item.value
                            HSL = functiontype in ('hsl(', 'hsla(')
                        continue

                    # save components
                    if type_ == Value.NUMBER:
                        raw.append(item.value.value)
                        check += 'N'
                    elif type_ == Value.PERCENTAGE:
                        if HSL:
                            # save as percentage fraction
                            raw.append(item.value.value / 100.0)
                        else:
                            # save as real value of percentage of 255
                            raw.append(int(255 * item.value.value / 100))
                        check += 'P'

                if HSL:
                    # convert to rgb
                    # h is 360 based (circle)
                    h, s, l_ = raw[0] / 360.0, raw[1], raw[2]
                    # ORDER h l s !!!
                    r, g, b = colorsys.hls_to_rgb(h, l_, s)
                    # back to 255 based
                    rgba = [
                        int(round(r * 255)),
                        int(round(g * 255)),
                        int(round(b * 255)),
                    ]

                    if len(raw) > 3:
                        rgba.append(raw[3])

                else:
                    # rgb, rgba
                    rgba = raw

                if len(rgba) < 4:
                    rgba.append(1.0)

                # validate
                checks = {
                    'rgb(': ('NNN', 'PPP'),
                    'rgba(': ('NNNN', 'PPPN'),
                    'hsl(': ('NPP', ),
                    'hsla(': ('NPPN', ),
                }
                if check not in checks[functiontype]:
                    self._log.error('ColorValue has invalid %s) parameters: '
                                    '%s (N=Number, P=Percentage)' %
                                    (functiontype, check))

            self._colorType = t
            self._red, self._green, self._blue, self._alpha = tuple(rgba)
            self._setSeq(seq)
예제 #17
0
    def _setCssText(self, cssText):
        """Setting this attribute will result in the parsing of the new value
        and resetting of all the properties in the declaration block
        including the removal or addition of properties.

        :exceptions:
            - :exc:`~xml.dom.NoModificationAllowedErr`:
              Raised if this declaration is readonly or a property is readonly.
            - :exc:`~xml.dom.SyntaxErr`:
              Raised if the specified CSS string value has a syntax error and
              is unparsable.

        Format::

            variableset
            : vardeclaration [ ';' S* vardeclaration ]*
            ;

            vardeclaration
            : varname ':' S* term
            ;

            varname
            : IDENT S*
            ;

            expr
            : [ VARCALL | term ] [ operator [ VARCALL | term ] ]*
            ;

        """
        self._checkReadonly()

        vardeclaration = Sequence(
            PreDef.ident(),
            PreDef.char(':', ':', toSeq=False, optional=True),
            # PreDef.S(toSeq=False, optional=True),
            Prod(
                name='term',
                match=lambda t, v: True,
                toSeq=lambda t, tokens: (
                    'value',
                    PropertyValue(itertools.chain([t], tokens), parent=self),
                ),
            ),
        )
        prods = Sequence(
            vardeclaration,
            Sequence(
                PreDef.S(optional=True),
                PreDef.char(';', ';', toSeq=False, optional=True),
                PreDef.S(optional=True),
                vardeclaration,
                minmax=lambda: (0, None),
            ),
            PreDef.S(optional=True),
            PreDef.char(';', ';', toSeq=False, optional=True),
        )
        # parse
        wellformed, seq, store, notused = ProdParser().parse(
            cssText, 'CSSVariableDeclaration', prods, emptyOk=True)
        if wellformed:
            newseq = self._tempSeq()
            newvars = {}

            # seq contains only name: value pairs plus comments etc
            nameitem = None
            for item in seq:
                if 'IDENT' == item.type:
                    nameitem = item
                elif 'value' == item.type:
                    nname = normalize(nameitem.value)
                    if nname in newvars:
                        # replace var with same name
                        for i, it in enumerate(newseq):
                            if normalize(it.value[0]) == nname:
                                newseq.replace(
                                    i,
                                    (nameitem.value, item.value),
                                    'var',
                                    nameitem.line,
                                    nameitem.col,
                                )
                    else:
                        # saved non normalized name for reserialization
                        newseq.append(
                            (nameitem.value, item.value),
                            'var',
                            nameitem.line,
                            nameitem.col,
                        )

                    #                    newseq.append((nameitem.value, item.value),
                    #                                  'var',
                    #                                  nameitem.line, nameitem.col)

                    newvars[nname] = item.value

                else:
                    newseq.appendItem(item)

            self._setSeq(newseq)
            self._vars = newvars
            self.wellformed = True
예제 #18
0
    def _setMediaText(self, mediaText):  # noqa: C901
        """
        :param mediaText:
            simple value or comma-separated list of media

        :exceptions:
            - - :exc:`~xml.dom.SyntaxErr`:
              Raised if the specified string value has a syntax error and is
              unparsable.
            - - :exc:`~xml.dom.NoModificationAllowedErr`:
              Raised if this media list is readonly.
        """
        self._checkReadonly()

        mediaquery = lambda: Prod(  # noqa
            name='MediaQueryStart',
            match=lambda t, v: t == 'IDENT' or v == '(',
            toSeq=lambda t, tokens: (
                'MediaQuery',
                MediaQuery(pushtoken(t, tokens), _partof=True),
            ),
        )
        prods = Sequence(
            Sequence(PreDef.comment(parent=self), minmax=lambda: (0, None)),
            mediaquery(),
            Sequence(PreDef.comma(toSeq=False),
                     mediaquery(),
                     minmax=lambda: (0, None)),
        )
        # parse
        ok, seq, store, unused = ProdParser().parse(mediaText,
                                                    'MediaList',
                                                    prods,
                                                    debug="ml")

        # each mq must be valid
        atleastone = False

        for item in seq:
            v = item.value
            if isinstance(v, MediaQuery):
                if not v.wellformed:
                    ok = False
                    break
                else:
                    atleastone = True

        # must be at least one value!
        if not atleastone:
            ok = False
            self._wellformed = ok
            self._log.error('MediaQuery: No content.', error=xml.dom.SyntaxErr)

        self._wellformed = ok

        if ok:
            mediaTypes = []
            finalseq = cssutils.util.Seq(readonly=False)
            commentseqonly = cssutils.util.Seq(readonly=False)
            for item in seq:
                # filter for doubles?
                if item.type == 'MediaQuery':
                    mediaType = item.value.mediaType
                    if mediaType:
                        if mediaType == 'all':
                            # remove anthing else and keep all+comments(!) only
                            finalseq = commentseqonly
                            finalseq.append(item)
                            break
                        elif mediaType in mediaTypes:
                            continue
                        else:
                            mediaTypes.append(mediaType)
                elif isinstance(item.value,
                                cssutils.css.csscomment.CSSComment):
                    commentseqonly.append(item)

                finalseq.append(item)

            self._setSeq(finalseq)
예제 #19
0
 def prods():
     return Sequence(PreDef.char(';', ';'), PreDef.char(':', ':'))
예제 #20
0
    def _setCssText(self, cssText):
        if isinstance(cssText, (int, float)):
            cssText = str(cssText)  # if it is a number
        """
        Format::

            unary_operator
              : '-' | '+'
              ;
            operator
              : '/' S* | ',' S* | /* empty */
              ;
            expr
              : term [ operator term ]*
              ;
            term
              : unary_operator?
                [ NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* |
                  ANGLE S* | TIME S* | FREQ S* ]
              | STRING S* | IDENT S* | URI S* | hexcolor | function
              | UNICODE-RANGE S*
              ;
            function
              : FUNCTION S* expr ')' S*
              ;
            /*
             * There is a constraint on the color that it must
             * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
             * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
             */
            hexcolor
              : HASH S*
              ;

        :exceptions:
            - :exc:`~xml.dom.SyntaxErr`:
              Raised if the specified CSS string value has a syntax error
              (according to the attached property) or is unparsable.
            - :exc:`~xml.dom.InvalidModificationErr`:
              TODO: Raised if the specified CSS string value represents a
              different type of values than the values allowed by the CSS
              property.
            - :exc:`~xml.dom.NoModificationAllowedErr`:
              Raised if this value is readonly.
        """
        self._checkReadonly()

        # used as operator is , / or S
        nextSor = ',/'
        term = Choice(
            _ColorProd(self, nextSor),
            _DimensionProd(self, nextSor),
            _URIProd(self, nextSor),
            _ValueProd(self, nextSor),
            #                      _Rect(self, nextSor),
            # all other functions
            _CSSVariableProd(self, nextSor),
            _MSValueProd(self, nextSor),
            _CalcValueProd(self, nextSor),
            _CSSFunctionProd(self, nextSor),
        )
        operator = Choice(
            PreDef.S(toSeq=False),
            PreDef.char('comma',
                        ',',
                        toSeq=lambda t, tokens: ('operator', t[1]),
                        optional=True),
            PreDef.char('slash',
                        '/',
                        toSeq=lambda t, tokens: ('operator', t[1]),
                        optional=True),
            optional=True,
        )
        prods = Sequence(
            term,
            Sequence(  # mayEnd this Sequence if whitespace
                operator,
                # TODO: only when setting via other class
                # used by variabledeclaration currently
                PreDef.char('END', ';', stopAndKeep=True, optional=True),
                # TODO: } and !important ends too!
                term,
                minmax=lambda: (0, None),
            ),
        )
        # parse
        ok, seq, store, unused = ProdParser().parse(cssText, 'PropertyValue',
                                                    prods)
        # must be at least one value!
        ok = ok and len(list(self.__items(seq))) > 0

        for item in seq:
            if hasattr(item.value, 'wellformed') and not item.value.wellformed:
                ok = False
                break

        self.wellformed = ok
        if ok:
            self._setSeq(seq)
        else:
            self._log.error('PropertyValue: Unknown syntax or no value: %s' %
                            self._valuestr(cssText))
예제 #21
0
    def _setCssText(self, cssText):
        """
        :exceptions:
            - :exc:`~xml.dom.SyntaxErr`:
              Raised if the specified CSS string value has a syntax error and
              is unparsable.
            - :exc:`~xml.dom.InvalidModificationErr`:
              Raised if the specified CSS string value represents a different
              type of rule than the current one.
            - :exc:`~xml.dom.HierarchyRequestErr`:
              Raised if the rule cannot be inserted at this point in the
              style sheet.
            - :exc:`~xml.dom.NoModificationAllowedErr`:
              Raised if the rule is readonly.
        """
        super(MarginRule, self)._setCssText(cssText)

        # TEMP: all style tokens are saved in store to fill styledeclaration
        # TODO: resolve when all generators
        styletokens = Prod(
            name='styletokens',
            match=lambda t, v: v != '}',
            # toSeq=False,
            toStore='styletokens',
            storeToken=True,
        )

        prods = Sequence(
            Prod(name='@ margin',
                 match=lambda t, v: t == 'ATKEYWORD' and self._normalize(v) in
                 MarginRule.margins,
                 toStore='margin'
                 # TODO?
                 # , exception=xml.dom.InvalidModificationErr
                 ),
            PreDef.char('OPEN', '{'),
            Sequence(
                Choice(PreDef.unknownrule(toStore='@'), styletokens),
                minmax=lambda: (0, None),
            ),
            PreDef.char('CLOSE', '}', stopAndKeep=True),
        )
        # parse
        ok, seq, store, unused = ProdParser().parse(cssText, 'MarginRule',
                                                    prods)

        if ok:
            # TODO: use seq for serializing instead of fixed stuff?
            self._setSeq(seq)

            if 'margin' in store:
                # may raise:
                self.margin = store['margin'].value
            else:
                self._log.error(
                    'No margin @keyword for this %s rule' % self.margin,
                    error=xml.dom.InvalidModificationErr,
                )

            # new empty style
            self.style = CSSStyleDeclaration(parentRule=self)

            if 'styletokens' in store:
                # may raise:
                self.style.cssText = store['styletokens']