package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.events.Event;
    import flash.utils.setTimeout;

    import FacilitatorSocket;
    import events.FacilitatorSocketEvent;

    import ProxyPair;
    import RTMFPProxyPair;
    import TCPProxyPair;

    import rtmfp.CirrusSocket;
    import rtmfp.events.CirrusSocketEvent;

    public class swfcat extends Sprite
    {
        /* Adobe's Cirrus server for RTMFP connections.
           The Cirrus key is defined at compile time by
           reading from the CIRRUS_KEY environment var. */
        private const DEFAULT_CIRRUS_ADDR:String = "rtmfp://p2p.rtmfp.net";
        private const DEFAULT_CIRRUS_KEY:String = RTMFP::CIRRUS_KEY;

        private const DEFAULT_FACILITATOR_ADDR:Object = {
            host: "tor-facilitator.bamsoftware.com",
            port: 9002
        };

        /* Default Tor client to use in case of RTMFP connection */
        private const DEFAULT_TOR_CLIENT_ADDR:Object = {
            host: "127.0.0.1",
            port: 9002
        };

        // Milliseconds.
        private const FACILITATOR_POLL_INTERVAL:int = 10000;

        // Bytes per second. Set to undefined to disable limit.
        public const RATE_LIMIT:Number = undefined;
        // Seconds.
        private const RATE_LIMIT_HISTORY:Number = 5.0;

        // Socket to Cirrus server
        private var s_c:CirrusSocket;
        // Socket to facilitator.
        private var s_f:FacilitatorSocket;
        // Handle local-remote traffic
        private var p_p:ProxyPair;

        private var client_id:String;
        private var proxy_pair_factory:Function;

        private var proxy_pairs:Array;

        private var debug_mode:Boolean;
        private var proxy_mode:Boolean;

        /* TextField for debug output. */
        private var output_text:TextField;

        /* Badge for display */
        private var badge:Badge;

        private var fac_addr:Object;
        private var relay_addr:Object;

        public var rate_limit:RateLimit;

        public function swfcat()
        {
            proxy_mode = false;
            debug_mode = false;

            // Absolute positioning.
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;

            if (RATE_LIMIT)
                rate_limit = new BucketRateLimit(RATE_LIMIT * RATE_LIMIT_HISTORY, RATE_LIMIT_HISTORY);
            else
                rate_limit = new RateUnlimit();

            // Wait until the query string parameters are loaded.
            this.loaderInfo.addEventListener(Event.COMPLETE, loaderinfo_complete);
        }

        public function puts(s:String):void
        {
            if (output_text != null) {
                output_text.appendText(s + "\n");
                output_text.scrollV = output_text.maxScrollV;
            }
        }

        private function loaderinfo_complete(e:Event):void
        {
            var fac_spec:String;
            var relay_spec:String;

            debug_mode = (this.loaderInfo.parameters["debug"] != null)
            proxy_mode = (this.loaderInfo.parameters["proxy"] != null);
            if (proxy_mode && !debug_mode) {
                badge = new Badge();
                addChild(badge);
            } else {
                output_text = new TextField();
                output_text.width = stage.stageWidth;
                output_text.height = stage.stageHeight;
                output_text.background = true;
                output_text.backgroundColor = 0x001f0f;
                output_text.textColor = 0x44cc44;
                addChild(output_text);
            }

            puts("Starting: parameters loaded.");

            /* TODO: use this to have multiple proxies going at once */
            proxy_pairs = new Array();

            fac_spec = this.loaderInfo.parameters["facilitator"];
            if (fac_spec) {
                puts("Facilitator spec: \"" + fac_spec + "\"");
                fac_addr = parse_addr_spec(fac_spec);
                if (!fac_addr) {
                    puts("Error: Facilitator spec must be in the form \"host:port\".");
                    return;
                }
            } else {
                fac_addr = DEFAULT_FACILITATOR_ADDR;
            }

            main();
        }

        /* The main logic begins here, after start-up issues are taken care of. */
        private function main():void
        {
            if (proxy_mode) {
                establish_facilitator_connection();
            } else {
                establish_cirrus_connection();
            }
        }

        private function establish_cirrus_connection():void
        {
            s_c = new CirrusSocket();
            s_c.addEventListener(CirrusSocketEvent.CONNECT_SUCCESS, function (e:CirrusSocketEvent):void {
                puts("Cirrus: connected with id " + s_c.id + ".");
                if (proxy_mode) {
                    start_proxy_pair();
                    s_c.send_hello(client_id);
                } else {
                    establish_facilitator_connection();
                }
            });
            s_c.addEventListener(CirrusSocketEvent.CONNECT_FAILED, function (e:CirrusSocketEvent):void {
                puts("Error: failed to connect to Cirrus.");
            });
            s_c.addEventListener(CirrusSocketEvent.CONNECT_CLOSED, function (e:CirrusSocketEvent):void {
                puts("Cirrus: closed connection.");
            });
            s_c.addEventListener(CirrusSocketEvent.HELLO_RECEIVED, function (e:CirrusSocketEvent):void {
                puts("Cirrus: received hello from peer " + e.peer);

                /* don't bother if we already have a proxy going */
                if (p_p != null && p_p.connected) {
                    return;
                }

                /* if we're in proxy mode, we should have already set
                   up a proxy pair */
                if (!proxy_mode) {
                    relay_addr = DEFAULT_TOR_CLIENT_ADDR;
                    proxy_pair_factory = rtmfp_proxy_pair_factory;
                    start_proxy_pair();
                    s_c.send_hello(e.peer);
                } else if (!debug_mode && badge != null) {
                    badge.proxy_begin();
                }

                p_p.client = {peer: e.peer, stream: e.stream};
            });

            s_c.connect(DEFAULT_CIRRUS_ADDR, DEFAULT_CIRRUS_KEY);
        }

        private function establish_facilitator_connection():void
        {
            s_f = new FacilitatorSocket(fac_addr.host, fac_addr.port);
            s_f.addEventListener(FacilitatorSocketEvent.CONNECT_FAILED, function (e:Event):void {
                puts("Facilitator: connect failed.");
                setTimeout(establish_facilitator_connection, FACILITATOR_POLL_INTERVAL);
            });

            if (proxy_mode) {
                s_f.addEventListener(FacilitatorSocketEvent.REGISTRATION_RECEIVED, function (e:FacilitatorSocketEvent):void {
                    var client_addr:Object = parse_addr_spec(e.client);
                    relay_addr = parse_addr_spec(e.relay);
                    if (client_addr == null) {
                        puts("Facilitator: got registration " + e.client);
                        proxy_pair_factory = rtmfp_proxy_pair_factory;
                        if (s_c == null || !s_c.connected) {
                            client_id = e.client;
                            establish_cirrus_connection();
                        } else {
                            start_proxy_pair();
                            s_c.send_hello(e.client);
                        }
                    } else {
                        proxy_pair_factory = tcp_proxy_pair_factory;
                        start_proxy_pair();
                        p_p.client = client_addr;
                    }
                });
                s_f.addEventListener(FacilitatorSocketEvent.REGISTRATIONS_EMPTY, function (e:Event):void {
                    puts("Facilitator: no registrations available.");
                    setTimeout(establish_facilitator_connection, FACILITATOR_POLL_INTERVAL);
                });
                puts("Facilitator: getting registration.");
                s_f.get_registration();
            } else {
                s_f.addEventListener(FacilitatorSocketEvent.REGISTRATION_FAILED, function (e:Event):void {
                    puts("Facilitator: registration failed.");
                    setTimeout(establish_facilitator_connection, FACILITATOR_POLL_INTERVAL);
                });
                puts("Facilitator: posting registration.");
                s_f.post_registration(s_c.id);
            }
        }

        private function start_proxy_pair():void
        {
            p_p = proxy_pair_factory();
            p_p.addEventListener(Event.CONNECT, function (e:Event):void {
                puts("ProxyPair: connected!");
            });
            p_p.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("ProxyPair: connection closed.");
                p_p = null;
                if (proxy_mode && !debug_mode && badge != null) {
                    badge.proxy_end();
                }
                establish_facilitator_connection();
            });
            p_p.relay = relay_addr;
        }

        private function rtmfp_proxy_pair_factory():ProxyPair
        {
            return new RTMFPProxyPair(this, s_c, s_c.local_stream_name);
        }

        private function tcp_proxy_pair_factory():ProxyPair
        {
            return new TCPProxyPair(this);
        }

        /* Parse an address in the form "host:port". Returns an Object with
           keys "host" (String) and "port" (int). Returns null on error. */
        private static function parse_addr_spec(spec:String):Object
        {
            var parts:Array;
            var addr:Object;

            parts = spec.split(":", 2);
            if (parts.length != 2 || !parseInt(parts[1]))
                return null;
            addr = {}
            addr.host = parts[0];
            addr.port = parseInt(parts[1]);

            return addr;
        }
    }
}

import flash.text.TextFormat;
import flash.text.TextField;
import flash.utils.getTimer;

class Badge extends flash.display.Sprite
{
    /* Number of proxy pairs currently connected. */
    private var num_proxy_pairs:int = 0;
    /* Number of proxy pairs ever connected. */
    private var total_proxy_pairs:int = 0;

    [Embed(source="badge.png")]
    private var BadgeImage:Class;
    private var tot_client_count_tf:TextField;
    private var tot_client_count_fmt:TextFormat;
    private var cur_client_count_tf:TextField;
    private var cur_client_count_fmt:TextFormat;

    public function Badge()
    {
        /* Setup client counter for badge. */
        tot_client_count_fmt = new TextFormat();
        tot_client_count_fmt.color = 0xFFFFFF;
        tot_client_count_fmt.align = "center";
        tot_client_count_fmt.font = "courier-new";
        tot_client_count_fmt.bold = true;
        tot_client_count_fmt.size = 10;
        tot_client_count_tf = new TextField();
        tot_client_count_tf.width = 20;
        tot_client_count_tf.height = 17;
        tot_client_count_tf.background = false;
        tot_client_count_tf.defaultTextFormat = tot_client_count_fmt;
        tot_client_count_tf.x=47;
        tot_client_count_tf.y=0;

        cur_client_count_fmt = new TextFormat();
        cur_client_count_fmt.color = 0xFFFFFF;
        cur_client_count_fmt.align = "center";
        cur_client_count_fmt.font = "courier-new";
        cur_client_count_fmt.bold = true;
        cur_client_count_fmt.size = 10;
        cur_client_count_tf = new TextField();
        cur_client_count_tf.width = 20;
        cur_client_count_tf.height = 17;
        cur_client_count_tf.background = false;
        cur_client_count_tf.defaultTextFormat = cur_client_count_fmt;
        cur_client_count_tf.x=47;
        cur_client_count_tf.y=6;

        addChild(new BadgeImage());
        addChild(tot_client_count_tf);
        addChild(cur_client_count_tf);

        /* Update the client counter on badge. */
        update_client_count();
    }

