beginning of vector background layers. And a whole load of other stuff too
[potlatch2.git] / net / systemeD / halcyon / styleparser / MapCSS.as
1 package net.systemeD.halcyon.styleparser {
2
3         /*
4                 MapCSS parser
5                 creates a RuleSet from a .mapcss file
6
7         */
8
9         import flash.events.*;
10         import flash.net.*;
11         import net.systemeD.halcyon.Globals;
12
13         // ** also needs to support @import rules
14
15         public class MapCSS {
16                 private var minscale:uint;
17                 private var maxscale:uint;
18                 private var choosers:Array;
19
20                 private static const WHITESPACE:RegExp  =/^ \s+ /sx;
21                 private static const COMMENT:RegExp             =/^ \/\* .+? \*\/ \s* /sx;      /* */
22                 private static const CLASS:RegExp               =/^ ([\.:]\w+) \s* /sx;
23                 private static const NOT_CLASS:RegExp   =/^ !([\.:]\w+) \s* /sx;
24                 private static const ZOOM:RegExp                =/^ \| \s* z([\d\-]+) \s* /isx;
25                 private static const GROUP:RegExp               =/^ , \s* /isx;
26                 private static const CONDITION:RegExp   =/^ \[(.+?)\] \s* /sx;
27                 private static const OBJECT:RegExp              =/^ (\w+) \s* /sx;
28                 private static const DECLARATION:RegExp =/^ \{(.+?)\} \s* /sx;
29                 private static const UNKNOWN:RegExp             =/^ (\S+) \s* /sx;
30
31                 private static const ZOOM_MINMAX:RegExp =/^ (\d+)\-(\d+) $/sx;
32                 private static const ZOOM_MIN:RegExp    =/^ (\d+)\-      $/sx;
33                 private static const ZOOM_MAX:RegExp    =/^      \-(\d+) $/sx;
34                 private static const ZOOM_SINGLE:RegExp =/^        (\d+) $/sx;
35
36                 private static const CONDITION_TRUE:RegExp      =/^ \s* ([:\w]+) \s* = \s* yes \s*  $/isx;
37                 private static const CONDITION_FALSE:RegExp     =/^ \s* ([:\w]+) \s* = \s* no  \s*  $/isx;
38                 private static const CONDITION_SET:RegExp       =/^ \s* ([:\w]+) \s* $/sx;
39                 private static const CONDITION_UNSET:RegExp     =/^ \s* !([:\w]+) \s* $/sx;
40                 private static const CONDITION_EQ:RegExp        =/^ \s* ([:\w]+) \s* =  \s* (.+) \s* $/sx;
41                 private static const CONDITION_NE:RegExp        =/^ \s* ([:\w]+) \s* != \s* (.+) \s* $/sx;
42                 private static const CONDITION_GT:RegExp        =/^ \s* ([:\w]+) \s* >  \s* (.+) \s* $/sx;
43                 private static const CONDITION_GE:RegExp        =/^ \s* ([:\w]+) \s* >= \s* (.+) \s* $/sx;
44                 private static const CONDITION_LT:RegExp        =/^ \s* ([:\w]+) \s* <  \s* (.+) \s* $/sx;
45                 private static const CONDITION_LE:RegExp        =/^ \s* ([:\w]+) \s* <= \s* (.+) \s* $/sx;
46                 private static const CONDITION_REGEX:RegExp     =/^ \s* ([:\w]+) \s* =~\/ \s* (.+) \/ \s* $/sx;
47
48                 private static const ASSIGNMENT_EVAL:RegExp     =/^ \s* (\S+) \s* \:      \s* eval \s* \( \s* ' (.+?) ' \s* \) \s* $/isx;
49                 private static const ASSIGNMENT:RegExp          =/^ \s* (\S+) \s* \:      \s*          (.+?) \s*                   $/sx;
50                 private static const SET_TAG_EVAL:RegExp        =/^ \s* set \s+(\S+)\s* = \s* eval \s* \( \s* ' (.+?) ' \s* \) \s* $/isx;
51                 private static const SET_TAG:RegExp                     =/^ \s* set \s+(\S+)\s* = \s*          (.+?) \s*                   $/isx;
52                 private static const SET_TAG_TRUE:RegExp        =/^ \s* set \s+(\S+)\s* $/isx;
53                 private static const EXIT:RegExp                        =/^ \s* exit \s* $/isx;
54
55                 private static const oZOOM:uint=2;
56                 private static const oGROUP:uint=3;
57                 private static const oCONDITION:uint=4;
58                 private static const oOBJECT:uint=5;
59                 private static const oDECLARATION:uint=6;
60
61                 private static const DASH:RegExp=/\-/g;
62                 private static const COLOR:RegExp=/color$/;
63                 private static const BOLD:RegExp=/^bold$/i;
64                 private static const ITALIC:RegExp=/^italic|oblique$/i;
65                 private static const UNDERLINE:RegExp=/^underline$/i;
66                 private static const CAPS:RegExp=/^uppercase$/i;
67                 private static const CENTER:RegExp=/^center$/i;
68
69                 private static const HEX:RegExp=/^#([0-9a-f]+)$/i;
70                 private static const CSSCOLORS:Object = {
71                         aliceblue:0xf0f8ff,
72                         antiquewhite:0xfaebd7,
73                         aqua:0x00ffff,
74                         aquamarine:0x7fffd4,
75                         azure:0xf0ffff,
76                         beige:0xf5f5dc,
77                         bisque:0xffe4c4,
78                         black:0x000000,
79                         blanchedalmond:0xffebcd,
80                         blue:0x0000ff,
81                         blueviolet:0x8a2be2,
82                         brown:0xa52a2a,
83                         burlywood:0xdeb887,
84                         cadetblue:0x5f9ea0,
85                         chartreuse:0x7fff00,
86                         chocolate:0xd2691e,
87                         coral:0xff7f50,
88                         cornflowerblue:0x6495ed,
89                         cornsilk:0xfff8dc,
90                         crimson:0xdc143c,
91                         cyan:0x00ffff,
92                         darkblue:0x00008b,
93                         darkcyan:0x008b8b,
94                         darkgoldenrod:0xb8860b,
95                         darkgray:0xa9a9a9,
96                         darkgreen:0x006400,
97                         darkkhaki:0xbdb76b,
98                         darkmagenta:0x8b008b,
99                         darkolivegreen:0x556b2f,
100                         darkorange:0xff8c00,
101                         darkorchid:0x9932cc,
102                         darkred:0x8b0000,
103                         darksalmon:0xe9967a,
104                         darkseagreen:0x8fbc8f,
105                         darkslateblue:0x483d8b,
106                         darkslategray:0x2f4f4f,
107                         darkturquoise:0x00ced1,
108                         darkviolet:0x9400d3,
109                         deeppink:0xff1493,
110                         deepskyblue:0x00bfff,
111                         dimgray:0x696969,
112                         dodgerblue:0x1e90ff,
113                         firebrick:0xb22222,
114                         floralwhite:0xfffaf0,
115                         forestgreen:0x228b22,
116                         fuchsia:0xff00ff,
117                         gainsboro:0xdcdcdc,
118                         ghostwhite:0xf8f8ff,
119                         gold:0xffd700,
120                         goldenrod:0xdaa520,
121                         gray:0x808080,
122                         green:0x008000,
123                         greenyellow:0xadff2f,
124                         honeydew:0xf0fff0,
125                         hotpink:0xff69b4,
126                         indianred :0xcd5c5c,
127                         indigo :0x4b0082,
128                         ivory:0xfffff0,
129                         khaki:0xf0e68c,
130                         lavender:0xe6e6fa,
131                         lavenderblush:0xfff0f5,
132                         lawngreen:0x7cfc00,
133                         lemonchiffon:0xfffacd,
134                         lightblue:0xadd8e6,
135                         lightcoral:0xf08080,
136                         lightcyan:0xe0ffff,
137                         lightgoldenrodyellow:0xfafad2,
138                         lightgrey:0xd3d3d3,
139                         lightgreen:0x90ee90,
140                         lightpink:0xffb6c1,
141                         lightsalmon:0xffa07a,
142                         lightseagreen:0x20b2aa,
143                         lightskyblue:0x87cefa,
144                         lightslategray:0x778899,
145                         lightsteelblue:0xb0c4de,
146                         lightyellow:0xffffe0,
147                         lime:0x00ff00,
148                         limegreen:0x32cd32,
149                         linen:0xfaf0e6,
150                         magenta:0xff00ff,
151                         maroon:0x800000,
152                         mediumaquamarine:0x66cdaa,
153                         mediumblue:0x0000cd,
154                         mediumorchid:0xba55d3,
155                         mediumpurple:0x9370d8,
156                         mediumseagreen:0x3cb371,
157                         mediumslateblue:0x7b68ee,
158                         mediumspringgreen:0x00fa9a,
159                         mediumturquoise:0x48d1cc,
160                         mediumvioletred:0xc71585,
161                         midnightblue:0x191970,
162                         mintcream:0xf5fffa,
163                         mistyrose:0xffe4e1,
164                         moccasin:0xffe4b5,
165                         navajowhite:0xffdead,
166                         navy:0x000080,
167                         oldlace:0xfdf5e6,
168                         olive:0x808000,
169                         olivedrab:0x6b8e23,
170                         orange:0xffa500,
171                         orangered:0xff4500,
172                         orchid:0xda70d6,
173                         palegoldenrod:0xeee8aa,
174                         palegreen:0x98fb98,
175                         paleturquoise:0xafeeee,
176                         palevioletred:0xd87093,
177                         papayawhip:0xffefd5,
178                         peachpuff:0xffdab9,
179                         peru:0xcd853f,
180                         pink:0xffc0cb,
181                         plum:0xdda0dd,
182                         powderblue:0xb0e0e6,
183                         purple:0x800080,
184                         red:0xff0000,
185                         rosybrown:0xbc8f8f,
186                         royalblue:0x4169e1,
187                         saddlebrown:0x8b4513,
188                         salmon:0xfa8072,
189                         sandybrown:0xf4a460,
190                         seagreen:0x2e8b57,
191                         seashell:0xfff5ee,
192                         sienna:0xa0522d,
193                         silver:0xc0c0c0,
194                         skyblue:0x87ceeb,
195                         slateblue:0x6a5acd,
196                         slategray:0x708090,
197                         snow:0xfffafa,
198                         springgreen:0x00ff7f,
199                         steelblue:0x4682b4,
200                         tan:0xd2b48c,
201                         teal:0x008080,
202                         thistle:0xd8bfd8,
203                         tomato:0xff6347,
204                         turquoise:0x40e0d0,
205                         violet:0xee82ee,
206                         wheat:0xf5deb3,
207                         white:0xffffff,
208                         whitesmoke:0xf5f5f5,
209                         yellow:0xffff00,
210                         yellowgreen:0x9acd32 };
211
212
213                 public function MapCSS(mins:uint,maxs:uint) {
214                         minscale=mins;
215                         maxscale=maxs;
216                 }
217                 
218                 public function parse(css:String):Array {
219                         var previous:uint=0;                                    // what was the previous CSS word?
220                         var sc:StyleChooser=new StyleChooser(); // currently being assembled
221                         choosers=new Array();
222
223                         var o:Object=new Object();
224                         while (css.length>0) {
225
226                                 // CSS comment
227                                 if ((o=COMMENT.exec(css))) {
228                                         css=css.replace(COMMENT,'');
229
230                                 // Whitespace (probably only at beginning of file)
231                                 } else if ((o=WHITESPACE.exec(css))) {
232                                         css=css.replace(WHITESPACE,'');
233
234                                 // Class - .motorway, .builtup, :hover
235                                 } else if ((o=CLASS.exec(css))) {
236                                         if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
237
238                                         css=css.replace(CLASS,'');
239                                         sc.addCondition(new Condition('set',o[1]));
240                                         previous=oCONDITION;
241
242                                 // Not class - !.motorway, !.builtup, !:hover
243                                 } else if ((o=NOT_CLASS.exec(css))) {
244                                         if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
245
246                                         css=css.replace(NOT_CLASS,'');
247                                         sc.addCondition(new Condition('unset',o[1]));
248                                         previous=oCONDITION;
249
250                                 // Zoom
251                                 } else if ((o=ZOOM.exec(css))) {
252                                         if (previous!=oOBJECT && previous!=oCONDITION) { sc.newObject(); }
253
254                                         css=css.replace(ZOOM,'');
255                                         var z:Array=parseZoom(o[1]);
256                                         sc.addZoom(z[0],z[1]);
257                                         previous=oZOOM;
258
259                                 // Grouping - just a comma
260                                 } else if ((o=GROUP.exec(css))) {
261                                         css=css.replace(GROUP,'');
262                                         sc.newGroup();
263                                         previous=oGROUP;
264
265                                 // Condition - [highway=primary]
266                                 } else if ((o=CONDITION.exec(css))) {
267                                         if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
268                                         if (previous!=oOBJECT && previous!=oZOOM && previous!=oCONDITION) { sc.newObject(); }
269                                         css=css.replace(CONDITION,'');
270                                         sc.addCondition(parseCondition(o[1]) as Condition);
271                                         previous=oCONDITION;
272
273                                 // Object - way, node, relation
274                                 } else if ((o=OBJECT.exec(css))) {
275                                         if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
276
277                                         css=css.replace(OBJECT,'');
278                                         sc.newObject(o[1]);
279                                         previous=oOBJECT;
280
281                                 // Declaration - {...}
282                                 } else if ((o=DECLARATION.exec(css))) {
283                                         css=css.replace(DECLARATION,'');
284                                         sc.addStyles(parseDeclaration(o[1]));
285                                         previous=oDECLARATION;
286                                 
287                                 // Unknown pattern
288                                 } else if ((o=UNKNOWN.exec(css))) {
289                                         css=css.replace(UNKNOWN,'');
290                                         Globals.vars.root.addDebug("unknown: "+o[1]);
291                                         // ** do some debugging with o[1]
292
293                                 } else {
294                                         Globals.vars.root.addDebug("choked on "+css);
295                                         return choosers;
296                                 }
297                         }
298                         if (previous==oDECLARATION) { saveChooser(sc); sc=new StyleChooser(); }
299                         return choosers;
300                 }
301                 
302                 private function saveChooser(sc:StyleChooser):void {
303                         choosers.push(sc);
304                 };
305
306                 // Parse declaration string into list of styles
307
308                 private function parseDeclaration(s:String):Array {
309                         var styles:Array=[];
310                         var t:Object=new Object();
311                         var o:Object=new Object();
312                         var a:String, k:String;
313
314                         // Create styles\10
315                         var ss:ShapeStyle =new ShapeStyle() ;
316                         var ps:PointStyle =new PointStyle() ; 
317                         var ts:TextStyle  =new TextStyle()  ; 
318                         var hs:ShieldStyle=new ShieldStyle(); 
319                         var xs:InstructionStyle=new InstructionStyle(); 
320
321                         for each (a in s.split(';')) {
322                                 if ((o=ASSIGNMENT_EVAL.exec(a)))   { t[o[1].replace(DASH,'_')]=new Eval(o[2]); }
323                                 else if ((o=ASSIGNMENT.exec(a)))   { t[o[1].replace(DASH,'_')]=o[2]; }
324                                 else if ((o=SET_TAG_EVAL.exec(a))) { xs.addSetTag(o[1],new Eval(o[2])); }
325                                 else if ((o=SET_TAG.exec(a)))      { xs.addSetTag(o[1],o[2]); }
326                                 else if ((o=SET_TAG_TRUE.exec(a))) { xs.addSetTag(o[1],true); }
327                                 else if ((o=EXIT.exec(a))) { xs.setPropertyFromString('breaker',true); }
328                         }
329
330                         // Find sublayer
331                         var sub:uint=5;
332                         if (t['z_index']) { sub=Number(t['z_index']); delete t['z_index']; }
333                         ss.sublayer=ps.sublayer=ts.sublayer=hs.sublayer=sub;
334                         xs.sublayer=10;
335
336                         // Munge special values
337                         if (t['font_weight']    ) { t['font_bold'  ]    = t['font_weight'    ].match(BOLD  )    ? true : false; }
338                         if (t['font_style']     ) { t['font_italic']    = t['font_style'     ].match(ITALIC)    ? true : false; }
339                         if (t['text_decoration']) { t['font_underline'] = t['text_decoration'].match(UNDERLINE) ? true : false; }
340                         if (t['text_position']  ) { t['text_center']    = t['text_position'  ].match(CENTER)    ? true : false; }
341                         if (t['text_transform']) {
342                                 // ** needs other transformations, e.g. lower-case, sentence-case
343                                 if (t['text_transform'].match(CAPS)) { t['font_caps']=true; } else { t['font_caps']=false; }
344                         }
345
346                         // ** Do compound settings (e.g. line: 5px dotted blue;)
347
348                         // Assign each property to the appropriate style
349                         for (a in t) {
350                                 // Parse properties
351                                 // ** also do units, e.g. px/pt
352                                 if (a.match(COLOR)) {
353                                         t[a] = parseCSSColor(t[a]);
354                                 }
355                                 
356                                 // Set in styles
357                                 if      (ss.hasOwnProperty(a)) { ss.setPropertyFromString(a,t[a]); }
358                                 else if (ps.hasOwnProperty(a)) { ps.setPropertyFromString(a,t[a]); }
359                                 else if (ts.hasOwnProperty(a)) { ts.setPropertyFromString(a,t[a]); }
360                                 else if (hs.hasOwnProperty(a)) { hs.setPropertyFromString(a,t[a]); }
361                         }
362
363                         // Add each style to list
364                         if (ss.edited) { styles.push(ss); }
365                         if (ps.edited) { styles.push(ps); }
366                         if (ts.edited) { styles.push(ts); }
367                         if (hs.edited) { styles.push(hs); }
368                         if (xs.edited) { styles.push(xs); }
369                         return styles;
370                 }
371                 
372                 private function parseZoom(s:String):Array {
373                         var o:Object=new Object();
374                         if ((o=ZOOM_MINMAX.exec(s))) { return [o[1],o[2]]; }
375                         else if ((o=ZOOM_MIN.exec(s))) { return [o[1],maxscale]; }
376                         else if ((o=ZOOM_MAX.exec(s))) { return [minscale,o[1]]; }
377                         else if ((o=ZOOM_SINGLE.exec(s))) { return [o[1],o[1]]; }
378                         return null;
379                 }
380
381                 private function parseCondition(s:String):Object {
382                         var o:Object=new Object();
383                         if      ((o=CONDITION_TRUE.exec(s)))  { return new Condition('true'     ,o[1]); }
384                         else if ((o=CONDITION_FALSE.exec(s))) { return new Condition('false',o[1]); }
385                         else if ((o=CONDITION_SET.exec(s)))   { return new Condition('set'      ,o[1]); }
386                         else if ((o=CONDITION_UNSET.exec(s))) { return new Condition('unset',o[1]); }
387                         else if ((o=CONDITION_NE.exec(s)))    { return new Condition('ne'       ,o[1],o[2]); }
388                         else if ((o=CONDITION_GT.exec(s)))    { return new Condition('>'        ,o[1],o[2]); }
389                         else if ((o=CONDITION_GE.exec(s)))    { return new Condition('>='       ,o[1],o[2]); }
390                         else if ((o=CONDITION_LT.exec(s)))    { return new Condition('<'        ,o[1],o[2]); }
391                         else if ((o=CONDITION_LE.exec(s)))    { return new Condition('<='       ,o[1],o[2]); }
392                         else if ((o=CONDITION_REGEX.exec(s))) { return new Condition('regex',o[1],o[2]); }
393                         else if ((o=CONDITION_EQ.exec(s)))    { return new Condition('eq'       ,o[1],o[2]); }
394                         return null;
395                 }
396
397         public static function parseCSSColor(colorStr:String):uint {
398             colorStr = colorStr.toLowerCase();
399             if (CSSCOLORS[colorStr])
400                 return CSSCOLORS[colorStr];
401             else {
402                 var match:Object = HEX.exec(colorStr);
403                 if ( match )
404                     return Number("0x"+match[1]);
405             }
406             return 0;
407         }
408         }
409 }