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

	import flash.utils.Dictionary;
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;

	public dynamic class DataProvider extends Proxy implements IDynamicDataProvider
	{
		private var _usage				: uint;
		private var _name				: String;
		private var _descriptor			: Object;

		private var _propertyChanged	: Signal;
		private var _propertyAdded		: Signal;
		private var _propertyRemoved	: Signal;

		private var _nameToProperty		: Object;
		private var _propertyToNames	: Dictionary;

		public function get usage() : uint
		{
			return _usage;
		}

		public function get dataDescriptor() : Object
		{
			return _descriptor;
		}

		public function get propertyChanged() : Signal
		{
			return _propertyChanged;
		}

		public function get propertyAdded() : Signal
		{
			return _propertyAdded;
		}

		public function get propertyRemoved() : Signal
		{
			return _propertyRemoved;
		}

		public function get name() : String
		{
			return _name;
		}

		public function set name(v : String) : void
		{
			_name = v;
		}

		public function DataProvider(properties	: Object	= null,
									 name		: String	= null,
									 usage		: uint		= 1)
		{
			_name	= name;
			_usage	= usage;

			initialize(properties);
		}

		private function initialize(properties : Object) : void
		{
			_descriptor = {};
			_propertyChanged = new Signal('DataProvider.changed');
			_propertyAdded = new Signal('DataProvider.propertyAdded');
			_propertyRemoved = new Signal('DataProvider.propertyRemoved');
			_nameToProperty = {};
			_propertyToNames = new Dictionary();

			setProperties(properties);
		}

		override flash_proxy function getProperty(name : *) : *
		{
			return getProperty(String(name));
		}

		override flash_proxy function setProperty(name : *, value : *) : void
		{
			setProperty(String(name), value);
		}

		override flash_proxy function deleteProperty(name : *) : Boolean
		{
			removeProperty(String(name));

			return true;
		}

		public function getProperty(name : String) : *
		{
			if (_nameToProperty)
				return _nameToProperty[name];
			else
				return null;
		}

		public function setProperty(name : String, newValue : Object) : DataProvider
		{
			var oldValue 			: Object			= _nameToProperty[name];
			var oldWatchedValue	    : IWatchable		= oldValue as IWatchable;
			var newWatchedValue	    : IWatchable		= newValue as IWatchable;
			var oldPropertyNames	: Vector.<String>	= _propertyToNames[oldWatchedValue];
			var newPropertyNames	: Vector.<String>	= _propertyToNames[newWatchedValue];

            if (newWatchedValue != oldWatchedValue)
            {
    			if (oldWatchedValue != null)
    			    unwatchProperty(name, oldWatchedValue);

    			if (newWatchedValue != null)
    				watchProperty(name, newWatchedValue);
            }

			var propertyAdded : Boolean = !_descriptor.hasOwnProperty(name);

			_descriptor[name]		= name;
			_nameToProperty[name]	= newValue;

			if (propertyAdded)
				_propertyAdded.execute(this, name, dataDescriptor[name], newValue);
			else
				_propertyChanged.execute(this, name, dataDescriptor[name], newValue);

			return this;
		}

		public function setProperties(properties : Object) : DataProvider
		{
			var propertyName : String = null;

			if (properties is DataProvider)
			{
				var provider : DataProvider = properties as DataProvider;

				for each (propertyName in provider.dataDescriptor)
					setProperty(propertyName, provider[propertyName]);
			}
			else
				for (propertyName in properties)
					setProperty(propertyName, properties[propertyName]);

			return this;
		}

		protected function watchProperty(name : String, property : IWatchable) : void
		{
			if (property != null)
			{
				var newPropertyNames : Vector.<String> = _propertyToNames[property] as Vector.<String>;

				if (newPropertyNames == null)
				{
					_propertyToNames[property] = newPropertyNames = new <String>[name];
					property.changed.add(propertyChangedHandler);
				}
				else
					newPropertyNames.push(name);
			}
		}

        protected function unwatchProperty(name : String, property : IWatchable) : void
        {
            var oldPropertyNames	: Vector.<String>	= _propertyToNames[property];

            if (oldPropertyNames.length == 1)
            {
                property.changed.remove(propertyChangedHandler);
                delete _propertyToNames[property];
            }
            else
            {
                var numPropertyNames : uint = oldPropertyNames.length - 1;

                oldPropertyNames[oldPropertyNames.indexOf(name)] = oldPropertyNames[numPropertyNames];
                oldPropertyNames.length = numPropertyNames;
            }
        }

		public function removeProperty(name : String) : DataProvider
		{
			if (_descriptor.hasOwnProperty(name))
			{
				var oldMonitoredValue	: IWatchable		= _nameToProperty[name] as IWatchable;
				var oldPropertyNames	: Vector.<String>	= _propertyToNames[oldMonitoredValue];
				var bindingName			: String			= _descriptor[name];

				delete _descriptor[name];
				delete _nameToProperty[name];

				if (oldMonitoredValue != null)
					unwatchProperty(name, oldMonitoredValue);

				_propertyRemoved.execute(this, name, bindingName, oldMonitoredValue);
			}

			return this;
		}

		public function removeAllProperties() : DataProvider
		{
			for (var propertyName : String in _nameToProperty)
				removeProperty(propertyName);

			return this;
		}

        public function dispose() : void
        {
            removeAllProperties();
            _propertyToNames = null;
            _nameToProperty = null;
            _descriptor = null;
            _propertyAdded = null;
            _propertyChanged = null;
            _propertyRemoved = null;
        }

		public function propertyExists(name : String) : Boolean
		{
			if (_descriptor)
				return _descriptor.hasOwnProperty(name);
			else
				return null;
		}

		public function invalidate() : DataProvider
		{
			_propertyChanged.execute(this, null);

			return this;
		}

		public function clone() : IDataProvider
		{
			switch (_usage)
			{
				case DataProviderUsage.EXCLUSIVE:
					return new DataProvider(_nameToProperty, _name + '_cloned', _usage);
				case DataProviderUsage.SHARED:
					return this;
				case DataProviderUsage.MANAGED:
					throw new Error('This dataprovider is managed, and must not be cloned');
				default:
					throw new Error('Unkown usage value');
			}
		}

		private function propertyChangedHandler(source : IWatchable) : void
		{
			var names		: Vector.<String>	= _propertyToNames[source];
			var numNames	: uint				= names.length;

			for (var nameId : uint = 0; nameId < numNames; ++nameId)
			{
				var propertyName : String = names[nameId];

				_propertyChanged.execute(this, propertyName, dataDescriptor[propertyName], source);
			}
		}
	}
}

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

	import flash.utils.Dictionary;
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;

	public dynamic class DataProvider extends Proxy implements IDynamicDataProvider
	{
		private var _usage				: uint;
		private var _name				: String;
		private var _descriptor			: Object;

		private var _propertyChanged	: Signal;
		private var _propertyAdded		: Signal;
		private var _propertyRemoved	: Signal;

		private var _nameToProperty		: Object;
		private var _propertyToNames	: Dictionary;

		public function get usage() : uint
		{
			return _usage;
		}

		public function get dataDescriptor() : Object
		{
			return _descriptor;
		}

		public function get propertyChanged() : Signal
		{
			return _propertyChanged;
		}

		public function get propertyAdded() : Signal
		{
			return _propertyAdded;
		}

		public function get propertyRemoved() : Signal
		{
			return _propertyRemoved;
		}

		public function get name() : String
		{
			return _name;
		}

		public function set name(v : String) : void
		{
			_name = v;
		}

		public function DataProvider(properties	: Object	= null,
									 name		: String	= null,
									 usage		: uint		= 1)
		{
			_name	= name;
			_usage	= usage;

			initialize(properties);
		}

		private function initialize(properties : Object) : void
		{
			_descriptor = {};
			_propertyChanged = new Signal('DataProvider.changed');
			_propertyAdded = new Signal('DataProvider.propertyAdded');
			_propertyRemoved = new Signal('DataProvider.propertyRemoved');
			_nameToProperty = {};
			_propertyToNames = new Dictionary();

			setProperties(properties);
		}

		override flash_proxy function getProperty(name : *) : *
		{
			return getProperty(String(name));
		}

		override flash_proxy function setProperty(name : *, value : *) : void
		{
			setProperty(String(name), value);
		}

		override flash_proxy function deleteProperty(name : *) : Boolean
		{
			removeProperty(String(name));

			return true;
		}

		public function getProperty(name : String) : *
		{
			if (_nameToProperty)
				return _nameToProperty[name];
			else
				return null;
		}

		public function setProperty(name : String, newValue : Object) : DataProvider
		{
			var oldValue 			: Object			= _nameToProperty[name];
			var oldWatchedValue	    : IWatchable		= oldValue as IWatchable;
			var newWatchedValue	    : IWatchable		= newValue as IWatchable;
			var oldPropertyNames	: Vector.<String>	= _propertyToNames[oldWatchedValue];
			var newPropertyNames	: Vector.<String>	= _propertyToNames[newWatchedValue];

            if (newWatchedValue != oldWatchedValue)
            {
    			if (oldWatchedValue != null)
    			    unwatchProperty(name, oldWatchedValue);

    			if (newWatchedValue != null)
    				watchProperty(name, newWatchedValue);
            }

			var propertyAdded : Boolean = !_descriptor.hasOwnProperty(name);

			_descriptor[name]		= name;
			_nameToProperty[name]	= newValue;

			if (propertyAdded)
				_propertyAdded.execute(this, name, dataDescriptor[name], newValue);
			else
				_propertyChanged.execute(this, name, dataDescriptor[name], newValue);

			return this;
		}

		public function setProperties(properties : Object) : DataProvider
		{
			var propertyName : String = null;

			if (properties is DataProvider)
			{
				var provider : DataProvider = properties as DataProvider;

				for each (propertyName in provider.dataDescriptor)
					setProperty(propertyName, provider[propertyName]);
			}
			else
				for (propertyName in properties)
					setProperty(propertyName, properties[propertyName]);

			return this;
		}

		protected function watchProperty(name : String, property : IWatchable) : void
		{
			if (property != null)
			{
				var newPropertyNames : Vector.<String> = _propertyToNames[property] as Vector.<String>;

				if (newPropertyNames == null)
				{
					_propertyToNames[property] = newPropertyNames = new <String>[name];
					property.changed.add(propertyChangedHandler);
				}
				else
					newPropertyNames.push(name);
			}
		}

        protected function unwatchProperty(name : String, property : IWatchable) : void
        {
            var oldPropertyNames	: Vector.<String>	= _propertyToNames[property];

            if (oldPropertyNames.length == 1)
            {
                property.changed.remove(propertyChangedHandler);
                delete _propertyToNames[property];
            }
            else
            {
                var numPropertyNames : uint = oldPropertyNames.length - 1;

                oldPropertyNames[oldPropertyNames.indexOf(name)] = oldPropertyNames[numPropertyNames];
                oldPropertyNames.length = numPropertyNames;
            }
        }

		public function removeProperty(name : String) : DataProvider
		{
			if (_descriptor.hasOwnProperty(name))
			{
				var oldMonitoredValue	: IWatchable		= _nameToProperty[name] as IWatchable;
				var oldPropertyNames	: Vector.<String>	= _propertyToNames[oldMonitoredValue];
				var bindingName			: String			= _descriptor[name];

				delete _descriptor[name];
				delete _nameToProperty[name];

				if (oldMonitoredValue != null)
					unwatchProperty(name, oldMonitoredValue);

				_propertyRemoved.execute(this, name, bindingName, oldMonitoredValue);
			}

			return this;
		}

		public function removeAllProperties() : DataProvider
		{
			for (var propertyName : String in _nameToProperty)
				removeProperty(propertyName);

			return this;
		}

        public function dispose() : void
        {
            removeAllProperties();
            _propertyToNames = null;
            _nameToProperty = null;
            _descriptor = null;
            _propertyAdded = null;
            _propertyChanged = null;
            _propertyRemoved = null;
        }

		public function propertyExists(name : String) : Boolean
		{
			if (_descriptor)
				return _descriptor.hasOwnProperty(name);
			else
				return false;
		}

		public function invalidate() : DataProvider
		{
			_propertyChanged.execute(this, null);

			return this;
		}

		public function clone() : IDataProvider
		{
			switch (_usage)
			{
				case DataProviderUsage.EXCLUSIVE:
					return new DataProvider(_nameToProperty, _name + '_cloned', _usage);
				case DataProviderUsage.SHARED:
					return this;
				case DataProviderUsage.MANAGED:
					throw new Error('This dataprovider is managed, and must not be cloned');
				default:
					throw new Error('Unkown usage value');
			}
		}

		private function propertyChangedHandler(source : IWatchable) : void
		{
			var names		: Vector.<String>	= _propertyToNames[source];
			var numNames	: uint				= names.length;

			for (var nameId : uint = 0; nameId < numNames; ++nameId)
			{
				var propertyName : String = names[nameId];

				_propertyChanged.execute(this, propertyName, dataDescriptor[propertyName], source);
			}
		}
	}
}

