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: cthedot $'
65 __date__ = '$LastChangedDate: 2007-08-19 16:17:06 +0200 (So, 19 Aug 2007) $'
66 __version__ = '$LastChangedRevision: 241 $'
67
68 import re
69
70 """
71 Define some regular expression fragments that will be used as
72 macros within the CSS property value regular expressions.
73 """
74 MACROS = {
75 'ident': r'[-]?{nmstart}{nmchar}*',
76 'name': r'{nmchar}+',
77 'nmstart': r'[_a-z]|{nonascii}|{escape}',
78 'nonascii': r'[^\0-\177]',
79 'unicode': r'\\[0-9a-f]{1,6}(\r\n|[ \n\r\t\f])?',
80 'escape': r'{unicode}|\\[ -~\200-\777]',
81
82 'int': r'[-]?\d+',
83 'nmchar': r'[\w-]|{nonascii}|{escape}',
84 'num': r'[-]?\d+|[-]?\d*\.\d+',
85 'number': r'{num}',
86 'string': r'{string1}|{string2}',
87 'string1': r'"(\\\"|[^\"])*"',
88 'string2': r"'(\\\'|[^\'])*'",
89 'nl': r'\n|\r\n|\r|\f',
90 'w': r'\s*',
91
92 'integer': r'{int}',
93 'length': r'0|{num}(em|ex|px|in|cm|mm|pt|pc)',
94 'angle': r'0|{num}(deg|grad|rad)',
95 'time': r'0|{num}m?s',
96 'frequency': r'0|{num}k?Hz',
97 '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}\)',
98 'uri': r'url\({w}({string}|(\\\)|[^\)])+){w}\)',
99 'percentage': r'{num}%',
100 'border-style': 'none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset',
101 'border-color': '{color}',
102 'border-width': '{length}|thin|medium|thick',
103
104 'background-color': r'{color}|transparent|inherit',
105 'background-image': r'{uri}|none|inherit',
106
107 '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',
108 'background-repeat': r'repeat|repeat-x|repeat-y|no-repeat|inherit',
109 'background-attachment': r'scroll|fixed|inherit',
110
111 'shape': r'rect\(({w}({length}|auto}){w},){3}{w}({length}|auto){w}\)',
112 'counter': r'counter\({w}{identifier}{w}(?:,{w}{list-style-type}{w})?\)',
113 'identifier': r'{ident}',
114 'family-name': r'{string}|{identifier}',
115 'generic-family': r'serif|sans-serif|cursive|fantasy|monospace',
116 'absolute-size': r'(x?x-)?(small|large)|medium',
117 'relative-size': r'smaller|larger',
118 'font-family': r'(({family-name}|{generic-family}){w},{w})*({family-name}|{generic-family})|inherit',
119 'font-size': r'{absolute-size}|{relative-size}|{length}|{percentage}|inherit',
120 'font-style': r'normal|italic|oblique|inherit',
121 'font-variant': r'normal|small-caps|inherit',
122 'font-weight': r'normal|bold|bolder|lighter|[1-9]00|inherit',
123 'line-height': r'normal|{number}|{length}|{percentage}|inherit',
124 'list-style-image': r'{uri}|none|inherit',
125 'list-style-position': r'inside|outside|inherit',
126 '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',
127 'margin-width': r'{length}|{percentage}|auto',
128 'outline-color': r'{color}|invert|inherit',
129 'outline-style': r'{border-style}|inherit',
130 'outline-width': r'{border-width}|inherit',
131 'padding-width': r'{length}|{percentage}',
132 'specific-voice': r'{identifier}',
133 'generic-voice': r'male|female|child',
134 'content': r'{string}|{uri}|{counter}|attr\({w}{identifier}{w}\)|open-quote|close-quote|no-open-quote|no-close-quote',
135 'border-attrs': r'{border-width}|{border-style}|{border-color}',
136 'background-attrs': r'{background-color}|{background-image}|{background-repeat}|{background-attachment}|{background-position}',
137 'list-attrs': r'{list-style-type}|{list-style-position}|{list-style-image}',
138 'font-attrs': r'{font-style}|{font-variant}|{font-weight}',
139 'outline-attrs': r'{outline-color}|{outline-style}|{outline-width}',
140 'text-attrs': r'underline|overline|line-through|blink',
141 }
142
143 """
144 Define the regular expressions for validation all CSS values
145 """
146 cssvalues = {
147 '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',
148 'background-attachment': r'{background-attachment}',
149 'background-color': r'{background-color}',
150 'background-image': r'{background-image}',
151 'background-position': r'{background-position}',
152 'background-repeat': r'{background-repeat}',
153
154 'background': r'{background-attrs}(\s+{background-attrs})*|inherit',
155 'border-collapse': r'collapse|separate|inherit',
156 'border-color': r'({border-color}|transparent)(\s+({border-color}|transparent)){0,3}|inherit',
157 'border-spacing': r'{length}(\s+{length})?|inherit',
158 'border-style': r'{border-style}(\s+{border-style}){0,3}|inherit',
159 'border-top': r'{border-attrs}(\s+{border-attrs})*|inherit',
160 'border-right': r'{border-attrs}(\s+{border-attrs})*|inherit',
161 'border-bottom': r'{border-attrs}(\s+{border-attrs})*|inherit',
162 'border-left': r'{border-attrs}(\s+{border-attrs})*|inherit',
163 'border-top-color': r'{border-color}|transparent|inherit',
164 'border-right-color': r'{border-color}|transparent|inherit',
165 'border-bottom-color': r'{border-color}|transparent|inherit',
166 'border-left-color': r'{border-color}|transparent|inherit',
167 'border-top-style': r'{border-style}|inherit',
168 'border-right-style': r'{border-style}|inherit',
169 'border-bottom-style': r'{border-style}|inherit',
170 'border-left-style': r'{border-style}|inherit',
171 'border-top-width': r'{border-width}|inherit',
172 'border-right-width': r'{border-width}|inherit',
173 'border-bottom-width': r'{border-width}|inherit',
174 'border-right-width': r'{border-width}|inherit',
175 'border-width': r'{border-width}(\s+{border-width}){0,3}|inherit',
176 'border': r'{border-attrs}(\s+{border-attrs})*|inherit',
177 'bottom': r'{length}|{percentage}|auto|inherit',
178 'caption-side': r'top|bottom|inherit',
179 'clear': r'none|left|right|both|inherit',
180 'clip': r'{shape}|auto|inherit',
181 'color': r'{color}|inherit',
182 'content': r'normal|{content}(\s+{content})*|inherit',
183 'counter-increment': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
184 'counter-reset': r'({identifier}(\s+{integer})?)(\s+({identifier}(\s+{integer})))*|none|inherit',
185 'cue-after': r'{uri}|none|inherit',
186 'cue-before': r'{uri}|none|inherit',
187 'cue': r'({uri}|none|inherit){1,2}|inherit',
188 'cursor': r'((({uri}{w},{w})*)?(auto|crosshair|default|pointer|move|(e|ne|nw|n|se|sw|s|w)-resize|text|wait|help|progress))|inherit',
189 'direction': r'ltr|rtl|inherit',
190 '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',
191 'elevation': r'{angle}|below|level|above|higher|lower|inherit',
192 'empty-cells': r'show|hide|inherit',
193 'float': r'left|right|none|inherit',
194 'font-family': r'{font-family}',
195 'font-size': r'{font-size}',
196 'font-style': r'{font-style}',
197 'font-variant': r'{font-variant}',
198 'font-weight': r'{font-weight}',
199 'font': r'({font-attrs}\s+)*{font-size}({w}/{w}{line-height})?\s+{font-family}|caption|icon|menu|message-box|small-caption|status-bar|inherit',
200 'height': r'{length}|{percentage}|auto|inherit',
201 'left': r'{length}|{percentage}|auto|inherit',
202 'letter-spacing': r'normal|{length}|inherit',
203 'line-height': r'{line-height}',
204 'list-style-image': r'{list-style-image}',
205 'list-style-position': r'{list-style-position}',
206 'list-style-type': r'{list-style-type}',
207 'list-style': r'{list-attrs}(\s+{list-attrs})*|inherit',
208 'margin-right': r'{margin-width}|inherit',
209 'margin-left': r'{margin-width}|inherit',
210 'margin-top': r'{margin-width}|inherit',
211 'margin-bottom': r'{margin-width}|inherit',
212 'margin': r'{margin-width}(\s+{margin-width}){0,3}|inherit',
213 'max-height': r'{length}|{percentage}|none|inherit',
214 'max-width': r'{length}|{percentage}|none|inherit',
215 'min-height': r'{length}|{percentage}|none|inherit',
216 'min-width': r'{length}|{percentage}|none|inherit',
217 'orphans': r'{integer}|inherit',
218 'outline-color': r'{outline-color}',
219 'outline-style': r'{outline-style}',
220 'outline-width': r'{outline-width}',
221 'outline': r'{outline-attrs}(\s+{outline-attrs})*|inherit',
222 'overflow': r'visible|hidden|scroll|auto|inherit',
223 'padding-top': r'{padding-width}|inherit',
224 'padding-right': r'{padding-width}|inherit',
225 'padding-bottom': r'{padding-width}|inherit',
226 'padding-left': r'{padding-width}|inherit',
227 'padding': r'{padding-width}(\s+{padding-width}){0,3}|inherit',
228 'page-break-after': r'auto|always|avoid|left|right|inherit',
229 'page-break-before': r'auto|always|avoid|left|right|inherit',
230 'page-break-inside': r'avoid|auto|inherit',
231 'pause-after': r'{time}|{percentage}|inherit',
232 'pause-before': r'{time}|{percentage}|inherit',
233 'pause': r'({time}|{percentage}){1,2}|inherit',
234 'pitch-range': r'{number}|inherit',
235 'pitch': r'{frequency}|x-low|low|medium|high|x-high|inherit',
236 'play-during': r'{uri}(\s+(mix|repeat))*|auto|none|inherit',
237 'position': r'static|relative|absolute|fixed|inherit',
238 'quotes': r'({string}\s+{string})(\s+{string}\s+{string})*|none|inherit',
239 'richness': r'{number}|inherit',
240 'right': r'{length}|{percentage}|auto|inherit',
241 'speak-header': r'once|always|inherit',
242 'speak-numeral': r'digits|continuous|inherit',
243 'speak-punctuation': r'code|none|inherit',
244 'speak': r'normal|none|spell-out|inherit',
245 'speech-rate': r'{number}|x-slow|slow|medium|fast|x-fast|faster|slower|inherit',
246 'stress': r'{number}|inherit',
247 'table-layout': r'auto|fixed|inherit',
248 'text-align': r'left|right|center|justify|inherit',
249 'text-decoration': r'none|{text-attrs}(\s+{text-attrs})*|inherit',
250 'text-indent': r'{length}|{percentage}|inherit',
251 'text-transform': r'capitalize|uppercase|lowercase|none|inherit',
252 'top': r'{length}|{percentage}|auto|inherit',
253 'unicode-bidi': r'normal|embed|bidi-override|inherit',
254 'vertical-align': r'baseline|sub|super|top|text-top|middle|bottom|text-bottom|{percentage}|{length}|inherit',
255 'visibility': r'visible|hidden|collapse|inherit',
256 'voice-family': r'({specific-voice}|{generic-voice}{w},{w})*({specific-voice}|{generic-voice})|inherit',
257 'volume': r'{number}|{percentage}|silent|x-soft|soft|medium|loud|x-loud|inherit',
258 'white-space': r'normal|pre|nowrap|pre-wrap|pre-line|inherit',
259 'widows': r'{integer}|inherit',
260 'width': r'{length}|{percentage}|auto|inherit',
261 'word-spacing': r'normal|{length}|inherit',
262 'z-index': r'auto|{integer}|inherit',
263 }
264
266 """ Expand macros in token dictionary """
267 def macro_value(m):
268 return '(?:%s)' % MACROS[m.groupdict()['macro']]
269 for key, value in tokdict.items():
270 while re.search(r'{[a-z][a-z0-9-]*}', value):
271 value = re.sub(r'{(?P<macro>[a-z][a-z0-9-]*)}',
272 macro_value, value)
273 tokdict[key] = value
274 return tokdict
275
277 """ Compile all regular expressions into callable objects """
278 for key, value in tokdict.items():
279 tokdict[key] = re.compile('^(?:%s)$' % value, re.I).match
280 return tokdict
281
282 _compile_regexes(_expand_macros(cssvalues))
283
284
285
286
287 _reCSStoDOMname = re.compile('-[a-z]', re.I)
289 """
290 returns DOMname for given CSSname e.g. for CSSname 'font-style' returns
291 'fontStyle'
292 """
293 def _doCSStoDOMname2(m): return m.group(0)[1].capitalize()
294 return _reCSStoDOMname.sub(_doCSStoDOMname2, CSSname)
295
296 _reDOMtoCSSname = re.compile('([A-Z])[a-z]+')
298 """
299 returns CSSname for given DOMname e.g. for DOMname 'fontStyle' returns
300 'font-style'
301 """
302 def _doDOMtoCSSname2(m): return '-' + m.group(0).lower()
303 return _reDOMtoCSSname.sub(_doDOMtoCSSname2, DOMname)
304
305
307 """
308 The CSS2Properties interface represents a convenience mechanism
309 for retrieving and setting properties within a CSSStyleDeclaration.
310 The attributes of this interface correspond to all the properties
311 specified in CSS2. Getting an attribute of this interface is
312 equivalent to calling the getPropertyValue method of the
313 CSSStyleDeclaration interface. Setting an attribute of this
314 interface is equivalent to calling the setProperty method of the
315 CSSStyleDeclaration interface.
316
317 cssutils actually also allows usage of ``del`` to remove a CSS property
318 from a CSSStyleDeclaration.
319
320 This is an abstract class, the following functions need to be present
321 in inheriting class:
322
323 - ``_getP``
324 - ``_setP``
325 - ``_delP``
326 """
327
328 - def _getP(self, CSSname): pass
329 - def _setP(self, CSSname, value): pass
330 - def _delP(self, CSSname): pass
331
332
333
334
335 CSS2Properties._properties = [_toDOMname(p) for p in cssvalues.keys()]
336
337
339 """
340 closure to keep name known in each properties accessor function
341 DOMname is converted to CSSname here, so actual calls use CSSname
342 """
343 CSSname = _toCSSname(DOMname)
344 def _get(self): return self._getP(CSSname)
345 def _set(self, value): self._setP(CSSname, value)
346 def _del(self): self._delP(CSSname)
347 return _get, _set, _del
348
349
350 for DOMname in CSS2Properties._properties:
351 setattr(CSS2Properties, DOMname,
352 property(*__named_property_def(DOMname)))
353
354
355
356 if __name__=='__main__':
357 c = CSS2Properties()
358 print CSS2Properties.color
359
360
361
362
363
364
365