    public function proxy_begin():void
    {
        num_proxy_pairs++;
        total_proxy_pairs++;
        update_client_count();
    }

    public function proxy_end():void
    {
        num_proxy_pairs--;
        update_client_count();
    }

    private function update_client_count():void
    {
        /* Update total client count. */
        if (String(total_proxy_pairs).length == 1)
            tot_client_count_tf.text = "0" + String(total_proxy_pairs);
        else
            tot_client_count_tf.text = String(total_proxy_pairs);

        /* Update current client count. */
        cur_client_count_tf.text = "";
        for(var i:Number = 0; i < num_proxy_pairs; i++)
            cur_client_count_tf.appendText(".");
    }
}

class RateLimit
{
    public function RateLimit()
    {
    }

    public function update(n:Number):Boolean
    {
        return true;
    }

    public function when():Number
    {
        return 0.0;
    }

    public function is_limited():Boolean
    {
        return false;
    }
}

class RateUnlimit extends RateLimit
{
    public function RateUnlimit()
    {
    }

    public override function update(n:Number):Boolean
    {
        return true;
    }

    public override function when():Number
    {
        return 0.0;
    }

    public override function is_limited():Boolean
    {
        return false;
    }
}

class BucketRateLimit extends RateLimit
{
    private var amount:Number;
    private var capacity:Number;
    private var time:Number;
    private var last_update:uint;

    public function BucketRateLimit(capacity:Number, time:Number)
    {
        this.amount = 0.0;
        /* capacity / time is the rate we are aiming for. */
        this.capacity = capacity;
        this.time = time;
        this.last_update = getTimer();
    }

    private function age():void
    {
        var now:uint;
        var delta:Number;

        now = getTimer();
        delta = (now - last_update) / 1000.0;
        last_update = now;

        amount -= delta * capacity / time;
        if (amount < 0.0)
            amount = 0.0;
    }

    public override function update(n:Number):Boolean
    {
        age();
        amount += n;

        return amount <= capacity;
    }

    public override function when():Number
    {
        age();
        return (amount - capacity) / (capacity / time);
    }

    public override function is_limited():Boolean
    {
        age();
        return amount > capacity;
    }
}

package
{
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.events.Event;
    import flash.utils.setTimeout;

    import FacilitatorSocket;
    import events.FacilitatorSocketEvent;

    import ProxyPair;
    import RTMFPProxyPair;
    import TCPProxyPair;

    import rtmfp.CirrusSocket;
    import rtmfp.events.CirrusSocketEvent;

    public class swfcat extends Sprite
    {
        /* Adobe's Cirrus server for RTMFP connections.
           The Cirrus key is defined at compile time by
           reading from the CIRRUS_KEY environment var. */
        private const DEFAULT_CIRRUS_ADDR:String = "rtmfp://p2p.rtmfp.net";
        private const DEFAULT_CIRRUS_KEY:String = RTMFP::CIRRUS_KEY;

        private const DEFAULT_FACILITATOR_ADDR:Object = {
            host: "tor-facilitator.bamsoftware.com",
            port: 9002
        };

        /* Default Tor client to use in case of RTMFP connection */
        private const DEFAULT_TOR_CLIENT_ADDR:Object = {
            host: "127.0.0.1",
            port: 9002
        };

        // Milliseconds.
        private const FACILITATOR_POLL_INTERVAL:int = 10000;

        // Bytes per second. Set to undefined to disable limit.
        public const RATE_LIMIT:Number = undefined;
        // Seconds.
        private const RATE_LIMIT_HISTORY:Number = 5.0;

        // Socket to Cirrus server
        private var s_c:CirrusSocket;
        // Socket to facilitator.
        private var s_f:FacilitatorSocket;
        // Handle local-remote traffic
        private var p_p:ProxyPair;

        private var client_id:String;
        private var proxy_pair_factory:Function;

        private var debug_mode:Boolean;
        private var proxy_mode:Boolean;

        /* TextField for debug output. */
        private var output_text:TextField;

        /* Badge for display */
        private var badge:Badge;

        private var fac_addr:Object;
        private var relay_addr:Object;

        public var rate_limit:RateLimit;

        public function swfcat()
        {
            proxy_mode = false;
            debug_mode = false;

            // Absolute positioning.
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;

            if (RATE_LIMIT)
                rate_limit = new BucketRateLimit(RATE_LIMIT * RATE_LIMIT_HISTORY, RATE_LIMIT_HISTORY);
            else
                rate_limit = new RateUnlimit();

            // Wait until the query string parameters are loaded.
            this.loaderInfo.addEventListener(Event.COMPLETE, loaderinfo_complete);
        }

        public function puts(s:String):void
        {
            if (output_text != null) {
                output_text.appendText(s + "\n");
                output_text.scrollV = output_text.maxScrollV;
            }
        }

        private function loaderinfo_complete(e:Event):void
        {
            var fac_spec:String;
            var relay_spec:String;

            debug_mode = (this.loaderInfo.parameters["debug"] != null)
            proxy_mode = (this.loaderInfo.parameters["proxy"] != null);
            if (proxy_mode && !debug_mode) {
                badge = new Badge();
                addChild(badge);
            } else {
                output_text = new TextField();
                output_text.width = stage.stageWidth;
                output_text.height = stage.stageHeight;
                output_text.background = true;
                output_text.backgroundColor = 0x001f0f;
                output_text.textColor = 0x44cc44;
                addChild(output_text);
            }

            puts("Starting: parameters loaded.");

            fac_spec = this.loaderInfo.parameters["facilitator"];
            if (fac_spec) {
                puts("Facilitator spec: \"" + fac_spec + "\"");
                fac_addr = parse_addr_spec(fac_spec);
                if (!fac_addr) {
                    puts("Error: Facilitator spec must be in the form \"host:port\".");
                    return;
                }
            } else {
                fac_addr = DEFAULT_FACILITATOR_ADDR;
            }

            main();
        }

        /* The main logic begins here, after start-up issues are taken care of. */
        private function main():void
        {
            if (proxy_mode) {
                establish_facilitator_connection();
            } else {
                establish_cirrus_connection();
            }
        }

        private function establish_cirrus_connection():void
        {
            s_c = new CirrusSocket();
            s_c.addEventListener(CirrusSocketEvent.CONNECT_SUCCESS, function (e:CirrusSocketEvent):void {
                puts("Cirrus: connected with id " + s_c.id + ".");
                if (proxy_mode) {
                    start_proxy_pair();
                    s_c.send_hello(client_id);
                } else {
                    establish_facilitator_connection();
                }
            });
            s_c.addEventListener(CirrusSocketEvent.CONNECT_FAILED, function (e:CirrusSocketEvent):void {
                puts("Error: failed to connect to Cirrus.");
            });
            s_c.addEventListener(CirrusSocketEvent.CONNECT_CLOSED, function (e:CirrusSocketEvent):void {
                puts("Cirrus: closed connection.");
            });
            s_c.addEventListener(CirrusSocketEvent.HELLO_RECEIVED, function (e:CirrusSocketEvent):void {
                puts("Cirrus: received hello from peer " + e.peer);

                /* don't bother if we already have a proxy going */
                if (p_p != null && p_p.connected) {
                    return;
                }

                /* if we're in proxy mode, we should have already set
                   up a proxy pair */
                if (!proxy_mode) {
                    relay_addr = DEFAULT_TOR_CLIENT_ADDR;
                    proxy_pair_factory = rtmfp_proxy_pair_factory;
                    start_proxy_pair();
                    s_c.send_hello(e.peer);
                } else if (!debug_mode && badge != null) {
                    badge.proxy_begin();
                }

                p_p.client = {peer: e.peer, stream: e.stream};
            });

            s_c.connect(DEFAULT_CIRRUS_ADDR, DEFAULT_CIRRUS_KEY);
        }

        private function establish_facilitator_connection():void
        {
            s_f = new FacilitatorSocket(fac_addr.host, fac_addr.port);
            s_f.addEventListener(FacilitatorSocketEvent.CONNECT_FAILED, function (e:Event):void {
                puts("Facilitator: connect failed.");
                setTimeout(establish_facilitator_connection, FACILITATOR_POLL_INTERVAL);
            });

            if (proxy_mode) {
                s_f.addEventListener(FacilitatorSocketEvent.REGISTRATION_RECEIVED, function (e:FacilitatorSocketEvent):void {
                    var client_addr:Object = parse_addr_spec(e.client);
                    relay_addr = parse_addr_spec(e.relay);
                    if (client_addr == null) {
                        puts("Facilitator: got registration " + e.client);
                        proxy_pair_factory = rtmfp_proxy_pair_factory;
                        if (s_c == null || !s_c.connected) {
                            client_id = e.client;
                            establish_cirrus_connection();
                        } else {
                            start_proxy_pair();
                            s_c.send_hello(e.client);
                        }
                    } else {
                        proxy_pair_factory = tcp_proxy_pair_factory;
                        start_proxy_pair();
                        p_p.client = client_addr;
                    }
                });
                s_f.addEventListener(FacilitatorSocketEvent.REGISTRATIONS_EMPTY, function (e:Event):void {
                    puts("Facilitator: no registrations available.");
                    setTimeout(establish_facilitator_connection, FACILITATOR_POLL_INTERVAL);
                });
                puts("Facilitator: getting registration.");
                s_f.get_registration();
            } else {
                s_f.addEventListener(FacilitatorSocketEvent.REGISTRATION_FAILED, function (e:Event):void {
                    puts("Facilitator: registration failed.");
                    setTimeout(establish_facilitator_connection, FACILITATOR_POLL_INTERVAL);
                });
                puts("Facilitator: posting registration.");
                s_f.post_registration(s_c.id);
            }
        }

        private function start_proxy_pair():void
        {
            p_p = proxy_pair_factory();
            p_p.addEventListener(Event.CONNECT, function (e:Event):void {
                puts("ProxyPair: connected!");
            });
            p_p.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("ProxyPair: connection closed.");
                p_p = null;
                if (proxy_mode && !debug_mode && badge != null) {
                    badge.proxy_end();
                }
                establish_facilitator_connection();
            });
            p_p.relay = relay_addr;
        }

        private function rtmfp_proxy_pair_factory():ProxyPair
        {
            return new RTMFPProxyPair(this, s_c, s_c.local_stream_name);
        }

        private function tcp_proxy_pair_factory():ProxyPair
        {
            return new TCPProxyPair(this);
        }

        /* Parse an address in the form "host:port". Returns an Object with
           keys "host" (String) and "port" (int). Returns null on error. */
        private static function parse_addr_spec(spec:String):Object
        {
            var parts:Array;
            var addr:Object;

            parts = spec.split(":", 2);
            if (parts.length != 2 || !parseInt(parts[1]))
                return null;
            addr = {}
            addr.host = parts[0];
            addr.port = parseInt(parts[1]);

            return addr;
        }
    }
}

import flash.text.TextFormat;
import flash.text.TextField;
import flash.utils.getTimer;