Remove warning.

Remove warning.


package
{
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.net.Socket;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.utils.ByteArray;
    import flash.utils.setTimeout;

    public class swfcat extends Sprite
    {
        /* David's relay (nickname 3VXRyxz67OeRoqHn) that also serves a
           crossdomain policy. */
        private const DEFAULT_TOR_ADDR:Object = {
            host: "173.255.221.44",
            port: 9001
        };

        // Socket to facilitator.
        private var s_f:Socket;

        private var output_text:TextField;

        private var fac_addr:Object;

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

        public function swfcat()
        {
            output_text = new TextField();
            output_text.width = 400;
            output_text.height = 300;
            output_text.background = true;
            output_text.backgroundColor = 0x001f0f;
            output_text.textColor = 0x44CC44;
            addChild(output_text);

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

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

            puts("Parameters loaded.");
            fac_spec = this.loaderInfo.parameters["facilitator"];
            if (!fac_spec) {
                puts("Error: no \"facilitator\" specification provided.");
                return;
            }
            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;
            }

            main();
        }

        /* The main logic begins here, after start-up issues are taken care of. */
        private function main():void
        {
            s_f = new Socket();

            s_f.addEventListener(Event.CONNECT, fac_connected);
            s_f.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("Facilitator: closed connection.");
            });
            s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("Facilitator: I/O error: " + e.text + ".");
            });
            s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("Facilitator: security error: " + e.text + ".");
            });

            puts("Facilitator: connecting to " + fac_addr.host + ":" + fac_addr.port + ".");
            s_f.connect(fac_addr.host, fac_addr.port);
        }

        private function fac_connected(e:Event):void
        {
            puts("Facilitator: connected.");

            s_f.addEventListener(ProgressEvent.SOCKET_DATA, fac_data);

            s_f.writeUTFBytes("GET / HTTP/1.0\r\n\r\n");
        }

        private function fac_data(e:ProgressEvent):void
        {
            var client_spec:String;
            var client_addr:Object;
            var proxy_pair:Object;

            client_spec = s_f.readMultiByte(e.bytesLoaded, "utf-8");
            puts("Facilitator: got \"" + client_spec + "\"");

            client_addr = parse_addr_spec(client_spec);
            if (!client_addr) {
                puts("Error: Client spec must be in the form \"host:port\".");
                setTimeout(main, 1000);
                return;
            }
            if (client_addr.host == "0.0.0.0" && client_addr.port == 0) {
                puts("Error: Facilitator has no clients.");
                setTimeout(main, 1000);
                return;
            }

            proxy_pair = new ProxyPair(this, client_addr, DEFAULT_TOR_ADDR);
            proxy_pair.connect();

            setTimeout(main, 1000);
        }

        /* 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.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.net.Socket;
import flash.utils.ByteArray;

/* An instance of a client-relay connection. */
class ProxyPair
{
    // Address ({host, port}) of client.
    private var addr_c:Object;
    // Address ({host, port}) of relay.
    private var addr_r:Object;

    // Socket to client.
    private var s_c:Socket;
    // Socket to relay.
    private var s_r:Socket;

    // Parent swfcat, for UI updates.
    private var ui:swfcat;

    private function log(msg:String):void
    {
        ui.puts(id() + ": " + msg)
    }

    // String describing this pair for output.
    private function id():String
    {
        return "<" + this.addr_c.host + ":" + this.addr_c.port +
            "," + this.addr_r.host + ":" + this.addr_r.port + ">";
    }

    public function ProxyPair(ui:swfcat, addr_c:Object, addr_r:Object)
    {
        this.ui = ui;
        this.addr_c = addr_c;
        this.addr_r = addr_r;
    }

    public function connect():void
    {
        s_r = new Socket();

        s_r.addEventListener(Event.CONNECT, tor_connected);
        s_r.addEventListener(Event.CLOSE, function (e:Event):void {
            log("Tor: closed.");
            if (s_c.connected)
                s_c.close();
        });
        s_r.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
            log("Tor: I/O error: " + e.text + ".");
            if (s_c.connected)
                s_c.close();
        });
        s_r.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
            log("Tor: security error: " + e.text + ".");
            if (s_c.connected)
                s_c.close();
        });

        log("Tor: connecting to " + addr_r.host + ":" + addr_r.port + ".");
        s_r.connect(addr_r.host, addr_r.port);
    }

    private function tor_connected(e:Event):void
    {
        log("Tor: connected.");

        s_c = new Socket();

        s_c.addEventListener(Event.CONNECT, client_connected);
        s_c.addEventListener(Event.CLOSE, function (e:Event):void {
            log("Client: closed.");
            if (s_r.connected)
                s_r.close();
        });
        s_c.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
            log("Client: I/O error: " + e.text + ".");
            if (s_r.connected)
                s_r.close();
        });
        s_c.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
            log("Client: security error: " + e.text + ".");
            if (s_r.connected)
                s_r.close();
        });

        log("Client: connecting to " + addr_c.host + ":" + addr_c.port + ".");
        s_c.connect(addr_c.host, addr_c.port);
    }

    private function client_connected(e:Event):void
    {
        log("Client: connected.");

        s_r.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
            var bytes:ByteArray = new ByteArray();
            s_r.readBytes(bytes, 0, e.bytesLoaded);
            log("Tor: read " + bytes.length + ".");
            s_c.writeBytes(bytes);
        });
        s_c.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
            var bytes:ByteArray = new ByteArray();
            s_c.readBytes(bytes, 0, e.bytesLoaded);
            log("Client: read " + bytes.length + ".");
            s_r.writeBytes(bytes);
        });
    }
}

