refactor node stateClasses and stylelist calculation
[potlatch2.git] / net / systemeD / halcyon / styleparser / StyleChooser.as
1 package net.systemeD.halcyon.styleparser {
2
3         import net.systemeD.halcyon.connection.Entity;
4         import net.systemeD.halcyon.Globals;
5
6         public class StyleChooser {
7
8                 /*
9                         A StyleChooser object is equivalent to one CSS selector+declaration.
10
11                         Its ruleChains property is an array of all the selectors, which would
12                         traditionally be comma-separated. For example:
13                                 h1, h2, h3 em
14                         is three ruleChains.
15                         
16                         Each ruleChain is itself an array of nested selectors. So the above 
17                         example would roughly be encoded as:
18                                 [[h1],[h2],[h3,em]]
19                                   ^^   ^^   ^^ ^^   each of these is a Rule
20
21                         The styles property is an array of all the style objects to be drawn
22                         if any of the ruleChains evaluate to true.
23
24                 */
25
26                 public var ruleChains:Array=[[]];                       // array of array of Rules
27                 public var styles:Array=[];                                     // array of ShapeStyle/ShieldStyle/TextStyle/PointStyle
28                 public var zoomSpecific:Boolean=false;          // are any of the rules zoom-specific?
29
30                 private var rcpos:uint=0;
31                 private var stylepos:uint=0;
32
33                 // Update the current StyleList from this StyleChooser
34
35                 public function updateStyles(obj:Entity, tags:Object, sl:StyleList, imageWidths:Object, zoom:uint):void {
36                         if (zoomSpecific) { sl.validAt=zoom; }
37
38                         // Are any of the ruleChains fulfilled?
39                         var w:Number;
40                         var fulfilled:Boolean=false;
41                         for each (var c:Array in ruleChains) {
42                                 if (testChain(c,-1,obj,tags,zoom)) {
43                                         fulfilled=true; break;
44                                 }
45                         }
46                         if (!fulfilled) { return; }
47
48                         // Update StyleList
49                         for each (var r:Style in styles) {
50                                 var a:*;
51                                 if (r is ShapeStyle) {
52                                         a=sl.shapeStyles;
53                                         if (ShapeStyle(r).width>sl.maxwidth && !r.evals['width']) { sl.maxwidth=ShapeStyle(r).width; }
54                                 } else if (r is ShieldStyle) {
55                                         a=sl.shieldStyles;
56                                 } else if (r is TextStyle) { 
57                                         a=sl.textStyles;
58                                 } else if (r is PointStyle) { 
59                                         a=sl.pointStyles;
60                                         w=0;
61                                         if (PointStyle(r).icon_width && !PointStyle(r).evals['icon_width']) {
62                                                 w=PointStyle(r).icon_width;
63                                         } else if (PointStyle(r).icon_image && imageWidths[PointStyle(r).icon_image]) {
64                                                 w=imageWidths[PointStyle(r).icon_image];
65                                         }
66                                         if (w>sl.maxwidth) { sl.maxwidth=w; }
67                                 } else if (r is InstructionStyle) {
68                                         if (InstructionStyle(r).breaker) { return; }
69                                         if (InstructionStyle(r).set_tags) {
70                                                 for (var k:String in InstructionStyle(r).set_tags) { tags[k]=InstructionStyle(r).set_tags[k]; }
71                                         }
72                                         continue;
73                                 }
74                                 if (r.drawn) { tags[':drawn']='yes'; }
75                                 tags['_width']=sl.maxwidth;
76                                 
77                                 r.runEvals(tags);
78                                 sl.addSublayer(r.sublayer);
79                                 if (a[r.sublayer]) {
80                                         // If there's already a style on this sublayer, then merge them
81                                         // (making a deep copy if necessary to avoid altering the root style)
82                                         if (!a[r.sublayer].merged) { a[r.sublayer]=a[r.sublayer].deepCopy(); }
83                                         a[r.sublayer].mergeWith(r);
84                                 } else {
85                                         // Otherwise, just assign it
86                                         a[r.sublayer]=r;
87                                 }
88                         }
89                 }
90
91
92                 // Test a ruleChain
93                 // - run a set of tests in the chain
94                 //              works backwards from at position "pos" in array, or -1  for the last
95                 //              separate tags object is required in case they've been dynamically retagged
96                 // - if they fail, return false
97                 // - if they succeed, and it's the last in the chain, return happily
98                 // - if they succeed, and there's more in the chain, rerun this for each parent until success
99                 
100                 private function testChain(chain:Array,pos:int,obj:Entity,tags:Object,zoom:uint):Boolean {
101                         if (pos==-1) { pos=chain.length-1; }
102
103                         var r:Rule=chain[pos];
104                         if (!r.test(obj, tags, zoom)) { return false; }
105                         if (pos==0) { return true; }
106                         
107                         var o:Array=obj.parentObjects;
108                         for each (var p:Entity in o) {
109                                 if (testChain(chain, pos-1, p, p.getTagsHash(), zoom )) { return true; }
110                         }
111                         return false;
112                 }
113                 
114                 
115                 // ---------------------------------------------------------------------------------------------
116                 // Methods to add properties (used by parsers such as MapCSS)
117                 
118                 // newGroup             <- starts a new ruleChain in this.ruleChains
119                 public function newGroup():void {
120                         if (ruleChains[rcpos].length>0) {
121                                 ruleChains[++rcpos]=[];
122                         }
123                 }
124
125                 // newObject    <- adds into the current ruleChain (starting a new Rule)
126                 public function newObject(e:String=''):void {
127                         ruleChains[rcpos].push(new Rule(e));
128                 }
129
130                 // addZoom              <- adds into the current ruleChain (existing Rule)
131                 public function addZoom(z1:uint,z2:uint):void {
132                         ruleChains[rcpos][ruleChains[rcpos].length-1].minZoom=z1;
133                         ruleChains[rcpos][ruleChains[rcpos].length-1].maxZoom=z2;
134                         zoomSpecific=true;
135                 }
136                 
137                 // addCondition <- adds into the current ruleChain (existing Rule)
138                 public function addCondition(c:Condition):void {
139                         ruleChains[rcpos][ruleChains[rcpos].length-1].conditions.push(c);
140                 }
141
142                 // addStyles    <- adds to this.styles
143                 public function addStyles(a:Array):void {
144                         styles=styles.concat(a);
145                 }
146                 
147         }
148 }