1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 """
18 ===========
19 CSS Tools
20 ===========
21
22 CSS Tools.
23 """
24 __author__ = u"Andr\xe9 Malo"
25 __docformat__ = "restructuredtext en"
26
27 from tdi import filters as _filters
28 from tdi.tools._util import norm_enc as _norm_enc
29
30
32 """
33 Cleanup a single CSS buffer
34
35 This methods removes CDATA and starting/finishing comment containers.
36
37 :Parameters:
38 `style` : ``basestring``
39 Buffer to cleanup
40
41 `encoding` : ``str``
42 Encoding in case that toescape is a ``str``. If omitted or
43 ``None``, no encoding is applied and `style` is expected to be
44 ASCII compatible.
45
46 :Return: The cleaned up buffer, typed as input
47 :Rtype: ``basestring``
48 """
49 isuni = isinstance(style, unicode)
50 if not isuni:
51
52
53 if encoding is None or _norm_enc(encoding) == 'ascii':
54 encoding = 'latin-1'
55 style = str(style).decode(encoding)
56 style = style.strip()
57 if style.startswith(u'<!--'):
58 style = style[4:]
59 if style.endswith(u'-->'):
60 style = style[:-3]
61 style = style.strip()
62 if style.startswith(u'/*'):
63 pos = style.find(u'*/')
64 if pos >= 0:
65 style = style[pos + 2:]
66 if style.endswith(u'*/'):
67 style = style[::-1]
68 pos = style.find(u'*/')
69 if pos >= 0:
70 style = style[pos + 2:]
71 style = style[::-1]
72 style = style.strip()
73 if style.startswith(u'<![CDATA['):
74 style = style[len(u'<![CDATA['):]
75 if style.endswith(u']]>'):
76 style = style[:-3]
77 style = style.strip()
78 if isuni:
79 return style
80 return style.encode(encoding)
81
82
83 -def cdata(style, encoding=None):
84 """
85 Add a failsafe CDATA container around a style
86
87 See <http://lists.w3.org/Archives/Public/www-html/2002Apr/0053.html>
88 for details.
89
90 :Parameters:
91 `style` : ``basestring``
92 JS to contain
93
94 `encoding` : ``str``
95 Encoding in case that toescape is a ``str``. If omitted or
96 ``None``, no encoding is applied and `style` is expected to be
97 ASCII compatible.
98
99 :Return: The contained style, typed as input
100 :Rtype: ``basestring``
101 """
102 isuni = isinstance(style, unicode)
103 if not isuni:
104
105
106 if encoding is None or _norm_enc(encoding) == 'ascii':
107 encoding = 'latin-1'
108 style = str(style).decode(encoding)
109 style = cleanup(style)
110 if style:
111 style = u'<!--/*--><![CDATA[/*><!--*/\n%s\n/*]]>*/-->' % style
112 if isuni:
113 return style
114 return style.encode(encoding)
115
116
117 -def minify(style, encoding=None):
118 """
119 Minify CSS (using `rcssmin`_)
120
121 .. _rcssmin: http://opensource.perlig.de/rcssmin/
122
123 :Parameters:
124 `style` : ``basestring``
125 CSS to minify
126
127 `encoding` : ``str``
128 Encoding in case that toescape is a ``str``. If omitted or
129 ``None``, no encoding is applied and `style` is expected to be
130 ASCII compatible.
131
132 :Return: Minified CSS, typed as input
133 :Rtype: ``basestring``
134 """
135 from tdi.tools import rcssmin as _rcssmin
136
137 isuni = isinstance(style, unicode)
138 if not isuni and encoding is not None:
139
140
141 if _norm_enc(encoding) == 'ascii':
142 encoding = 'latin-1'
143 return _rcssmin.cssmin(style.decode(encoding)).encode(encoding)
144 return _rcssmin.cssmin(style)
145
146
148 """
149 TDI filter for modifying inline css
150
151 :IVariables:
152 `_collecting` : ``bool``
153 Currently collecting CSS text?
154
155 `_buffer` : ``list``
156 Collection buffer
157
158 `_starttag` : ``tuple`` or ``None``
159 Original style starttag parameters
160
161 `_modify` : callable
162 Modifier function
163
164 `_attribute` : ``str``
165 ``tdi`` attribute name or ``None`` (if standalone)
166
167 `_strip` : ``bool``
168 Strip empty style elements?
169 """
170
171 - def __init__(self, builder, modifier, strip_empty=True, standalone=False):
172 """
173 Initialization
174
175 :Parameters:
176 `builder` : `tdi.interfaces.BuildingListenerInterface`
177 Builder
178
179 `modifier` : callable
180 Modifier function. Takes a style and returns the (possibly)
181 modified result.
182
183 `strip_empty` : ``bool``
184 Strip empty style elements?
185
186 `standalone` : ``bool``
187 Standalone context? In this case, we won't watch out for TDI
188 attributes.
189 """
190 super(CSSInlineFilter, self).__init__(builder)
191 self._collecting = False
192 self._buffer = []
193 self._starttag = None
194 self._modify = modifier
195 self._normalize = self.builder.decoder.normalize
196 if standalone:
197 self._attribute = None
198 else:
199 self._attribute = self._normalize(
200 self.builder.analyze.attribute
201 )
202 self._strip = strip_empty
203
205 """
206 Handle starttag
207
208 Style starttags are delayed until the endtag is found. The whole
209 element is then evaluated (and possibly thrown away).
210
211 :See: `tdi.interfaces.ListenerInterface`
212 """
213 if not closed and self._normalize(name) == 'style':
214 self._collecting = True
215 self._buffer = []
216 self._starttag = name, attr, closed, data
217 else:
218 self.builder.handle_starttag(name, attr, closed, data)
219
221 """
222 Handle endtag
223
224 When currently collecting, it must be a style endtag. The style
225 element content is then cleaned up (using `cleanup`) and then
226 modified (using the modifiy function passed during initialization).
227 The result replaces the original. If it's empty and the starttag
228 does not provide a ``tdi`` attribute and the filter was
229 configured to do so: the whole element is thrown away.
230
231 :See: `tdi.interfaces.ListenerInterface`
232 """
233 if self._collecting:
234 normalize = self._normalize
235 if normalize(name) != 'style':
236 raise AssertionError("Invalid event stream")
237
238 self._collecting = False
239 style, self._buffer = ''.join(self._buffer), []
240 style = self._modify(cleanup(style))
241
242 if not style and self._strip:
243 attrdict = dict((normalize(name), val)
244 for name, val in self._starttag[1]
245 )
246 if self._attribute is None or self._attribute not in attrdict:
247 return
248
249 self.builder.handle_starttag(*self._starttag)
250 self._starttag = None
251 self.builder.handle_text(style)
252
253 self.builder.handle_endtag(name, data)
254
255 - def handle_text(self, data):
256 """
257 Handle text
258
259 While collecting style text, the received data is buffered.
260 Otherwise the event is just passed through.
261
262 :See: `tdi.interfaces.ListenerInterface`
263 """
264 if not self._collecting:
265 return self.builder.handle_text(data)
266 self._buffer.append(data)
267
268
270 """
271 TDI Filter for minifying inline CSS
272
273 :Parameters:
274 `minifier` : callable``
275 Minifier function. If omitted or ``None``, the `builtin minifier`_ is
276 applied.
277
278 .. _builtin minifier: http://opensource.perlig.de/rcssmin/
279
280 `standalone` : ``bool``
281 Standalone context? In this case, we won't watch out for TDI
282 attributes.
283 """
284
285 if minifier is None:
286 minifier = minify
287 return CSSInlineFilter(builder, minify, standalone=standalone)
288
289
291 """
292 TDI filter for adding failsafe CDATA containers around CSS styles
293
294 See <http://lists.w3.org/Archives/Public/www-html/2002Apr/0053.html>
295 for details.
296
297 :Parameters:
298 `standalone` : ``bool``
299 Standalone context? In this case, we won't watch out for TDI
300 attributes.
301 """
302 return CSSInlineFilter(builder, cdata, standalone=standalone)
303