package
{
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.net.Socket;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.utils.ByteArray;
    import flash.utils.setTimeout;

    public class swfcat extends Sprite
    {
        /* David's relay (nickname 3VXRyxz67OeRoqHn) that also serves a
           crossdomain policy. */
        private const DEFAULT_TOR_ADDR:Object = {
            host: "173.255.221.44",
            port: 9001
        };

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

        // Socket to facilitator.
        private var s_f:Socket;

        private var output_text:TextField;

        private var fac_addr:Object;

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

        public function swfcat()
        {
            output_text = new TextField();
            output_text.width = 400;
            output_text.height = 300;
            output_text.background = true;
            output_text.backgroundColor = 0x001f0f;
            output_text.textColor = 0x44CC44;
            addChild(output_text);

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

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

            puts("Parameters loaded.");
            fac_spec = this.loaderInfo.parameters["facilitator"];
            if (!fac_spec) {
                puts("Error: no \"facilitator\" specification provided.");
                return;
            }
            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;
            }

            main();
        }

        /* The main logic begins here, after start-up issues are taken care of. */
        private function main():void
        {
            s_f = new Socket();

            s_f.addEventListener(Event.CONNECT, fac_connected);
            s_f.addEventListener(Event.CLOSE, function (e:Event):void {
                puts("Facilitator: closed connection.");
            });
            s_f.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
                puts("Facilitator: I/O error: " + e.text + ".");
            });
            s_f.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
                puts("Facilitator: security error: " + e.text + ".");
            });

            puts("Facilitator: connecting to " + fac_addr.host + ":" + fac_addr.port + ".");
            s_f.connect(fac_addr.host, fac_addr.port);
        }

        private function fac_connected(e:Event):void
        {
            puts("Facilitator: connected.");

            s_f.addEventListener(ProgressEvent.SOCKET_DATA, fac_data);

            s_f.writeUTFBytes("GET / HTTP/1.0\r\n\r\n");
        }

        private function fac_data(e:ProgressEvent):void
        {
            var client_spec:String;
            var client_addr:Object;
            var proxy_pair:Object;

            client_spec = s_f.readMultiByte(e.bytesLoaded, "utf-8");
            puts("Facilitator: got \"" + client_spec + "\"");

            client_addr = parse_addr_spec(client_spec);
            if (!client_addr) {
                puts("Error: Client spec must be in the form \"host:port\".");
                setTimeout(main, FACILITATOR_POLL_INTERVAL);
                return;
            }
            if (client_addr.host == "0.0.0.0" && client_addr.port == 0) {
                puts("Error: Facilitator has no clients.");
                setTimeout(main, FACILITATOR_POLL_INTERVAL);
                return;
            }

            proxy_pair = new ProxyPair(this, client_addr, DEFAULT_TOR_ADDR);
            proxy_pair.connect();

            setTimeout(main, FACILITATOR_POLL_INTERVAL);
        }

        /* 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.display.Sprite;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.events.SecurityErrorEvent;
import flash.net.Socket;
import flash.utils.ByteArray;

/* An instance of a client-relay connection. */
class ProxyPair
{
    // Address ({host, port}) of client.
    private var addr_c:Object;
    // Address ({host, port}) of relay.
    private var addr_r:Object;

    // Socket to client.
    private var s_c:Socket;
    // Socket to relay.
    private var s_r:Socket;

    // Parent swfcat, for UI updates.
    private var ui:swfcat;

    private function log(msg:String):void
    {
        ui.puts(id() + ": " + msg)
    }

    // String describing this pair for output.
    private function id():String
    {
        return "<" + this.addr_c.host + ":" + this.addr_c.port +
            "," + this.addr_r.host + ":" + this.addr_r.port + ">";
    }

    public function ProxyPair(ui:swfcat, addr_c:Object, addr_r:Object)
    {
        this.ui = ui;
        this.addr_c = addr_c;
        this.addr_r = addr_r;
    }

    public function connect():void
    {
        s_r = new Socket();

        s_r.addEventListener(Event.CONNECT, tor_connected);
        s_r.addEventListener(Event.CLOSE, function (e:Event):void {
            log("Tor: closed.");
            if (s_c.connected)
                s_c.close();
        });
        s_r.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
            log("Tor: I/O error: " + e.text + ".");
            if (s_c.connected)
                s_c.close();
        });
        s_r.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
            log("Tor: security error: " + e.text + ".");
            if (s_c.connected)
                s_c.close();
        });

        log("Tor: connecting to " + addr_r.host + ":" + addr_r.port + ".");
        s_r.connect(addr_r.host, addr_r.port);
    }

    private function tor_connected(e:Event):void
    {
        log("Tor: connected.");

        s_c = new Socket();

        s_c.addEventListener(Event.CONNECT, client_connected);
        s_c.addEventListener(Event.CLOSE, function (e:Event):void {
            log("Client: closed.");
            if (s_r.connected)
                s_r.close();
        });
        s_c.addEventListener(IOErrorEvent.IO_ERROR, function (e:IOErrorEvent):void {
            log("Client: I/O error: " + e.text + ".");
            if (s_r.connected)
                s_r.close();
        });
        s_c.addEventListener(SecurityErrorEvent.SECURITY_ERROR, function (e:SecurityErrorEvent):void {
            log("Client: security error: " + e.text + ".");
            if (s_r.connected)
                s_r.close();
        });

        log("Client: connecting to " + addr_c.host + ":" + addr_c.port + ".");
        s_c.connect(addr_c.host, addr_c.port);
    }

    private function client_connected(e:Event):void
    {
        log("Client: connected.");

        s_r.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
            var bytes:ByteArray = new ByteArray();
            s_r.readBytes(bytes, 0, e.bytesLoaded);
            log("Tor: read " + bytes.length + ".");
            s_c.writeBytes(bytes);
        });
        s_c.addEventListener(ProgressEvent.SOCKET_DATA, function (e:ProgressEvent):void {
            var bytes:ByteArray = new ByteArray();
            s_c.readBytes(bytes, 0, e.bytesLoaded);
            log("Client: read " + bytes.length + ".");
            s_r.writeBytes(bytes);
        });
    }
}

Make a named constant FACILITATOR_POLL_INTERVAL.


