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