Merge branch 'master' into history
[potlatch2.git] / net / systemeD / halcyon / styleparser / Style.as
1 package net.systemeD.halcyon.styleparser {
2
3         import flash.utils.ByteArray;
4         import flash.net.*;
5
6         /** A Style is a set of graphic properties (e.g. stroke colour and width, casing colour and width, 
7                 font and text size), typically derived from a MapCSS descriptor. This is the base class
8                 for particular style groupings such as ShapeStyle and PointStyle.
9                 
10                 @see net.systemeD.halcyon.styleparser.StyleList
11                 @see net.systemeD.halcyon.styleparser.StyleChooser
12         */
13
14         public class Style {
15
16                 /** Has this style had another style merged into it?
17                         (When styles cascade, then we need to merge the first style with any subsequent styles that apply.) */
18                 public var merged:Boolean=false;
19
20                 /** Is the style active (properties have been set)? */
21                 public var edited:Boolean=false;
22
23                 /** The sublayer is the z-index property _within_ an OSM layer.
24                         It enables (for example) trunk roads to be rendered above primary roads within that OSM layer, 
25                         and so on. "OSM layer 1 / sublayer 5" will render above "OSM layer 1 / sublayer 4", but below 
26                         "OSM layer 2 / sublayer 4". */
27                 public var sublayer:Number=5;
28
29                 /** Does this style permit mouse interaction?
30                         (Some styling, such as P2's back-level yellow highlight for selected ways, should not respond 
31                         to mouse events.) */
32                 public var interactive:Boolean=true;    
33
34                 /** Compiled SWFs for each eval. We keep it here, not in the property itself, so that we can retain typing for each property. */
35                 public var evals:Object={};
36                 
37                 /** TagValue assignments, e.g. { width: tag('lanes'); } */
38                 public var tagvalues:Object={};
39                 
40                 /** Make an exact copy of an object.
41                         Used when merging cascading styles. (FIXME: this needs some benchmarking - it may be quicker to simply iterate over .properties, 
42                         copying each one. */
43                 public function deepCopy():* {
44                         registerClassAlias("net.systemeD.halcyon.styleparser.ShapeStyle",ShapeStyle);
45                         registerClassAlias("net.systemeD.halcyon.styleparser.TextStyle",TextStyle);
46                         registerClassAlias("net.systemeD.halcyon.styleparser.PointStyle",PointStyle);
47                         registerClassAlias("net.systemeD.halcyon.styleparser.ShieldStyle",ShieldStyle);
48                         registerClassAlias("net.systemeD.halcyon.styleparser.InstructionStyle",InstructionStyle);
49                         var a:ByteArray=new ByteArray();
50                         a.writeObject(this);
51                         a.position=0;
52                         return (a.readObject());
53                 }
54
55                 /** Merge two Style objects. */
56                 public function mergeWith(additional:Style):void {
57                         for each (var prop:String in properties) {
58                                 // Note extra check for empty arrays, which we use to mean 'undefined' (see setPropertyFromString below)
59                                 if (additional[prop]!=undefined && !((additional[prop] is Number) && isNaN(additional[prop])) && !((additional[prop] is Array) && additional[prop].length==0)) {
60                                         this[prop]=additional[prop];
61                                 }
62                         }
63                         this.merged=true;
64                 }
65
66                 /** Properties getter, to be overridden. */
67                 public function get properties():Array {
68                         return [];
69                 }
70                 
71                 /** Does this style require anything to be drawn? (To be overridden.) */
72                 public function get drawn():Boolean {
73                         return false;
74                 }
75                 
76                 /** Are there any eval functions defined? (This isn't used.) */
77                 public function hasEvals():Boolean {
78                         for (var k:String in evals) { return true; }
79                         return false;
80                 }
81                 
82                 /** Run all evals for this Style over the supplied tags.
83                         If, for example, the stylesheet contains width=eval('_width+2'), then this will set Style.width to 7. */
84                 public function runEvals(tags:Object):void {
85                         for (var k:String in evals) this[k]=evals[k].exec(tags);
86                 }
87                 
88                 /** Set a property, casting as correct type. */
89                 public function setPropertyFromString(k:String,v:*):Boolean {
90                         if (!this.hasOwnProperty(k)) { return false; }
91                         if (v is Eval) { evals[k]=v; v=1; }
92                         else if (v is TagValue) { tagvalues[k]=v; v=1; }
93
94                         // Arrays don't return a proper typeof, so check manually
95                         // Note that undefined class variables always have typeof=object,
96                         // so we need to declare them as empty arrays (cf ShapeStyle)
97                         if (this[k] is Array) {
98                                 // Split comma-separated array and coerce as numbers
99                                 this[k]=v.split(',').map(function(el:Object,index:int,array:Array):Number { return Number(el); });
100                                 edited=true; return true;
101                         }
102
103                         // Check for other object types
104                         switch (typeof(this[k])) {
105                                 case "number":  this[k]=Number(v) ; edited=true; return true;
106                                 case "object":  // **for now, just assume objects are undefined strings
107                                                                 // We'll probably need to fix this in future if we have more complex
108                                                                 // properties
109                                 case "string":  this[k]=String(v) ; edited=true; return true;
110                                 case "boolean": this[k]=Boolean(v); edited=true; return true;
111                         }
112                         return false;
113                 }
114
115                 /** Summarise Style as String - for debugging. */
116                 public function toString():String {
117                         var str:String='';
118             for each (var k:String in this.properties) {
119                                 if (this.hasOwnProperty(k)) { str+=k+"="+this[k]+"; "; }
120                         }
121                         for each (k in tagvalues) str+=k+";"; 
122                         return str;
123         }
124         }
125 }