| 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 |