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