package core.service
{
	import com.adobe.serialization.json.JSON;

	import core.events.ServiceEvent;
	import core.model.ImportModel;
	import core.model.vo.ExportVO;
	import core.model.vo.ImportVO;
	import core.suppotClass._BaseService;
	import core.utils.BitmapDataUtil;
	import core.utils.DataUtils;
	import core.utils.GlobalConstValues;
	import core.utils.PNGEncoder;

	import dragonBones.objects.DataParser;
	import dragonBones.utils.ConstValues;

	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	import flash.utils.ByteArray;

	import light.managers.ErrorManager;

	import zero.zip.Zip;

	public final class ImportDataToExportDataService extends _BaseService
	{
		public static const IMPORT_TO_EXPORT_ERROR:String = "IMPORT_TO_EXPORT_ERROR";
		public static const IMPORT_TO_EXPORT_COMPLETE:String = "IMPORT_TO_EXPORT_COMPLETE";

		[Inject (name='exportModel')]
		public var importModel:ImportModel;

		private var _exportVO:ExportVO;

		public function ImportDataToExportDataService()
		{

		}

		public function changeTo(importVO:ImportVO, exportVO:ExportVO):void
		{
			importModel.vo = importVO.clone();
			_exportVO = exportVO;

			//更新skeleton,textureAtalsConfig的name
			importModel.name = _exportVO.name || importModel.name;
			_exportVO.name = importModel.name;

			// only skeleton
			if(importModel.vo.skeleton && !importModel.vo.textureAtlasConfig)
			{
				var retult:ByteArray = new ByteArray();
				retult.writeObject(importModel.vo.skeleton);

				_exportVO.name += "." + GlobalConstValues.XML_SUFFIX;

				exportSave(retult);
				return;
			}

			if(_exportVO.textureAtlasType == GlobalConstValues.TEXTURE_ATLAS_TYPE_SWF)
			{
				//swf格式的动画数据缩放没有意义
				_exportVO.scale = 1;
			}
			else if(_exportVO.scale != 1)
			{
				scaleData(_exportVO.scale);
			}

			if(_exportVO.configType == GlobalConstValues.CONFIG_TYPE_MERGED)
			{
				if (changeToMergedData())
				{
					return;
				}
			}
			else if (_exportVO.textureAtlasType == GlobalConstValues.TEXTURE_ATLAS_TYPE_PNGS)
			{
				if (changeToSubTextureData())
				{
					return;
				}
			}
			else
			{
				if (changeToTextureAtlasData())
				{
					return;
				}
			}

			light.managers.ErrorManager.getInstance().dispatchErrorEvent(this, IMPORT_TO_EXPORT_ERROR);
		}

		private function scaleData(scale:Number):void
		{
			var subBitmapDataMap:Object;
			var movieClip:MovieClip = importModel.vo.textureAtlasSWF as MovieClip;
			if(movieClip && movieClip.totalFrames >= 3)
			{
				//第一帧是textureAtlas，最后一帧是空，如果大于等于3帧，说明有至少一个贴图，否则可能是shape贴图
				subBitmapDataMap = {};
				var helpMatrix:Matrix = new Matrix();
				for each (var displayXML:XML in importModel.getSubTextureList())
				{
					var displayName:String = displayXML.@[ConstValues.A_NAME];
					movieClip.gotoAndStop(movieClip.totalFrames);
					movieClip.gotoAndStop(displayName);
					var subDisplay:DisplayObject = movieClip.getChildAt(0);

					if(scale < 1)
					{
						var rectOffSet:Rectangle = subDisplay.getBounds(subDisplay);
						helpMatrix.tx = -rectOffSet.x;
						helpMatrix.ty = -rectOffSet.y;

						var subBitmapData:BitmapData = new BitmapData(subDisplay.width, subDisplay.height, true, 0xFF00FF);
						subBitmapData.draw(subDisplay, helpMatrix);
						subBitmapDataMap[displayName] = subBitmapData;
					}
					else
					{
						subBitmapDataMap[displayName] = subDisplay;
					}
				}
			}
			else
			{
				subBitmapDataMap =
					BitmapDataUtil.getSubBitmapDataDic(
						importModel.vo.textureAtlas,
						importModel.getSubTextureRectMap()
					);
			}

			importModel.scaleData(scale);

			importModel.vo.textureAtlas =
				BitmapDataUtil.getMergeBitmapData(
					subBitmapDataMap,
					importModel.getSubTextureRectMap(),
					importModel.textureAtlasWidth,
					importModel.textureAtlasHeight,
					scale
				);
		}

		private function changeToMergedData():Boolean
		{
			var textureAtlasBytes:ByteArray;
			if (
				_exportVO.textureAtlasType == GlobalConstValues.TEXTURE_ATLAS_TYPE_SWF &&
				importModel.vo.textureAtlasType == GlobalConstValues.TEXTURE_ATLAS_TYPE_SWF
			)
			{
				_exportVO.name += "." + GlobalConstValues.DBSWF_SUFFIX;
				textureAtlasBytes = importModel.vo.textureAtlasBytes;
			}
			else
			{
				_exportVO.name += "." + GlobalConstValues.PNG_SUFFIX;
				textureAtlasBytes = getPNGBytes();
			}
			exportSave(
				DataParser.compressData(
					DataUtils.xmlToObject(importModel.vo.skeleton, GlobalConstValues.XML_LIST_NAMES),
					DataUtils.xmlToObject(importModel.vo.textureAtlasConfig, GlobalConstValues.XML_LIST_NAMES),
					textureAtlasBytes
				)
			);
			return true;
		}

		private function changeToTextureAtlasData():Boolean
		{
			var textureAtlasBytes:ByteArray;
			var zip:Zip = new Zip();
			var date:Date = new Date();

			// update texture atlas and it's name
			_exportVO.textureAtlasFileName = _exportVO.textureAtlasFileName || GlobalConstValues.TEXTURE_ATLAS_DATA_NAME;
			if (
				_exportVO.textureAtlasType == GlobalConstValues.TEXTURE_ATLAS_TYPE_SWF &&
				importModel.vo.textureAtlasType == GlobalConstValues.TEXTURE_ATLAS_TYPE_SWF
			)
			{
				_exportVO.textureAtlasFileName += "." + GlobalConstValues.SWF_SUFFIX;
				textureAtlasBytes = importModel.vo.textureAtlasBytes;
			}
			else
			{
				_exportVO.textureAtlasFileName += "." + GlobalConstValues.PNG_SUFFIX;
				textureAtlasBytes = getPNGBytes();
			}

			// add texture atlas to zip
			zip.add(
				textureAtlasBytes,
				_exportVO.textureAtlasFileName,
				date
			);

			// update texture atlas path
			importModel.textureAtlasPath = (_exportVO.textureAtlasPath || "") + _exportVO.textureAtlasFileName;

			// update skeleton and texture atlas config file name and add them to zip
			_exportVO.dragonBonesFileName = _exportVO.dragonBonesFileName || GlobalConstValues.DRAGON_BONES_DATA_NAME;
			_exportVO.textureAtlasConfigFileName = _exportVO.textureAtlasConfigFileName || GlobalConstValues.TEXTURE_ATLAS_DATA_NAME;
			switch (_exportVO.configType)
			{
				case GlobalConstValues.CONFIG_TYPE_XML:
					zip.add(
						importModel.vo.skeleton.toXMLString(),
						_exportVO.dragonBonesFileName + "." + GlobalConstValues.XML_SUFFIX,
						date
					);
					zip.add(
						importModel.vo.textureAtlasConfig.toXMLString(),
						_exportVO.textureAtlasConfigFileName + "." + GlobalConstValues.XML_SUFFIX,
						date
					);
					break;

				case GlobalConstValues.CONFIG_TYPE_JSON:

					var obj:Object = DataUtils.xmlToObject(importModel.vo.skeleton, GlobalConstValues.XML_LIST_NAMES);
					trace(com.adobe.serialization.json.JSON.encode(obj));
					DataUtils.convertDragonBonesDataToRelativeObject(obj);
					trace(com.adobe.serialization.json.JSON.encode(obj));
					zip.add(
						com.adobe.serialization.json.JSON.encode(DataUtils.xmlToObject(importModel.vo.skeleton, GlobalConstValues.XML_LIST_NAMES)),
						_exportVO.dragonBonesFileName + "." + GlobalConstValues.JSON_SUFFIX,
						date
					);
					zip.add(
						com.adobe.serialization.json.JSON.encode(DataUtils.xmlToObject(importModel.vo.textureAtlasConfig, GlobalConstValues.XML_LIST_NAMES)),
						_exportVO.textureAtlasConfigFileName + "." + GlobalConstValues.JSON_SUFFIX,
						date
					);
					break;

				case GlobalConstValues.CONFIG_TYPE_AMF3:
					var bytes:ByteArray = new ByteArray();
					bytes.writeObject(DataUtils.xmlToObject(importModel.vo.skeleton, GlobalConstValues.XML_LIST_NAMES));
					bytes.compress();
					zip.add(
						bytes,
						_exportVO.dragonBonesFileName + "." + GlobalConstValues.AMF3_SUFFIX,
						date
					);

					bytes = new ByteArray();
					bytes.writeObject(DataUtils.xmlToObject(importModel.vo.textureAtlasConfig, GlobalConstValues.XML_LIST_NAMES));
					bytes.compress();
					zip.add(
						bytes,
						_exportVO.textureAtlasConfigFileName + "." + GlobalConstValues.AMF3_SUFFIX,
						date
					);
					break;
			}

			_exportVO.name += "." + GlobalConstValues.ZIP_SUFFIX;
			exportSave(zip.encode());
			zip.clear();
			return true;
		}

		private function changeToSubTextureData():Boolean
		{
			/*
			_xmlDataProxy.changePath();
			*/
			var zip:Zip = new Zip();
			var date:Date = new Date();

			var subBitmapDataMap:Object = BitmapDataUtil.getSubBitmapDataDic(
				importModel.vo.textureAtlas,
				importModel.getSubTextureRectMap()
			);

			// update texture folder name and add subtextures to zip
			_exportVO.subTextureFolderName = _exportVO.subTextureFolderName || GlobalConstValues.TEXTURE_ATLAS_DATA_NAME;
			for (var subTextureName:String in subBitmapDataMap)
			{
				var subBitmapData:BitmapData = subBitmapDataMap[subTextureName];
				zip.add(
					PNGEncoder.encode(subBitmapData),
					_exportVO.subTextureFolderName + "/" + subTextureName + "." + GlobalConstValues.PNG_SUFFIX,
					date
				);
				subBitmapData.dispose();
			}

			// update skeleton file name and add it to zip
			_exportVO.dragonBonesFileName = _exportVO.dragonBonesFileName || GlobalConstValues.DRAGON_BONES_DATA_NAME;
			switch (_exportVO.configType)
			{
				case GlobalConstValues.CONFIG_TYPE_XML:
					zip.add(
						importModel.vo.skeleton.toXMLString(),
						_exportVO.dragonBonesFileName + "." + GlobalConstValues.XML_SUFFIX,
						date
					);
					break;

				case GlobalConstValues.CONFIG_TYPE_JSON:
					zip.add(
						com.adobe.serialization.json.JSON.encode(DataUtils.xmlToObject(importModel.vo.skeleton, GlobalConstValues.XML_LIST_NAMES)),
						_exportVO.dragonBonesFileName + "." + GlobalConstValues.JSON_SUFFIX,
						date
					);
					break;

				case GlobalConstValues.CONFIG_TYPE_AMF3:
					var bytes:ByteArray = new ByteArray();
					bytes.writeObject(DataUtils.xmlToObject(importModel.vo.skeleton, GlobalConstValues.XML_LIST_NAMES));
					bytes.compress();
					zip.add(
						bytes,
						_exportVO.dragonBonesFileName + "." + GlobalConstValues.AMF3_SUFFIX,
						date
					);
					break;
			}

			_exportVO.name += "." + GlobalConstValues.ZIP_SUFFIX;
			exportSave(zip.encode());
			zip.clear();
			return true;
		}

		private function getPNGBytes():ByteArray
		{
			if(_exportVO.enableBackgroundColor)
			{
				var bitmapData:BitmapData = new BitmapData(importModel.vo.textureAtlas.width, importModel.vo.textureAtlas.height, false, _exportVO.backgroundColor);
				bitmapData.draw(importModel.vo.textureAtlas);

				var byteArray:ByteArray = PNGEncoder.encode(bitmapData);
				bitmapData.dispose();
				return byteArray;
			}

			return PNGEncoder.encode(importModel.vo.textureAtlas);
		}

		private function exportSave(fileData:ByteArray):void
		{
			//缩放后的bitmapData需要dispose
			this.dispatchEvent(new ServiceEvent(IMPORT_TO_EXPORT_COMPLETE, [fileData, _exportVO, importModel.vo]));
		}
	}
}