class Badge extends flash.display.Sprite
{
    /* Number of proxy pairs currently connected. */
    private var num_proxy_pairs:int = 0;
    /* Number of proxy pairs ever connected. */
    private var total_proxy_pairs:int = 0;

    [Embed(source="badge.png")]
    private var BadgeImage:Class;
    private var tot_client_count_tf:TextField;
    private var tot_client_count_fmt:TextFormat;
    private var cur_client_count_tf:TextField;
    private var cur_client_count_fmt:TextFormat;

    public function Badge()
    {
        /* Setup client counter for badge. */
        tot_client_count_fmt = new TextFormat();
        tot_client_count_fmt.color = 0xFFFFFF;
        tot_client_count_fmt.align = "center";
        tot_client_count_fmt.font = "courier-new";
        tot_client_count_fmt.bold = true;
        tot_client_count_fmt.size = 10;
        tot_client_count_tf = new TextField();
        tot_client_count_tf.width = 20;
        tot_client_count_tf.height = 17;
        tot_client_count_tf.background = false;
        tot_client_count_tf.defaultTextFormat = tot_client_count_fmt;
        tot_client_count_tf.x=47;
        tot_client_count_tf.y=0;

        cur_client_count_fmt = new TextFormat();
        cur_client_count_fmt.color = 0xFFFFFF;
        cur_client_count_fmt.align = "center";
        cur_client_count_fmt.font = "courier-new";
        cur_client_count_fmt.bold = true;
        cur_client_count_fmt.size = 10;
        cur_client_count_tf = new TextField();
        cur_client_count_tf.width = 20;
        cur_client_count_tf.height = 17;
        cur_client_count_tf.background = false;
        cur_client_count_tf.defaultTextFormat = cur_client_count_fmt;
        cur_client_count_tf.x=47;
        cur_client_count_tf.y=6;

        addChild(new BadgeImage());
        addChild(tot_client_count_tf);
        addChild(cur_client_count_tf);

        /* Update the client counter on badge. */
        update_client_count();
    }

    public function proxy_begin():void
    {
        num_proxy_pairs++;
        total_proxy_pairs++;
        update_client_count();
    }

    public function proxy_end():void
    {
        num_proxy_pairs--;
        update_client_count();
    }

    private function update_client_count():void
    {
        /* Update total client count. */
        if (String(total_proxy_pairs).length == 1)
            tot_client_count_tf.text = "0" + String(total_proxy_pairs);
        else
            tot_client_count_tf.text = String(total_proxy_pairs);

        /* Update current client count. */
        cur_client_count_tf.text = "";
        for(var i:Number = 0; i < num_proxy_pairs; i++)
            cur_client_count_tf.appendText(".");
    }
}

class RateLimit
{
    public function RateLimit()
    {
    }

    public function update(n:Number):Boolean
    {
        return true;
    }

    public function when():Number
    {
        return 0.0;
    }

    public function is_limited():Boolean
    {
        return false;
    }
}

class RateUnlimit extends RateLimit
{
    public function RateUnlimit()
    {
    }

    public override function update(n:Number):Boolean
    {
        return true;
    }

    public override function when():Number
    {
        return 0.0;
    }

    public override function is_limited():Boolean
    {
        return false;
    }
}

class BucketRateLimit extends RateLimit
{
    private var amount:Number;
    private var capacity:Number;
    private var time:Number;
    private var last_update:uint;

    public function BucketRateLimit(capacity:Number, time:Number)
    {
        this.amount = 0.0;
        /* capacity / time is the rate we are aiming for. */
        this.capacity = capacity;
        this.time = time;
        this.last_update = getTimer();
    }

    private function age():void
    {
        var now:uint;
        var delta:Number;

        now = getTimer();
        delta = (now - last_update) / 1000.0;
        last_update = now;

        amount -= delta * capacity / time;
        if (amount < 0.0)
            amount = 0.0;
    }

    public override function update(n:Number):Boolean
    {
        age();
        amount += n;

        return amount <= capacity;
    }

    public override function when():Number
    {
        age();
        return (amount - capacity) / (capacity / time);
    }

    public override function is_limited():Boolean
    {
        age();
        return amount > capacity;
    }
}

Remove unused and unnecessary proxy_pairs local.


package com.kemsky.impl
{
    import flash.utils.getDefinitionByName;

    public class Flex
    {
        private static const instance:Flex = new Flex();

        protected var collection:Class;
        protected var arrayList:Class;
        protected var list:Class;

        public var available:Boolean;

        public function Flex()
        {
            try
            {
                this.collection = getDefinitionByName("mx.collections::ArrayCollection") as Class;
                this.arrayList = getDefinitionByName("mx.collections::ArrayList") as Class;
                this.list = getDefinitionByName("mx.collections::IList") as Class;
                this.available = true;
            }
            catch (e:Error)
            {

            }
        }

        public static function get list():Class
        {
            return instance.list;
        }

        public static function get arrayList():Class
        {
            return instance.arrayList;
        }

        public static function get collection():Class
        {
            return instance.collection;
        }

        public static function get available():Boolean
        {
            return instance.available;
        }
    }
}

package com.kemsky.impl
{
    import flash.utils.getDefinitionByName;

    public class Flex
    {
        private static const instance:Flex = new Flex();

        protected var collection:Class;
        protected var list:Class;

        public var available:Boolean;

        public function Flex()
        {
            try
            {
                this.collection = getDefinitionByName("mx.collections::ArrayCollection") as Class;
                this.list = getDefinitionByName("mx.collections::IList") as Class;
                this.available = true;
            }
            catch (e:Error)
            {

            }
        }

        public static function get list():Class
        {
            return instance.list;
        }

        public static function get collection():Class
        {
            return instance.collection;
        }

        public static function get available():Boolean
        {
            return instance.available;
        }
    }
}

remove flex dependencies

remove flex dependencies


