You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1251 lines
51 KiB
1251 lines
51 KiB
"""CSSValue related classes
|
|
|
|
- CSSValue implements DOM Level 2 CSS CSSValue
|
|
- CSSPrimitiveValue implements DOM Level 2 CSS CSSPrimitiveValue
|
|
- CSSValueList implements DOM Level 2 CSS CSSValueList
|
|
|
|
"""
|
|
__all__ = ['CSSValue', 'CSSPrimitiveValue', 'CSSValueList', 'RGBColor',
|
|
'CSSVariable']
|
|
__docformat__ = 'restructuredtext'
|
|
__version__ = '$Id$'
|
|
|
|
from cssutils.prodparser import *
|
|
import cssutils
|
|
import cssutils.helper
|
|
import math
|
|
import re
|
|
import xml.dom
|
|
|
|
|
|
class CSSValue(cssutils.util._NewBase):
|
|
"""The CSSValue interface represents a simple or a complex value.
|
|
A CSSValue object only occurs in a context of a CSS property.
|
|
"""
|
|
|
|
# The value is inherited and the cssText contains "inherit".
|
|
CSS_INHERIT = 0
|
|
# The value is a CSSPrimitiveValue.
|
|
CSS_PRIMITIVE_VALUE = 1
|
|
# The value is a CSSValueList.
|
|
CSS_VALUE_LIST = 2
|
|
# The value is a custom value.
|
|
CSS_CUSTOM = 3
|
|
# The value is a CSSVariable.
|
|
CSS_VARIABLE = 4
|
|
|
|
_typestrings = {0: 'CSS_INHERIT' ,
|
|
1: 'CSS_PRIMITIVE_VALUE',
|
|
2: 'CSS_VALUE_LIST',
|
|
3: 'CSS_CUSTOM',
|
|
4: 'CSS_VARIABLE'}
|
|
|
|
def __init__(self, cssText=None, parent=None, readonly=False):
|
|
"""
|
|
:param cssText:
|
|
the parsable cssText of the value
|
|
:param readonly:
|
|
defaults to False
|
|
"""
|
|
super(CSSValue, self).__init__()
|
|
|
|
self._cssValueType = None
|
|
self.wellformed = False
|
|
self.parent = parent
|
|
if cssText is not None: # may be 0
|
|
if isinstance(cssText, int):
|
|
cssText = unicode(cssText) # if it is an integer
|
|
elif isinstance(cssText, float):
|
|
cssText = u'%f' % cssText # if it is a floating point number
|
|
|
|
self.cssText = cssText
|
|
|
|
self._readonly = readonly
|
|
|
|
def __repr__(self):
|
|
return u"cssutils.css.%s(%r)" % (self.__class__.__name__,
|
|
self.cssText)
|
|
|
|
def __str__(self):
|
|
return u"<cssutils.css.%s object cssValueTypeString=%r cssText=%r at "\
|
|
u"0x%x>" % (self.__class__.__name__,
|
|
self.cssValueTypeString,
|
|
self.cssText,
|
|
id(self))
|
|
|
|
def _setCssText(self, cssText):
|
|
"""
|
|
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 = u',/'
|
|
|
|
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 (u'expression(',
|
|
u'alpha(',
|
|
u'blur(',
|
|
u'chroma(',
|
|
u'dropshadow(',
|
|
u'fliph(',
|
|
u'flipv(',
|
|
u'glow(',
|
|
u'gray(',
|
|
u'invert(',
|
|
u'mask(',
|
|
u'shadow(',
|
|
u'wave(',
|
|
u'xray(') or
|
|
v.startswith(u'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,
|
|
u'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) == (u',', '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 == u'/':
|
|
# / separated items count as one
|
|
newseq.appendItem(item)
|
|
|
|
elif item.value == u'-' or item.value == u'+':
|
|
# 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(
|
|
u'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], basestring) and\
|
|
u'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=u''.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 u',' == item.value:
|
|
if not commalist:
|
|
# save last item to commalist
|
|
commalist.append(itemValue(self._seq[i - 1]))
|
|
commalist.append(u',')
|
|
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
|
|
|
|
cssText = property(lambda self: cssutils.ser.do_css_CSSValue(self),
|
|
_setCssText,
|
|
doc="A string representation of the current value.")
|
|
|
|
cssValueType = property(lambda self: self._cssValueType,
|
|
doc="A (readonly) code defining the type of the value.")
|
|
|
|
cssValueTypeString = property(
|
|
lambda self: CSSValue._typestrings.get(self.cssValueType, None),
|
|
doc="(readonly) Name of cssValueType.")
|
|
|
|
|
|
class CSSPrimitiveValue(CSSValue):
|
|
"""Represents a single CSS Value. May be used to determine the value of a
|
|
specific style property currently set in a block or to set a specific
|
|
style property explicitly within the block. Might be obtained from the
|
|
getPropertyCSSValue method of CSSStyleDeclaration.
|
|
|
|
Conversions are allowed between absolute values (from millimeters to
|
|
centimeters, from degrees to radians, and so on) but not between
|
|
relative values. (For example, a pixel value cannot be converted to a
|
|
centimeter value.) Percentage values can't be converted since they are
|
|
relative to the parent value (or another property value). There is one
|
|
exception for color percentage values: since a color percentage value
|
|
is relative to the range 0-255, a color percentage value can be
|
|
converted to a number; (see also the RGBColor interface).
|
|
"""
|
|
# constant: type of this CSSValue class
|
|
cssValueType = CSSValue.CSS_PRIMITIVE_VALUE
|
|
|
|
__types = cssutils.cssproductions.CSSProductions
|
|
|
|
# An integer indicating which type of unit applies to the value.
|
|
CSS_UNKNOWN = 0 # only obtainable via cssText
|
|
CSS_NUMBER = 1
|
|
CSS_PERCENTAGE = 2
|
|
CSS_EMS = 3
|
|
CSS_EXS = 4
|
|
CSS_PX = 5
|
|
CSS_CM = 6
|
|
CSS_MM = 7
|
|
CSS_IN = 8
|
|
CSS_PT = 9
|
|
CSS_PC = 10
|
|
CSS_DEG = 11
|
|
CSS_RAD = 12
|
|
CSS_GRAD = 13
|
|
CSS_MS = 14
|
|
CSS_S = 15
|
|
CSS_HZ = 16
|
|
CSS_KHZ = 17
|
|
CSS_DIMENSION = 18
|
|
CSS_STRING = 19
|
|
CSS_URI = 20
|
|
CSS_IDENT = 21
|
|
CSS_ATTR = 22
|
|
CSS_COUNTER = 23
|
|
CSS_RECT = 24
|
|
CSS_RGBCOLOR = 25
|
|
# NOT OFFICIAL:
|
|
CSS_RGBACOLOR = 26
|
|
CSS_UNICODE_RANGE = 27
|
|
|
|
_floattypes = (CSS_NUMBER, CSS_PERCENTAGE, CSS_EMS, CSS_EXS,
|
|
CSS_PX, CSS_CM, CSS_MM, CSS_IN, CSS_PT, CSS_PC,
|
|
CSS_DEG, CSS_RAD, CSS_GRAD, CSS_MS, CSS_S,
|
|
CSS_HZ, CSS_KHZ, CSS_DIMENSION)
|
|
_stringtypes = (CSS_ATTR, CSS_IDENT, CSS_STRING, CSS_URI)
|
|
_countertypes = (CSS_COUNTER,)
|
|
_recttypes = (CSS_RECT,)
|
|
_rbgtypes = (CSS_RGBCOLOR, CSS_RGBACOLOR)
|
|
_lengthtypes = (CSS_NUMBER, CSS_EMS, CSS_EXS,
|
|
CSS_PX, CSS_CM, CSS_MM, CSS_IN, CSS_PT, CSS_PC)
|
|
|
|
# oldtype: newType: converterfunc
|
|
_converter = {
|
|
# cm <-> mm <-> in, 1 inch is equal to 2.54 centimeters.
|
|
# pt <-> pc, the points used by CSS 2.1 are equal to 1/72nd of an inch.
|
|
# pc: picas - 1 pica is equal to 12 points
|
|
(CSS_CM, CSS_MM): lambda x: x * 10,
|
|
(CSS_MM, CSS_CM): lambda x: x / 10,
|
|
|
|
(CSS_PT, CSS_PC): lambda x: x * 12,
|
|
(CSS_PC, CSS_PT): lambda x: x / 12,
|
|
|
|
(CSS_CM, CSS_IN): lambda x: x / 2.54,
|
|
(CSS_IN, CSS_CM): lambda x: x * 2.54,
|
|
(CSS_MM, CSS_IN): lambda x: x / 25.4,
|
|
(CSS_IN, CSS_MM): lambda x: x * 25.4,
|
|
|
|
(CSS_IN, CSS_PT): lambda x: x / 72,
|
|
(CSS_PT, CSS_IN): lambda x: x * 72,
|
|
(CSS_CM, CSS_PT): lambda x: x / 2.54 / 72,
|
|
(CSS_PT, CSS_CM): lambda x: x * 72 * 2.54,
|
|
(CSS_MM, CSS_PT): lambda x: x / 25.4 / 72,
|
|
(CSS_PT, CSS_MM): lambda x: x * 72 * 25.4,
|
|
|
|
(CSS_IN, CSS_PC): lambda x: x / 72 / 12,
|
|
(CSS_PC, CSS_IN): lambda x: x * 12 * 72,
|
|
(CSS_CM, CSS_PC): lambda x: x / 2.54 / 72 / 12,
|
|
(CSS_PC, CSS_CM): lambda x: x * 12 * 72 * 2.54,
|
|
(CSS_MM, CSS_PC): lambda x: x / 25.4 / 72 / 12,
|
|
(CSS_PC, CSS_MM): lambda x: x * 12 * 72 * 25.4,
|
|
|
|
# hz <-> khz
|
|
(CSS_KHZ, CSS_HZ): lambda x: x * 1000,
|
|
(CSS_HZ, CSS_KHZ): lambda x: x / 1000,
|
|
# s <-> ms
|
|
(CSS_S, CSS_MS): lambda x: x * 1000,
|
|
(CSS_MS, CSS_S): lambda x: x / 1000,
|
|
|
|
(CSS_RAD, CSS_DEG): lambda x: math.degrees(x),
|
|
(CSS_DEG, CSS_RAD): lambda x: math.radians(x),
|
|
# TODO: convert grad <-> deg or rad
|
|
#(CSS_RAD, CSS_GRAD): lambda x: math.degrees(x),
|
|
#(CSS_DEG, CSS_GRAD): lambda x: math.radians(x),
|
|
#(CSS_GRAD, CSS_RAD): lambda x: math.radians(x),
|
|
#(CSS_GRAD, CSS_DEG): lambda x: math.radians(x)
|
|
}
|
|
|
|
def __init__(self, cssText=None, parent=None, readonly=False):
|
|
"""See CSSPrimitiveValue.__init__()"""
|
|
super(CSSPrimitiveValue, self).__init__(cssText=cssText,
|
|
parent=parent,
|
|
readonly=readonly)
|
|
|
|
def __str__(self):
|
|
return u"<cssutils.css.%s object primitiveType=%s cssText=%r at 0x%x>"\
|
|
% (self.__class__.__name__,
|
|
self.primitiveTypeString,
|
|
self.cssText,
|
|
id(self))
|
|
|
|
_unitnames = ['CSS_UNKNOWN',
|
|
'CSS_NUMBER', 'CSS_PERCENTAGE',
|
|
'CSS_EMS', 'CSS_EXS',
|
|
'CSS_PX',
|
|
'CSS_CM', 'CSS_MM',
|
|
'CSS_IN',
|
|
'CSS_PT', 'CSS_PC',
|
|
'CSS_DEG', 'CSS_RAD', 'CSS_GRAD',
|
|
'CSS_MS', 'CSS_S',
|
|
'CSS_HZ', 'CSS_KHZ',
|
|
'CSS_DIMENSION',
|
|
'CSS_STRING', 'CSS_URI', 'CSS_IDENT',
|
|
'CSS_ATTR', 'CSS_COUNTER', 'CSS_RECT',
|
|
'CSS_RGBCOLOR', 'CSS_RGBACOLOR',
|
|
'CSS_UNICODE_RANGE'
|
|
]
|
|
|
|
_reNumDim = re.compile(ur'([+-]?\d*\.\d+|[+-]?\d+)(.*)$', re.I | re.U | re.X)
|
|
|
|
def _unitDIMENSION(value):
|
|
"""Check val for dimension name."""
|
|
units = {'em': 'CSS_EMS', 'ex': 'CSS_EXS',
|
|
'px': 'CSS_PX',
|
|
'cm': 'CSS_CM', 'mm': 'CSS_MM',
|
|
'in': 'CSS_IN',
|
|
'pt': 'CSS_PT', 'pc': 'CSS_PC',
|
|
'deg': 'CSS_DEG', 'rad': 'CSS_RAD', 'grad': 'CSS_GRAD',
|
|
'ms': 'CSS_MS', 's': 'CSS_S',
|
|
'hz': 'CSS_HZ', 'khz': 'CSS_KHZ'
|
|
}
|
|
val, dim = CSSPrimitiveValue._reNumDim.findall(cssutils.helper.normalize(value))[0]
|
|
return units.get(dim, 'CSS_DIMENSION')
|
|
|
|
def _unitFUNCTION(value):
|
|
"""Check val for function name."""
|
|
units = {'attr(': 'CSS_ATTR',
|
|
'counter(': 'CSS_COUNTER',
|
|
'rect(': 'CSS_RECT',
|
|
'rgb(': 'CSS_RGBCOLOR',
|
|
'rgba(': 'CSS_RGBACOLOR',
|
|
}
|
|
return units.get(re.findall(ur'^(.*?\()',
|
|
cssutils.helper.normalize(value.cssText),
|
|
re.U)[0],
|
|
'CSS_UNKNOWN')
|
|
|
|
__unitbytype = {
|
|
__types.NUMBER: 'CSS_NUMBER',
|
|
__types.PERCENTAGE: 'CSS_PERCENTAGE',
|
|
__types.STRING: 'CSS_STRING',
|
|
__types.UNICODE_RANGE: 'CSS_UNICODE_RANGE',
|
|
__types.URI: 'CSS_URI',
|
|
__types.IDENT: 'CSS_IDENT',
|
|
__types.HASH: 'CSS_RGBCOLOR',
|
|
__types.DIMENSION: _unitDIMENSION,
|
|
__types.FUNCTION: _unitFUNCTION
|
|
}
|
|
|
|
def __set_primitiveType(self):
|
|
"""primitiveType is readonly but is set lazy if accessed"""
|
|
# TODO: check unary and font-family STRING a, b, "c"
|
|
val, type_ = self._value
|
|
# try get by type_
|
|
pt = self.__unitbytype.get(type_, 'CSS_UNKNOWN')
|
|
if callable(pt):
|
|
# multiple options, check value too
|
|
pt = pt(val)
|
|
self._primitiveType = getattr(self, pt)
|
|
|
|
def _getPrimitiveType(self):
|
|
if not hasattr(self, '_primitivetype'):
|
|
self.__set_primitiveType()
|
|
return self._primitiveType
|
|
|
|
primitiveType = property(_getPrimitiveType,
|
|
doc="(readonly) The type of the value as defined "
|
|
"by the constants in this class.")
|
|
|
|
def _getPrimitiveTypeString(self):
|
|
return self._unitnames[self.primitiveType]
|
|
|
|
primitiveTypeString = property(_getPrimitiveTypeString,
|
|
doc="Name of primitive type of this value.")
|
|
|
|
def _getCSSPrimitiveTypeString(self, type):
|
|
"get TypeString by given type which may be unknown, used by setters"
|
|
try:
|
|
return self._unitnames[type]
|
|
except (IndexError, TypeError):
|
|
return u'%r (UNKNOWN TYPE)' % type
|
|
|
|
def _getNumDim(self, value=None):
|
|
"Split self._value in numerical and dimension part."
|
|
if value is None:
|
|
value = cssutils.helper.normalize(self._value[0])
|
|
|
|
try:
|
|
val, dim = CSSPrimitiveValue._reNumDim.findall(value)[0]
|
|
except IndexError:
|
|
val, dim = value, u''
|
|
try:
|
|
val = float(val)
|
|
if val == int(val):
|
|
val = int(val)
|
|
except ValueError:
|
|
raise xml.dom.InvalidAccessErr(
|
|
u'CSSPrimitiveValue: No float value %r' % self._value[0])
|
|
|
|
return val, dim
|
|
|
|
def getFloatValue(self, unitType=None):
|
|
"""(DOM) This method is used to get a float value in a
|
|
specified unit. If this CSS value doesn't contain a float value
|
|
or can't be converted into the specified unit, a DOMException
|
|
is raised.
|
|
|
|
:param unitType:
|
|
to get the float value. The unit code can only be a float unit type
|
|
(i.e. CSS_NUMBER, CSS_PERCENTAGE, CSS_EMS, CSS_EXS, CSS_PX, CSS_CM,
|
|
CSS_MM, CSS_IN, CSS_PT, CSS_PC, CSS_DEG, CSS_RAD, CSS_GRAD, CSS_MS,
|
|
CSS_S, CSS_HZ, CSS_KHZ, CSS_DIMENSION) or None in which case
|
|
the current dimension is used.
|
|
|
|
:returns:
|
|
not necessarily a float but some cases just an integer
|
|
e.g. if the value is ``1px`` it return ``1`` and **not** ``1.0``
|
|
|
|
Conversions might return strange values like 1.000000000001
|
|
"""
|
|
if unitType is not None and unitType not in self._floattypes:
|
|
raise xml.dom.InvalidAccessErr(
|
|
u'unitType Parameter is not a float type')
|
|
|
|
val, dim = self._getNumDim()
|
|
|
|
if unitType is not None and self.primitiveType != unitType:
|
|
# convert if needed
|
|
try:
|
|
val = self._converter[self.primitiveType, unitType](val)
|
|
except KeyError:
|
|
raise xml.dom.InvalidAccessErr(
|
|
u'CSSPrimitiveValue: Cannot coerce primitiveType %r to %r'
|
|
% (self.primitiveTypeString,
|
|
self._getCSSPrimitiveTypeString(unitType)))
|
|
|
|
if val == int(val):
|
|
val = int(val)
|
|
|
|
return val
|
|
|
|
def setFloatValue(self, unitType, floatValue):
|
|
"""(DOM) A method to set the float value with a specified unit.
|
|
If the property attached with this value can not accept the
|
|
specified unit or the float value, the value will be unchanged and
|
|
a DOMException will be raised.
|
|
|
|
:param unitType:
|
|
a unit code as defined above. The unit code can only be a float
|
|
unit type
|
|
:param floatValue:
|
|
the new float value which does not have to be a float value but
|
|
may simple be an int e.g. if setting::
|
|
|
|
setFloatValue(CSS_PX, 1)
|
|
|
|
:exceptions:
|
|
- :exc:`~xml.dom.InvalidAccessErr`:
|
|
Raised if the attached property doesn't
|
|
support the float value or the unit type.
|
|
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
|
Raised if this property is readonly.
|
|
"""
|
|
self._checkReadonly()
|
|
if unitType not in self._floattypes:
|
|
raise xml.dom.InvalidAccessErr(
|
|
u'CSSPrimitiveValue: unitType %r is not a float type' %
|
|
self._getCSSPrimitiveTypeString(unitType))
|
|
try:
|
|
val = float(floatValue)
|
|
except ValueError, e:
|
|
raise xml.dom.InvalidAccessErr(
|
|
u'CSSPrimitiveValue: floatValue %r is not a float' %
|
|
floatValue)
|
|
|
|
oldval, dim = self._getNumDim()
|
|
if self.primitiveType != unitType:
|
|
# convert if possible
|
|
try:
|
|
val = self._converter[unitType, self.primitiveType](val)
|
|
except KeyError:
|
|
raise xml.dom.InvalidAccessErr(
|
|
u'CSSPrimitiveValue: Cannot coerce primitiveType %r to %r'
|
|
% (self.primitiveTypeString,
|
|
self._getCSSPrimitiveTypeString(unitType)))
|
|
|
|
if val == int(val):
|
|
val = int(val)
|
|
|
|
self.cssText = '%s%s' % (val, dim)
|
|
|
|
def getStringValue(self):
|
|
"""(DOM) This method is used to get the string value. If the
|
|
CSS value doesn't contain a string value, a DOMException is raised.
|
|
|
|
Some properties (like 'font-family' or 'voice-family')
|
|
convert a whitespace separated list of idents to a string.
|
|
|
|
Only the actual value is returned so e.g. all the following return the
|
|
actual value ``a``: url(a), attr(a), "a", 'a'
|
|
"""
|
|
if self.primitiveType not in self._stringtypes:
|
|
raise xml.dom.InvalidAccessErr(
|
|
u'CSSPrimitiveValue %r is not a string type'
|
|
% self.primitiveTypeString)
|
|
|
|
if CSSPrimitiveValue.CSS_ATTR == self.primitiveType:
|
|
return self._value[0].cssText[5:-1]
|
|
else:
|
|
return self._value[0]
|
|
|
|
def setStringValue(self, stringType, stringValue):
|
|
"""(DOM) A method to set the string value with the specified
|
|
unit. If the property attached to this value can't accept the
|
|
specified unit or the string value, the value will be unchanged and
|
|
a DOMException will be raised.
|
|
|
|
:param stringType:
|
|
a string code as defined above. The string code can only be a
|
|
string unit type (i.e. CSS_STRING, CSS_URI, CSS_IDENT, and
|
|
CSS_ATTR).
|
|
:param stringValue:
|
|
the new string value
|
|
Only the actual value is expected so for (CSS_URI, "a") the
|
|
new value will be ``url(a)``. For (CSS_STRING, "'a'")
|
|
the new value will be ``"\\'a\\'"`` as the surrounding ``'`` are
|
|
not part of the string value
|
|
|
|
:exceptions:
|
|
- :exc:`~xml.dom.InvalidAccessErr`:
|
|
Raised if the CSS value doesn't contain a
|
|
string value or if the string value can't be converted into
|
|
the specified unit.
|
|
|
|
- :exc:`~xml.dom.NoModificationAllowedErr`:
|
|
Raised if this property is readonly.
|
|
"""
|
|
self._checkReadonly()
|
|
# self not stringType
|
|
if self.primitiveType not in self._stringtypes:
|
|
raise xml.dom.InvalidAccessErr(
|
|
u'CSSPrimitiveValue %r is not a string type'
|
|
% self.primitiveTypeString)
|
|
# given stringType is no StringType
|
|
if stringType not in self._stringtypes:
|
|
raise xml.dom.InvalidAccessErr(
|
|
u'CSSPrimitiveValue: stringType %s is not a string type'
|
|
% self._getCSSPrimitiveTypeString(stringType))
|
|
|
|
if self._primitiveType != stringType:
|
|
raise xml.dom.InvalidAccessErr(
|
|
u'CSSPrimitiveValue: Cannot coerce primitiveType %r to %r'
|
|
% (self.primitiveTypeString,
|
|
self._getCSSPrimitiveTypeString(stringType)))
|
|
|
|
if CSSPrimitiveValue.CSS_STRING == self._primitiveType:
|
|
self.cssText = cssutils.helper.string(stringValue)
|
|
elif CSSPrimitiveValue.CSS_URI == self._primitiveType:
|
|
self.cssText = cssutils.helper.uri(stringValue)
|
|
elif CSSPrimitiveValue.CSS_ATTR == self._primitiveType:
|
|
self.cssText = u'attr(%s)' % stringValue
|
|
else:
|
|
self.cssText = stringValue
|
|
self._primitiveType = stringType
|
|
|
|
def getCounterValue(self):
|
|
"""(DOM) This method is used to get the Counter value. If
|
|
this CSS value doesn't contain a counter value, a DOMException
|
|
is raised. Modification to the corresponding style property
|
|
can be achieved using the Counter interface.
|
|
|
|
**Not implemented.**
|
|
"""
|
|
if not self.CSS_COUNTER == self.primitiveType:
|
|
raise xml.dom.InvalidAccessErr(u'Value is not a counter type')
|
|
# TODO: use Counter class
|
|
raise NotImplementedError()
|
|
|
|
def getRGBColorValue(self):
|
|
"""(DOM) This method is used to get the RGB color. If this
|
|
CSS value doesn't contain a RGB color value, a DOMException
|
|
is raised. Modification to the corresponding style property
|
|
can be achieved using the RGBColor interface.
|
|
"""
|
|
if self.primitiveType not in self._rbgtypes:
|
|
raise xml.dom.InvalidAccessErr(u'Value is not a RGBColor value')
|
|
return RGBColor(self._value[0])
|
|
|
|
def getRectValue(self):
|
|
"""(DOM) This method is used to get the Rect value. If this CSS
|
|
value doesn't contain a rect value, a DOMException is raised.
|
|
Modification to the corresponding style property can be achieved
|
|
using the Rect interface.
|
|
|
|
**Not implemented.**
|
|
"""
|
|
if self.primitiveType not in self._recttypes:
|
|
raise xml.dom.InvalidAccessErr(u'value is not a Rect value')
|
|
# TODO: use Rect class
|
|
raise NotImplementedError()
|
|
|
|
def _getCssText(self):
|
|
"""Overwrites CSSValue."""
|
|
return cssutils.ser.do_css_CSSPrimitiveValue(self)
|
|
|
|
def _setCssText(self, cssText):
|
|
"""Use CSSValue."""
|
|
return super(CSSPrimitiveValue, self)._setCssText(cssText)
|
|
|
|
cssText = property(_getCssText, _setCssText,
|
|
doc="A string representation of the current value.")
|
|
|
|
|
|
class CSSValueList(CSSValue):
|
|
"""The CSSValueList interface provides the abstraction of an ordered
|
|
collection of CSS values.
|
|
|
|
Some properties allow an empty list into their syntax. In that case,
|
|
these properties take the none identifier. So, an empty list means
|
|
that the property has the value none.
|
|
|
|
The items in the CSSValueList are accessible via an integral index,
|
|
starting from 0.
|
|
"""
|
|
cssValueType = CSSValue.CSS_VALUE_LIST
|
|
|
|
def __init__(self, cssText=None, parent=None, readonly=False):
|
|
"""Init a new CSSValueList"""
|
|
super(CSSValueList, self).__init__(cssText=cssText,
|
|
parent=parent,
|
|
readonly=readonly)
|
|
self._items = []
|
|
|
|
def __iter__(self):
|
|
"CSSValueList is iterable."
|
|
for item in self.__items():
|
|
yield item.value
|
|
|
|
def __str__(self):
|
|
return u"<cssutils.css.%s object cssValueType=%r cssText=%r length=%r "\
|
|
u"at 0x%x>" % (self.__class__.__name__,
|
|
self.cssValueTypeString,
|
|
self.cssText,
|
|
self.length,
|
|
id(self))
|
|
|
|
def __items(self):
|
|
return [item for item in self._seq
|
|
if isinstance(item.value, CSSValue)]
|
|
|
|
def item(self, index):
|
|
"""(DOM) Retrieve a CSSValue by ordinal `index`. The
|
|
order in this collection represents the order of the values in the
|
|
CSS style property. If `index` is greater than or equal to the number
|
|
of values in the list, this returns ``None``.
|
|
"""
|
|
try:
|
|
return self.__items()[index].value
|
|
except IndexError:
|
|
return None
|
|
|
|
length = property(lambda self: len(self.__items()),
|
|
doc=u"(DOM attribute) The number of CSSValues in the "
|
|
u"list.")
|
|
|
|
|
|
class CSSFunction(CSSPrimitiveValue):
|
|
"""A CSS function value like rect() etc."""
|
|
_functionName = u'CSSFunction'
|
|
primitiveType = CSSPrimitiveValue.CSS_UNKNOWN
|
|
|
|
def __init__(self, cssText=None, parent=None, readonly=False):
|
|
"""
|
|
Init a new CSSFunction
|
|
|
|
:param cssText:
|
|
the parsable cssText of the value
|
|
:param readonly:
|
|
defaults to False
|
|
"""
|
|
super(CSSFunction, self).__init__(parent=parent)
|
|
self._funcType = None
|
|
self.valid = False
|
|
self.wellformed = False
|
|
if cssText is not None:
|
|
self.cssText = cssText
|
|
self._readonly = readonly
|
|
|
|
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
|
|
|
|
def _setCssText(self, cssText):
|
|
self._checkReadonly()
|
|
# store: colorType, parts
|
|
wellformed, seq, store, unusedtokens = ProdParser().parse(cssText,
|
|
self._functionName,
|
|
self._productiondefinition(),
|
|
keepS=True)
|
|
if wellformed:
|
|
# combine +/- and following CSSPrimitiveValue, remove S
|
|
newseq = self._tempSeq()
|
|
i, end = 0, len(seq)
|
|
while i < end:
|
|
item = seq[i]
|
|
if item.type == self._prods.S:
|
|
pass
|
|
elif item.value == u'+' or item.value == u'-':
|
|
i += 1
|
|
next = seq[i]
|
|
newval = next.value
|
|
if isinstance(newval, CSSPrimitiveValue):
|
|
newval.setFloatValue(newval.primitiveType,
|
|
float(item.value + str(newval.getFloatValue())))
|
|
newseq.append(newval, next.type,
|
|
item.line, item.col)
|
|
else:
|
|
# expressions only?
|
|
newseq.appendItem(item)
|
|
newseq.appendItem(next)
|
|
else:
|
|
newseq.appendItem(item)
|
|
|
|
i += 1
|
|
|
|
self.wellformed = True
|
|
self._setSeq(newseq)
|
|
self._funcType = newseq[0].value
|
|
|
|
cssText = property(lambda self: cssutils.ser.do_css_FunctionValue(self),
|
|
_setCssText)
|
|
|
|
funcType = property(lambda self: self._funcType)
|
|
|
|
|
|
class RGBColor(CSSFunction):
|
|
"""A CSS color like RGB, RGBA or a simple value like `#000` or `red`."""
|
|
|
|
_functionName = u'Function rgb()'
|
|
|
|
def __init__(self, cssText=None, parent=None, readonly=False):
|
|
"""
|
|
Init a new RGBColor
|
|
|
|
:param cssText:
|
|
the parsable cssText of the value
|
|
:param readonly:
|
|
defaults to False
|
|
"""
|
|
super(CSSFunction, self).__init__(parent=parent)
|
|
self._colorType = None
|
|
self.valid = False
|
|
self.wellformed = False
|
|
if cssText is not None:
|
|
try:
|
|
# if it is a Function object
|
|
cssText = cssText.cssText
|
|
except AttributeError:
|
|
pass
|
|
self.cssText = cssText
|
|
|
|
self._readonly = readonly
|
|
|
|
def __repr__(self):
|
|
return u"cssutils.css.%s(%r)" % (self.__class__.__name__,
|
|
self.cssText)
|
|
|
|
def __str__(self):
|
|
return u"<cssutils.css.%s object colorType=%r cssText=%r at 0x%x>" % (
|
|
self.__class__.__name__,
|
|
self.colorType,
|
|
self.cssText,
|
|
id(self))
|
|
|
|
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,
|
|
u'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)
|
|
|
|
cssText = property(lambda self: cssutils.ser.do_css_RGBColor(self),
|
|
_setCssText)
|
|
|
|
colorType = property(lambda self: self._colorType)
|
|
|
|
|
|
class CalcValue(CSSFunction):
|
|
"""Calc Function"""
|
|
_functionName = u'Function calc()'
|
|
|
|
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='calc',
|
|
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: (CSSFunction._functionName,
|
|
CSSFunction(cssutils.helper.pushtoken(t,
|
|
tokens)))
|
|
),
|
|
Prod(name='part',
|
|
match=lambda t, v: v != u')',
|
|
toSeq=lambda t, tokens: (t[0], t[1])),
|
|
),
|
|
minmax=lambda: (0, None)),
|
|
PreDef.funcEnd(stop=True))
|
|
return funcProds
|
|
|
|
def _getCssText(self):
|
|
return cssutils.ser.do_css_CalcValue(self)
|
|
|
|
def _setCssText(self, cssText):
|
|
return super(CalcValue, self)._setCssText(cssText)
|
|
|
|
cssText = property(_getCssText, _setCssText,
|
|
doc=u"A string representation of the current value.")
|
|
|
|
|
|
class ExpressionValue(CSSFunction):
|
|
"""Special IE only CSSFunction which may contain *anything*.
|
|
Used for expressions and ``alpha(opacity=100)`` currently."""
|
|
_functionName = u'Expression (IE only)'
|
|
|
|
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 != u')',
|
|
toSeq=lambda t, tokens: (t[0], t[1])),
|
|
),
|
|
minmax=lambda: (0, None)),
|
|
PreDef.funcEnd(stop=True))
|
|
return funcProds
|
|
|
|
def _getCssText(self):
|
|
return cssutils.ser.do_css_ExpressionValue(self)
|
|
|
|
def _setCssText(self, cssText):
|
|
#self._log.warn(u'CSSValue: Unoffial and probably invalid MS value used!')
|
|
return super(ExpressionValue, self)._setCssText(cssText)
|
|
|
|
cssText = property(_getCssText, _setCssText,
|
|
doc=u"A string representation of the current value.")
|
|
|
|
|
|
class CSSVariable(CSSValue):
|
|
"""The CSSVariable represents a call to CSS Variable."""
|
|
|
|
def __init__(self, cssText=None, parent=None, readonly=False):
|
|
"""Init a new CSSVariable.
|
|
|
|
:param cssText:
|
|
the parsable cssText of the value, e.g. ``var(x)``
|
|
:param readonly:
|
|
defaults to False
|
|
"""
|
|
self._name = None
|
|
super(CSSVariable, self).__init__(cssText=cssText,
|
|
parent=parent,
|
|
readonly=readonly)
|
|
|
|
def __repr__(self):
|
|
return u"cssutils.css.%s(%r)" % (self.__class__.__name__, self.cssText)
|
|
|
|
def __str__(self):
|
|
return u"<cssutils.css.%s object name=%r value=%r at 0x%x>" % (
|
|
self.__class__.__name__,
|
|
self.name,
|
|
self.value,
|
|
id(self))
|
|
|
|
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,
|
|
u'CSSVariable',
|
|
funcProds,
|
|
keepS=True)
|
|
if wellformed:
|
|
self._name = store['ident'].value
|
|
self._setSeq(seq)
|
|
self.wellformed = True
|
|
|
|
cssText = property(lambda self: cssutils.ser.do_css_CSSVariable(self),
|
|
_setCssText,
|
|
doc=u"A string representation of the current variable.")
|
|
|
|
cssValueType = CSSValue.CSS_VARIABLE
|
|
|
|
# TODO: writable? check if var (value) available?
|
|
name = property(lambda self: self._name)
|
|
|
|
def _getValue(self):
|
|
"Find contained sheet and @variables there"
|
|
try:
|
|
variables = self.parent.parent.parentRule.parentStyleSheet.variables
|
|
except AttributeError:
|
|
return None
|
|
else:
|
|
try:
|
|
return variables[self.name]
|
|
except KeyError:
|
|
return None
|
|
|
|
value = property(_getValue)
|
|
|