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)
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))
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
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