MapCSS in progress. Vast amounts still to do, but the meat of it is here. POIs are...
[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
29                 private var rcpos:uint=0;
30                 private var stylepos:uint=0;
31
32                 // Update the current StyleList from this StyleChooser
33
34                 public function updateStyles(obj:Entity, tags:Object, sl:StyleList):void {
35
36                         // Are any of the ruleChains fulfilled?
37                         // ** needs to cope with min/max zoom
38                         var fulfilled:Boolean=false;
39                         for each (var c:Array in ruleChains) {
40                                 if (testChain(c,-1,obj,tags)) {
41 Globals.vars.root.addDebug("object "+obj+", testChain "+c+" returned true"); 
42                                         fulfilled=true; break;
43                                 }
44                         }
45                         if (!fulfilled) { return; }
46
47                         // Update StyleList
48                         for each (var r:Style in styles) {
49                                 var a:*;
50                                 if      (r is ShapeStyle ) { a=sl.shapeStyles; }
51                                 else if (r is ShieldStyle) { a=sl.shieldStyles; }
52                                 else if (r is TextStyle  ) { a=sl.textStyles;  }
53                                 else if (r is PointStyle ) { a=sl.pointStyles; }
54                                 else if (r is InstructionStyle) {
55                                         if (InstructionStyle(r).breaker) { return; }
56                                         if (InstructionStyle(r).set_tags) {
57                                                 for (var k:String in InstructionStyle(r).set_tags) { tags[k]=InstructionStyle(r).set_tags[k]; }
58                                         }
59                                         continue;
60                                 }
61
62 Globals.vars.root.addDebug("style on "+r.sublayer); 
63                                 if (a[r.sublayer]) {
64                                         // If there's already a style on this sublayer, then merge them
65                                         // (making a deep copy if necessary to avoid altering the root style)
66                                         if (!a[r.sublayer].merged) { a[r.sublayer]=a[r.sublayer].deepCopy(); }
67                                         a[r.sublayer].mergeWith(r);
68 Globals.vars.root.addDebug("merging style on sl"+r.sublayer); 
69                                 } else {
70                                         // Otherwise, just assign it
71                                         a[r.sublayer]=r;
72 Globals.vars.root.addDebug("assigning style on sl"+r.sublayer); 
73                                 }
74                         }
75                 }
76
77
78                 // Test a ruleChain
79                 // - run a set of tests in the chain
80                 //              works backwards from at position "pos" in array, or -1  for the last
81                 //              separate tags object is required in case they've been dynamically retagged
82                 // - if they fail, return false
83                 // - if they succeed, and it's the last in the chain, return happily
84                 // - if they succeed, and there's more in the chain, rerun this for each parent until success
85                 
86                 private function testChain(chain:Array,pos:int,obj:Entity,tags:Object):Boolean {
87                         if (pos==-1) { pos=chain.length-1; }
88
89                         var r:Rule=chain[pos];
90                         if (!r.test(obj, tags)) { return false; }
91                         if (pos==0) { return true; }
92                         
93                         for each (var p:Entity in obj.parentObjects) {
94                                 if (testChain(chain.slice(0), pos-1, p, p.getTagsHash() )) { return true; }
95                         }
96                         return false;
97                 }
98                 
99                 
100                 // ---------------------------------------------------------------------------------------------
101                 // Methods to add properties (used by parsers such as MapCSS)
102                 
103                 // newGroup             <- starts a new ruleChain in this.ruleChains
104                 public function newGroup():void {
105 Globals.vars.root.addDebug("* newGroup");
106                         if (ruleChains[rcpos].length>0) {
107                                 ruleChains[++rcpos]=[];
108                         }
109                 }
110
111                 // newObject    <- adds into the current ruleChain (starting a new Rule)
112                 public function newObject(e:String=''):void {
113 Globals.vars.root.addDebug("* newObject");
114                         ruleChains[rcpos].push(new Rule(e));
115                 }
116
117                 // addZoom              <- adds into the current ruleChain (existing Rule)
118                 public function addZoom(z1:uint,z2:uint):void {
119 Globals.vars.root.addDebug("* addZoom");
120                         ruleChains[rcpos][ruleChains[rcpos].length-1].minZoom=z1;
121                         ruleChains[rcpos][ruleChains[rcpos].length-1].minZoom=z2;
122                 }
123                 
124                 // addCondition <- adds into the current ruleChain (existing Rule)
125                 public function addCondition(c:Condition):void {
126 Globals.vars.root.addDebug("* addCondition");
127                         ruleChains[rcpos][ruleChains[rcpos].length-1].conditions.push(c);
128 Globals.vars.root.addDebug("    now "+ruleChains[rcpos][ruleChains[rcpos].length-1].conditions);
129                 }
130
131                 // addStyles    <- adds to this.styles
132                 public function addStyles(a:Array):void {
133 Globals.vars.root.addDebug("* Styles ("+ruleChains[0][0].conditions+")");
134                         styles=styles.concat(a);
135                 }
136                 
137         }
138 }