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

Source Code for Module tdi.tools.css

  1  # -*- coding: ascii -*- 
  2  # 
  3  # Copyright 2006 - 2013 
  4  # Andr\xe9 Malo or his licensors, as applicable 
  5  # 
  6  # Licensed under the Apache License, Version 2.0 (the "License"); 
  7  # you may not use this file except in compliance with the License. 
  8  # You may obtain a copy of the License at 
  9  # 
 10  #     http://www.apache.org/licenses/LICENSE-2.0 
 11  # 
 12  # Unless required by applicable law or agreed to in writing, software 
 13  # distributed under the License is distributed on an "AS IS" BASIS, 
 14  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 15  # See the License for the specific language governing permissions and 
 16  # limitations under the License. 
 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   
31 -def cleanup(style, encoding=None):
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 # don't decode ascii, but latin-1. just in case, if it's a 52 # dumb default. Doesn't hurt here, but avoids failures. 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 # don't decode ascii, but latin-1. just in case, if it's a 105 # dumb default. Doesn't hurt here, but avoids failures. 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 # don't decode ascii, but latin-1. just in case, if it's a 140 # dumb default. Doesn't hurt here, but avoids failures. 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
147 -class CSSInlineFilter(_filters.BaseEventFilter):
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
204 - def handle_starttag(self, name, attr, closed, data):
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
220 - def handle_endtag(self, name, data):
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
269 -def MinifyFilter(builder, minifier=None, standalone=False):
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 # pylint: disable = C0103 285 if minifier is None: 286 minifier = minify 287 return CSSInlineFilter(builder, minify, standalone=standalone)
288 289
290 -def CDATAFilter(builder, standalone=False): # pylint: disable = C0103
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