1"""CSSVariables implements (and only partly) experimental 2`CSS Variables <http://disruptive-innovations.com/zoo/cssvariables/>`_ 3""" 4__all__ = ['CSSVariablesRule'] 5 6from .cssvariablesdeclaration import CSSVariablesDeclaration 7from . import cssrule 8import cssutils 9import xml.dom 10 11 12class CSSVariablesRule(cssrule.CSSRule): 13 """ 14 The CSSVariablesRule interface represents a @variables rule within a CSS 15 style sheet. The @variables rule is used to specify variables. 16 17 cssutils uses a :class:`~cssutils.css.CSSVariablesDeclaration` to 18 represent the variables. 19 20 Format:: 21 22 variables 23 VARIABLES_SYM S* medium [ COMMA S* medium ]* LBRACE S* 24 variableset* '}' S* 25 ; 26 27 for variableset see :class:`cssutils.css.CSSVariablesDeclaration` 28 29 **Media are not implemented. Reason is that cssutils is using CSS 30 variables in a kind of preprocessing and therefor no media information 31 is available at this stage. For now do not use media!** 32 33 Example:: 34 35 @variables { 36 CorporateLogoBGColor: #fe8d12; 37 } 38 39 div.logoContainer { 40 background-color: var(CorporateLogoBGColor); 41 } 42 """ 43 44 def __init__( 45 self, 46 mediaText=None, 47 variables=None, 48 parentRule=None, 49 parentStyleSheet=None, 50 readonly=False, 51 ): 52 """ 53 If readonly allows setting of properties in constructor only. 54 """ 55 super(CSSVariablesRule, self).__init__( 56 parentRule=parentRule, parentStyleSheet=parentStyleSheet 57 ) 58 self._atkeyword = '@variables' 59 60 # dummy 61 self._media = cssutils.stylesheets.MediaList(mediaText, readonly=readonly) 62 63 if variables: 64 self.variables = variables 65 else: 66 self.variables = CSSVariablesDeclaration(parentRule=self) 67 68 self._readonly = readonly 69 70 def __repr__(self): 71 return "cssutils.css.%s(mediaText=%r, variables=%r)" % ( 72 self.__class__.__name__, 73 self._media.mediaText, 74 self.variables.cssText, 75 ) 76 77 def __str__(self): 78 return ( 79 "<cssutils.css.%s object mediaText=%r variables=%r valid=%r " 80 "at 0x%x>" 81 % ( 82 self.__class__.__name__, 83 self._media.mediaText, 84 self.variables.cssText, 85 self.valid, 86 id(self), 87 ) 88 ) 89 90 def _getCssText(self): 91 """Return serialized property cssText.""" 92 return cssutils.ser.do_CSSVariablesRule(self) 93 94 def _setCssText(self, cssText): 95 """ 96 :exceptions: 97 - :exc:`~xml.dom.SyntaxErr`: 98 Raised if the specified CSS string value has a syntax error and 99 is unparsable. 100 - :exc:`~xml.dom.InvalidModificationErr`: 101 Raised if the specified CSS string value represents a different 102 type of rule than the current one. 103 - :exc:`~xml.dom.HierarchyRequestErr`: 104 Raised if the rule cannot be inserted at this point in the 105 style sheet. 106 - :exc:`~xml.dom.NoModificationAllowedErr`: 107 Raised if the rule is readonly. 108 109 Format:: 110 111 variables 112 : VARIABLES_SYM S* medium [ COMMA S* medium ]* LBRACE S* 113 variableset* '}' S* 114 ; 115 116 variableset 117 : LBRACE S* vardeclaration [ ';' S* vardeclaration ]* '}' S* 118 ; 119 """ 120 super(CSSVariablesRule, self)._setCssText(cssText) 121 122 tokenizer = self._tokenize2(cssText) 123 attoken = self._nexttoken(tokenizer, None) 124 if self._type(attoken) != self._prods.VARIABLES_SYM: 125 self._log.error( 126 'CSSVariablesRule: No CSSVariablesRule found: %s' 127 % self._valuestr(cssText), 128 error=xml.dom.InvalidModificationErr, 129 ) 130 else: 131 newVariables = CSSVariablesDeclaration(parentRule=self) 132 ok = True 133 134 beforetokens, brace = self._tokensupto2( 135 tokenizer, blockstartonly=True, separateEnd=True 136 ) 137 if self._tokenvalue(brace) != '{': 138 ok = False 139 self._log.error( 140 'CSSVariablesRule: No start { of variable ' 141 'declaration found: %r' % self._valuestr(cssText), 142 brace, 143 ) 144 145 # parse stuff before { which should be comments and S only 146 new = {'wellformed': True} 147 newseq = self._tempSeq() # [] 148 149 beforewellformed, expected = self._parse( 150 expected=':', 151 seq=newseq, 152 tokenizer=self._tokenize2(beforetokens), 153 productions={}, 154 ) 155 ok = ok and beforewellformed and new['wellformed'] 156 157 variablestokens, braceorEOFtoken = self._tokensupto2( 158 tokenizer, blockendonly=True, separateEnd=True 159 ) 160 161 val, type_ = self._tokenvalue(braceorEOFtoken), self._type(braceorEOFtoken) 162 if val != '}' and type_ != 'EOF': 163 ok = False 164 self._log.error( 165 'CSSVariablesRule: No "}" after variables ' 166 'declaration found: %r' % self._valuestr(cssText) 167 ) 168 169 nonetoken = self._nexttoken(tokenizer) 170 if nonetoken: 171 ok = False 172 self._log.error( 173 'CSSVariablesRule: Trailing content found.', token=nonetoken 174 ) 175 176 if 'EOF' == type_: 177 # add again as variables needs it 178 variablestokens.append(braceorEOFtoken) 179 # SET but may raise: 180 newVariables.cssText = variablestokens 181 182 if ok: 183 # contains probably comments only upto { 184 self._setSeq(newseq) 185 self.variables = newVariables 186 187 cssText = property( 188 _getCssText, 189 _setCssText, 190 doc="(DOM) The parsable textual representation of this " "rule.", 191 ) 192 193 media = property( 194 doc="NOT IMPLEMENTED! As cssutils resolves variables " 195 "during serializing media information is lost." 196 ) 197 198 def _setVariables(self, variables): 199 """ 200 :param variables: 201 a CSSVariablesDeclaration or string 202 """ 203 self._checkReadonly() 204 if isinstance(variables, str): 205 self._variables = CSSVariablesDeclaration( 206 cssText=variables, parentRule=self 207 ) 208 else: 209 variables._parentRule = self 210 self._variables = variables 211 212 variables = property( 213 lambda self: self._variables, 214 _setVariables, 215 doc="(DOM) The variables of this rule set, a " 216 ":class:`cssutils.css.CSSVariablesDeclaration`.", 217 ) 218 219 type = property( 220 lambda self: self.VARIABLES_RULE, 221 doc="The type of this rule, as defined by a CSSRule " "type constant.", 222 ) 223 224 valid = property(lambda self: True, doc='NOT IMPLEMTED REALLY (TODO)') 225 226 # constant but needed: 227 wellformed = property(lambda self: True) 228