/*
 * Copyright 2014 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package flash.display {
import flash.errors.IllegalOperationError;
import flash.events.UncaughtErrorEvents;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.system.SecurityDomain;
import flash.utils.ByteArray;

[native(cls='LoaderClass')]
public class Loader extends DisplayObjectContainer {
  public function Loader() {
  }
  public native function get content():DisplayObject;
  public native function get contentLoaderInfo():LoaderInfo;
  public function get uncaughtErrorEvents():UncaughtErrorEvents {
    var events:UncaughtErrorEvents = _getUncaughtErrorEvents();
    if (!events) {
      events = new UncaughtErrorEvents();
      _setUncaughtErrorEvents(events);
    }
    return events;
  }
  public override function addChild(child:DisplayObject):DisplayObject {
    Error.throwError(IllegalOperationError, 2069);
    return null;
  }
  public override function addChildAt(child:DisplayObject, index:int):DisplayObject {
    Error.throwError(IllegalOperationError, 2069);
    return null;
  }
  public override function removeChild(child:DisplayObject):DisplayObject {
    Error.throwError(IllegalOperationError, 2069);
    return null;
  }
  public override function removeChildAt(index:int):DisplayObject {
    Error.throwError(IllegalOperationError, 2069);
    return null;
  }
  public override function setChildIndex(child:DisplayObject, index:int):void
  {
    Error.throwError(IllegalOperationError, 2069);
  }
  public function load(request:URLRequest, context:LoaderContext = null):void {
    context = sanitizeContext(context);
    _load(request, context.checkPolicyFile, context.applicationDomain, context.securityDomain,
          context.requestedContentParent, context.parameters,
          _getJPEGLoaderContextdeblockingfilter(context), context.allowCodeImport,
          context.imageDecodingPolicy);
  }
  private function sanitizeContext(context:LoaderContext):LoaderContext {
    if (!context) {
      context = new LoaderContext();
    }
    if (!context.applicationDomain) {
      context.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
    }
    context.parameters = cloneObject(context.parameters);
    return context;
  }
  public function loadBytes(bytes:ByteArray, context:LoaderContext = null):void {
    context = sanitizeContext(context);
    _loadBytes(bytes, context.checkPolicyFile, context.applicationDomain, context.securityDomain,
          context.requestedContentParent, context.parameters,
          _getJPEGLoaderContextdeblockingfilter(context), context.allowCodeImport,
          context.imageDecodingPolicy);
  }
  public function close():void {
    _close();
  }
  public function unload():void {
    _unload(false, false);
  }
  public function unloadAndStop(gc:Boolean = true):void {
    _unload(true, gc);
  }

  private function cloneObject(obj:Object):Object {
    if (!obj) {
      return null;
    }
    var clone:Object = {};
    for (var key:String in obj) {
      clone[key] = obj[key];
    }
    return clone;
  }
  private native function _close():void;
  private native function _unload(stopExecution:Boolean, gc:Boolean):void;
  private native function _getJPEGLoaderContextdeblockingfilter(context:LoaderContext):Number;
  private native function _getUncaughtErrorEvents():UncaughtErrorEvents;
  private native function _setUncaughtErrorEvents(value:UncaughtErrorEvents):void;
  private native function _load(request:URLRequest, checkPolicyFile:Boolean,
                                applicationDomain:ApplicationDomain, securityDomain:SecurityDomain,
                                requestedContentParent:DisplayObjectContainer, parameters:Object,
                                deblockingFilter:Number, allowCodeExecution:Boolean,
                                imageDecodingPolicy:String):void;
  private native function _loadBytes(bytes:ByteArray, checkPolicyFile:Boolean,
                                applicationDomain:ApplicationDomain, securityDomain:SecurityDomain,
                                requestedContentParent:DisplayObjectContainer, parameters:Object,
                                deblockingFilter:Number, allowCodeExecution:Boolean,
                                imageDecodingPolicy:String):void;
}
}

/*
 * Copyright 2014 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package flash.display {
import flash.errors.IllegalOperationError;
import flash.events.UncaughtErrorEvents;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.system.SecurityDomain;
import flash.utils.ByteArray;

[native(cls='LoaderClass')]
public class Loader extends DisplayObjectContainer {
  public native function Loader();
  public native function get content():DisplayObject;
  public native function get contentLoaderInfo():LoaderInfo;
  public function get uncaughtErrorEvents():UncaughtErrorEvents {
    var events:UncaughtErrorEvents = _getUncaughtErrorEvents();
    if (!events) {
      events = new UncaughtErrorEvents();
      _setUncaughtErrorEvents(events);
    }
    return events;
  }
  public override function addChild(child:DisplayObject):DisplayObject {
    Error.throwError(IllegalOperationError, 2069);
    return null;
  }
  public override function addChildAt(child:DisplayObject, index:int):DisplayObject {
    Error.throwError(IllegalOperationError, 2069);
    return null;
  }
  public override function removeChild(child:DisplayObject):DisplayObject {
    Error.throwError(IllegalOperationError, 2069);
    return null;
  }
  public override function removeChildAt(index:int):DisplayObject {
    Error.throwError(IllegalOperationError, 2069);
    return null;
  }
  public override function setChildIndex(child:DisplayObject, index:int):void
  {
    Error.throwError(IllegalOperationError, 2069);
  }
  public function load(request:URLRequest, context:LoaderContext = null):void {
    context = sanitizeContext(context);
    _load(request, context.checkPolicyFile, context.applicationDomain, context.securityDomain,
          context.requestedContentParent, context.parameters,
          _getJPEGLoaderContextdeblockingfilter(context), context.allowCodeImport,
          context.imageDecodingPolicy);
  }
  private function sanitizeContext(context:LoaderContext):LoaderContext {
    if (!context) {
      context = new LoaderContext();
    }
    if (!context.applicationDomain) {
      context.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);
    }
    context.parameters = cloneObject(context.parameters);
    return context;
  }
  public function loadBytes(bytes:ByteArray, context:LoaderContext = null):void {
    context = sanitizeContext(context);
    _loadBytes(bytes, context.checkPolicyFile, context.applicationDomain, context.securityDomain,
          context.requestedContentParent, context.parameters,
          _getJPEGLoaderContextdeblockingfilter(context), context.allowCodeImport,
          context.imageDecodingPolicy);
  }
  public function close():void {
    _close();
  }
  public function unload():void {
    _unload(false, false);
  }
  public function unloadAndStop(gc:Boolean = true):void {
    _unload(true, gc);
  }

  private function cloneObject(obj:Object):Object {
    if (!obj) {
      return null;
    }
    var clone:Object = {};
    for (var key:String in obj) {
      clone[key] = obj[key];
    }
    return clone;
  }
  private native function _close():void;
  private native function _unload(stopExecution:Boolean, gc:Boolean):void;
  private native function _getJPEGLoaderContextdeblockingfilter(context:LoaderContext):Number;
  private native function _getUncaughtErrorEvents():UncaughtErrorEvents;
  private native function _setUncaughtErrorEvents(value:UncaughtErrorEvents):void;
  private native function _load(request:URLRequest, checkPolicyFile:Boolean,
                                applicationDomain:ApplicationDomain, securityDomain:SecurityDomain,
                                requestedContentParent:DisplayObjectContainer, parameters:Object,
                                deblockingFilter:Number, allowCodeExecution:Boolean,
                                imageDecodingPolicy:String):void;
  private native function _loadBytes(bytes:ByteArray, checkPolicyFile:Boolean,
                                applicationDomain:ApplicationDomain, securityDomain:SecurityDomain,
                                requestedContentParent:DisplayObjectContainer, parameters:Object,
                                deblockingFilter:Number, allowCodeExecution:Boolean,
                                imageDecodingPolicy:String):void;
}
}

Make Loader constructor native

Make Loader constructor native


package cc.milkshape.grid
{
	import cc.milkshape.framework.mvc.Model;
	import cc.milkshape.grid.events.GridEvent;
	import cc.milkshape.grid.events.GridFocusEvent;
	import cc.milkshape.grid.events.GridLineEvent;
	import cc.milkshape.grid.events.GridMoveEvent;
	import cc.milkshape.grid.events.GridZoomEvent;
	import cc.milkshape.grid.square.*;
	import cc.milkshape.grid.square.events.SquareEvent;
	import cc.milkshape.grid.square.events.SquareFormEvent;

	public class GridModel extends Model
	{
		private var _issueSlug:String;
		private var _issue:Object;
		private var _minX:int;
		private var _minY:int;
		private var _maxX:int;
		private var _maxY:int;
		private var _lstPosition:Array;
		private var _focusX:int;
		private var _focusY:int;
		private var _currentScale:int;
		private var _minScale:int;
		private var _maxScale:int;
		private var _gridLineVisible:Boolean;
		private var _posX:int;
		private var _posY:int;
		private var _isShowForm:Boolean;

		public function GridModel(issueSlug:String)
		{
			_issueSlug = issueSlug;
		}

		public function get showDisableSquare():int
		{
			return _issue.show_disable_square;
		}

		public function set showDisableSquare(v:int):void
		{
			_issue.show_disable_square = v;
		}

		public function get gridLineVisible():Boolean
		{
			return _gridLineVisible;
		}

		public function get maxScale():int
		{
			return _maxScale;
		}

		public function set maxScale(v:int):void
		{
			_maxScale = v;
		}

		public function get minScale():int
		{
			return _minScale;
		}

		public function set minScale(v:int):void
		{
			_minScale = v;
		}

		public function get focusY():int
		{
			return _focusY;
		}

		public function get focusX():int
		{
			return _focusX;
		}

		public function get squareSize():int
		{
			return _issue.size;
		}

		public function set squareSize(v:int):void
		{
			_issue.size = v;
		}

		public function get posY():int
		{
			return _posY;
		}

		public function set posY(v:int):void
		{
			_posY = v;
		}

		public function get posX():int
		{
			return _posX;
		}

		public function set posX(v:int):void
		{
			_posX = v;
		}

		public function get currentScale():int
		{
			return _currentScale;
		}

		public function get issue():Object
		{
			return _issue;
		}

		public function set issue(issue:Object):void
		{
			_issue = issue;
			_posX = 0;
			_posY = 0;
			_gridLineVisible = true;

			dispatchEvent(new GridEvent(GridEvent.INFO_READY, _issue.steps, nbHSquare, nbVSquare, squareSize));
		}

		public function get issueSlug():String
		{
			return _issueSlug;
		}

		public function set issueSlug(v:String):void
		{
			_issueSlug = v;
		}

		public function setFocus(x:int, y:int):void {
			focusX = y;
			focusY = x;
		}

		public function initSquares(squares:Array, squaresOpen:Array):void
		{
			_lstPosition = new Array(nbHSquare);
			for(var i:int = 0 ; i < nbHSquare ; ++i)
			{
				_lstPosition[i] = new Array(nbVSquare);
			}
			var square:Object;
			for each(square in squares)
			{
				var squareObject:*;
				// si status = false et pas de background alors c'est une booked
				if(square.status || square.background_image != null && square.status){
					squareObject = new SquareFull(square.pos_x + minX, square.pos_y + minY, square.background_image_path, squareSize);
					squareObject.layers = square.layers;
					squareObject.neighbors = square.neighbors_keys;
				} else {
					squareObject = new SquareBooked(square.pos_x + minX, square.pos_y + minY, squareSize);
				}
				_addPosition(squareObject);
			}

			for each(square in squaresOpen)
			{
				_addPosition(new SquareOpen(square.pos_x + minX, square.pos_y + minY, squareSize));
			}
			/*
			if(showDisableSquare)
			{
				for(i = 0 ; i < nbVSquare ; ++i)
				{
					for(var j:int = 0 ; j < nbHSquare ; ++j)
					{
						if(_lstPosition[i][j] == null)
						{
							_addPosition(new SquareDisable(i, j, squareSize));;
						}
					}
				}
			}*/

			dispatchEvent(new GridEvent(GridEvent.READY));
		}

		public function zoomTo(op:int):void
		{
			var futurScale:int = currentScale + op < minScale ? minScale : currentScale + op > maxScale ? maxScale : currentScale + op;

			if(currentScale != futurScale)// Si le zoom change
			{
				if(_isShowForm)
				{
					_isShowForm = false;
					dispatchEvent(new SquareFormEvent(SquareFormEvent.CLOSE));
				}
				currentScale = futurScale;
				dispatchEvent(new GridZoomEvent(GridZoomEvent.ZOOM, futurScale));
				moveTo();
			}
		}

		public function moveTo():void
		{
			if(currentScale == maxScale)// Si on est au zoom maximal
			{
				trace('ok');
				var square:Square = focusSquare;
				if(square is SquareOpen)
				{
					_isShowForm = true;
					dispatchEvent(new SquareFormEvent(SquareFormEvent.SHOW_OPEN));
				}
				else if(square is SquareBooked)
				{
					_isShowForm = true;
					dispatchEvent(new SquareFormEvent(SquareFormEvent.SHOW_BOOKED));
				}
				else if(_isShowForm)
				{
					_isShowForm = false;
					dispatchEvent(new SquareFormEvent(SquareFormEvent.CLOSE));
				}
			}

			if(currentScale != minScale)// Si on n'est pas au zoom minimal
			{
				posX = focusX * _issue.steps[currentScale] + _issue.steps[currentScale] / 2;
				posY = focusY * _issue.steps[currentScale] + _issue.steps[currentScale] / 2;
			}
			else
			{
				posX = nbVSquare * _issue.steps[currentScale] / 2;
				posY = nbHSquare * _issue.steps[currentScale] / 2;
			}

			dispatchEvent(new GridMoveEvent(GridMoveEvent.MOVE, posX, posY));
		}

		private function _addPosition(square:Square):void
		{
			_lstPosition[square.X][square.Y] = SquareManager.length() - 1;
			dispatchEvent(new SquareEvent(SquareEvent.CREATION, square));
		}

		public function set currentScale(scale:int):void {
			_currentScale = scale;
			dispatchEvent(new GridZoomEvent(GridZoomEvent.ZOOM, scale));
		}

		public function set focusX(x:int):void {
			_focusX = x < 0 ? 0 : x >= nbVSquare ? nbVSquare - 1 : x;
			dispatchEvent(new GridFocusEvent(GridFocusEvent.FOCUS));
		}

		public function set focusY(y:int):void {
			_focusY = y < 0 ? 0 : y >= nbHSquare ? nbHSquare - 1 : y;
			dispatchEvent(new GridFocusEvent(GridFocusEvent.FOCUS));
		}

		public function set gridLineVisible(b:Boolean):void
		{
			_gridLineVisible = b;
			dispatchEvent(new GridLineEvent(b ? GridLineEvent.SHOW : GridLineEvent.HIDE));
		}

		public function get minX():int
		{
			return 0;
		}

		public function get minY():int
		{
			return 0;
		}

		public function get nbHSquare():int
		{
			return _issue.nb_case_x;
		}

		public function get nbVSquare():int
		{
			return issue.nb_case_y;
		}

		public function get focusSquare():Square {
			return SquareManager.get(_lstPosition[_focusY][_focusX]);
		}

		public function get currentStep():int
		{
			return _issue.steps[_currentScale];
		}
	}
}

