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

Source Code for Module tdi.tools.css

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