better still...
[potlatch2.git] / net / systemeD / halcyon / styleparser / RuleSet.as
1 package net.systemeD.halcyon.styleparser {
2
3         import flash.events.*;
4         import flash.net.*;
5         import net.systemeD.halcyon.ExtendedLoader;
6         import net.systemeD.halcyon.ExtendedURLLoader;
7     import net.systemeD.halcyon.connection.Entity;
8
9     import net.systemeD.halcyon.connection.*;
10         import net.systemeD.halcyon.Globals;
11 //      import bustin.dev.Inspector;
12         
13         public class RuleSet {
14
15                 public var loaded:Boolean=false;                        // has it loaded yet?
16                 public var images:Object=new Object();          // loaded images
17                 public var imageWidths:Object=new Object();     // width of each bitmap image
18                 private var redrawCallback:Function=null;       // function to call when CSS loaded
19                 private var iconCallback:Function=null;         // function to call when all icons loaded
20                 private var iconsToLoad:uint=0;                         // number of icons left to load (fire iconCallback when ==0)
21                 private var evalsToLoad:uint=0;                         // number of evals left to load (fire redrawCallback when ==0)
22
23                 private var minscale:uint;
24                 private var maxscale:uint;
25                 public var choosers:Array;
26                 public var evals:Array;
27
28                 private static const WHITESPACE:RegExp  =/^ \s+ /sx;
29                 private static const COMMENT:RegExp             =/^ \/\* .+? \*\/ \s* /sx;      /* */
30                 private static const CLASS:RegExp               =/^ ([\.:]\w+) \s* /sx;
31                 private static const NOT_CLASS:RegExp   =/^ !([\.:]\w+) \s* /sx;
32                 private static const ZOOM:RegExp                =/^ \| \s* z([\d\-]+) \s* /isx;
33                 private static const GROUP:RegExp               =/^ , \s* /isx;
34                 private static const CONDITION:RegExp   =/^ \[(.+?)\] \s* /sx;
35                 private static const OBJECT:RegExp              =/^ (\w+) \s* /sx;
36                 private static const DECLARATION:RegExp =/^ \{(.+?)\} \s* /sx;
37                 private static const UNKNOWN:RegExp             =/^ (\S+) \s* /sx;
38
39                 private static const ZOOM_MINMAX:RegExp =/^ (\d+)\-(\d+) $/sx;
40                 private static const ZOOM_MIN:RegExp    =/^ (\d+)\-      $/sx;
41                 private static const ZOOM_MAX:RegExp    =/^      \-(\d+) $/sx;
42                 private static const ZOOM_SINGLE:RegExp =/^        (\d+) $/sx;
43
44                 private static const CONDITION_TRUE:RegExp      =/^ \s* ([:\w]+) \s* = \s* yes \s*  $/isx;
45                 private static const CONDITION_FALSE:RegExp     =/^ \s* ([:\w]+) \s* = \s* no  \s*  $/isx;
46                 private static const CONDITION_SET:RegExp       =/^ \s* ([:\w]+) \s* $/sx;
47                 private static const CONDITION_UNSET:RegExp     =/^ \s* !([:\w]+) \s* $/sx;
48                 private static const CONDITION_EQ:RegExp        =/^ \s* ([:\w]+) \s* =  \s* (.+) \s* $/sx;
49                 private static const CONDITION_NE:RegExp        =/^ \s* ([:\w]+) \s* != \s* (.+) \s* $/sx;
50                 private static const CONDITION_GT:RegExp        =/^ \s* ([:\w]+) \s* >  \s* (.+) \s* $/sx;
51                 private static const CONDITION_GE:RegExp        =/^ \s* ([:\w]+) \s* >= \s* (.+) \s* $/sx;
52                 private static const CONDITION_LT:RegExp        =/^ \s* ([:\w]+) \s* <  \s* (.+) \s* $/sx;
53                 private static const CONDITION_LE:RegExp        =/^ \s* ([:\w]+) \s* <= \s* (.+) \s* $/sx;
54                 private static const CONDITION_REGEX:RegExp     =/^ \s* ([:\w]+) \s* =~\/ \s* (.+) \/ \s* $/sx;
55
56                 private static const ASSIGNMENT_EVAL:RegExp     =/^ \s* (\S+) \s* \:      \s* eval \s* \( \s* ' (.+?) ' \s* \) \s* $/isx;
57                 private static const ASSIGNMENT:RegExp          =/^ \s* (\S+) \s* \:      \s*          (.+?) \s*                   $/sx;
58                 private static const SET_TAG_EVAL:RegExp        =/^ \s* set \s+(\S+)\s* = \s* eval \s* \( \s* ' (.+?) ' \s* \) \s* $/isx;
59                 private static const SET_TAG:RegExp                     =/^ \s* set \s+(\S+)\s* = \s*          (.+?) \s*                   $/isx;
60                 private static const SET_TAG_TRUE:RegExp        =/^ \s* set \s+(\S+)\s* $/isx;
61                 private static const EXIT:RegExp                        =/^ \s* exit \s* $/isx;
62
63                 private static const oZOOM:uint=2;
64                 private static const oGROUP:uint=3;
65                 private static const oCONDITION:uint=4;
66                 private static const oOBJECT:uint=5;
67                 private static const oDECLARATION:uint=6;
68
69                 private static const DASH:RegExp=/\-/g;
70                 private static const COLOR:RegExp=/color$/;
71                 private static const BOLD:RegExp=/^bold$/i;
72                 private static const ITALIC:RegExp=/^italic|oblique$/i;
73                 private static const UNDERLINE:RegExp=/^underline$/i;
74                 private static const CAPS:RegExp=/^uppercase$/i;
75                 private static const CENTER:RegExp=/^center$/i;
76                 private static const FALSE:RegExp=/^(no|false|0)$/i;
77
78                 private static const HEX:RegExp=/^#([0-9a-f]+)$/i;
79                 private static const CSSCOLORS:Object = {
80                         aliceblue:0xf0f8ff,
81                         antiquewhite:0xfaebd7,
82                         aqua:0x00ffff,
83                         aquamarine:0x7fffd4,
84                         azure:0xf0ffff,
85                         beige:0xf5f5dc,
86                         bisque:0xffe4c4,
87                         black:0x000000,
88                         blanchedalmond:0xffebcd,
89                         blue:0x0000ff,
90                         blueviolet:0x8a2be2,
91                         brown:0xa52a2a,
92                         burlywood:0xdeb887,
93                         cadetblue:0x5f9ea0,
94                         chartreuse:0x7fff00,
95                         chocolate:0xd2691e,
96                         coral:0xff7f50,
97                         cornflowerblue:0x6495ed,
98                         cornsilk:0xfff8dc,
99                         crimson:0xdc143c,
100                         cyan:0x00ffff,
101                         darkblue:0x00008b,
102                         darkcyan:0x008b8b,
103                         darkgoldenrod:0xb8860b,
104                         darkgray:0xa9a9a9,
105                         darkgreen:0x006400,
106                         darkkhaki:0xbdb76b,
107                         darkmagenta:0x8b008b,
108                         darkolivegreen:0x556b2f,
109                         darkorange:0xff8c00,
110                         darkorchid:0x9932cc,
111                         darkred:0x8b0000,
112                         darksalmon:0xe9967a,
113                         darkseagreen:0x8fbc8f,
114                         darkslateblue:0x483d8b,
115                         darkslategray:0x2f4f4f,
116                         darkturquoise:0x00ced1,
117                         darkviolet:0x9400d3,
118                         deeppink:0xff1493,
119                         deepskyblue:0x00bfff,
120                         dimgray:0x696969,
121                         dodgerblue:0x1e90ff,
122                         firebrick:0xb22222,
123                         floralwhite:0xfffaf0,
124                         forestgreen:0x228b22,
125                         fuchsia:0xff00ff,
126                         gainsboro:0xdcdcdc,
127                         ghostwhite:0xf8f8ff,
128                         gold:0xffd700,
129                         goldenrod:0xdaa520,
130                         gray:0x808080,
131                         green:0x008000,
132                         greenyellow:0xadff2f,
133                         honeydew:0xf0fff0,
134                         hotpink:0xff69b4,
135                         indianred :0xcd5c5c,
136                         indigo :0x4b0082,
137                         ivory:0xfffff0,
138                         khaki:0xf0e68c,
139                         lavender:0xe6e6fa,
140                         lavenderblush:0xfff0f5,
141                         lawngreen:0x7cfc00,
142                         lemonchiffon:0xfffacd,
143                         lightblue:0xadd8e6,
144                         lightcoral:0xf08080,
145                         lightcyan:0xe0ffff,
146                         lightgoldenrodyellow:0xfafad2,
147                         lightgrey:0xd3d3d3,
148                         lightgreen:0x90ee90,
149                         lightpink:0xffb6c1,
150                         lightsalmon:0xffa07a,
151                         lightseagreen:0x20b2aa,
152                         lightskyblue:0x87cefa,
153                         lightslategray:0x778899,
154                         lightsteelblue:0xb0c4de,
155                         lightyellow:0xffffe0,
156                         lime:0x00ff00,
157                         limegreen:0x32cd32,
158                         linen:0xfaf0e6,
159                         magenta:0xff00ff,
160                         maroon:0x800000,
161                         mediumaquamarine:0x66cdaa,
162                         mediumblue:0x0000cd,
163                         mediumorchid:0xba55d3,
164                         mediumpurple:0x9370d8,
165                         mediumseagreen:0x3cb371,
166                         mediumslateblue:0x7b68ee,
167                         mediumspringgreen:0x00fa9a,
168                         mediumturquoise:0x48d1cc,
169                         mediumvioletred:0xc71585,
170                         midnightblue:0x191970,
171                         mintcream:0xf5fffa,
172                         mistyrose:0xffe4e1,
173                         moccasin:0xffe4b5,
174                         navajowhite:0xffdead,
175                         navy:0x000080,
176                         oldlace:0xfdf5e6,
177                         olive:0x808000,
178                         olivedrab:0x6b8e23,
179                         orange:0xffa500,
180                         orangered:0xff4500,
181                         orchid:0xda70d6,
182                         palegoldenrod:0xeee8aa,
183                         palegreen:0x98fb98,
184                         paleturquoise:0xafeeee,
185                         palevioletred:0xd87093,
186                         papayawhip:0xffefd5,
187                         peachpuff:0xffdab9,
188                         peru:0xcd853f,
189                         pink:0xffc0cb,
190                         plum:0xdda0dd,
191                         powderblue:0xb0e0e6,
192                         purple:0x800080,
193                         red:0xff0000,
194                         rosybrown:0xbc8f8f,
195                         royalblue:0x4169e1,
196                         saddlebrown:0x8b4513,
197                         salmon:0xfa8072,
198                         sandybrown:0xf4a460,
199                         seagreen:0x2e8b57,
200                         seashell:0xfff5ee,
201                         sienna:0xa0522d,
202                         silver:0xc0c0c0,
203                         skyblue:0x87ceeb,
204                         slateblue:0x6a5acd,
205                         slategray:0x708090,
206                         snow:0xfffafa,
207                         springgreen:0x00ff7f,
208                         steelblue:0x4682b4,
209                         tan:0xd2b48c,
210                         teal:0x008080,
211                         thistle:0xd8bfd8,
212                         tomato:0xff6347,
213                         turquoise:0x40e0d0,
214                         violet:0xee82ee,
215                         wheat:0xf5deb3,
216                         white:0xffffff,
217                         whitesmoke:0xf5f5f5,
218                         yellow:0xffff00,
219                         yellowgreen:0x9acd32 };
220
221                 public function RuleSet(mins:uint,maxs:uint,redrawCall:Function=null,iconLoadedCallback:Function=null):void {
222                         minscale = mins;
223                         maxscale = maxs;
224                         redrawCallback = redrawCall;
225                         iconCallback = iconLoadedCallback;
226                 }
227
228                 // Get styles for an object
229
230                 public function getStyles(obj:Entity, tags:Object, zoom:uint):StyleList {
231                         var sl:StyleList=new StyleList();
232                         for each (var sc:StyleChooser in choosers) {
233                                 sc.updateStyles(obj,tags,sl,imageWidths,zoom);
234                         }
235                         return sl;
236                 }
237
238                 // ---------------------------------------------------------------------------------------------------------
239                 // Loading stylesheet
240
241                 public function loadFromCSS(str:String):void {
242                         if (str.match(/[\s\n\r\t]/)!=null) { parseCSS(str); loaded=true; redrawCallback(); return; }
243
244                         var request:URLRequest=new URLRequest(str+"?"+Math.random());
245                         var loader:URLLoader=new URLLoader();
246
247                         request.method=URLRequestMethod.GET;
248                         loader.dataFormat = URLLoaderDataFormat.TEXT;
249                         loader.addEventListener(Event.COMPLETE,                                         doParseCSS);
250                         loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,            httpStatusHandler);
251                         loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,      securityErrorHandler);
252                         loader.addEventListener(IOErrorEvent.IO_ERROR,                          ioErrorHandler);
253                         loader.load(request);
254                 }
255
256                 private function doParseCSS(e:Event):void {
257                         parseCSS(e.target.data);
258                 }
259
260                 private function parseCSS(str:String):void {
261                         parse(str);
262                         loaded=true;
263                         if (evals.length==0) { redrawCallback(); }
264                         loadImages();
265                 }
266
267
268                 // ------------------------------------------------------------------------------------------------
269                 // Load all referenced images
270                 // ** will duplicate if referenced twice, shouldn't
271                 
272                 public function loadImages():void {
273                         var filename:String;
274                         for each (var chooser:StyleChooser in choosers) {
275                                 for each (var style:Style in chooser.styles) {
276                                         if      (style is PointStyle  && PointStyle(style).icon_image   ) { filename=PointStyle(style).icon_image; }
277                                         else if (style is ShapeStyle  && ShapeStyle(style).fill_image   ) { filename=ShapeStyle(style).fill_image; }
278                                         else if (style is ShieldStyle && ShieldStyle(style).shield_image) { filename=ShieldStyle(style).shield_image; }
279                                         else { continue; }
280                                         if (filename=='square' || filename=='circle') { continue; }
281                                 
282                                         iconsToLoad++;
283                                         var request:URLRequest=new URLRequest(filename);
284                                         var loader:ExtendedURLLoader=new ExtendedURLLoader();
285                                         loader.dataFormat=URLLoaderDataFormat.BINARY;
286                                         loader.info['filename']=filename;
287                                         loader.addEventListener(Event.COMPLETE,                                         loadedImage,                    false, 0, true);
288                                         loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,            httpStatusHandler,              false, 0, true);
289                                         loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,      securityErrorHandler,   false, 0, true);
290                                         loader.addEventListener(IOErrorEvent.IO_ERROR,                          ioErrorHandler,                 false, 0, true);
291                                         loader.load(request);
292                                 }
293                         }
294                 }
295
296                 // data handler
297
298                 private function loadedImage(event:Event):void {
299                         var fn:String=event.target.info['filename'];
300                         images[fn]=event.target.data;
301
302                         var loader:ExtendedLoader = new ExtendedLoader();
303                         loader.info['filename']=fn;
304                         loader.contentLoaderInfo.addEventListener(Event.COMPLETE, measureWidth);
305                         loader.loadBytes(images[fn]);
306                 }
307                 
308                 private function measureWidth(event:Event):void {
309                         var fn:String=event.target.loader.info['filename'];
310                         imageWidths[fn]=event.target.width;
311                         // ** do we need to explicitly remove the loader object now?
312
313                         iconsToLoad--;
314                         if (iconsToLoad==0 && iconCallback!=null) { iconCallback(); }
315                 }
316
317                 private function httpStatusHandler( event:HTTPStatusEvent ):void { }
318                 private function securityErrorHandler( event:SecurityErrorEvent ):void { Globals.vars.root.addDebug("securityerrorevent"); }
319                 private function ioErrorHandler( event:IOErrorEvent ):void { Globals.vars.root.addDebug("ioerrorevent"); }
320
321                 // ------------------------------------------------------------------------------------------------
322                 // Parse CSS
323
324                 public function parse(css:String):void {
325                         var previous:uint=0;                                    // what was the previous CSS word?
326                         var sc:StyleChooser=new StyleChooser(); // currently being assembled
327                         choosers=new Array();
328                         evals=new Array();
329
330                         var o:Object=new Object();
331                         while (css.length>0) {
332
333                                 // CSS comment
334                                 if ((o=COMMENT.exec(css))) {
335                                         css=css.replace(COMMENT,'');
336
337                                 // Whitespace (probably only at beginning of file)
338                                 } else if ((o=WHITESPACE.exec(css))) {
339                                         css=css.replace(WHITESPACE,'');
340
341                                 // Class - .motorway, .builtup, :hover
342                                 } else if ((o=CLASS.exec(css))) {
343                                         if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
344
345                                         css=css.replace(CLASS,'');
346                                         sc.addCondition(new Condition('set',o[1]));
347                                         previous=oCONDITION;
348
349                                 // Not class - !.motorway, !.builtup, !:hover
350                                 } else if ((o=NOT_CLASS.exec(css))) {
351                                         if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
352
353                                         css=css.replace(NOT_CLASS,'');
354                                         sc.addCondition(new Condition('unset',o[1]));
355                                         previous=oCONDITION;
356
357                                 // Zoom
358                                 } else if ((o=ZOOM.exec(css))) {
359                                         if (previous!=oOBJECT && previous!=oCONDITION) { sc.newObject(); }
360
361                                         css=css.replace(ZOOM,'');
362                                         var z:Array=parseZoom(o[1]);
363                                         sc.addZoom(z[0],z[1]);
364                                         previous=oZOOM;
365
366                                 // Grouping - just a comma
367                                 } else if ((o=GROUP.exec(css))) {
368                                         css=css.replace(GROUP,'');
369                                         sc.newGroup();
370                                         previous=oGROUP;
371
372                                 // Condition - [highway=primary]
373                                 } else if ((o=CONDITION.exec(css))) {
374                                         if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
375                                         if (previous!=oOBJECT && previous!=oZOOM && previous!=oCONDITION) { sc.newObject(); }
376                                         css=css.replace(CONDITION,'');
377                                         sc.addCondition(parseCondition(o[1]) as Condition);
378                                         previous=oCONDITION;
379
380                                 // Object - way, node, relation
381                                 } else if ((o=OBJECT.exec(css))) {
382                                         if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
383
384                                         css=css.replace(OBJECT,'');
385                                         sc.newObject(o[1]);
386                                         previous=oOBJECT;
387
388                                 // Declaration - {...}
389                                 } else if ((o=DECLARATION.exec(css))) {
390                                         css=css.replace(DECLARATION,'');
391                                         sc.addStyles(parseDeclaration(o[1]));
392                                         previous=oDECLARATION;
393                                 
394                                 // Unknown pattern
395                                 } else if ((o=UNKNOWN.exec(css))) {
396                                         css=css.replace(UNKNOWN,'');
397                                         Globals.vars.root.addDebug("unknown: "+o[1]);
398                                         // ** do some debugging with o[1]
399
400                                 } else {
401                                         Globals.vars.root.addDebug("choked on "+css);
402                                         return;
403                                 }
404                         }
405                         if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
406                 }
407                 
408                 private function saveChooser(sc:StyleChooser):void {
409                         choosers.push(sc);
410                 };
411                 
412                 private function saveEval(expr:String):Eval {
413                         evalsToLoad++;
414                         var e:Eval=new Eval(expr);
415                         e.addEventListener("swf_loaded",evalLoaded);
416                         evals.push(e);
417                         return e;
418                 }
419                 
420                 private function evalLoaded(e:Event):void {
421                         evalsToLoad--;
422                         if (evalsToLoad==0) { redrawCallback(); }
423                 }
424
425                 // Parse declaration string into list of styles
426
427                 private function parseDeclaration(s:String):Array {
428                         var styles:Array=[];
429                         var t:Object=new Object();
430                         var o:Object=new Object();
431                         var a:String, k:String, v:*;
432
433                         // Create styles\10
434                         var ss:ShapeStyle =new ShapeStyle() ;
435                         var ps:PointStyle =new PointStyle() ; 
436                         var ts:TextStyle  =new TextStyle()  ; 
437                         var hs:ShieldStyle=new ShieldStyle(); 
438                         var xs:InstructionStyle=new InstructionStyle(); 
439
440                         for each (a in s.split(';')) {
441                                 if ((o=ASSIGNMENT_EVAL.exec(a)))   { t[o[1].replace(DASH,'_')]=saveEval(o[2]); }
442                                 else if ((o=ASSIGNMENT.exec(a)))   { t[o[1].replace(DASH,'_')]=o[2]; }
443                                 else if ((o=SET_TAG_EVAL.exec(a))) { xs.addSetTag(o[1],saveEval(o[2])); }
444                                 else if ((o=SET_TAG.exec(a)))      { xs.addSetTag(o[1],o[2]); }
445                                 else if ((o=SET_TAG_TRUE.exec(a))) { xs.addSetTag(o[1],true); }
446                                 else if ((o=EXIT.exec(a))) { xs.setPropertyFromString('breaker',true); }
447                         }
448
449                         // Find sublayer
450                         var sub:uint=5;
451                         if (t['z_index']) { sub=Number(t['z_index']); delete t['z_index']; }
452                         ss.sublayer=ps.sublayer=ts.sublayer=hs.sublayer=sub;
453                         xs.sublayer=10;
454                         
455                         // Find interactive
456                         var inter:Boolean=true;
457                         if (t['interactive']) { inter=t['interactive'].match(FALSE) ? false : true; delete t['interactive']; }
458                         ss.interactive=ps.interactive=ts.interactive=hs.interactive=xs.interactive=inter;
459
460                         // Munge special values
461                         if (t['font_weight']    ) { t['font_bold'  ]    = t['font_weight'    ].match(BOLD  )    ? true : false; delete t['font_weight']; }
462                         if (t['font_style']     ) { t['font_italic']    = t['font_style'     ].match(ITALIC)    ? true : false; delete t['font_style']; }
463                         if (t['text_decoration']) { t['font_underline'] = t['text_decoration'].match(UNDERLINE) ? true : false; delete t['text_decoration']; }
464                         if (t['text_position']  ) { t['text_center']    = t['text_position'  ].match(CENTER)    ? true : false; delete t['text_position']; }
465                         if (t['text_transform']) {
466                                 // ** needs other transformations, e.g. lower-case, sentence-case
467                                 if (t['text_transform'].match(CAPS)) { t['font_caps']=true; } else { t['font_caps']=false; }
468                                 delete t['text_transform'];
469                         }
470
471                         // ** Do compound settings (e.g. line: 5px dotted blue;)
472
473                         // Assign each property to the appropriate style
474                         for (a in t) {
475                                 // Parse properties
476                                 // ** also do units, e.g. px/pt/m
477                                 if (a.match(COLOR)) { v = parseCSSColor(t[a]); }
478                                                else { v = t[a]; }
479                                 
480                                 // Set in styles
481                                 if      (ss.hasOwnProperty(a)) { ss.setPropertyFromString(a,v); }
482                                 else if (ps.hasOwnProperty(a)) { ps.setPropertyFromString(a,v); }
483                                 else if (ts.hasOwnProperty(a)) { ts.setPropertyFromString(a,v); }
484                                 else if (hs.hasOwnProperty(a)) { hs.setPropertyFromString(a,v); }
485                         }
486
487                         // Add each style to list
488                         if (ss.edited) { styles.push(ss); }
489                         if (ps.edited) { styles.push(ps); }
490                         if (ts.edited) { styles.push(ts); }
491                         if (hs.edited) { styles.push(hs); }
492                         if (xs.edited) { styles.push(xs); }
493                         return styles;
494                 }
495                 
496                 private function parseZoom(s:String):Array {
497                         var o:Object=new Object();
498                         if ((o=ZOOM_MINMAX.exec(s))) { return [o[1],o[2]]; }
499                         else if ((o=ZOOM_MIN.exec(s))) { return [o[1],maxscale]; }
500                         else if ((o=ZOOM_MAX.exec(s))) { return [minscale,o[1]]; }
501                         else if ((o=ZOOM_SINGLE.exec(s))) { return [o[1],o[1]]; }
502                         return null;
503                 }
504
505                 private function parseCondition(s:String):Object {
506                         var o:Object=new Object();
507                         if      ((o=CONDITION_TRUE.exec(s)))  { return new Condition('true'     ,o[1]); }
508                         else if ((o=CONDITION_FALSE.exec(s))) { return new Condition('false',o[1]); }
509                         else if ((o=CONDITION_SET.exec(s)))   { return new Condition('set'      ,o[1]); }
510                         else if ((o=CONDITION_UNSET.exec(s))) { return new Condition('unset',o[1]); }
511                         else if ((o=CONDITION_NE.exec(s)))    { return new Condition('ne'       ,o[1],o[2]); }
512                         else if ((o=CONDITION_GT.exec(s)))    { return new Condition('>'        ,o[1],o[2]); }
513                         else if ((o=CONDITION_GE.exec(s)))    { return new Condition('>='       ,o[1],o[2]); }
514                         else if ((o=CONDITION_LT.exec(s)))    { return new Condition('<'        ,o[1],o[2]); }
515                         else if ((o=CONDITION_LE.exec(s)))    { return new Condition('<='       ,o[1],o[2]); }
516                         else if ((o=CONDITION_REGEX.exec(s))) { return new Condition('regex',o[1],o[2]); }
517                         else if ((o=CONDITION_EQ.exec(s)))    { return new Condition('eq'       ,o[1],o[2]); }
518                         return null;
519                 }
520
521         public static function parseCSSColor(colorStr:String):uint {
522             colorStr = colorStr.toLowerCase();
523             if (CSSCOLORS[colorStr]) {
524                 return CSSCOLORS[colorStr];
525             } else {
526                 var match:Object = HEX.exec(colorStr);
527                 if ( match ) { 
528                   if ( match[1].length == 3) {
529                     // repeat digits. #abc => 0xaabbcc
530                     return Number("0x"+match[1].charAt(0)+match[1].charAt(0)+
531                                        match[1].charAt(1)+match[1].charAt(1)+
532                                        match[1].charAt(2)+match[1].charAt(2));
533                   } else if ( match[1].length == 6) {
534                     return Number("0x"+match[1]);
535                   } else {
536                     return Number("0x000000"); //as good as any
537                   }
538                 }
539             }
540             return 0;
541         }
542         }
543 }