package cc.milkshape.grid
{
	import cc.milkshape.framework.mvc.Model;
	import cc.milkshape.grid.events.GridEvent;
	import cc.milkshape.grid.events.GridFocusEvent;
	import cc.milkshape.grid.events.GridLineEvent;
	import cc.milkshape.grid.events.GridMoveEvent;
	import cc.milkshape.grid.events.GridZoomEvent;
	import cc.milkshape.grid.square.*;
	import cc.milkshape.grid.square.events.SquareEvent;
	import cc.milkshape.grid.square.events.SquareFormEvent;

	public class GridModel extends Model
	{
		private var _issueSlug:String;
		private var _issue:Object;
		private var _minX:int;
		private var _minY:int;
		private var _maxX:int;
		private var _maxY:int;
		private var _lstPosition:Array;
		private var _focusX:int;
		private var _focusY:int;
		private var _currentScale:int;
		private var _minScale:int;
		private var _maxScale:int;
		private var _gridLineVisible:Boolean;
		private var _posX:int;
		private var _posY:int;
		private var _isShowForm:Boolean;

		public function GridModel(issueSlug:String)
		{
			_issueSlug = issueSlug;
		}

		public function get showDisableSquare():int
		{
			return _issue.show_disable_square;
		}

		public function set showDisableSquare(v:int):void
		{
			_issue.show_disable_square = v;
		}

		public function get gridLineVisible():Boolean
		{
			return _gridLineVisible;
		}

		public function get maxScale():int
		{
			return _maxScale;
		}

		public function set maxScale(v:int):void
		{
			_maxScale = v;
		}

		public function get minScale():int
		{
			return _minScale;
		}

		public function set minScale(v:int):void
		{
			_minScale = v;
		}

		public function get focusY():int
		{
			return _focusY;
		}

		public function get focusX():int
		{
			return _focusX;
		}

		public function get squareSize():int
		{
			return _issue.size;
		}

		public function set squareSize(v:int):void
		{
			_issue.size = v;
		}

		public function get posY():int
		{
			return _posY;
		}

		public function set posY(v:int):void
		{
			_posY = v;
		}

		public function get posX():int
		{
			return _posX;
		}

		public function set posX(v:int):void
		{
			_posX = v;
		}

		public function get currentScale():int
		{
			return _currentScale;
		}

		public function get issue():Object
		{
			return _issue;
		}

		public function set issue(issue:Object):void
		{
			_issue = issue;
			_posX = 0;
			_posY = 0;
			_gridLineVisible = true;

			dispatchEvent(new GridEvent(GridEvent.INFO_READY, _issue.steps, nbHSquare, nbVSquare, squareSize));
		}

		public function get issueSlug():String
		{
			return _issueSlug;
		}

		public function set issueSlug(v:String):void
		{
			_issueSlug = v;
		}

		public function setFocus(x:int, y:int):void {
			focusX = y;
			focusY = x;
		}

		public function initSquares(squares:Array, squaresOpen:Array):void
		{
			_lstPosition = new Array(nbHSquare);
			for(var i:int = 0 ; i < nbHSquare ; ++i)
			{
				_lstPosition[i] = new Array(nbVSquare);
			}
			var square:Object;
			for each(square in squares)
			{
				var squareObject:*;
				// si status = false et pas de background alors c'est une booked
				if(square.status || (square.background_image != null && !square.status)){
					squareObject = new SquareFull(square.pos_x + minX, square.pos_y + minY, square.background_image_path, squareSize);
					squareObject.layers = square.layers;
					squareObject.neighbors = square.neighbors_keys;
				} else {
					squareObject = new SquareBooked(square.pos_x + minX, square.pos_y + minY, squareSize);
				}
				_addPosition(squareObject);
			}

			for each(square in squaresOpen)
			{
				_addPosition(new SquareOpen(square.pos_x + minX, square.pos_y + minY, squareSize));
			}
			/*
			if(showDisableSquare)
			{
				for(i = 0 ; i < nbVSquare ; ++i)
				{
					for(var j:int = 0 ; j < nbHSquare ; ++j)
					{
						if(_lstPosition[i][j] == null)
						{
							_addPosition(new SquareDisable(i, j, squareSize));;
						}
					}
				}
			}*/

			dispatchEvent(new GridEvent(GridEvent.READY));
		}

		public function zoomTo(op:int):void
		{
			var futurScale:int = currentScale + op < minScale ? minScale : currentScale + op > maxScale ? maxScale : currentScale + op;

			if(currentScale != futurScale)// Si le zoom change
			{
				if(_isShowForm)
				{
					_isShowForm = false;
					dispatchEvent(new SquareFormEvent(SquareFormEvent.CLOSE));
				}
				currentScale = futurScale;
				dispatchEvent(new GridZoomEvent(GridZoomEvent.ZOOM, futurScale));
				moveTo();
			}
		}

		public function moveTo():void
		{
			if(currentScale == maxScale)// Si on est au zoom maximal
			{
				var square:Square = focusSquare;
				if(square is SquareOpen)
				{
					_isShowForm = true;
					dispatchEvent(new SquareFormEvent(SquareFormEvent.SHOW_OPEN));
				}
				else if(square is SquareBooked)
				{
					_isShowForm = true;
					dispatchEvent(new SquareFormEvent(SquareFormEvent.SHOW_BOOKED));
				}
				else if(_isShowForm)
				{
					_isShowForm = false;
					dispatchEvent(new SquareFormEvent(SquareFormEvent.CLOSE));
				}
			}

			if(currentScale != minScale)// Si on n'est pas au zoom minimal
			{
				posX = focusX * _issue.steps[currentScale] + _issue.steps[currentScale] / 2;
				posY = focusY * _issue.steps[currentScale] + _issue.steps[currentScale] / 2;
			}
			else
			{
				posX = nbVSquare * _issue.steps[currentScale] / 2;
				posY = nbHSquare * _issue.steps[currentScale] / 2;
			}

			dispatchEvent(new GridMoveEvent(GridMoveEvent.MOVE, posX, posY));
		}

		private function _addPosition(square:Square):void
		{
			_lstPosition[square.X][square.Y] = SquareManager.length() - 1;
			dispatchEvent(new SquareEvent(SquareEvent.CREATION, square));
		}

		public function set currentScale(scale:int):void {
			_currentScale = scale;
			dispatchEvent(new GridZoomEvent(GridZoomEvent.ZOOM, scale));
		}

		public function set focusX(x:int):void {
			_focusX = x < 0 ? 0 : x >= nbVSquare ? nbVSquare - 1 : x;
			dispatchEvent(new GridFocusEvent(GridFocusEvent.FOCUS));
		}

		public function set focusY(y:int):void {
			_focusY = y < 0 ? 0 : y >= nbHSquare ? nbHSquare - 1 : y;
			dispatchEvent(new GridFocusEvent(GridFocusEvent.FOCUS));
		}

		public function set gridLineVisible(b:Boolean):void
		{
			_gridLineVisible = b;
			dispatchEvent(new GridLineEvent(b ? GridLineEvent.SHOW : GridLineEvent.HIDE));
		}

		public function get minX():int
		{
			return 0;
		}

		public function get minY():int
		{
			return 0;
		}

		public function get nbHSquare():int
		{
			return _issue.nb_case_x;
		}

		public function get nbVSquare():int
		{
			return issue.nb_case_y;
		}

		public function get focusSquare():Square {
			return SquareManager.get(_lstPosition[_focusY][_focusX]);
		}

		public function get currentStep():int
		{
			return _issue.steps[_currentScale];
		}
	}
}

fix square process form

fix square process form


package aerys.minko.type.binding
{
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.Signal;
	import aerys.minko.type.enum.DataProviderUsage;

	import flash.utils.Dictionary;

	public final class DataBindings
	{
		private var _owner						: ISceneNode;

		private var _providers					: Vector.<IDataProvider>;
		private var _bindingNames				: Vector.<String>;

		private var _bindingNameToValue			: Object;
		private var _bindingNameToChangedSignal	: Object;

		private var _bindingNameToProvider		: Object;
		private var _providerToBindingNames		: Dictionary;

		private var _consumers					: Vector.<IDataBindingsConsumer>;

		public function get owner() : ISceneNode
		{
			return _owner;
		}

		public function get numProviders() : uint
		{
			return _providers.length;
		}

		public function get numProperties() : uint
		{
			return _bindingNames.length;
		}

		public function DataBindings(owner : ISceneNode)
		{
			_owner = owner;

			_providers = new <IDataProvider>[];
			_bindingNames = new <String>[];
			_bindingNameToValue = {};
			_bindingNameToChangedSignal = {};
			_bindingNameToProvider = {};
			_providerToBindingNames = new Dictionary(true);
			_consumers = new <IDataBindingsConsumer>[];
		}

		public function contains(dataProvider : IDataProvider) : Boolean
		{
			return _providers.indexOf(dataProvider) != -1;
		}

		public function addProvider(provider : IDataProvider) : void
		{
			if (_providerToBindingNames[provider])
				throw new Error('This provider is already bound.');

			var dataDescriptor	: Object	= provider.dataDescriptor;

			provider.propertyChanged.add(propertyChangedHandler);
			if (provider is IDynamicDataProvider)
			{
				var dynamicProvider : IDynamicDataProvider = provider as IDynamicDataProvider;

				dynamicProvider.propertyAdded.add(addBinding);
				dynamicProvider.propertyRemoved.add(removeBinding);
			}

			_providerToBindingNames[provider] = new <String>[];
			_providers.push(provider);

			for (var propertyName : String in dataDescriptor)
				addBinding(
					provider,
					propertyName,
					dataDescriptor[propertyName],
					provider[propertyName]
				);
		}

		private function addBinding(provider		: IDataProvider,
									propertyName	: String,
									bindingName		: String,
									value			: Object) : void
		{
			var providerBindingNames	: Vector.<String> 	= _providerToBindingNames[provider];

			if (_bindingNames.indexOf(bindingName) != -1)
				throw new Error(
					'Another data provider is already declaring the \'' + bindingName
					+ '\' property.'
				);

			_bindingNameToProvider[bindingName] = provider;
			_bindingNameToValue[bindingName] = value;

			providerBindingNames.push(bindingName);
			_bindingNames.push(bindingName);

            var numConsumers : uint = _consumers.length;
            for (var consumerId : uint = 0; consumerId < numConsumers; ++consumerId)
                _consumers[consumerId].setProperty(bindingName, value);

			if (_bindingNameToChangedSignal[bindingName])
				_bindingNameToChangedSignal[bindingName].execute(this, bindingName, null, value);
		}

		public function removeProvider(provider : IDataProvider) : void
		{
			var providerBindingsNames 	: Vector.<String> 	= _providerToBindingNames[provider];

			if (providerBindingsNames == null)
				throw new ArgumentError('Unknown provider.');

			var numProviders : uint = _providers.length - 1;

			provider.propertyChanged.remove(propertyChangedHandler);
			if (provider is IDynamicDataProvider)
			{
				var dynamicProvider : IDynamicDataProvider = provider as IDynamicDataProvider;

				dynamicProvider.propertyAdded.remove(addBinding);
				dynamicProvider.propertyRemoved.remove(removeBinding);
			}

			_providers[_providers.indexOf(provider)] = _providers[numProviders];
			_providers.length = numProviders;
			delete _providerToBindingNames[provider];

			var dataDescriptor : Object = provider.dataDescriptor;

			for (var propertyName : String in dataDescriptor)
				removeBinding(
					provider,
					propertyName,
					dataDescriptor[propertyName],
					provider[propertyName]
				);
		}

		public function removeBinding(provider		: IDataProvider,
									  propertyName	: String,
									  bindingName	: String,
									  value			: Object) : void
		{
			var numBindings 	: uint 		= _bindingNames.length - 1;
			var changedSignal 	: Signal 	= _bindingNameToChangedSignal[bindingName] as Signal;

			delete _bindingNameToValue[bindingName];
			delete _bindingNameToProvider[bindingName];

			_bindingNames[_bindingNames.indexOf(bindingName)] = _bindingNames[numBindings];
			_bindingNames.length = numBindings;

            var numConsumers : uint = _consumers.length;
            for (var consumerId : uint = 0; consumerId < numConsumers; ++consumerId)
                _consumers[consumerId].setProperty(bindingName, null);

			if (changedSignal != null)
				changedSignal.execute(this, bindingName, value, null);
		}

		public function removeAllProviders() : void
		{
			var numProviders : uint = this.numProviders;

			for (var providerId : int = numProviders - 1; providerId >= 0; --providerId)
				removeProvider(getProviderAt(providerId));
		}

		public function hasCallback(bindingName	: String,
									callback	: Function) : Boolean
		{
			var signal : Signal = _bindingNameToChangedSignal[bindingName];

			return signal != null && signal.hasCallback(callback);
		}

		public function addCallback(bindingName : String,
									callback	: Function) : void
		{
			_bindingNameToChangedSignal[bindingName] ||= new Signal(
				'DataBindings.changed[' + bindingName + ']'
			);

			Signal(_bindingNameToChangedSignal[bindingName]).add(callback);
		}

		public function removeCallback(bindingName	: String,
									   callback		: Function) : void
		{
			var signal : Signal = _bindingNameToChangedSignal[bindingName];

			if (!signal)
				throw new ArgumentError('Unkown property \'' + bindingName + '\'.');

			signal.remove(callback);

			if (signal.numCallbacks == 0)
				delete _bindingNameToChangedSignal[bindingName];
		}

		public function getProviderAt(index : uint) : IDataProvider
		{
			return _providers[index];
		}

		public function getProviderByBindingName(bindingName : String) : IDataProvider
		{
			if (_bindingNameToProvider[bindingName] == null)
				throw new ArgumentError('Unkown property \'' + bindingName + '\'.');

			return _bindingNameToProvider[bindingName];
		}

		public function propertyExists(bindingName : String) : Boolean
		{
			return _bindingNameToValue.hasOwnProperty(bindingName);
		}

		public function getProperty(bindingName : String) : *
		{
			if (_bindingNames.indexOf(bindingName) < 0)
				throw new Error('The property \'' + bindingName + '\' does not exist.');

			return _bindingNameToValue[bindingName];
		}

		public function getPropertyName(bindingIndex : uint) : String
		{
			if (bindingIndex > numProperties)
				throw new ArgumentError('No such binding');

			return _bindingNames[bindingIndex];
		}

		public function copySharedProvidersFrom(source : DataBindings) : void
		{
			var numProviders : uint = source._providers.length;

			for (var providerId : uint = 0; providerId < numProviders; ++providerId)
			{
				var provider : IDataProvider = source._providers[providerId];

				if (provider.usage == DataProviderUsage.SHARED)
					addProvider(provider);
			}
		}

		private function propertyChangedHandler(source 			: IDataProvider,
												propertyName 	: String,
												bindingName		: String,
												value			: Object) : void
		{
			if (propertyName == null)
				throw new Error('DataProviders must change only one property at a time.');

			var oldValue : Object = _bindingNameToValue[bindingName];

			_bindingNameToValue[bindingName] = value;

			var numConsumers : uint = _consumers.length;
			for (var consumerId : uint = 0; consumerId < numConsumers; ++consumerId)
				_consumers[consumerId].setProperty(bindingName, value);

			if (_bindingNameToChangedSignal[bindingName])
				_bindingNameToChangedSignal[bindingName].execute(
					this,
					bindingName,
					oldValue,
					value
				);
		}

		public function addConsumer(consumer : IDataBindingsConsumer) : void
		{
			_consumers.push(consumer);

			var numProperties : uint = this.numProperties;
			for (var propertyId : uint = 0; propertyId < numProperties; ++propertyId)
			{
				var bindingName : String = _bindingNames[propertyId];

				consumer.setProperty(bindingName, _bindingNameToValue[bindingName]);
			}
		}

		public function removeConsumer(consumer : IDataBindingsConsumer) : void
		{
			var numConsumers : uint = _consumers.length - 1;
			var index : int = _consumers.indexOf(consumer);

			if (index < 0)
				throw new Error('This consumer does not exist.');

			_consumers[index] = _consumers[numConsumers];
			_consumers.length = numConsumers;
		}
	}
}