package core.service
{
	import com.adobe.serialization.json.JSON;

	import core.events.ServiceEvent;
	import core.model.ImportModel;
	import core.model.vo.ExportVO;
	import core.model.vo.ImportVO;
	import core.suppotClass._BaseService;
	import core.utils.BitmapDataUtil;
	import core.utils.DataUtils;
	import core.utils.GlobalConstValues;
	import core.utils.PNGEncoder;

	import dragonBones.objects.DataParser;
	import dragonBones.utils.ConstValues;

	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	import flash.utils.ByteArray;

	import light.managers.ErrorManager;

	import zero.zip.Zip;

	public final class ImportDataToExportDataService extends _BaseService
	{
		public static const IMPORT_TO_EXPORT_ERROR:String = "IMPORT_TO_EXPORT_ERROR";
		public static const IMPORT_TO_EXPORT_COMPLETE:String = "IMPORT_TO_EXPORT_COMPLETE";

		[Inject (name='exportModel')]
		public var importModel:ImportModel;

		private var _exportVO:ExportVO;

		public function ImportDataToExportDataService()
		{

		}

		public function changeTo(importVO:ImportVO, exportVO:ExportVO):void
		{
			importModel.vo = importVO.clone();
			_exportVO = exportVO;

			//更新skeleton,textureAtalsConfig的name
			importModel.name = _exportVO.name || importModel.name;
			_exportVO.name = importModel.name;

			// only skeleton
			if(importModel.vo.skeleton && !importModel.vo.textureAtlasConfig)
			{
				var retult:ByteArray = new ByteArray();
				retult.writeObject(importModel.vo.skeleton);

				_exportVO.name += "." + GlobalConstValues.XML_SUFFIX;

				exportSave(retult);
				return;
			}

			if(_exportVO.textureAtlasType == GlobalConstValues.TEXTURE_ATLAS_TYPE_SWF)
			{
				//swf格式的动画数据缩放没有意义
				_exportVO.scale = 1;
			}
			else if(_exportVO.scale != 1)
			{
				scaleData(_exportVO.scale);
			}

			if(_exportVO.configType == GlobalConstValues.CONFIG_TYPE_MERGED)
			{
				if (changeToMergedData())
				{
					return;
				}
			}
			else if (_exportVO.textureAtlasType == GlobalConstValues.TEXTURE_ATLAS_TYPE_PNGS)
			{
				if (changeToSubTextureData())
				{
					return;
				}
			}
			else
			{
				if (changeToTextureAtlasData())
				{
					return;
				}
			}

			light.managers.ErrorManager.getInstance().dispatchErrorEvent(this, IMPORT_TO_EXPORT_ERROR);
		}

		private function scaleData(scale:Number):void
		{
			var subBitmapDataMap:Object;
			var movieClip:MovieClip = importModel.vo.textureAtlasSWF as MovieClip;
			if(movieClip && movieClip.totalFrames >= 3)
			{
				//第一帧是textureAtlas，最后一帧是空，如果大于等于3帧，说明有至少一个贴图，否则可能是shape贴图
				subBitmapDataMap = {};
				var helpMatrix:Matrix = new Matrix();
				for each (var displayXML:XML in importModel.getSubTextureList())
				{
					var displayName:String = displayXML.@[ConstValues.A_NAME];
					movieClip.gotoAndStop(movieClip.totalFrames);
					movieClip.gotoAndStop(displayName);
					var subDisplay:DisplayObject = movieClip.getChildAt(0);

					if(scale < 1)
					{
						var rectOffSet:Rectangle = subDisplay.getBounds(subDisplay);
						helpMatrix.tx = -rectOffSet.x;
						helpMatrix.ty = -rectOffSet.y;

						var subBitmapData:BitmapData = new BitmapData(subDisplay.width, subDisplay.height, true, 0xFF00FF);
						subBitmapData.draw(subDisplay, helpMatrix);
						subBitmapDataMap[displayName] = subBitmapData;
					}
					else
					{
						subBitmapDataMap[displayName] = subDisplay;
					}
				}
			}
			else
			{
				subBitmapDataMap =
					BitmapDataUtil.getSubBitmapDataDic(
						importModel.vo.textureAtlas,
						importModel.getSubTextureRectMap()
					);
			}

			importModel.scaleData(scale);

			importModel.vo.textureAtlas =
				BitmapDataUtil.getMergeBitmapData(
					subBitmapDataMap,
					importModel.getSubTextureRectMap(),
					importModel.textureAtlasWidth,
					importModel.textureAtlasHeight,
					scale
				);
		}

		private function changeToMergedData():Boolean
		{
			var textureAtlasBytes:ByteArray;
			if (
				_exportVO.textureAtlasType == GlobalConstValues.TEXTURE_ATLAS_TYPE_SWF &&
				importModel.vo.textureAtlasType == GlobalConstValues.TEXTURE_ATLAS_TYPE_SWF
			)
			{
				_exportVO.name += "." + GlobalConstValues.DBSWF_SUFFIX;
				textureAtlasBytes = importModel.vo.textureAtlasBytes;
			}
			else
			{
				_exportVO.name += "." + GlobalConstValues.PNG_SUFFIX;
				textureAtlasBytes = getPNGBytes();
			}
			exportSave(
				DataParser.compressData(
					DataUtils.xmlToObject(importModel.vo.skeleton, GlobalConstValues.XML_LIST_NAMES),
					DataUtils.xmlToObject(importModel.vo.textureAtlasConfig, GlobalConstValues.XML_LIST_NAMES),
					textureAtlasBytes
				)
			);
			return true;
		}

		private function changeToTextureAtlasData():Boolean
		{
			var textureAtlasBytes:ByteArray;
			var zip:Zip = new Zip();
			var date:Date = new Date();

			// update texture atlas and it's name
			_exportVO.textureAtlasFileName = _exportVO.textureAtlasFileName || GlobalConstValues.TEXTURE_ATLAS_DATA_NAME;
			if (
				_exportVO.textureAtlasType == GlobalConstValues.TEXTURE_ATLAS_TYPE_SWF &&
				importModel.vo.textureAtlasType == GlobalConstValues.TEXTURE_ATLAS_TYPE_SWF
			)
			{
				_exportVO.textureAtlasFileName += "." + GlobalConstValues.SWF_SUFFIX;
				textureAtlasBytes = importModel.vo.textureAtlasBytes;
			}
			else
			{
				_exportVO.textureAtlasFileName += "." + GlobalConstValues.PNG_SUFFIX;
				textureAtlasBytes = getPNGBytes();
			}

			// add texture atlas to zip
			zip.add(
				textureAtlasBytes,
				_exportVO.textureAtlasFileName,
				date
			);

			// update texture atlas path
			importModel.textureAtlasPath = (_exportVO.textureAtlasPath || "") + _exportVO.textureAtlasFileName;

			// update skeleton and texture atlas config file name and add them to zip
			_exportVO.dragonBonesFileName = _exportVO.dragonBonesFileName || GlobalConstValues.DRAGON_BONES_DATA_NAME;
			_exportVO.textureAtlasConfigFileName = _exportVO.textureAtlasConfigFileName || GlobalConstValues.TEXTURE_ATLAS_DATA_NAME;
			switch (_exportVO.configType)
			{
				case GlobalConstValues.CONFIG_TYPE_XML:
					zip.add(
						importModel.vo.skeleton.toXMLString(),
						_exportVO.dragonBonesFileName + "." + GlobalConstValues.XML_SUFFIX,
						date
					);
					zip.add(
						importModel.vo.textureAtlasConfig.toXMLString(),
						_exportVO.textureAtlasConfigFileName + "." + GlobalConstValues.XML_SUFFIX,
						date
					);
					break;

				case GlobalConstValues.CONFIG_TYPE_JSON:

					var obj:Object = DataUtils.xmlToObject(importModel.vo.skeleton, GlobalConstValues.XML_LIST_NAMES);
					trace(com.adobe.serialization.json.JSON.encode(obj));
					DataUtils.convertDragonBonesDataToRelativeObject(obj);
					trace(com.adobe.serialization.json.JSON.encode(obj));
					zip.add(
						com.adobe.serialization.json.JSON.encode(obj),
						_exportVO.dragonBonesFileName + "." + GlobalConstValues.JSON_SUFFIX,
						date
					);
					zip.add(
						com.adobe.serialization.json.JSON.encode(DataUtils.xmlToObject(importModel.vo.textureAtlasConfig, GlobalConstValues.XML_LIST_NAMES)),
						_exportVO.textureAtlasConfigFileName + "." + GlobalConstValues.JSON_SUFFIX,
						date
					);
					break;

				case GlobalConstValues.CONFIG_TYPE_AMF3:
					var bytes:ByteArray = new ByteArray();
					bytes.writeObject(DataUtils.xmlToObject(importModel.vo.skeleton, GlobalConstValues.XML_LIST_NAMES));
					bytes.compress();
					zip.add(
						bytes,
						_exportVO.dragonBonesFileName + "." + GlobalConstValues.AMF3_SUFFIX,
						date
					);

					bytes = new ByteArray();
					bytes.writeObject(DataUtils.xmlToObject(importModel.vo.textureAtlasConfig, GlobalConstValues.XML_LIST_NAMES));
					bytes.compress();
					zip.add(
						bytes,
						_exportVO.textureAtlasConfigFileName + "." + GlobalConstValues.AMF3_SUFFIX,
						date
					);
					break;
			}

			_exportVO.name += "." + GlobalConstValues.ZIP_SUFFIX;
			exportSave(zip.encode());
			zip.clear();
			return true;
		}

		private function changeToSubTextureData():Boolean
		{
			/*
			_xmlDataProxy.changePath();
			*/
			var zip:Zip = new Zip();
			var date:Date = new Date();

			var subBitmapDataMap:Object = BitmapDataUtil.getSubBitmapDataDic(
				importModel.vo.textureAtlas,
				importModel.getSubTextureRectMap()
			);

			// update texture folder name and add subtextures to zip
			_exportVO.subTextureFolderName = _exportVO.subTextureFolderName || GlobalConstValues.TEXTURE_ATLAS_DATA_NAME;
			for (var subTextureName:String in subBitmapDataMap)
			{
				var subBitmapData:BitmapData = subBitmapDataMap[subTextureName];
				zip.add(
					PNGEncoder.encode(subBitmapData),
					_exportVO.subTextureFolderName + "/" + subTextureName + "." + GlobalConstValues.PNG_SUFFIX,
					date
				);
				subBitmapData.dispose();
			}

			// update skeleton file name and add it to zip
			_exportVO.dragonBonesFileName = _exportVO.dragonBonesFileName || GlobalConstValues.DRAGON_BONES_DATA_NAME;
			switch (_exportVO.configType)
			{
				case GlobalConstValues.CONFIG_TYPE_XML:
					zip.add(
						importModel.vo.skeleton.toXMLString(),
						_exportVO.dragonBonesFileName + "." + GlobalConstValues.XML_SUFFIX,
						date
					);
					break;

				case GlobalConstValues.CONFIG_TYPE_JSON:
					zip.add(
						com.adobe.serialization.json.JSON.encode(DataUtils.xmlToObject(importModel.vo.skeleton, GlobalConstValues.XML_LIST_NAMES)),
						_exportVO.dragonBonesFileName + "." + GlobalConstValues.JSON_SUFFIX,
						date
					);
					break;

				case GlobalConstValues.CONFIG_TYPE_AMF3:
					var bytes:ByteArray = new ByteArray();
					bytes.writeObject(DataUtils.xmlToObject(importModel.vo.skeleton, GlobalConstValues.XML_LIST_NAMES));
					bytes.compress();
					zip.add(
						bytes,
						_exportVO.dragonBonesFileName + "." + GlobalConstValues.AMF3_SUFFIX,
						date
					);
					break;
			}

			_exportVO.name += "." + GlobalConstValues.ZIP_SUFFIX;
			exportSave(zip.encode());
			zip.clear();
			return true;
		}

		private function getPNGBytes():ByteArray
		{
			if(_exportVO.enableBackgroundColor)
			{
				var bitmapData:BitmapData = new BitmapData(importModel.vo.textureAtlas.width, importModel.vo.textureAtlas.height, false, _exportVO.backgroundColor);
				bitmapData.draw(importModel.vo.textureAtlas);

				var byteArray:ByteArray = PNGEncoder.encode(bitmapData);
				bitmapData.dispose();
				return byteArray;
			}

			return PNGEncoder.encode(importModel.vo.textureAtlas);
		}

		private function exportSave(fileData:ByteArray):void
		{
			//缩放后的bitmapData需要dispose
			this.dispatchEvent(new ServiceEvent(IMPORT_TO_EXPORT_COMPLETE, [fileData, _exportVO, importModel.vo]));
		}
	}
}

