OLD | NEW |
| (Empty) |
1 import cPickle | |
2 import os.path | |
3 import sys | |
4 from collections import defaultdict | |
5 | |
6 from pyparsing import ParseBaseException | |
7 | |
8 from scss import SORTING | |
9 from scss.base import Node, Empty, ParseNode, ContentNode, IncludeNode | |
10 from scss.control import Variable, Expression, Function, Mixin, Include, MixinPa
ram, Extend, Variables, Option, FunctionDefinition, FunctionReturn, If, For, Sep
ValString, Stylet | |
11 from scss.function import warn, _nest | |
12 from scss.grammar import * | |
13 from scss.value import NumberValue, StringValue, ColorValue, QuotedStringValue,
PointValue, RepeatValue | |
14 | |
15 | |
16 class Comment(Node): | |
17 """ Comment node. | |
18 """ | |
19 delim = '' | |
20 def __str__(self): | |
21 """ Clean comments if option `comments` disabled | |
22 or enabled option `compress` | |
23 """ | |
24 if self.root.get_opt('comments') and not self.root.get_opt('compress'): | |
25 return super(Comment, self).__str__() | |
26 return '' | |
27 | |
28 | |
29 class Warn(Empty): | |
30 """ Warning node @warn. | |
31 """ | |
32 def parse(self, target): | |
33 """ Write message to stderr. | |
34 """ | |
35 if self.root.get_opt('warn'): | |
36 warn(self.data[1]) | |
37 | |
38 | |
39 class Import(Node): | |
40 """ Import node @import. | |
41 """ | |
42 def __str__(self): | |
43 """ Write @import to outstring. | |
44 """ | |
45 return "%s;\n" % super(Import, self).__str__() | |
46 | |
47 # TODO(terry): Need to eliminate the { } as literal and consume with notion of | |
48 # DECLARATION_BODY, ANIMATION_BODY, AnimationDecls, etc. This will | |
49 # make the formatting code general and succinct. | |
50 class AnimationDecls(ContentNode): | |
51 def __str__(self): | |
52 nl, ws, ts = self.root.cache['delims'] | |
53 | |
54 # Formatting for animation declarations. Each declaration body is | |
55 # wrapped by curly braces with one level of indentation (2 spaces). | |
56 # Within a curly brace each declaration is indented 2 spaced and each | |
57 # declaration ends with a semi-colon. | |
58 declLen = len(self.data) | |
59 if (declLen > 0): | |
60 declLen -= 1 | |
61 assert self.data[0] == '{' and self.data[declLen] == '}' | |
62 return ''.join(['%s{%s' % (ws, nl), | |
63 '%s%s' % (ws, ws), | |
64 '%s%s%s;' % (ws, ws, | |
65 ''.join(str(decl) for decl in self.data[1:declLen])), | |
66 '%s%s%s}%s%s%s' % (nl, ws, ws, nl, ws, ws)]) | |
67 | |
68 class AnimationBody(ContentNode): | |
69 def __str__(self): | |
70 nl, ws, ts = self.root.cache['delims'] | |
71 | |
72 # Formatting for the body of the animation. The body is surrounded by an | |
73 # open curly brace and newline followed by a repeated field selector | |
74 # (indented by 2 spaces) and the animation declaration. When all | |
75 # animation declarations are emitted add the end curly brace for the | |
76 # entire animation. | |
77 bodyLen = len(self.data) | |
78 if bodyLen > 0: | |
79 bodyLen -= 1 | |
80 assert self.data[0] == '{' and self.data[bodyLen] == '}' | |
81 return ''.join(['%s{%s' % (ws, nl), | |
82 '%s%s' % (ws, ws), | |
83 ''.join(str(body) for body in self.data[1:bodyLen]), | |
84 '%s' % nl, | |
85 '}%s%s' % (nl, nl)]) | |
86 | |
87 class Ruleset(ContentNode): | |
88 """ Rule node. | |
89 """ | |
90 def parse(self, target): | |
91 """ Parse nested rulesets | |
92 and save it in cache. | |
93 """ | |
94 if isinstance(target, ContentNode): | |
95 if target.name: | |
96 self.parent = target | |
97 self.name.parse(self) | |
98 self.name += target.name | |
99 target.ruleset.append(self) | |
100 self.root.cache['rset'][str(self.name).split()[0]].add(self) | |
101 super(Ruleset, self).parse(target) | |
102 | |
103 | |
104 class Declaration(ParseNode): | |
105 """ Declaration node. | |
106 """ | |
107 def __init__(self, s, n, t): | |
108 """ Add self.name and self.expr to object. | |
109 """ | |
110 super(Declaration, self).__init__(s, n, t) | |
111 self.name = self.expr = '' | |
112 | |
113 def parse(self, target): | |
114 """ Parse nested declaration. | |
115 """ | |
116 if not isinstance(target, Node): | |
117 parent = ContentNode(None, None, []) | |
118 parent.parse(target) | |
119 target = parent | |
120 | |
121 super(Declaration, self).parse(target) | |
122 self.name = str(self.data[0]) | |
123 while isinstance(target, Declaration): | |
124 self.name = '-'.join(( str(target.data[0]), self.name)) | |
125 target = target.parent | |
126 | |
127 self.expr = ' '.join(str(n) for n in self.data[2:] if not isinstance(n,
Declaration)) | |
128 if self.expr: | |
129 target.declareset.append(self) | |
130 | |
131 def __str__(self): | |
132 """ Warning on unknown declaration | |
133 and write current in outstring. | |
134 """ | |
135 if ( not SORTING.has_key(self.name.strip('*_')) | |
136 and self.root.get_opt('warn') ): | |
137 warn("Unknown declaration: %s" % self.name) | |
138 | |
139 return (":%s" % self.root.cache['delims'][1] ).join( | |
140 (self.name, self.expr)) | |
141 | |
142 | |
143 class DeclarationName(ParseNode): | |
144 """ Name of declaration node. | |
145 For spliting it in one string. | |
146 """ | |
147 delim = '' | |
148 | |
149 | |
150 class SelectorTree(ParseNode): | |
151 """ Tree of selectors in ruleset. | |
152 """ | |
153 delim = ', ' | |
154 | |
155 def extend(self, target): | |
156 """ @extend selectors tree. | |
157 """ | |
158 self_test = ', '.join(map(str, self.data)) | |
159 target_test = ', '.join(map(str, target.data)) | |
160 self.data = (self_test + ', ' + self_test.replace(str(self.data[0].data[
0]), target_test)).split(', ') | |
161 | |
162 def __add__(self, target): | |
163 """ Add selectors from parent nodes. | |
164 """ | |
165 if isinstance(target, SelectorTree): | |
166 self_test = ', '.join(map(str, self.data)) | |
167 target_test = ', '.join(map(str, target.data)) | |
168 self.data = _nest(target_test, self_test).split(', ') | |
169 return self | |
170 | |
171 def __str__(self): | |
172 selOut = '' | |
173 for entry in self.data: | |
174 if (isinstance(entry, str)): | |
175 # Combinator and commas are as raw strings. | |
176 selOut += ' %s ' % entry | |
177 else: | |
178 for selEntry in entry.data: | |
179 if (isinstance(selEntry, str)): | |
180 selOut += ' %s ' % selEntry | |
181 else: | |
182 # Construct the pseudo elements etc. multiple regex tokens as str. | |
183 selOut += ''.join(str(n) for n in selEntry.data); | |
184 selOut += ' ' | |
185 return selOut | |
186 | |
187 | |
188 class Selector(ParseNode): | |
189 """ Simple selector node. | |
190 """ | |
191 delim = '' | |
192 | |
193 def __str__(self): | |
194 """ Write to output. | |
195 """ | |
196 return ''.join(StringValue(n).value for n in self.data) | |
197 | |
198 | |
199 class VarDefinition(ParseNode, Empty): | |
200 """ Variable definition. | |
201 """ | |
202 def __init__(self, s, n, t): | |
203 """ Save self.name, self.default, self.expression | |
204 """ | |
205 super(VarDefinition, self).__init__(s, n, t) | |
206 self.name = t[0][1:] | |
207 self.default = len(t) > 2 | |
208 self.expression = t[1] | |
209 | |
210 def parse(self, target): | |
211 """ Update root and parent context. | |
212 """ | |
213 super(VarDefinition, self).parse(target) | |
214 if isinstance(self.parent, ParseNode): | |
215 self.parent.ctx.update({ self.name: self.expression.value }) | |
216 self.root.set_var(self) | |
217 | |
218 | |
219 class Media(ParseNode): | |
220 def __str__(self): | |
221 selOut = '' | |
222 # TODO(terry): Need to retain parenthesis in expression for the media | |
223 # selector. Parenthesis are important and need a better | |
224 # grammar. For now hack is to wrap the 'and' operator and | |
225 # comma operator to use emit parentheses around expressions. | |
226 anyOper = False | |
227 openParen = 0 | |
228 for entry in self.data: | |
229 if isinstance(entry, str) and entry == 'and': | |
230 if openParen > 0: | |
231 selOut += ') and ' | |
232 openParen -= 1 | |
233 else: | |
234 selOut += ' and ' | |
235 anyOper = True | |
236 elif isinstance(entry, str) and entry == '{': | |
237 selOut += ''.join([')' for n in xrange(openParen)]) | |
238 selOut += ' {\n' | |
239 # Handle the expression. | |
240 elif isinstance(entry, StringValue): | |
241 if openParen > 0: | |
242 selOut += ') , (%s' % entry | |
243 elif anyOper: | |
244 selOut += '(%s' % entry | |
245 anyOper = False | |
246 openParen += 1 | |
247 else: | |
248 selOut += ' %s ' % entry | |
249 else: | |
250 selOut += ' %s ' % entry | |
251 | |
252 selOut += '}\n' | |
253 return selOut | |
254 | |
255 class Stylesheet(object): | |
256 """ Root stylesheet node. | |
257 """ | |
258 | |
259 def_delims = '\n', ' ', '\t' | |
260 | |
261 # Known stylet base classes. | |
262 styletClasses = [] | |
263 | |
264 # List of known CSS class names emitted. We only want to emit one constant | |
265 # per CSS class. | |
266 knownClassNames = [] | |
267 | |
268 # Dart class to output only class name selectors with style properties. | |
269 cssClass = [] | |
270 | |
271 # Dart class to output only class name selectors with no style properties. | |
272 cssStateSelectors = [] | |
273 | |
274 currentOptions = [] | |
275 currentFile = '' | |
276 | |
277 scssIncludes = [] | |
278 | |
279 exportedClassName = [] | |
280 classNameAsState = [] | |
281 | |
282 def __init__(self, cache = None, options=None): | |
283 self.cache = cache or dict( | |
284 | |
285 # Variables context | |
286 ctx = dict(), | |
287 | |
288 # Mixin context | |
289 mix = dict(), | |
290 | |
291 # Rules context | |
292 rset = defaultdict(set), | |
293 | |
294 # Options context | |
295 opts = dict( | |
296 comments = True, | |
297 warn = True, | |
298 sort = True, | |
299 path = os.getcwd(), | |
300 ), | |
301 | |
302 # CSS delimeters | |
303 delims = self.def_delims, | |
304 | |
305 ) | |
306 | |
307 if options: | |
308 self.currentOptions = options | |
309 for option in options.items(): | |
310 self.set_opt(*option) | |
311 | |
312 self.setup() | |
313 Node.root = self | |
314 | |
315 def setup(self): | |
316 | |
317 # Values | |
318 OTHER_VALUE.setParseAction(NumberValue) | |
319 PERCENTAGE_VALUE.setParseAction(NumberValue) | |
320 IDENT.setParseAction(StringValue) | |
321 PATH.setParseAction(StringValue) | |
322 POINT.setParseAction(PointValue) | |
323 COLOR_VALUE.setParseAction(ColorValue) | |
324 REPEAT_VALUE.setParseAction(RepeatValue) | |
325 quotedString.setParseAction(QuotedStringValue) | |
326 EXPRESSION.setParseAction(Expression) | |
327 SEP_VAL_STRING.setParseAction(SepValString) | |
328 | |
329 # Vars | |
330 VARIABLE.setParseAction(Variable) | |
331 VAR_DEFINITION.setParseAction(VarDefinition) | |
332 VARIABLES.setParseAction(Variables) | |
333 FUNCTION.setParseAction(Function) | |
334 FUNCTION_DEFINITION.setParseAction(FunctionDefinition) | |
335 FUNCTION_RETURN.setParseAction(FunctionReturn) | |
336 | |
337 # Coments | |
338 SCSS_COMMENT.setParseAction(lambda x: '') | |
339 CSS_COMMENT.setParseAction(Comment) | |
340 | |
341 # At rules | |
342 IMPORT.setParseAction(Import) | |
343 CHARSET.setParseAction(Import) | |
344 MEDIA.setParseAction(Media) | |
345 STYLET.setParseAction(Stylet) | |
346 ANIMATION_DECLARATIONS.setParseAction(AnimationDecls) | |
347 ANIMATION_BODY.setParseAction(AnimationBody) | |
348 | |
349 # Rules | |
350 RULESET.setParseAction(Ruleset) | |
351 DECLARATION.setParseAction(Declaration) | |
352 DECLARATION_NAME.setParseAction(DeclarationName) | |
353 SELECTOR.setParseAction(Selector) | |
354 SELECTOR_GROUP.setParseAction(ParseNode) | |
355 SELECTOR_TREE.setParseAction(SelectorTree) | |
356 FONT_FACE.setParseAction(ContentNode) | |
357 | |
358 # SCSS Directives | |
359 MIXIN.setParseAction(Mixin) | |
360 MIXIN_PARAM.setParseAction(MixinParam) | |
361 INCLUDE.setParseAction(Include) | |
362 EXTEND.setParseAction(Extend) | |
363 OPTION.setParseAction(Option) | |
364 IF.setParseAction(If) | |
365 IF_BODY.setParseAction(IncludeNode) | |
366 ELSE.setParseAction(IncludeNode) | |
367 FOR.setParseAction(For) | |
368 FOR_BODY.setParseAction(IncludeNode) | |
369 WARN.setParseAction(Warn) | |
370 | |
371 @property | |
372 def ctx(self): | |
373 return self.cache['ctx'] | |
374 | |
375 def set_var(self, vardef): | |
376 """ Set variable to global stylesheet context. | |
377 """ | |
378 if not(vardef.default and self.cache['ctx'].get(vardef.name)): | |
379 self.cache['ctx'][vardef.name] = vardef.expression.value | |
380 | |
381 def set_opt(self, name, value): | |
382 """ Set option. | |
383 """ | |
384 self.cache['opts'][name] = value | |
385 | |
386 if name == 'compress': | |
387 self.cache['delims'] = self.def_delims if not value else ('', '', ''
) | |
388 | |
389 def get_opt(self, name): | |
390 """ Get option. | |
391 """ | |
392 return self.cache['opts'].get(name) | |
393 | |
394 def update(self, cache): | |
395 """ Update self cache from other. | |
396 """ | |
397 self.cache['delims'] = cache.get('delims') | |
398 self.cache['opts'].update(cache.get('opts')) | |
399 self.cache['rset'].update(cache.get('rset')) | |
400 self.cache['mix'].update(cache.get('mix')) | |
401 map(self.set_var, cache['ctx'].values()) | |
402 | |
403 def scan(self, src): | |
404 """ Scan scss from string and return nodes. | |
405 """ | |
406 assert isinstance(src, basestring) | |
407 try: | |
408 nodes = STYLESHEET.parseString(src, parseAll=True) | |
409 return nodes | |
410 except ParseBaseException: | |
411 err = sys.exc_info()[1] | |
412 print >> sys.stderr, err.line | |
413 print >> sys.stderr, " "*(err.column-1) + "^" | |
414 print >> sys.stderr, err | |
415 sys.exit(1) | |
416 | |
417 def parse(self, nodes): | |
418 map(lambda n: n.parse(self) if isinstance(n, Node) else None, nodes) | |
419 | |
420 def loads(self, src): | |
421 """ Compile css from scss string. | |
422 """ | |
423 assert isinstance(src, basestring) | |
424 nodes = self.scan(src.strip()) | |
425 self.parse(nodes) | |
426 return ''.join(map(str, nodes)) | |
427 | |
428 def load(self, f, precache=None): | |
429 """ Compile scss from file. | |
430 File is string path of file object. | |
431 """ | |
432 precache = precache or self.get_opt('cache') or False | |
433 nodes = None | |
434 if isinstance(f, file): | |
435 path = os.path.abspath(f.name) | |
436 | |
437 else: | |
438 path = os.path.abspath(f.name) | |
439 f = open(f) | |
440 | |
441 cache_path = os.path.splitext(path)[0] + '.ccss' | |
442 | |
443 if precache and os.path.exists(cache_path): | |
444 ptime = os.path.getmtime(cache_path) | |
445 ttime = os.path.getmtime(path) | |
446 if ptime > ttime: | |
447 dump = open(cache_path, 'rb').read() | |
448 nodes = cPickle.loads(dump) | |
449 | |
450 if not nodes: | |
451 src = f.read() | |
452 nodes = self.scan(src.strip()) | |
453 | |
454 if precache: | |
455 f = open(cache_path, 'wb') | |
456 cPickle.dump(nodes, f) | |
457 | |
458 self.parse(nodes) | |
459 return ''.join(map(str, nodes)) | |
460 | |
461 # TODO(terry): For now special function for returning nodes. | |
462 def loadReturnNodes(self, f, precache=None): | |
463 """ Compile scss from file. | |
464 File is string path of file object. | |
465 """ | |
466 self.currentFile = f | |
467 | |
468 precache = precache or self.get_opt('cache') or False | |
469 nodes = None | |
470 if isinstance(f, file): | |
471 path = os.path.abspath(f.name) | |
472 | |
473 else: | |
474 path = os.path.abspath(f.name) | |
475 f = open(f) | |
476 | |
477 cache_path = os.path.splitext(path)[0] + '.ccss' | |
478 | |
479 if precache and os.path.exists(cache_path): | |
480 ptime = os.path.getmtime(cache_path) | |
481 ttime = os.path.getmtime(path) | |
482 if ptime > ttime: | |
483 dump = open(cache_path, 'rb').read() | |
484 nodes = cPickle.loads(dump) | |
485 | |
486 if not nodes: | |
487 src = f.read() | |
488 nodes = self.scan(src.strip()) | |
489 | |
490 if precache: | |
491 f = open(cache_path, 'wb') | |
492 cPickle.dump(nodes, f) | |
493 | |
494 self.parse(nodes) | |
495 return nodes | |
496 | |
497 def __str__(self): | |
498 return 'media'; | |
499 | |
500 # Format of the Dart CSS class: | |
501 # | |
502 # class Layout1 { | |
503 # // selector, properties<propertyName, value> | |
504 # static final selectors = const { | |
505 # '#elemId' : const { | |
506 # 'left' : '20px' | |
507 # }, | |
508 # '.className' : const { | |
509 # 'color' : 'red' | |
510 # } | |
511 # }; | |
512 # } | |
513 # | |
514 # Emit pre-defined classes for Dart CSS. | |
515 # | |
516 def emitPreDefineDart(self, sourceFile): | |
517 return ['// File generated by SCSS from source file %s\n' % sourceFile, | |
518 '// Do not edit.\n\n' | |
519 ] | |
520 | |
521 def emitCssClass(self, cssName): | |
522 self.cssClass.append('class {0} {{\n'.format(cssName)) | |
523 self.cssClass.append(' // CSS class selectors:\n') | |
524 | |
525 # Emit stylet instance: | |
526 # | |
527 # static final CssLayout layout = new CssLayout(); | |
528 def emitStyletInstance(self, styletName): | |
529 styletClass = 'STYLET_{0}'.format(styletName) | |
530 return ' static final {0} {1} = new {0}();\n\n'.format(styletClass, | |
531 styletName) | |
532 | |
533 # Emit each exported stylet. | |
534 # | |
535 # class {stylet name} { | |
536 # // selector, properties<propertyName, value> | |
537 # static final selectors = const { | |
538 def emitStyletClass(self, stylet): | |
539 self.styletClasses.append('class {0} {{\n'. | |
540 format(stylet.name)) | |
541 self.styletClasses.append(' // selector, properties<propertyName, value>\
n') | |
542 self.styletClasses.append(' static final selectors = const {\n') | |
543 return stylet.name | |
544 | |
545 def emitCloseCSSStyletSelector(self): | |
546 self.styletClasses.append("\n }") | |
547 | |
548 # Emit the selector for this style: | |
549 # where selectorValue is valid selector (e.g., #elemId, .className, etc.) | |
550 # | |
551 # 'selectorValue' : const { | |
552 def emitCSSStyletSelector(self, selectorTree, anySelectors): | |
553 if anySelectors: | |
554 self.styletClasses.append(",\n"); | |
555 self.styletClasses.append(" '%s' : const {\n" % selectorTree.data[0]) | |
556 | |
557 # Emit each property in the stylet property map: | |
558 # 'propName' : 'propValue' | |
559 def emitStyletProperty(self, decl, anyProps): | |
560 if anyProps: | |
561 self.styletClasses.append(',\n') | |
562 propValues = len(decl) | |
563 if propValues > 0: | |
564 values = [] | |
565 first = True | |
566 for value in decl[-(propValues - 2):]: | |
567 values.append(('{0}' if first else ' {0}').format(value)) | |
568 first = False | |
569 self.styletClasses.append(" '{0}' : \"{1}\"". | |
570 format(decl[0], ''.join(values))) | |
571 anyProps = True | |
572 return anyProps | |
573 | |
574 def emitEndStyletClass(self): | |
575 self.styletClasses.append('\n };\n') | |
576 self.styletClasses.append('}\n\n') | |
577 | |
578 # Emit each CSS class name as a constant string in the generated Dart class, | |
579 # but only once. | |
580 def emitDartCSSClassName(self, selectorTree): | |
581 # Only process selectors ignore declarations associated with the | |
582 # selectors. | |
583 for parseNode in selectorTree.data: | |
584 if isinstance(parseNode, ParseNode): | |
585 assert isinstance(parseNode.data[0], ParseNode) | |
586 | |
587 if len(parseNode.data) == 1: | |
588 for selector in parseNode.data: | |
589 # Tree will contain selector and strings (e.g., commas, etc.). | |
590 if isinstance(selector, Selector): | |
591 if len(selector.data) == 1: | |
592 selectName = selector.data[0] | |
593 if selectName[0] == '.' and selectName.rfind('.') == 0: | |
594 # static final String ARTICLE = 'article'; | |
595 className = selectName[1:] | |
596 if not(className in self.knownClassNames): | |
597 # Class name hasn't been emitted; emit now. | |
598 | |
599 # TODO(terry): Better mechanism then replacing - | |
600 # with _. Possibly add export name | |
601 # attribute. | |
602 exportedName = className.upper().replace('-', '_') | |
603 self.cssClass.append( | |
604 " static final String {0} = '{1}';\n". | |
605 format(exportedName, className)) | |
606 # Add to list of known class names emitted. | |
607 self.knownClassNames.append(className) | |
608 | |
609 # Decide if class selector should be as exported class name (that has a | |
610 # a real CSS style) or as a class name used for state (no CSS style). | |
611 def addToKnownClassList(self, selectorTree): | |
612 for parseNode in selectorTree.data: | |
613 if isinstance(parseNode, ParseNode): | |
614 if len(parseNode.data) == 1: | |
615 name = parseNode.data[0].data[0] | |
616 if name[0] == '.': | |
617 cssName = name[1:] | |
618 if not(cssName in self.exportedClassName): | |
619 self.exportedClassName.append(cssName) | |
620 else: | |
621 for node in parseNode.data: | |
622 if isinstance(node, Selector): | |
623 for name in node.data: | |
624 if name[0] == '.': | |
625 cssName = name[1:] | |
626 if not(cssName in self.exportedClassName) and not( | |
627 cssName in self.classNameAsState): | |
628 self.classNameAsState.append(cssName) | |
629 | |
630 def emitCssSelector(self, name): | |
631 exportedName = name.upper().replace('-', '_') | |
632 self.cssClass.append(" static final String {0} = '{1}';\n". | |
633 format(exportedName, name)) | |
634 | |
635 def emitCssStateSelectors(self): | |
636 self.cssStateSelectors.append('\n // CSS state class selectors:\n') | |
637 | |
638 def emitCssStateSelector(self, name): | |
639 exportedName = name.upper().replace('-', '_') | |
640 self.cssStateSelectors.append(" static final String {0} = '{1}';\n". | |
641 format(exportedName, name)) | |
642 | |
643 #class cssTest { | |
644 # static final String FRONTVIEW = 'frontview'; | |
645 # static final String QUERY = 'query'; | |
646 # | |
647 # static final CssLayout layout = new CssLayout(); | |
648 #} | |
649 def dartClass(self, sourceFile, cssName, cssFiles): | |
650 self.styletClasses = self.emitPreDefineDart(sourceFile) | |
651 self.knownClassNames = [] | |
652 self.cssClass = [] | |
653 | |
654 # One class per CSS file. | |
655 self.emitCssClass(cssName) | |
656 | |
657 # Iterate though all rulesets. | |
658 stylets = [] | |
659 anyProps = False | |
660 | |
661 for file in cssFiles: | |
662 nodes = file[1] | |
663 for node in nodes: | |
664 if isinstance(node, Ruleset): | |
665 ruleset = node | |
666 for selectorTree in ruleset.data: | |
667 if isinstance(selectorTree, SelectorTree): | |
668 # Process both class selectors with style properties and class | |
669 # selectors used as state. | |
670 self.addToKnownClassList(selectorTree) | |
671 elif isinstance(node, Stylet): | |
672 # We've hit a stylet emit the stylet class definition. | |
673 stylet = node | |
674 stylets.append(self.emitStyletClass(stylet)) | |
675 styletRulesets = stylet.data[2:] | |
676 styletSelectorOpen = False; | |
677 anySelectors = False | |
678 for styletRuleset in styletRulesets: | |
679 assert isinstance(styletRuleset, Ruleset) | |
680 for selectorTree in styletRuleset.data: | |
681 if isinstance(selectorTree, SelectorTree): | |
682 if styletSelectorOpen: | |
683 self.emitCloseCSSStyletSelector(); | |
684 styletSelectorOpen = False; | |
685 anyProps = False; | |
686 self.emitCSSStyletSelector(selectorTree, anySelectors); | |
687 anySelectors = True; | |
688 styletSelectorOpen = True; | |
689 elif isinstance(selectorTree, Declaration): | |
690 anyProps = self.emitStyletProperty(selectorTree.data, anyProps
) | |
691 | |
692 if styletSelectorOpen: | |
693 self.emitCloseCSSStyletSelector(); | |
694 anySelectors = False; | |
695 anyProps = False; | |
696 | |
697 # Close the CSSStylet properties array and the style class we've | |
698 # been emitting. | |
699 self.emitEndStyletClass() | |
700 anyProps = False | |
701 | |
702 # Emit all CSS class selectors that have a real CSS properties. | |
703 for cssClassSelector in self.exportedClassName: | |
704 self.emitCssSelector(cssClassSelector) | |
705 | |
706 # Emit the CSS_State class for CSS class selectors that have not CSS | |
707 # properties associated with the class name. | |
708 self.emitCssStateSelectors() | |
709 for cssStateName in self.classNameAsState: | |
710 if not(cssStateName in self.exportedClassName): | |
711 self.emitCssStateSelector(cssStateName) | |
712 | |
713 # Emit CSS selectors that have no CSS style properties (used for state). | |
714 self.cssClass.extend(self.cssStateSelectors) | |
715 | |
716 # Close the CSS class we're done. | |
717 self.cssClass.append('}\n') | |
718 | |
719 self.styletClasses.extend(self.cssClass) | |
720 return "".join(self.styletClasses) | |
721 | |
722 def addInclude(self, filename, nodes): | |
723 self.scssIncludes.append([filename, nodes]) | |
724 | |
725 def parse( src, cache=None ): | |
726 """ Parse from string. | |
727 """ | |
728 parser = Stylesheet(cache) | |
729 return parser.loads(src) | |
730 | |
731 | |
732 def load(path, cache=None, precache=False): | |
733 """ Parse from file. | |
734 """ | |
735 parser = Stylesheet(cache) | |
736 return parser.load(path, precache=precache) | |
OLD | NEW |