package aerys.minko.type.binding
{
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.Signal;
	import aerys.minko.type.enum.DataProviderUsage;

	import flash.utils.Dictionary;

	public final class DataBindings
	{
		private var _owner						: ISceneNode;

		private var _providers					: Vector.<IDataProvider>;
		private var _bindingNames				: Vector.<String>;

		private var _bindingNameToValue			: Object;
		private var _bindingNameToChangedSignal	: Object;

		private var _bindingNameToProvider		: Object;
		private var _providerToBindingNames		: Dictionary;

		private var _consumers					: Vector.<IDataBindingsConsumer>;

		public function get owner() : ISceneNode
		{
			return _owner;
		}

		public function get numProviders() : uint
		{
			return _providers.length;
		}

		public function get numProperties() : uint
		{
			return _bindingNames.length;
		}

		public function DataBindings(owner : ISceneNode)
		{
			_owner = owner;

			_providers = new <IDataProvider>[];
			_bindingNames = new <String>[];
			_bindingNameToValue = {};
			_bindingNameToChangedSignal = {};
			_bindingNameToProvider = {};
			_providerToBindingNames = new Dictionary(true);
			_consumers = new <IDataBindingsConsumer>[];
		}

		public function contains(dataProvider : IDataProvider) : Boolean
		{
			return _providers.indexOf(dataProvider) != -1;
		}

		public function addProvider(provider : IDataProvider) : void
		{
			if (_providerToBindingNames[provider])
				throw new Error('This provider is already bound.');

			var dataDescriptor	: Object	= provider.dataDescriptor;

			provider.propertyChanged.add(propertyChangedHandler);
			if (provider is IDynamicDataProvider)
			{
				var dynamicProvider : IDynamicDataProvider = provider as IDynamicDataProvider;

				dynamicProvider.propertyAdded.add(addBinding);
				dynamicProvider.propertyRemoved.add(removeBinding);
			}

			_providerToBindingNames[provider] = new <String>[];
			_providers.push(provider);

			for (var propertyName : String in dataDescriptor)
				addBinding(
					provider,
					propertyName,
					dataDescriptor[propertyName],
					provider[propertyName]
				);
		}

		private function addBinding(provider		: IDataProvider,
									propertyName	: String,
									bindingName		: String,
									value			: Object) : void
		{
			var providerBindingNames	: Vector.<String> 	= _providerToBindingNames[provider];

			if (_bindingNames.indexOf(bindingName) != -1)
				throw new Error(
					'Another data provider is already declaring the \'' + bindingName
					+ '\' property.'
				);

			_bindingNameToProvider[bindingName] = provider;
			_bindingNameToValue[bindingName] = value;

			providerBindingNames.push(bindingName);
			_bindingNames.push(bindingName);

            var numConsumers : uint = _consumers.length;
            for (var consumerId : uint = 0; consumerId < numConsumers; ++consumerId)
                _consumers[consumerId].setProperty(bindingName, value);

			if (_bindingNameToChangedSignal[bindingName])
				_bindingNameToChangedSignal[bindingName].execute(this, bindingName, null, value);
		}

		public function removeProvider(provider : IDataProvider) : void
		{
			var providerBindingsNames 	: Vector.<String> 	= _providerToBindingNames[provider];

			if (providerBindingsNames == null)
				throw new ArgumentError('Unknown provider.');

			var numProviders : uint = _providers.length - 1;

			provider.propertyChanged.remove(propertyChangedHandler);
			if (provider is IDynamicDataProvider)
			{
				var dynamicProvider : IDynamicDataProvider = provider as IDynamicDataProvider;

				dynamicProvider.propertyAdded.remove(addBinding);
				dynamicProvider.propertyRemoved.remove(removeBinding);
			}

			_providers[_providers.indexOf(provider)] = _providers[numProviders];
			_providers.length = numProviders;
			delete _providerToBindingNames[provider];

			var dataDescriptor : Object = provider.dataDescriptor;

			for (var propertyName : String in dataDescriptor)
				removeBinding(
					provider,
					propertyName,
					dataDescriptor[propertyName],
					provider[propertyName]
				);
		}

		public function removeBinding(provider		: IDataProvider,
									  propertyName	: String,
									  bindingName	: String,
									  value			: Object) : void
		{
			var numBindings 	: uint 		= _bindingNames.length - 1;
			var changedSignal 	: Signal 	= _bindingNameToChangedSignal[bindingName] as Signal;

			delete _bindingNameToValue[bindingName];
			delete _bindingNameToProvider[bindingName];

			_bindingNames[_bindingNames.indexOf(bindingName)] = _bindingNames[numBindings];
			_bindingNames.length = numBindings;

            var numConsumers : uint = _consumers.length;
            for (var consumerId : uint = 0; consumerId < numConsumers; ++consumerId)
                _consumers[consumerId].setProperty(bindingName, null);

			if (changedSignal != null)
				changedSignal.execute(this, bindingName, value, null);
		}

		public function removeAllProviders() : void
		{
			var numProviders : uint = this.numProviders;

			for (var providerId : int = numProviders - 1; providerId >= 0; --providerId)
				removeProvider(getProviderAt(providerId));
		}

		public function hasCallback(bindingName	: String,
									callback	: Function) : Boolean
		{
			var signal : Signal = _bindingNameToChangedSignal[bindingName];

			return signal != null && signal.hasCallback(callback);
		}

		public function addCallback(bindingName : String,
									callback	: Function) : void
		{
			_bindingNameToChangedSignal[bindingName] ||= new Signal(
				'DataBindings.changed[' + bindingName + ']'
			);

			Signal(_bindingNameToChangedSignal[bindingName]).add(callback);
		}

		public function removeCallback(bindingName	: String,
									   callback		: Function) : void
		{
			var signal : Signal = _bindingNameToChangedSignal[bindingName];

			if (!signal)
				throw new ArgumentError('Unkown property \'' + bindingName + '\'.');

			signal.remove(callback);

			if (signal.numCallbacks == 0)
				delete _bindingNameToChangedSignal[bindingName];
		}

		public function getProviderAt(index : uint) : IDataProvider
		{
			return _providers[index];
		}

		public function getProviderByBindingName(bindingName : String) : IDataProvider
		{
			if (_bindingNameToProvider[bindingName] == null)
				throw new ArgumentError('Unkown property \'' + bindingName + '\'.');

			return _bindingNameToProvider[bindingName];
		}

		public function propertyExists(bindingName : String) : Boolean
		{
			return _bindingNameToValue.hasOwnProperty(bindingName);
		}

		public function getProperty(bindingName : String) : *
		{
			if (_bindingNames.indexOf(bindingName) < 0)
				throw new Error('The property \'' + bindingName + '\' does not exist.');

			return _bindingNameToValue[bindingName];
		}

		public function getPropertyName(bindingIndex : uint) : String
		{
			if (bindingIndex > numProperties)
				throw new ArgumentError('No such binding');

			return _bindingNames[bindingIndex];
		}

		public function copySharedProvidersFrom(source : DataBindings) : void
		{
			var numProviders : uint = source._providers.length;

			for (var providerId : uint = 0; providerId < numProviders; ++providerId)
			{
				var provider : IDataProvider = source._providers[providerId];

				if (provider.usage == DataProviderUsage.SHARED)
					addProvider(provider);
			}
		}

        public function hasProvider(provider : DataProvider) : Boolean
        {
            return provider in _providers;
        }

		private function propertyChangedHandler(source 			: IDataProvider,
												propertyName 	: String,
												bindingName		: String,
												value			: Object) : void
		{
			if (propertyName == null)
				throw new Error('DataProviders must change only one property at a time.');

			var oldValue : Object = _bindingNameToValue[bindingName];

			_bindingNameToValue[bindingName] = value;

			var numConsumers : uint = _consumers.length;
			for (var consumerId : uint = 0; consumerId < numConsumers; ++consumerId)
				_consumers[consumerId].setProperty(bindingName, value);

			if (_bindingNameToChangedSignal[bindingName])
				_bindingNameToChangedSignal[bindingName].execute(
					this,
					bindingName,
					oldValue,
					value
				);
		}

		public function addConsumer(consumer : IDataBindingsConsumer) : void
		{
			_consumers.push(consumer);

			var numProperties : uint = this.numProperties;
			for (var propertyId : uint = 0; propertyId < numProperties; ++propertyId)
			{
				var bindingName : String = _bindingNames[propertyId];

				consumer.setProperty(bindingName, _bindingNameToValue[bindingName]);
			}
		}

		public function removeConsumer(consumer : IDataBindingsConsumer) : void
		{
			var numConsumers : uint = _consumers.length - 1;
			var index : int = _consumers.indexOf(consumer);

			if (index < 0)
				throw new Error('This consumer does not exist.');

			_consumers[index] = _consumers[numConsumers];
			_consumers.length = numConsumers;
		}
	}
}