add code to export

add code to export


package org.mangui.chromeless {

    import org.mangui.HLS.parsing.Level;
    import org.mangui.HLS.*;
    import flash.display.*;
    import flash.events.*;
    import flash.external.ExternalInterface;
    import flash.geom.Rectangle;
    import flash.media.Video;
    import flash.media.SoundTransform;
    import flash.media.StageVideo;
    import flash.media.StageVideoAvailability;
    import flash.utils.setTimeout;

    public class ChromelessPlayer extends Sprite {

        /** reference to the framework. **/
        private var _hls:HLS;
        /** Sheet to place on top of the video. **/
        private var _sheet:Sprite;
        /** Reference to the video element. **/
        private var _video:StageVideo;
        /** current media position */
        private var _media_position:Number;

        /** Initialization. **/
        public function ChromelessPlayer():void {
            // Set stage properties
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.fullScreenSourceRect = new Rectangle(0,0,stage.stageWidth,stage.stageHeight);
            stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState);
            // Draw sheet for catching clicks
            _sheet = new Sprite();
            _sheet.graphics.beginFill(0x000000,0);
            _sheet.graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight);
            _sheet.addEventListener(MouseEvent.CLICK,_clickHandler);
            _sheet.buttonMode = true;
            addChild(_sheet);
            // Connect getters to JS.
            ExternalInterface.addCallback("getLevel",_getLevel);
            ExternalInterface.addCallback("getLevels",_getLevels);
            ExternalInterface.addCallback("getMetrics",_getMetrics);
            ExternalInterface.addCallback("getPosition",_getPosition);
            ExternalInterface.addCallback("getState",_getState);
            ExternalInterface.addCallback("getType",_getType);
            ExternalInterface.addCallback("getmaxBufferLength",_getmaxBufferLength);
            ExternalInterface.addCallback("getbufferLength",_getbufferLength);
            // Connect calls to JS.
            ExternalInterface.addCallback("load",_load);
            ExternalInterface.addCallback("play",_play);
            ExternalInterface.addCallback("pause",_pause);
            ExternalInterface.addCallback("resume",_resume);
            ExternalInterface.addCallback("seek",_seek);
            ExternalInterface.addCallback("stop",_stop);
            ExternalInterface.addCallback("volume",_volume);
            ExternalInterface.addCallback("setLevel",_setLevel);
            ExternalInterface.addCallback("setmaxBufferLength",_setmaxBufferLength);
            setTimeout(_pingJavascript,50);
        };

        /** Notify javascript the framework is ready. **/
        private function _pingJavascript():void {
            ExternalInterface.call("onHLSReady",ExternalInterface.objectID);
        };

        /** Forward events from the framework. **/
        private function _completeHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onComplete");
            }
        };
        private function _errorHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onError",event.message);
            }
        };

        private function _fragmentHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onFragment",event.metrics.bandwidth,event.metrics.level,event.metrics.screenwidth);
            }
        };

        private function _manifestHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onManifest",event.levels[0].duration);
            }
        };
        private function _mediaTimeHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                _media_position = event.mediatime.position;
                ExternalInterface.call("onPosition",event.mediatime.position,event.mediatime.duration);
            }
        };
        private function _stateHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onState",event.state);
            }
        };
        private function _switchHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onSwitch",event.level);
            }
        };

        /** Javascript getters. **/
        private function _getLevel():Number { return _hls.getLevel(); };
        private function _getLevels():Vector.<Level> { return _hls.getLevels(); };
        private function _getMetrics():Object { return _hls.getMetrics(); };
        private function _getPosition():Number { return _hls.getPosition(); };
        private function _getState():String { return _hls.getState(); };
        private function _getType():String { return _hls.getType(); };
        private function _getbufferLength():Number { return _hls.getBufferLength(); };
        private function _getmaxBufferLength():Number { return _hls.maxBufferLength; };

        /** Javascript calls. **/
        private function _load(url:String):void { _hls.load(url); };
        private function _play():void { _hls.stream.play(); };
        private function _pause():void { _hls.stream.pause(); };
        private function _resume():void { _hls.stream.resume(); };
        private function _seek(position:Number):void { _hls.stream.seek(position); };
        private function _stop():void { _hls.stream.close(); };
        private function _volume(percent:Number):void { _hls.stream.soundTransform = new SoundTransform(percent/100);};
        private function _setLevel(level:Number):void { _hls.setPlaybackQuality(level); if (!isNaN(_media_position)) {_hls.stream.seek(_media_position);}};
        private function _setmaxBufferLength(new_len:Number):void { _hls.maxBufferLength = new_len;};

        /** Mouse click handler. **/
        private function _clickHandler(event:MouseEvent):void {
            if(stage.displayState == StageDisplayState.FULL_SCREEN_INTERACTIVE || stage.displayState==StageDisplayState.FULL_SCREEN) {
                stage.displayState = StageDisplayState.NORMAL;
            } else {
                stage.displayState = StageDisplayState.FULL_SCREEN;
            }
            _hls.setWidth(stage.stageWidth);
        };

        /** StageVideo detector. **/
        private function _onStageVideoState(event:StageVideoAvailabilityEvent):void {
            var available:Boolean = (event.availability == StageVideoAvailability.AVAILABLE);
            if (available && stage.stageVideos.length > 0) {
              _video = stage.stageVideos[0];
              _video.viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
              _hls = new HLS();
              _video.attachNetStream(_hls.stream);
            } else {
              var video:Video = new Video(stage.stageWidth, stage.stageHeight);
              addChild(video);
              _hls = new HLS();
              video.smoothing = true;
              video.attachNetStream(_hls.stream);
            }
            _hls.setWidth(stage.stageWidth);
            _hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE,_completeHandler);
            _hls.addEventListener(HLSEvent.ERROR,_errorHandler);
            _hls.addEventListener(HLSEvent.FRAGMENT_LOADED,_fragmentHandler);
            _hls.addEventListener(HLSEvent.MANIFEST_LOADED,_manifestHandler);
            _hls.addEventListener(HLSEvent.MEDIA_TIME,_mediaTimeHandler);
            _hls.addEventListener(HLSEvent.STATE,_stateHandler);
            _hls.addEventListener(HLSEvent.QUALITY_SWITCH,_switchHandler);
        };

    }

}

