Exemplo n.º 1
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)
Exemplo n.º 2
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