add DataBindings.hasProvider()

add DataBindings.hasProvider()


/*
 * Copyright 2008 Adobe Systems Inc., 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributor(s):
 *   Zwetan Kjukov <zwetan@gmail.com>.
 *   Marc Alcaraz <ekameleon@gmail.com>.
 */

package com.google.analytics
{
    import com.google.analytics.core.Buffer;
    import com.google.analytics.core.Ecommerce;
    import com.google.analytics.core.EventTracker;
    import com.google.analytics.core.GIFRequest;
    import com.google.analytics.core.IdleTimer;
    import com.google.analytics.core.ServerOperationMode;
    import com.google.analytics.core.TrackerCache;
    import com.google.analytics.core.TrackerMode;
    import com.google.analytics.core.ga_internal;
    import com.google.analytics.debug.DebugConfiguration;
    import com.google.analytics.debug.Layout;
    import com.google.analytics.events.AnalyticsEvent;
    import com.google.analytics.external.AdSenseGlobals;
    import com.google.analytics.external.HTMLDOM;
    import com.google.analytics.external.JavascriptProxy;
    import com.google.analytics.utils.Environment;
    import com.google.analytics.utils.Version;
    import com.google.analytics.v4.Bridge;
    import com.google.analytics.v4.Configuration;
    import com.google.analytics.v4.GoogleAnalyticsAPI;
    import com.google.analytics.v4.Tracker;

    import flash.display.DisplayObject;
    import flash.events.Event;
    import flash.events.EventDispatcher;

    /* force import for type in the includes */
    EventTracker;
    ServerOperationMode;

    /**
     * Dispatched after the factory has built the tracker object.
     * @eventType com.google.analytics.events.AnalyticsEvent.READY
     */
    [Event(name="ready", type="com.google.analytics.events.AnalyticsEvent")]

    /**
     * Google Analytic Tracker Code (GATC)'s code-only component.
     */
    public class GATracker implements AnalyticsTracker
    {
        private var _ready:Boolean = false;

        private var _display:DisplayObject;
        private var _eventDispatcher:EventDispatcher;
        private var _tracker:GoogleAnalyticsAPI;

        //factory
        private var _config:Configuration;
        private var _debug:DebugConfiguration;
        private var _env:Environment;
        private var _buffer:Buffer;
        private var _gifRequest:GIFRequest;
        private var _jsproxy:JavascriptProxy;
        private var _dom:HTMLDOM;
        private var _adSense:AdSenseGlobals;
        private var _idleTimer:IdleTimer;
        private var _ecom:Ecommerce;

        //object properties
        private var _account:String;
        private var _mode:String;
        private var _visualDebug:Boolean;

        /**
         * Indicates if the tracker is automatically build.
         */
        public static var autobuild:Boolean = true;

        /**
         * The version of the tracker.
         */
        public static var version:Version = API.version;

        /**
         * Creates a new GATracker instance.
         *
Note: the GATracker need to be instancied and added to the Stage or at least
         * being placed in a display list.

         */
        public function GATracker( display:DisplayObject, account:String,
                                   mode:String = "AS3", visualDebug:Boolean = false,
                                   config:Configuration = null, debug:DebugConfiguration = null )
        {
            _display = display;
            _eventDispatcher = new EventDispatcher( this ) ;
            _tracker = new TrackerCache();

            this.account     = account;
            this.mode        = mode;
            this.visualDebug = visualDebug;

            if( !debug )
            {
                this.debug = new DebugConfiguration();
            }

            if( !config )
            {
                this.config = new Configuration( debug );
            }
            else
            {
            	this.config = config;
            }

            if( autobuild )
            {
                _factory();
            }
        }

        /**
         * @private
         * Factory to build the different trackers
         */
        private function _factory():void
        {
            _jsproxy = new JavascriptProxy( debug );

            if( visualDebug )
            {
                debug.layout = new Layout( debug, _display );
                debug.active = visualDebug;
            }

            var activeTracker:GoogleAnalyticsAPI;
            var cache:TrackerCache = _tracker as TrackerCache;

            switch( mode )
            {
                case TrackerMode.BRIDGE :
                {
                    activeTracker = _bridgeFactory();
                    break;
                }

                case TrackerMode.AS3 :
                default:
                {
                    activeTracker = _trackerFactory();
                }
            }

            if( !cache.isEmpty() )
            {
                cache.tracker = activeTracker;
                cache.flush();
            }

            _tracker = activeTracker;

            _ready = true;
            dispatchEvent( new AnalyticsEvent( AnalyticsEvent.READY, this ) );
        }

        /**
        * @private
        * Factory method for returning a Tracker object.
        *
        * @return {GoogleAnalyticsAPI}
        */
        private function _trackerFactory():GoogleAnalyticsAPI
        {
            debug.info( "GATracker (AS3) v" + version +"\naccount: " + account );

            /* note:
               for unit testing and to avoid 2 different branches AIR/Flash
               here we will detect if we are in the Flash Player or AIR
               and pass the infos to the LocalInfo

               By default we will define "Flash" for our local tests
            */

            _adSense    = new AdSenseGlobals( debug );

            _dom        = new HTMLDOM( debug );
            _dom.cacheProperties();

            _env        = new Environment( "", "", "", debug, _dom );

            _buffer     = new Buffer( config, debug, false );
            _gifRequest = new GIFRequest( config, debug, _buffer, _env );
            _idleTimer  = new IdleTimer( config, debug, _display, _buffer );
            _ecom       = new Ecommerce ( _debug );

            /* note:
               To be able to obtain the URL of the main SWF containing the GA API
               we need to be able to access the stage property of a DisplayObject,
               here we open the internal namespace to be able to set that reference
               at instanciation-time.

               We keep the implementation internal to be able to change it if required later.
            */
            use namespace ga_internal;
            _env.url = _display.stage.loaderInfo.url;

            return new Tracker( account, config, debug, _env, _buffer, _gifRequest, _adSense, _ecom );
        }

        /**
        * @private
        * Factory method for returning a Bridge object.
        *
        * @return {GoogleAnalyticsAPI}
        */
        private function _bridgeFactory():GoogleAnalyticsAPI
        {
            debug.info( "GATracker (Bridge) v" + version +"\naccount: " + account );
            return new Bridge( account, _debug, _jsproxy );
        }

        /**
         * Indicates the account value of the tracking.
         */
        public function get account():String
        {
            return _account;
        }

        /**
         * @private
         */
        public function set account( value:String ):void
        {
            _account = value;
        }

        /**
         * Determinates the Configuration object of the tracker.
         */
        public function get config():Configuration
        {
            return _config;
        }

        /**
         * @private
         */
        public function set config( value:Configuration ):void
        {
            _config = value;
        }

        /**
         * Determinates the DebugConfiguration of the tracker.
         */
        public function get debug():DebugConfiguration
        {
            return _debug;
        }

        /**
         * @private
         */
        public function set debug( value:DebugConfiguration ):void
        {
            _debug = value;
        }

        /**
         * Indicates if the tracker is ready to use.
         */
        public function isReady():Boolean
        {
            return _ready;
        }

        /**
         * Indicates the mode of the tracking "AS3" or "Bridge".
         */
        public function get mode():String
        {
            return _mode;
        }

        /**
         * @private
         */
        public function set mode( value:String ):void
        {
            _mode = value ;
        }

        /**
         * Indicates if the tracker use a visual debug.
         */
        public function get visualDebug():Boolean
        {
            return _visualDebug;
        }

        /**
         * @private
         */
        public function set visualDebug( value:Boolean ):void
        {
            _visualDebug = value;
        }

        /**
         * Builds the tracker.
         */
        public function build():void
        {
            if( !isReady() )
            {
                _factory();
            }
        }

        // IEventDispatcher implementation

        /**
         * Allows the registration of event listeners on the event target.
         * @param type A string representing the event type to listen for. If eventName value is "ALL" addEventListener use addGlobalListener
         * @param listener The Function that receives a notification when an event of the specified type occurs.
         * @param useCapture Determinates if the event flow use capture or not.
         * @param priority Determines the priority level of the event listener.
         * @param useWeakReference Indicates if the listener is a weak reference.
         */
        public function addEventListener( type:String, listener:Function, useCapture:Boolean = false, priority:int = 0,
                                          useWeakReference:Boolean = false):void
        {
            _eventDispatcher.addEventListener( type, listener, useCapture, priority, useWeakReference );
        }

        /**
         * Dispatches an event into the event flow.
         * @param event The Event object that is dispatched into the event flow.
         * @return true if the Event is dispatched.
         */
        public function dispatchEvent( event:Event ):Boolean
        {
            return _eventDispatcher.dispatchEvent( event );
        }

        /**
         * Checks whether the EventDispatcher object has any listeners registered for a specific type of event.
         * This allows you to determine where altered handling of an event type has been introduced in the event flow heirarchy by an EventDispatcher object.
         */
        public function hasEventListener( type:String ):Boolean
        {
            return _eventDispatcher.hasEventListener( type );
        }

        /**
         * Removes a listener from the EventDispatcher object.
         * If there is no matching listener registered with the EventDispatcher object, then calling this method has no effect.
         * @param type Specifies the type of event.
         * @param listener The Function that receives a notification when an event of the specified type occurs.
         * @param useCapture Determinates if the event flow use capture or not.
         */
        public function removeEventListener( type:String, listener:Function, useCapture:Boolean = false ):void
        {
            _eventDispatcher.removeEventListener( type, listener, useCapture );
        }

        /**
         * Checks whether an event listener is registered with this EventDispatcher object or any of its ancestors for the specified event type.
         * This method returns true if an event listener is triggered during any phase of the event flow when an event of the specified type is dispatched to this EventDispatcher object or any of its descendants.
         * @return A value of true if a listener of the specified type will be triggered; false otherwise.
         */
        public function willTrigger( type:String ):Boolean
        {
            return _eventDispatcher.willTrigger( type );
        }

        include "common.txt"

    }
}