package org.mangui.chromeless {

    import org.mangui.HLS.parsing.Level;
    import org.mangui.HLS.*;
    import flash.display.*;
    import flash.events.*;
    import flash.external.ExternalInterface;
    import flash.geom.Rectangle;
    import flash.media.Video;
    import flash.media.SoundTransform;
    import flash.media.StageVideo;
    import flash.media.StageVideoAvailability;
    import flash.utils.setTimeout;

    public class ChromelessPlayer extends Sprite {

        /** reference to the framework. **/
        private var _hls:HLS;
        /** Sheet to place on top of the video. **/
        private var _sheet:Sprite;
        /** Reference to the video element. **/
        private var _video:StageVideo;
        /** current media position */
        private var _media_position:Number;

        /** Initialization. **/
        public function ChromelessPlayer():void {
            // Set stage properties
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.fullScreenSourceRect = new Rectangle(0,0,stage.stageWidth,stage.stageHeight);
            stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, _onStageVideoState);
            // Draw sheet for catching clicks
            _sheet = new Sprite();
            _sheet.graphics.beginFill(0x000000,0);
            _sheet.graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight);
            _sheet.addEventListener(MouseEvent.CLICK,_clickHandler);
            _sheet.buttonMode = true;
            addChild(_sheet);
            // Connect getters to JS.
            ExternalInterface.addCallback("getLevel",_getLevel);
            ExternalInterface.addCallback("getLevels",_getLevels);
            ExternalInterface.addCallback("getMetrics",_getMetrics);
            ExternalInterface.addCallback("getPosition",_getPosition);
            ExternalInterface.addCallback("getState",_getState);
            ExternalInterface.addCallback("getType",_getType);
            ExternalInterface.addCallback("getmaxBufferLength",_getmaxBufferLength);
            ExternalInterface.addCallback("getbufferLength",_getbufferLength);
            // Connect calls to JS.
            ExternalInterface.addCallback("playerLoad",_load);
            ExternalInterface.addCallback("playerPlay",_play);
            ExternalInterface.addCallback("playerPause",_pause);
            ExternalInterface.addCallback("playerResume",_resume);
            ExternalInterface.addCallback("playerSeek",_seek);
            ExternalInterface.addCallback("playerStop",_stop);
            ExternalInterface.addCallback("playerVolume",_volume);
            ExternalInterface.addCallback("playerSetLevel",_setLevel);
            ExternalInterface.addCallback("playerSetmaxBufferLength",_setmaxBufferLength);
            setTimeout(_pingJavascript,50);
        };

        /** Notify javascript the framework is ready. **/
        private function _pingJavascript():void {
            ExternalInterface.call("onHLSReady",ExternalInterface.objectID);
        };

        /** Forward events from the framework. **/
        private function _completeHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onComplete");
            }
        };
        private function _errorHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onError",event.message);
            }
        };

        private function _fragmentHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onFragment",event.metrics.bandwidth,event.metrics.level,event.metrics.screenwidth);
            }
        };

        private function _manifestHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onManifest",event.levels[0].duration);
            }
        };
        private function _mediaTimeHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                _media_position = event.mediatime.position;
                ExternalInterface.call("onPosition",event.mediatime.position,event.mediatime.duration);
            }
        };
        private function _stateHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onState",event.state);
            }
        };
        private function _switchHandler(event:HLSEvent):void {
            if (ExternalInterface.available) {
                ExternalInterface.call("onSwitch",event.level);
            }
        };

        /** Javascript getters. **/
        private function _getLevel():Number { return _hls.getLevel(); };
        private function _getLevels():Vector.<Level> { return _hls.getLevels(); };
        private function _getMetrics():Object { return _hls.getMetrics(); };
        private function _getPosition():Number { return _hls.getPosition(); };
        private function _getState():String { return _hls.getState(); };
        private function _getType():String { return _hls.getType(); };
        private function _getbufferLength():Number { return _hls.getBufferLength(); };
        private function _getmaxBufferLength():Number { return _hls.maxBufferLength; };

        /** Javascript calls. **/
        private function _load(url:String):void { _hls.load(url); };
        private function _play():void { _hls.stream.play(); };
        private function _pause():void { _hls.stream.pause(); };
        private function _resume():void { _hls.stream.resume(); };
        private function _seek(position:Number):void { _hls.stream.seek(position); };
        private function _stop():void { _hls.stream.close(); };
        private function _volume(percent:Number):void { _hls.stream.soundTransform = new SoundTransform(percent/100);};
        private function _setLevel(level:Number):void { _hls.setPlaybackQuality(level); if (!isNaN(_media_position)) {_hls.stream.seek(_media_position);}};
        private function _setmaxBufferLength(new_len:Number):void { _hls.maxBufferLength = new_len;};

        /** Mouse click handler. **/
        private function _clickHandler(event:MouseEvent):void {
            if(stage.displayState == StageDisplayState.FULL_SCREEN_INTERACTIVE || stage.displayState==StageDisplayState.FULL_SCREEN) {
                stage.displayState = StageDisplayState.NORMAL;
            } else {
                stage.displayState = StageDisplayState.FULL_SCREEN;
            }
            _hls.setWidth(stage.stageWidth);
        };

        /** StageVideo detector. **/
        private function _onStageVideoState(event:StageVideoAvailabilityEvent):void {
            var available:Boolean = (event.availability == StageVideoAvailability.AVAILABLE);
            if (available && stage.stageVideos.length > 0) {
              _video = stage.stageVideos[0];
              _video.viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
              _hls = new HLS();
              _video.attachNetStream(_hls.stream);
            } else {
              var video:Video = new Video(stage.stageWidth, stage.stageHeight);
              addChild(video);
              _hls = new HLS();
              video.smoothing = true;
              video.attachNetStream(_hls.stream);
            }
            _hls.setWidth(stage.stageWidth);
            _hls.addEventListener(HLSEvent.PLAYBACK_COMPLETE,_completeHandler);
            _hls.addEventListener(HLSEvent.ERROR,_errorHandler);
            _hls.addEventListener(HLSEvent.FRAGMENT_LOADED,_fragmentHandler);
            _hls.addEventListener(HLSEvent.MANIFEST_LOADED,_manifestHandler);
            _hls.addEventListener(HLSEvent.MEDIA_TIME,_mediaTimeHandler);
            _hls.addEventListener(HLSEvent.STATE,_stateHandler);
            _hls.addEventListener(HLSEvent.QUALITY_SWITCH,_switchHandler);
        };

    }

}

rename external interface callbacks.

rename external interface callbacks.

