Package tdi :: Package tools :: Module css
[frames] | no frames]

Source Code for Module tdi.tools.css

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