/*
 * Copyright 2008 Adobe Systems Inc., 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Contributor(s):
 *   Zwetan Kjukov <zwetan@gmail.com>.
 *   Marc Alcaraz <ekameleon@gmail.com>.
 */

package com.google.analytics
{
    import com.google.analytics.core.Buffer;
    import com.google.analytics.core.Ecommerce;
    import com.google.analytics.core.EventTracker;
    import com.google.analytics.core.GIFRequest;
    import com.google.analytics.core.IdleTimer;
    import com.google.analytics.core.ServerOperationMode;
    import com.google.analytics.core.TrackerCache;
    import com.google.analytics.core.TrackerMode;
    import com.google.analytics.core.ga_internal;
    import com.google.analytics.debug.DebugConfiguration;
    import com.google.analytics.debug.Layout;
    import com.google.analytics.events.AnalyticsEvent;
    import com.google.analytics.external.AdSenseGlobals;
    import com.google.analytics.external.HTMLDOM;
    import com.google.analytics.external.JavascriptProxy;
    import com.google.analytics.utils.Environment;
    import com.google.analytics.v4.Bridge;
    import com.google.analytics.v4.Configuration;
    import com.google.analytics.v4.GoogleAnalyticsAPI;
    import com.google.analytics.v4.Tracker;

    import core.version;

    import flash.display.DisplayObject;
    import flash.events.Event;
    import flash.events.EventDispatcher;

    EventTracker;
    ServerOperationMode;

    /**
     * Dispatched after the factory has built the tracker object.
     * @eventType com.google.analytics.events.AnalyticsEvent.READY
     */
    [Event(name="ready", type="com.google.analytics.events.AnalyticsEvent")]

    /**
     * Google Analytic Tracker Code (GATC)'s code-only component.
     */
    public class GATracker implements AnalyticsTracker
    {
        private var _ready:Boolean = false;

        private var _display:DisplayObject;
        private var _eventDispatcher:EventDispatcher;
        private var _tracker:GoogleAnalyticsAPI;

        //factory
        private var _config:Configuration;
        private var _debug:DebugConfiguration;
        private var _env:Environment;
        private var _buffer:Buffer;
        private var _gifRequest:GIFRequest;
        private var _jsproxy:JavascriptProxy;
        private var _dom:HTMLDOM;
        private var _adSense:AdSenseGlobals;
        private var _idleTimer:IdleTimer;
        private var _ecom:Ecommerce;

        //object properties
        private var _account:String;
        private var _mode:String;
        private var _visualDebug:Boolean;

        /**
         * Indicates if the tracker is automatically build.
         */
        public static var autobuild:Boolean = true;

        /**
         * The version of the tracker.
         */
        public static var version:core.version = API.version;

        /**
         * Creates a new GATracker instance.
         *
Note: the GATracker need to be instancied and added to the Stage or at least
         * being placed in a display list.

         */
        public function GATracker( display:DisplayObject, account:String,
                                   mode:String = "AS3", visualDebug:Boolean = false,
                                   config:Configuration = null, debug:DebugConfiguration = null )
        {
            _display = display;
            _eventDispatcher = new EventDispatcher( this ) ;
            _tracker = new TrackerCache();

            this.account     = account;
            this.mode        = mode;
            this.visualDebug = visualDebug;

            if( !debug )
            {
                this.debug = new DebugConfiguration();
            }

            if( !config )
            {
                this.config = new Configuration( debug );
            }
            else
            {
            	this.config = config;
            }

            if( autobuild )
            {
                _factory();
            }
        }

        /**
         * @private
         * Factory to build the different trackers
         */
        private function _factory():void
        {
            _jsproxy = new JavascriptProxy( debug );

            if( visualDebug )
            {
                debug.layout = new Layout( debug, _display );
                debug.active = visualDebug;
            }

            var activeTracker:GoogleAnalyticsAPI;
            var cache:TrackerCache = _tracker as TrackerCache;

            switch( mode )
            {
                case TrackerMode.BRIDGE :
                {
                    activeTracker = _bridgeFactory();
                    break;
                }

                case TrackerMode.AS3 :
                default:
                {
                    activeTracker = _trackerFactory();
                }
            }

            if( !cache.isEmpty() )
            {
                cache.tracker = activeTracker;
                cache.flush();
            }

            _tracker = activeTracker;

            _ready = true;
            dispatchEvent( new AnalyticsEvent( AnalyticsEvent.READY, this ) );
        }

        /**
        * @private
        * Factory method for returning a Tracker object.
        *
        * @return {GoogleAnalyticsAPI}
        */
        private function _trackerFactory():GoogleAnalyticsAPI
        {
            debug.info( "GATracker (AS3) v" + version +"\naccount: " + account );

            /* note:
               for unit testing and to avoid 2 different branches AIR/Flash
               here we will detect if we are in the Flash Player or AIR
               and pass the infos to the LocalInfo

               By default we will define "Flash" for our local tests
            */

            _adSense    = new AdSenseGlobals( debug );

            _dom        = new HTMLDOM( debug );
            _dom.cacheProperties();

            _env        = new Environment( "", "", "", debug, _dom );

            _buffer     = new Buffer( config, debug, false );
            _gifRequest = new GIFRequest( config, debug, _buffer, _env );
            _idleTimer  = new IdleTimer( config, debug, _display, _buffer );
            _ecom       = new Ecommerce ( _debug );

            /* note:
               To be able to obtain the URL of the main SWF containing the GA API
               we need to be able to access the stage property of a DisplayObject,
               here we open the internal namespace to be able to set that reference
               at instanciation-time.

               We keep the implementation internal to be able to change it if required later.
            */
            use namespace ga_internal;
            _env.url = _display.stage.loaderInfo.url;

            return new Tracker( account, config, debug, _env, _buffer, _gifRequest, _adSense, _ecom );
        }

        /**
        * @private
        * Factory method for returning a Bridge object.
        *
        * @return {GoogleAnalyticsAPI}
        */
        private function _bridgeFactory():GoogleAnalyticsAPI
        {
            debug.info( "GATracker (Bridge) v" + version +"\naccount: " + account );
            return new Bridge( account, _debug, _jsproxy );
        }

        /**
         * Indicates the account value of the tracking.
         */
        public function get account():String
        {
            return _account;
        }

        /**
         * @private
         */
        public function set account( value:String ):void
        {
            _account = value;
        }

        /**
         * Determinates the Configuration object of the tracker.
         */
        public function get config():Configuration
        {
            return _config;
        }

        /**
         * @private
         */
        public function set config( value:Configuration ):void
        {
            _config = value;
        }

        /**
         * Determinates the DebugConfiguration of the tracker.
         */
        public function get debug():DebugConfiguration
        {
            return _debug;
        }

        /**
         * @private
         */
        public function set debug( value:DebugConfiguration ):void
        {
            _debug = value;
        }

        /**
         * Indicates if the tracker is ready to use.
         */
        public function isReady():Boolean
        {
            return _ready;
        }

        /**
         * Indicates the mode of the tracking "AS3" or "Bridge".
         */
        public function get mode():String
        {
            return _mode;
        }

        /**
         * @private
         */
        public function set mode( value:String ):void
        {
            _mode = value ;
        }

        /**
         * Indicates if the tracker use a visual debug.
         */
        public function get visualDebug():Boolean
        {
            return _visualDebug;
        }

        /**
         * @private
         */
        public function set visualDebug( value:Boolean ):void
        {
            _visualDebug = value;
        }

        /**
         * Builds the tracker.
         */
        public function build():void
        {
            if( !isReady() )
            {
                _factory();
            }
        }

        // IEventDispatcher implementation

        /**
         * Allows the registration of event listeners on the event target.
         * @param type A string representing the event type to listen for. If eventName value is "ALL" addEventListener use addGlobalListener
         * @param listener The Function that receives a notification when an event of the specified type occurs.
         * @param useCapture Determinates if the event flow use capture or not.
         * @param priority Determines the priority level of the event listener.
         * @param useWeakReference Indicates if the listener is a weak reference.
         */
        public function addEventListener( type:String, listener:Function, useCapture:Boolean = false, priority:int = 0,
                                          useWeakReference:Boolean = false):void
        {
            _eventDispatcher.addEventListener( type, listener, useCapture, priority, useWeakReference );
        }

        /**
         * Dispatches an event into the event flow.
         * @param event The Event object that is dispatched into the event flow.
         * @return true if the Event is dispatched.
         */
        public function dispatchEvent( event:Event ):Boolean
        {
            return _eventDispatcher.dispatchEvent( event );
        }

        /**
         * Checks whether the EventDispatcher object has any listeners registered for a specific type of event.
         * This allows you to determine where altered handling of an event type has been introduced in the event flow heirarchy by an EventDispatcher object.
         */
        public function hasEventListener( type:String ):Boolean
        {
            return _eventDispatcher.hasEventListener( type );
        }

        /**
         * Removes a listener from the EventDispatcher object.
         * If there is no matching listener registered with the EventDispatcher object, then calling this method has no effect.
         * @param type Specifies the type of event.
         * @param listener The Function that receives a notification when an event of the specified type occurs.
         * @param useCapture Determinates if the event flow use capture or not.
         */
        public function removeEventListener( type:String, listener:Function, useCapture:Boolean = false ):void
        {
            _eventDispatcher.removeEventListener( type, listener, useCapture );
        }

        /**
         * Checks whether an event listener is registered with this EventDispatcher object or any of its ancestors for the specified event type.
         * This method returns true if an event listener is triggered during any phase of the event flow when an event of the specified type is dispatched to this EventDispatcher object or any of its descendants.
         * @return A value of true if a listener of the specified type will be triggered; false otherwise.
         */
        public function willTrigger( type:String ):Boolean
        {
            return _eventDispatcher.willTrigger( type );
        }

        include "common.txt"

    }
}

replace class Version by core.version

replace class Version by core.version