Support @import directives in MapCSS files
[potlatch2.git] / net / systemeD / halcyon / styleparser / RuleSet.as
index d5aeeea5eeb390a322f5d2e94da4dd70c1bf6fdc..f66609b70c647a2df1f3edbf78107ee4b88bcdc5 100644 (file)
@@ -4,17 +4,22 @@ package net.systemeD.halcyon.styleparser {
        import flash.net.*;
        import net.systemeD.halcyon.ExtendedLoader;
        import net.systemeD.halcyon.ExtendedURLLoader;
+       import net.systemeD.halcyon.DebugURLRequest;
     import net.systemeD.halcyon.connection.Entity;
 
     import net.systemeD.halcyon.connection.*;
        import net.systemeD.halcyon.Globals;
 //     import bustin.dev.Inspector;
        
+       /** A set of rules for rendering a map, as retrieved from a MapCSS file. */
        public class RuleSet {
 
-               public var loaded:Boolean=false;                        // has it loaded yet?
-               public var images:Object=new Object();          // loaded images
-               public var imageWidths:Object=new Object();     // width of each bitmap image
+               /** Has it loaded yet? */
+               public var loaded:Boolean=false; 
+               /** Loaded images */
+               public var images:Object=new Object();
+               /** Width of each bitmap image. */
+               public var imageWidths:Object=new Object();     
                private var redrawCallback:Function=null;       // function to call when CSS loaded
                private var iconCallback:Function=null;         // function to call when all icons loaded
                private var iconsToLoad:uint=0;                         // number of icons left to load (fire iconCallback when ==0)
@@ -73,6 +78,7 @@ package net.systemeD.halcyon.styleparser {
                private static const UNDERLINE:RegExp=/^underline$/i;
                private static const CAPS:RegExp=/^uppercase$/i;
                private static const CENTER:RegExp=/^center$/i;
+               private static const FALSE:RegExp=/^(no|false|0)$/i;
 
                private static const HEX:RegExp=/^#([0-9a-f]+)$/i;
                private static const CSSCOLORS:Object = {
@@ -217,6 +223,7 @@ package net.systemeD.halcyon.styleparser {
                        yellow:0xffff00,
                        yellowgreen:0x9acd32 };
 
+               /** Constructor */
                public function RuleSet(mins:uint,maxs:uint,redrawCall:Function=null,iconLoadedCallback:Function=null):void {
                        minscale = mins;
                        maxscale = maxs;
@@ -224,12 +231,12 @@ package net.systemeD.halcyon.styleparser {
                        iconCallback = iconLoadedCallback;
                }
 
-               // Get styles for an object
+               /** Get styles for an object*/
 
-               public function getStyles(obj:Entity,tags:Object):StyleList {
+               public function getStyles(obj:Entity, tags:Object, zoom:uint):StyleList {
                        var sl:StyleList=new StyleList();
                        for each (var sc:StyleChooser in choosers) {
-                               sc.updateStyles(obj,tags,sl,imageWidths);
+                               sc.updateStyles(obj,tags,sl,imageWidths,zoom);
                        }
                        return sl;
                }
@@ -237,23 +244,17 @@ package net.systemeD.halcyon.styleparser {
                // ---------------------------------------------------------------------------------------------------------
                // Loading stylesheet
 
+        /** Load ruleset the MapCSS file referenced in <code>str</code>.*/
                public function loadFromCSS(str:String):void {
                        if (str.match(/[\s\n\r\t]/)!=null) { parseCSS(str); loaded=true; redrawCallback(); return; }
 
-                       var request:URLRequest=new URLRequest(str);
-                       var loader:URLLoader=new URLLoader();
-
-                       request.method=URLRequestMethod.GET;
-                       loader.dataFormat = URLLoaderDataFormat.TEXT;
-                       loader.addEventListener(Event.COMPLETE,                                         doParseCSS);
-                       loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,            httpStatusHandler);
-                       loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,      securityErrorHandler);
-                       loader.addEventListener(IOErrorEvent.IO_ERROR,                          ioErrorHandler);
-                       loader.load(request);
+                       var cssLoader:NestedCSSLoader=new NestedCSSLoader();
+                       cssLoader.addEventListener(Event.COMPLETE, doParseCSS);
+                       cssLoader.load(str);
                }
 
                private function doParseCSS(e:Event):void {
-                       parseCSS(e.target.data);
+                       parseCSS(e.target.css);
                }
 
                private function parseCSS(str:String):void {
@@ -265,7 +266,7 @@ package net.systemeD.halcyon.styleparser {
 
 
                // ------------------------------------------------------------------------------------------------
-               // Load all referenced images
+               /** Load all referenced images*/
                // ** will duplicate if referenced twice, shouldn't
                
                public function loadImages():void {
@@ -279,15 +280,15 @@ package net.systemeD.halcyon.styleparser {
                                        if (filename=='square' || filename=='circle') { continue; }
                                
                                        iconsToLoad++;
-                                       var request:URLRequest=new URLRequest(filename);
+                                       var request:DebugURLRequest=new DebugURLRequest(filename);
                                        var loader:ExtendedURLLoader=new ExtendedURLLoader();
                                        loader.dataFormat=URLLoaderDataFormat.BINARY;
                                        loader.info['filename']=filename;
                                        loader.addEventListener(Event.COMPLETE,                                         loadedImage,                    false, 0, true);
                                        loader.addEventListener(HTTPStatusEvent.HTTP_STATUS,            httpStatusHandler,              false, 0, true);
-                                       loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,      securityErrorHandler,   false, 0, true);
-                                       loader.addEventListener(IOErrorEvent.IO_ERROR,                          ioErrorHandler,                 false, 0, true);
-                                       loader.load(request);
+                                       loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,      onImageLoadSecurityError,       false, 0, true);
+                                       loader.addEventListener(IOErrorEvent.IO_ERROR,                          onImageLoadioError,                     false, 0, true);
+                                       loader.load(request.request);
                                }
                        }
                }
@@ -309,10 +310,24 @@ package net.systemeD.halcyon.styleparser {
                        imageWidths[fn]=event.target.width;
                        // ** do we need to explicitly remove the loader object now?
 
-                       iconsToLoad--;
-                       if (iconsToLoad==0 && iconCallback!=null) { iconCallback(); }
+                       oneLessImageToLoad();
                }
 
+        private function oneLessImageToLoad():void {
+            iconsToLoad--;
+            if (iconsToLoad<=0 && iconCallback!=null) { iconCallback(); }
+        }
+
+        private function onImageLoadioError ( event:IOErrorEvent ):void {
+            trace("ioerrorevent: "+event.target.info['filename']);
+            oneLessImageToLoad();
+        }
+
+        private function onImageLoadSecurityError ( event:SecurityErrorEvent ):void {
+            trace("securityerrorevent: "+event.target.info['filename']);
+            oneLessImageToLoad();
+        }
+
                private function httpStatusHandler( event:HTTPStatusEvent ):void { }
                private function securityErrorHandler( event:SecurityErrorEvent ):void { Globals.vars.root.addDebug("securityerrorevent"); }
                private function ioErrorHandler( event:IOErrorEvent ):void { Globals.vars.root.addDebug("ioerrorevent"); }
@@ -320,6 +335,7 @@ package net.systemeD.halcyon.styleparser {
                // ------------------------------------------------------------------------------------------------
                // Parse CSS
 
+               /** Parse the given MapCSS file by repeatedly throwing regular expressions at it. */
                public function parse(css:String):void {
                        var previous:uint=0;                                    // what was the previous CSS word?
                        var sc:StyleChooser=new StyleChooser(); // currently being assembled
@@ -411,7 +427,7 @@ package net.systemeD.halcyon.styleparser {
                private function saveEval(expr:String):Eval {
                        evalsToLoad++;
                        var e:Eval=new Eval(expr);
-                       e.addEventListener("swf_loaded",evalLoaded);
+                       e.addEventListener("swf_loaded",evalLoaded, false, 0, true);
                        evals.push(e);
                        return e;
                }
@@ -450,6 +466,11 @@ package net.systemeD.halcyon.styleparser {
                        if (t['z_index']) { sub=Number(t['z_index']); delete t['z_index']; }
                        ss.sublayer=ps.sublayer=ts.sublayer=hs.sublayer=sub;
                        xs.sublayer=10;
+                       
+                       // Find "interactive" property - it's true unless explicitly set false.
+                       var inter:Boolean=true;
+                       if (t['interactive']) { inter=t['interactive'].match(FALSE) ? false : true; delete t['interactive']; }
+                       ss.interactive=ps.interactive=ts.interactive=hs.interactive=xs.interactive=inter;
 
                        // Munge special values
                        if (t['font_weight']    ) { t['font_bold'  ]    = t['font_weight'    ].match(BOLD  )    ? true : false; delete t['font_weight']; }
@@ -467,7 +488,7 @@ package net.systemeD.halcyon.styleparser {
                        // Assign each property to the appropriate style
                        for (a in t) {
                                // Parse properties
-                               // ** also do units, e.g. px/pt
+                               // ** also do units, e.g. px/pt/m
                                if (a.match(COLOR)) { v = parseCSSColor(t[a]); }
                                               else { v = t[a]; }
                                
@@ -512,6 +533,8 @@ package net.systemeD.halcyon.styleparser {
                        return null;
                }
 
+        /** Convert a color string given as either descriptive ("blue"), short hex ("#abc") or long hex ("#a0b0c0"), to an integer. 
+        * @default 0*/
         public static function parseCSSColor(colorStr:String):uint {
             colorStr = colorStr.toLowerCase();
             if (CSSCOLORS[colorStr]) {