It seems that some function names are reserved in IE.
(cf: http://zaalabs.com/2011/01/adobe-flash-externalinterface-issues-with-internet-explorer/)
Prefix function names with player. But it will break backward compatibility with older version of player.


//
// Flump - Copyright 2012 Three Rings Design

package flump.xfl {

import flash.utils.Dictionary;

import flump.executor.load.LoadedSwf;
import flump.export.Atlas;
import flump.mold.KeyframeMold;
import flump.mold.LayerMold;
import flump.mold.LibraryMold;
import flump.mold.MovieMold;

import com.threerings.util.Map;
import com.threerings.util.Maps;
import com.threerings.util.Set;
import com.threerings.util.Sets;

public class XflLibrary
{
    /**
     * When an exported movie contains an unexported movie, it gets assigned a generated symbol
     * name with this prefix.
     */
    public static const IMPLICIT_PREFIX :String = "~";

    public var swf :LoadedSwf;

    public var frameRate :Number;

    // The MD5 of the published library SWF
    public var md5 :String;

    public var location :String;

    public const movies :Vector.<MovieMold> = new Vector.<MovieMold>();
    public const textures :Vector.<XflTexture> = new Vector.<XflTexture>();

    public function XflLibrary(location :String) {
        this.location = location;
    }

    public function get (id :String, requiredType :Class=null) :* {
        const result :* = _ids[id];
        if (result === undefined) throw new Error("Unknown library item '" + id + "'");
        else if (requiredType != null) return requiredType(result);
        else return result;
    }

    public function isExported (movie :MovieMold) :Boolean {
        return _moldToSymbol.containsKey(movie);
    }

    public function get publishedMovies () :Vector.<MovieMold> {
        const result :Vector.<MovieMold> = new Vector.<MovieMold>();
        for each (var movie :MovieMold in _toPublish.toArray()) result.push(movie);
        return result;
    }

    public function finishLoading () :void {
        for each (var movie :MovieMold in movies) if (isExported(movie)) addToPublished(movie);
    }

    protected function addToPublished (movie :MovieMold) :void {
        if (!_toPublish.add(movie) || movie.flipbook) return;
        for each (var layer :LayerMold in movie.layers) {
            for each (var kf :KeyframeMold in layer.keyframes) {
                if (kf.ref == null) continue;
                kf.ref = _libraryNameToId.get(kf.ref);
                var item :Object = _ids[kf.ref];
                if (item == null) {
                    addTopLevelError(ParseError.CRIT,
                            "unrecognized library item '" + kf.ref + "'");
                } else if (item is MovieMold) addToPublished(MovieMold(item));
            }
        }
    }

    public function createId (mold :Object, libraryName :String, symbol :String) :String {
        if (symbol != null) _moldToSymbol.put(mold, symbol);
        const id :String = symbol == null ? IMPLICIT_PREFIX + libraryName : symbol;
        _libraryNameToId.put(libraryName, id);
        _ids[id] = mold;
        return id;
    }

    public function getErrors (sev :String=null) :Vector.<ParseError> {
        if (sev == null) return _errors;
        const sevOrdinal :int = ParseError.severityToOrdinal(sev);
        return _errors.filter(function (err :ParseError, ..._) :Boolean {
            return err.sevOrdinal >= sevOrdinal;
        });
    }

    public function get valid () :Boolean { return getErrors(ParseError.CRIT).length == 0; }

    public function addTopLevelError(severity :String, message :String, e :Object=null) :void {
        addError(location, severity, message, e);
    }

    public function addError(location :String, severity :String, message :String, e :Object=null) :void {
        _errors.push(new ParseError(location, severity, message, e));
    }

    public function toJSONString (atlases :Vector.<Atlas>, pretty :Boolean=false) :String {
        return JSON.stringify(toMold(atlases), null, pretty ? "  " : null);
    }

    public function toMold (atlases :Vector.<Atlas>) :LibraryMold {
        const mold :LibraryMold = new LibraryMold();
        mold.frameRate = frameRate;
        mold.md5 = md5;
        mold.movies = movies;
        for each (var atlas :Atlas in atlases) mold.atlases.push(atlas.toMold());
        return mold;
    }

    /** Object to symbol name for all exported textures and movies in the library */
    protected const _moldToSymbol :Map = Maps.newMapOf(Object);

    /** Library name to symbol or generated symbol for all textures and movies in the library */
    protected const _libraryNameToId :Map = Maps.newMapOf(String);

    /** Exported movies or movies used in exported movies. */
    protected const _toPublish :Set = Sets.newSetOf(MovieMold);

    /** Symbol or generated symbol to texture or movie. */
    protected const _ids :Dictionary = new Dictionary();

    protected const _errors :Vector.<ParseError> = new Vector.<ParseError>;
}
}

//
// Flump - Copyright 2012 Three Rings Design

package flump.xfl {

import flash.utils.Dictionary;

import flump.executor.load.LoadedSwf;
import flump.export.Atlas;
import flump.mold.KeyframeMold;
import flump.mold.LayerMold;
import flump.mold.LibraryMold;
import flump.mold.MovieMold;

import com.threerings.util.Map;
import com.threerings.util.Maps;
import com.threerings.util.Set;
import com.threerings.util.Sets;

public class XflLibrary
{
    /**
     * When an exported movie contains an unexported movie, it gets assigned a generated symbol
     * name with this prefix.
     */
    public static const IMPLICIT_PREFIX :String = "~";

    public var swf :LoadedSwf;

    public var frameRate :Number;

    // The MD5 of the published library SWF
    public var md5 :String;

    public var location :String;

    public const movies :Vector.<MovieMold> = new Vector.<MovieMold>();
    public const textures :Vector.<XflTexture> = new Vector.<XflTexture>();

    public function XflLibrary(location :String) {
        this.location = location;
    }

    public function get (id :String, requiredType :Class=null) :* {
        const result :* = _ids[id];
        if (result === undefined) throw new Error("Unknown library item '" + id + "'");
        else if (requiredType != null) return requiredType(result);
        else return result;
    }

    public function isExported (movie :MovieMold) :Boolean {
        return _moldToSymbol.containsKey(movie);
    }

    public function get publishedMovies () :Vector.<MovieMold> {
        const result :Vector.<MovieMold> = new Vector.<MovieMold>();
        for each (var movie :MovieMold in _toPublish.toArray().sortOn("id")) result.push(movie);
        return result;
    }

    public function finishLoading () :void {
        for each (var movie :MovieMold in movies) if (isExported(movie)) addToPublished(movie);
    }

    protected function addToPublished (movie :MovieMold) :void {
        if (!_toPublish.add(movie) || movie.flipbook) return;
        for each (var layer :LayerMold in movie.layers) {
            for each (var kf :KeyframeMold in layer.keyframes) {
                if (kf.ref == null) continue;
                kf.ref = _libraryNameToId.get(kf.ref);
                var item :Object = _ids[kf.ref];
                if (item == null) {
                    addTopLevelError(ParseError.CRIT,
                            "unrecognized library item '" + kf.ref + "'");
                } else if (item is MovieMold) addToPublished(MovieMold(item));
            }
        }
    }

    public function createId (mold :Object, libraryName :String, symbol :String) :String {
        if (symbol != null) _moldToSymbol.put(mold, symbol);
        const id :String = symbol == null ? IMPLICIT_PREFIX + libraryName : symbol;
        _libraryNameToId.put(libraryName, id);
        _ids[id] = mold;
        return id;
    }

    public function getErrors (sev :String=null) :Vector.<ParseError> {
        if (sev == null) return _errors;
        const sevOrdinal :int = ParseError.severityToOrdinal(sev);
        return _errors.filter(function (err :ParseError, ..._) :Boolean {
            return err.sevOrdinal >= sevOrdinal;
        });
    }

    public function get valid () :Boolean { return getErrors(ParseError.CRIT).length == 0; }

    public function addTopLevelError(severity :String, message :String, e :Object=null) :void {
        addError(location, severity, message, e);
    }

    public function addError(location :String, severity :String, message :String, e :Object=null) :void {
        _errors.push(new ParseError(location, severity, message, e));
    }

    public function toJSONString (atlases :Vector.<Atlas>, pretty :Boolean=false) :String {
        return JSON.stringify(toMold(atlases), null, pretty ? "  " : null);
    }

    public function toMold (atlases :Vector.<Atlas>) :LibraryMold {
        const mold :LibraryMold = new LibraryMold();
        mold.frameRate = frameRate;
        mold.md5 = md5;
        mold.movies = movies;
        for each (var atlas :Atlas in atlases) mold.atlases.push(atlas.toMold());
        return mold;
    }

    /** Object to symbol name for all exported textures and movies in the library */
    protected const _moldToSymbol :Map = Maps.newMapOf(Object);

    /** Library name to symbol or generated symbol for all textures and movies in the library */
    protected const _libraryNameToId :Map = Maps.newMapOf(String);

    /** Exported movies or movies used in exported movies. */
    protected const _toPublish :Set = Sets.newSetOf(MovieMold);

    /** Symbol or generated symbol to texture or movie. */
    protected const _ids :Dictionary = new Dictionary();

    protected const _errors :Vector.<ParseError> = new Vector.<ParseError>;
}
}

sort published movies by name so that our output is deterministic