1 """CSS2Properties (partly!) implements DOM Level 2 CSS CSS2Properties used
2 by CSSStyleDeclaration
3
4 TODO: CSS2Properties
5 If an implementation does implement this interface, it is expected to
6 understand the specific syntax of the shorthand properties, and apply
7 their semantics; when the margin property is set, for example, the
8 marginTop, marginRight, marginBottom and marginLeft properties are
9 actually being set by the underlying implementation.
10
11 When dealing with CSS "shorthand" properties, the shorthand properties
12 should be decomposed into their component longhand properties as
13 appropriate, and when querying for their value, the form returned
14 should be the shortest form exactly equivalent to the declarations made
15 in the ruleset. However, if there is no shorthand declaration that
16 could be added to the ruleset without changing in any way the rules
17 already declared in the ruleset (i.e., by adding longhand rules that
18 were previously not declared in the ruleset), then the empty string
19 should be returned for the shorthand property.
20
21 For example, querying for the font property should not return
22 "normal normal normal 14pt/normal Arial, sans-serif", when
23 "14pt Arial, sans-serif" suffices. (The normals are initial values, and
24 are implied by use of the longhand property.)
25
26 If the values for all the longhand properties that compose a particular
27 string are the initial values, then a string consisting of all the
28 initial values should be returned (e.g. a border-width value of
29 "medium" should be returned as such, not as "").
30
31 For some shorthand properties that take missing values from other
32 sides, such as the margin, padding, and border-[width|style|color]
33 properties, the minimum number of sides possible should be used; i.e.,
34 "0px 10px" will be returned instead of "0px 10px 0px 10px".
35
36 If the value of a shorthand property can not be decomposed into its
37 component longhand properties, as is the case for the font property
38 with a value of "menu", querying for the values of the component
39 longhand properties should return the empty string.
40
41 TODO: CSS2Properties DOMImplementation
42 The interface found within this section are not mandatory. A DOM
43 application can use the hasFeature method of the DOMImplementation
44 interface to determine whether it is supported or not. The feature
45 string for this extended interface listed in this section is "CSS2"
46 and the version is "2.0".
47
48
49 cssvalues
50 =========
51 contributed by Kevin D. Smith, thanks!
52
53 "cssvalues" is used as a property validator.
54 it is an importable object that contains a dictionary of compiled regular
55 expressions. The keys of this dictionary are all of the valid CSS property
56 names. The values are compiled regular expressions that can be used to
57 validate the values for that property. (Actually, the values are references
58 to the 'match' method of a compiled regular expression, so that they are
59 simply called like functions.)
60
61 """
62 __all__ = ['CSS2Properties', 'cssvalues']
63 __docformat__ = 'restructuredtext'
64 __author__ = '$LastChangedBy: doerwalter $'
65 __date__ = '$LastChangedDate: 2007-08-02 22:58:23 +0200 (Do, 02 Aug 2007) $'
66 __version__ = '0.9.2b3, $LastChangedRevision: 160 $'
67
68 import re
69
70
71 """
72 Define some regular expression fragments that will be used as
73 macros within the CSS property value regular expressions.
74 """
75 MACROS = {
76 'ident': r'[-]?{nmstart}{nmchar}*',
77 'name': r'{nmchar}+',
78 'nmstart': r'[_a-z]|{nonascii}|{escape}',
79 'nonascii': r'[^\0-\177]',
80 'unicode': r'\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?',
81 'escape': r'{unicode}|\\[ -~\200-\777]',
82
83 'int': r'[-]?\d+',
84 'nmchar': r'[\w-]|{nonascii}|{escape}',
85 'num': r'[-]?\d+|[-]?\d*\.\d+',
86 'number': r'{num}',
87 'string': r'{string1}|{string2}',
88 'string1': r'"(\\\"|[^\"])*"',
89 'string2': r"'(\\\'|[^\'])*'",
90 'nl': r'\n|\r\n|\r|\f',
91 'w': r'\s*',
92
93 'integer': r'{int}',
94 'length': r'0|{num}(em|ex|px|in|cm|mm|pt|pc)',
95 'angle': r'0|{num}(deg|grad|rad)',
96 'time': r'0|{num}m?s',
97 'frequency': r'0|{num}k?Hz',
98 'color': r'(maroon|red|orange|yellow|olive|purple|fuchsia|white|lime|green|navy|blue|aqua|teal|black|silver|gray|ActiveBorder|ActiveCaption|AppWorkspace|Background|ButtonFace|ButtonHighlight|ButtonShadow|ButtonText|CaptionText|GrayText|Highlight|HighlightText|InactiveBorder|InactiveCaption|InactiveCaptionText|InfoBackground|InfoText|Menu|MenuText|Scrollbar|ThreeDDarkShadow|ThreeDFace|ThreeDHighlight|ThreeDLightShadow|ThreeDShadow|Window|WindowFrame|WindowText)|#[0-9a-f]{3}|#[0-9a-f]{6}|rgb\({w}{int}{w},{w}{int}{w},{w}{int}{w}\)|rgb\({w}{num}%{w},{w}{num}%{w},{w}{num}%{w}\)',
99 'uri': r'url\({w}({string}|(\\\)|[^\)])+){w}\)',
100 'percentage': r'{num}%',
101 'border-style': 'none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset',
102 'border-color': '{color}',
103 'border-width': '{length}|thin|medium|thick',
104
105 'background-color': r'{color}|transparent|inherit',
106 'background-image': r'{uri}|none|inherit',
107
108 'background-position': r'(({percentage}|{length}|left|center|right)(\s*{percentage}|{length}|top|center|bottom)?)|(left|center|right|top|center|bottom)(\s*(left|center|right|top|center|bottom))?|inherit',
109 'background-repeat': r'repeat|repeat-x|repeat-y|no-repeat|inherit',
110 'background-attachment': r'scroll|fixed|inherit',
111
112 'shape': r'rect\(({w}({length}|auto}){w},){3}{w}({length}|auto){w}\)',
113 'counter': r'counter\({w}{identifier}{w}(?:,{w}{list-style-type}{w})?\)',
114 'identifier': r'{ident}',
115 'family-name': r'{string}|{identifier}',
116 'generic-family': r'serif|sans-serif|cursive|fantasy|monospace',
117 'absolute-size': r'(x?x-)?(small|large)|medium',
118 'relative-size': r'smaller|larger',
119 'font-family': r'(({family-name}|{generic-family}){w},{w})*({family-name}|{generic-family})|inherit',
120 'font-size': r'{absolute-size}|{relative-size}|{length}|{percentage}|inherit',
121 'font-style': r'normal|italic|oblique|inherit',
122 'font-variant': r'normal|small-caps|inherit',
123 'font-weight': r'normal|bold|bolder|lighter|[1-9]00|inherit',
124 'line-height': r'normal|{number}|{length}|{percentage}|inherit',
125 'list-style-image': r'{uri}|none|inherit',
126 'list-style-position': r'inside|outside|inherit',
127 'list-style-type': r'disc|circle|square|decimal|decimal-leading-zero|lower-roman|upper-roman|lower-greek|lower-(latin|alpha)|upper-(latin|alpha)|armenian|georgian|none|inherit',
128 'margin-width': r'{length}|{percentage}|auto',
129 'outline-color': r'{color}|invert|inherit',
130 'outline-style': r'{border-style}|inherit',
131 'outline-width': r'{border-width}|inherit',
132 'padding-width': r'{length}|{percentage}',
133 'specific-voice': r'{identifier}',
134 'generic-voice': r'male|female|child',
135 'content': r'{string}|{uri}|{counter}|attr\({w}{identifier}{w}\)|open-quote|close-quote|no-open-quote|no-close-quote',
136 'border-attrs': r'{border-width}|{border-style}|{border-color}',
137 'background-attrs': r'{background-color}|{background-image}|{background-repeat}|{background-attachment}|{background-position}',
138 'list-attrs': r'{list-style-type}|{list-style-position}|{list-style-image}',
139 'font-attrs': r'{font-style}|{font-variant}|{font-weight}',
140 'outline-attrs': r'{outline-color}|{outline-style}|{outline-width}',
141 'text-attrs': r'underline|overline|line-through|blink',
142 }
143
144 """
145 Define the regular expressions for validation all CSS values
146 """
147 cssvalues = {
148 'azimuth': r'{angle}|(behind\s+)?(left-side|far-left|left|center-left|center|center-right|right|far-right|right-side)(\s+behind)?|behind|leftwards|rightwards|inherit',
149 'background-attachment': r'{background-attachment}',
150 'background-color': r'{background-color}',
151 'background-image': r'{background-image}',
152 'background-position': r'{background-position}',
153 'background-repeat': r'{background-repeat}',
154
155 'background': r'{background-attrs}(\s+{background-attrs})*|inherit',
156 'border-collapse': r'collapse|separate|inherit',
157 'border-color': r'({border-color}|transparent)(\s+({border-color}|transparent)){0,3}|inherit',
158 'border-spacing': r'{length}(\s+{length})?|inherit',
159 'border-style': r'{border-style}(\s+{border-style}){0,3}|inherit',
160 'border-top': r'{border-attrs}(\s+{border-attrs})*|inherit',
161 'border-right': r'{border-attrs}(\s+{border-attrs})*|inherit',
162 'border-bottom': r'{border-attrs}(\s+{border-attrs})*|inherit',
163 'border-left': r'{border-attrs}(\s+{border-attrs})*|inherit',
164 'border-top-color': r'{border-color}|transparent|inherit',
165 'border-right-color': r'{border-color}|transparent|inherit',
166 'border-bottom-color': r'{border-color}|transparent|inherit',
167 'border-left-color': r'{border-color}|transparent|inherit',
168 'border-top-style': r'{border-style}|inherit',
169 'border-right-style': r'{border-style}|inherit',
170 'border-bottom-style': r'{border-style}|inherit',
171 'border-left-style': r'{border-style}|inherit',
172 'border-top-width': r'{border-width}|inherit',
173 'border-right-width': r'{border-width}|inherit',
174 'border-bottom-width': r'{border-width}|inherit',
175 'border-right-width': r'{border-width}|inherit',
176 'border-width': r'{border-width}(\s+{border-width}){0,3}|inherit',
177 'border': r'{border-attrs}(\s+{border-attrs})*|inherit',
178 'bottom': r'{length}|{percentage}|auto|inherit',
179 'caption-side': r'top|bottom|inherit',
180 'clear': r'none|left|right|both|inherit',
181 'clip': r'{shape}|auto|inherit',
182 'color': r'{color}|inherit',
183 'content': r'normal|{content}(\s+{content})*|inherit',
184 'counter-increment': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
185 'counter-reset': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
186 'cue-after': r'{uri}|none|inherit',
187 'cue-before': r'{uri}|none|inherit',
188 'cue': r'({uri}|none|inherit){1,2}|inherit',
189 'cursor': r'((({uri}{w},{w})*)?(auto|crosshair|default|pointer|move|(e|ne|nw|n|se|sw|s|w)-resize|text|wait|help|progress))|inherit',
190 'direction': r'ltr|rtl|inherit',
191 'display': r'inline|block|list-item|run-in|inline-block|table|inline-table|table-row-group|table-header-group|table-footer-group|table-row|table-column-group|table-column|table-cell|table-caption|none|inherit',
192 'elevation': r'{angle}|below|level|above|higher|lower|inherit',
193 'empty-cells': r'show|hide|inherit',
194 'float': r'left|right|none|inherit',
195 'font-family': r'{font-family}',
196 'font-size': r'{font-size}',
197 'font-style': r'{font-style}',
198 'font-variant': r'{font-variant}',
199 'font-weight': r'{font-weight}',
200 'font': r'({font-attrs}\s+)*{font-size}({w}/{w}{line-height})?\s+{font-family}|caption|icon|menu|message-box|small-caption|status-bar|inherit',
201 'height': r'{length}|{percentage}|auto|inherit',
202 'left': r'{length}|{percentage}|auto|inherit',
203 'letter-spacing': r'normal|{length}|inherit',
204 'line-height': r'{line-height}',
205 'list-style-image': r'{list-style-image}',
206 'list-style-position': r'{list-style-position}',
207 'list-style-type': r'{list-style-type}',
208 'list-style': r'{list-attrs}(\s+{list-attrs})*|inherit',
209 'margin-right': r'{margin-width}|inherit',
210 'margin-left': r'{margin-width}|inherit',
211 'margin-top': r'{margin-width}|inherit',
212 'margin-bottom': r'{margin-width}|inherit',
213 'margin': r'{margin-width}(\s+{margin-width}){0,3}|inherit',
214 'max-height': r'{length}|{percentage}|none|inherit',
215 'max-width': r'{length}|{percentage}|none|inherit',
216 'min-height': r'{length}|{percentage}|none|inherit',
217 'min-width': r'{length}|{percentage}|none|inherit',
218 'orphans': r'{integer}|inherit',
219 'outline-color': r'{outline-color}',
220 'outline-style': r'{outline-style}',
221 'outline-width': r'{outline-width}',
222 'outline': r'{outline-attrs}(\s+{outline-attrs})*|inherit',
223 'overflow': r'visible|hidden|scroll|auto|inherit',
224 'padding-top': r'{padding-width}|inherit',
225 'padding-right': r'{padding-width}|inherit',
226 'padding-bottom': r'{padding-width}|inherit',
227 'padding-left': r'{padding-width}|inherit',
228 'padding': r'{padding-width}(\s+{padding-width}){0,3}|inherit',
229 'page-break-after': r'auto|always|avoid|left|right|inherit',
230 'page-break-before': r'auto|always|avoid|left|right|inherit',
231 'page-break-inside': r'avoid|auto|inherit',
232 'pause-after': r'{time}|{percentage}|inherit',
233 'pause-before': r'{time}|{percentage}|inherit',
234 'pause': r'({time}|{percentage}){1,2}|inherit',
235 'pitch-range': r'{number}|inherit',
236 'pitch': r'{frequency}|x-low|low|medium|high|x-high|inherit',
237 'play-during': r'{uri}(\s+(mix|repeat))*|auto|none|inherit',
238 'position': r'static|relative|absolute|fixed|inherit',
239 'quotes': r'({string}\s+{string})(\s+{string}\s+{string})*|none|inherit',
240 'richness': r'{number}|inherit',
241 'right': r'{length}|{percentage}|auto|inherit',
242 'speak-header': r'once|always|inherit',
243 'speak-numeral': r'digits|continuous|inherit',
244 'speak-punctuation': r'code|none|inherit',
245 'speak': r'normal|none|spell-out|inherit',
246 'speech-rate': r'{number}|x-slow|slow|medium|fast|x-fast|faster|slower|inherit',
247 'stress': r'{number}|inherit',
248 'table-layout': r'auto|fixed|inherit',
249 'text-align': r'left|right|center|justify|inherit',
250 'text-decoration': r'none|{text-attrs}(\s+{text-attrs})*|inherit',
251 'text-indent': r'{length}|{percentage}|inherit',
252 'text-transform': r'capitalize|uppercase|lowercase|none|inherit',
253 'top': r'{length}|{percentage}|auto|inherit',
254 'unicode-bidi': r'normal|embed|bidi-override|inherit',
255 'vertical-align': r'baseline|sub|super|top|text-top|middle|bottom|text-bottom|{percentage}|{length}|inherit',
256 'visibility': r'visible|hidden|collapse|inherit',
257 'voice-family': r'({specific-voice}|{generic-voice}{w},{w})*({specific-voice}|{generic-voice})|inherit',
258 'volume': r'{number}|{percentage}|silent|x-soft|soft|medium|loud|x-loud|inherit',
259 'white-space': r'normal|pre|nowrap|pre-wrap|pre-line|inherit',
260 'widows': r'{integer}|inherit',
261 'width': r'{length}|{percentage}|auto|inherit',
262 'word-spacing': r'normal|{length}|inherit',
263 'z-index': r'auto|{integer}|inherit',
264 }
265
267 """ Expand macros in token dictionary """
268 def macro_value(m):
269 return '(?:%s)' % MACROS[m.groupdict()['macro']]
270 for key, value in tokdict.items():
271 while re.search(r'{[a-z][a-z0-9-]*}', value):
272 value = re.sub(r'{(?P<macro>[a-z][a-z0-9-]*)}',
273 macro_value, value)
274 tokdict[key] = value
275 return tokdict
276
278 """ Compile all regular expressions into callable objects """
279 for key, value in tokdict.items():
280 tokdict[key] = re.compile('^(?:%s)$' % value, re.I).match
281 return tokdict
282
283 _compile_regexes(_expand_macros(cssvalues))
284
285
286
287
288 _reCSStoDOMname = re.compile('-[a-z]', re.I)
290 """
291 returns DOMname for given CSSname e.g. for CSSname 'font-style' returns
292 'fontStyle'
293 """
294 def _doCSStoDOMname2(m): return m.group(0)[1].capitalize()
295 return _reCSStoDOMname.sub(_doCSStoDOMname2, CSSname)
296
297 _reDOMtoCSSname = re.compile('([A-Z])[a-z]+')
299 """
300 returns CSSname for given DOMname e.g. for DOMname 'fontStyle' returns
301 'font-style'
302 """
303 def _doDOMtoCSSname2(m): return '-' + m.group(0).lower()
304 return _reDOMtoCSSname.sub(_doDOMtoCSSname2, DOMname)
305
306
308 """
309 The CSS2Properties interface represents a convenience mechanism
310 for retrieving and setting properties within a CSSStyleDeclaration.
311 The attributes of this interface correspond to all the properties
312 specified in CSS2. Getting an attribute of this interface is
313 equivalent to calling the getPropertyValue method of the
314 CSSStyleDeclaration interface. Setting an attribute of this
315 interface is equivalent to calling the setProperty method of the
316 CSSStyleDeclaration interface.
317
318 cssutils actually also allows usage of ``del`` to remove a CSS property
319 from a CSSStyleDeclaration.
320
321 This is an abstract class, the following functions need to be present
322 in inheriting class:
323
324 - ``_getP``
325 - ``_setP``
326 - ``_delP``
327 """
328
329 - def _getP(self, CSSname): pass
330 - def _setP(self, CSSname, value): pass
331 - def _delP(self, CSSname): pass
332
333
334
335
336
337 CSS2Properties._properties = [_toDOMname(p) for p in cssvalues.keys()]
338
339
341 """
342 closure to keep name known in each properties accessor function
343 DOMname is converted to CSSname here, so actual calls use CSSname
344 """
345 CSSname = _toCSSname(DOMname)
346 def _get(self): return self._getP(CSSname)
347 def _set(self, value): self._setP(CSSname, value)
348 def _del(self): self._delP(CSSname)
349 return _get, _set, _del
350
351
352 for DOMname in CSS2Properties._properties:
353 setattr(CSS2Properties, DOMname,
354 property(*__named_property_def(DOMname)))
355
356
357
358 if __name__=='__main__':
359 c = CSS2Properties()
360 print CSS2Properties.color
361
362
363
364
365
366
367