package aerys.minko.type.loader.parser
{
	import aerys.minko.render.effect.Effect;
	import aerys.minko.type.loader.ILoader;
	import aerys.minko.type.loader.SceneLoader;
	import aerys.minko.type.loader.TextureLoader;

	import flash.net.URLRequest;

	/**
	 * ParserOptions objects provide properties and function references
	 * to customize how a LoaderGroup object will load and interpret
	 * content.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class ParserOptions
	{
		private var _loadDependencies			: Boolean	= false;
		private var _dependencyLoaderClosure	: Function	= defaultDependencyLoaderClosure;
		private var _mipmapTextures				: Boolean	= true;
		private var _meshEffect					: Effect	= null;
		private var _vertexStreamUsage			: uint		= 0;
		private var _indexStreamUsage			: uint		= 0;
		private var _parser						: Class		= null;

		public function get parser():Class
		{
			return _parser;
		}

		public function set parser(value:Class):void
		{
			_parser = value;
		}

		public function get indexStreamUsage():uint
		{
			return _indexStreamUsage;
		}

		public function set indexStreamUsage(value:uint):void
		{
			_indexStreamUsage = value;
		}

		public function get vertexStreamUsage():uint
		{
			return _vertexStreamUsage;
		}

		public function set vertexStreamUsage(value:uint):void
		{
			_vertexStreamUsage = value;
		}

		public function get effect():Effect
		{
			return _meshEffect;
		}

		public function set effect(value:Effect):void
		{
			_meshEffect = value;
		}

		public function get mipmapTextures():Boolean
		{
			return _mipmapTextures;
		}

		public function set mipmapTextures(value:Boolean):void
		{
			_mipmapTextures = value;
		}

		public function get dependencyLoaderClosure():Function
		{
			return _dependencyLoaderClosure;
		}

		public function set dependencyLoaderClosure(value:Function):void
		{
			_dependencyLoaderClosure = value;
		}

		public function get loadDependencies():Boolean
		{
			return _loadDependencies;
		}

		public function set loadDependencies(value:Boolean):void
		{
			_loadDependencies = value;
		}

		public function clone(): ParserOptions
		{
			return new ParserOptions(
				_loadDependencies,
				_dependencyLoaderClosure,
				_mipmapTextures,
				_meshEffect,
				_vertexStreamUsage,
				_indexStreamUsage,
				_parser
			);
		}

		public function ParserOptions(loadDependencies			: Boolean = false,
									  dependencyLoaderClosure	: Function = null,
									  mipmapTextures			: Boolean = true,
									  meshEffect				: Effect = null,
									  vertexStreamUsage			: uint = 0,
									  indexStreamUsage			: uint = 0,
									  parser					: Class = null)
		{
			_loadDependencies			= loadDependencies;
			_dependencyLoaderClosure	= dependencyLoaderClosure || _dependencyLoaderClosure;
			_mipmapTextures				= mipmapTextures;
			_meshEffect					= meshEffect;
			_vertexStreamUsage			= vertexStreamUsage;
			_indexStreamUsage			= indexStreamUsage;
			_parser						= parser;
		}

		private function defaultDependencyLoaderClosure(dependencyPath	: String,
														isTexture		: Boolean,
														options			: ParserOptions) : ILoader
		{
			var loader : ILoader;

			if (isTexture)
			{
				loader = new TextureLoader(true);
				loader.load(new URLRequest(dependencyPath));
			}
			else
			{
				loader = new SceneLoader(options);
				loader.load(new URLRequest(dependencyPath));
			}

			return loader;
		}
	}
}

package aerys.minko.type.loader.parser
{
	import aerys.minko.render.effect.Effect;
	import aerys.minko.type.loader.ILoader;
	import aerys.minko.type.loader.SceneLoader;
	import aerys.minko.type.loader.TextureLoader;

	import flash.net.URLRequest;

	/**
	 * ParserOptions objects provide properties and function references
	 * to customize how a LoaderGroup object will load and interpret
	 * content.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class ParserOptions
	{
		private var _loadDependencies			: Boolean	= false;
        private var _loadSkin                   : Boolean   = false;
		private var _dependencyLoaderClosure	: Function	= defaultDependencyLoaderClosure;
		private var _mipmapTextures				: Boolean	= true;
		private var _meshEffect					: Effect	= null;
		private var _vertexStreamUsage			: uint		= 0;
		private var _indexStreamUsage			: uint		= 0;
		private var _parser						: Class		= null;

		public function get parser():Class
		{
			return _parser;
		}

		public function set parser(value:Class):void
		{
			_parser = value;
		}

		public function get indexStreamUsage():uint
		{
			return _indexStreamUsage;
		}

		public function set indexStreamUsage(value:uint):void
		{
			_indexStreamUsage = value;
		}

		public function get vertexStreamUsage():uint
		{
			return _vertexStreamUsage;
		}

		public function set vertexStreamUsage(value:uint):void
		{
			_vertexStreamUsage = value;
		}

		public function get effect():Effect
		{
			return _meshEffect;
		}

		public function set effect(value:Effect):void
		{
			_meshEffect = value;
		}

		public function get mipmapTextures():Boolean
		{
			return _mipmapTextures;
		}

		public function set mipmapTextures(value:Boolean):void
		{
			_mipmapTextures = value;
		}

		public function get dependencyLoaderClosure():Function
		{
			return _dependencyLoaderClosure;
		}

		public function set dependencyLoaderClosure(value:Function):void
		{
			_dependencyLoaderClosure = value;
		}

		public function get loadDependencies():Boolean
		{
			return _loadDependencies;
		}

		public function set loadDependencies(value:Boolean):void
		{
			_loadDependencies = value;
		}

        public function get loadSkin():Boolean
        {
            return _loadSkin;
        }

        public function set loadSkin(value:Boolean):void
        {
            _loadSkin = value;
        }

		public function clone(): ParserOptions
		{
			var parserOptions : ParserOptions = new ParserOptions(
				_loadDependencies,
				_dependencyLoaderClosure,
				_mipmapTextures,
				_meshEffect,
				_vertexStreamUsage,
				_indexStreamUsage,
				_parser
			);
            parserOptions.loadSkin = _loadSkin;
            return parserOptions;
		}

		public function ParserOptions(loadDependencies			: Boolean = false,
									  dependencyLoaderClosure	: Function = null,
									  mipmapTextures			: Boolean = true,
									  meshEffect				: Effect = null,
									  vertexStreamUsage			: uint = 0,
									  indexStreamUsage			: uint = 0,
									  parser					: Class = null)
		{
			_loadDependencies			= loadDependencies;
			_dependencyLoaderClosure	= dependencyLoaderClosure || _dependencyLoaderClosure;
			_mipmapTextures				= mipmapTextures;
			_meshEffect					= meshEffect;
			_vertexStreamUsage			= vertexStreamUsage;
			_indexStreamUsage			= indexStreamUsage;
			_parser						= parser;
		}

		private function defaultDependencyLoaderClosure(dependencyPath	: String,
														isTexture		: Boolean,
														options			: ParserOptions) : ILoader
		{
			var loader : ILoader;

			if (isTexture)
			{
				loader = new TextureLoader(true);
				loader.load(new URLRequest(dependencyPath));
			}
			else
			{
				loader = new SceneLoader(options);
				loader.load(new URLRequest(dependencyPath));
			}

			return loader;
		}

    }
}

Add loadSkin option in ParserOptions

Add loadSkin option in ParserOptions


//
// $Id$

package com.threerings.util {

import flash.display.Loader;
import flash.display.LoaderInfo;

import flash.events.AsyncErrorEvent;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;

import flash.net.URLRequest;

import flash.system.ApplicationDomain;
import flash.system.LoaderContext;

import flash.utils.ByteArray;
import flash.utils.Dictionary;

/**
 * Easy loader for many things, including managing multiple downloads.
 * More documentation coming.
 */
public class MultiLoader
{
    /**
     * Load one or more sources and return DisplayObjects.
     *
     * @param sources an Array, Dictionary, or Object containing sources as values, or a single
     * source value. The sources may be Strings (representing urls), URLRequests, ByteArrays,
     * or a Class that can be instantiated to become a URLRequest or ByteArray. Note that
     * the format of your sources Object dictates the format of the return Object.
     * @param completeCallback the function to call when complete. The signature should be:
     * function (value :Object) :void. Note that the structure of the return Object
     * is dictated by the sources parameter. If you pass in an Array, you get your results
     * in an Array. If you use a Dictionary or Object, the results will be returned as the same,
     * with the same keys used in sources now pointing to the results. If your sources parameter
     * was just a single source (like a String) then the result will just be a single result,
     * like a DisplayObject. Each result will be a DisplayObject or an Error
     * describing the problem.
     * @param forEach if true, each value or error will be returned as soon as possible. The values
     * or errors will be returned directly to the completeCallback. Any keys are lost, so you
     * probably only want to use this with an Array sources.
     * @param appDom the ApplicationDomain in which to load the contents, or null to specify
     * that it should load in a child of the current ApplicationDomain.
     *
     * @example Load one embed, add it as a child.
     * <listing version="3.0">
     * MultiLoader.getContents(EMBED_CONSTANT, addChild);
     * </listing>
     *
     * @example Load 3 embeds, add them as children.
     * <listing version="3.0">
     * MultiLoader.getContents([EMBED1, EMBED2, EMBED3], addChild, true);
     * </listing>
     *
     * @example Load multiple URLs, have the contents returned to the result function one at
     * a time.
     * <listing version="3.0">
     * function handleComplete (result :Object) :void {
     *    // process a result here. Result may be a DisplayObject or an Error.
     * };
     *
     * var obj :Object = {
     *     key1: "http://somehost.com/someImage.gif",
     *     key2: "http://somehost.com/someOtherImage.gif"
     * };
     *
     * MultiLoader.getContents(obj, handleComplete, true);
     * </listing>
     *
     * @example Load 3 embeds, wait to handle them until they're all loaded.
     * <listing version="3.0">
     * function handleComplete (results :Array) :void {
     *     // process results here
     * };
     *
     * MultiLoader.getContents([EMBED1, EMBED2, EMBED3], handleComplete);
     * </listing>
     */
    public static function getContents (
        sources :Object, completeCallback :Function, forEach :Boolean = false,
        appDom :ApplicationDomain = null) :void
    {
        var complete :Function = function (retval :Object) :void {
            completeCallback(processProperty(retval, Loader, "content"));
        };
        getLoaders(sources, complete, forEach, appDom);
    }

    /**
     * Exactly like getContents() only it returns the Loader objects rather than their contents.
     *
     * @example Advanced usage: Loading classes.
     * <listing version="3.0">
     * // A holder for new classes, created as a child of the system domain.
     * var appDom :ApplicationDomain = new ApplicationDomain(null);
     *

     * function handleComplete (results :Object) :void {
     *     // now we can retrieve classes
     *     var clazz :Class = appDom.getDefinition("com.package.SomeClass") as Class;
     * }
     *

     * // load all the classes contained in the specified sources
     * MultiLoader.getLoaders([EMBED, "http://site.com/pack.swf"], handleComplete, false, appDom);
     *

     * [Embed(source="resource.swf", mimeType="application/octet-stream")]
     * private static const EMBED :Class;
     * </listing>
     *
     * @see getContents()
     */
    public static function getLoaders (
        sources :Object, completeCallback :Function, forEach :Boolean = false,
        appDom :ApplicationDomain = null) :void
    {
        var generator :Function = function (source :*) :Object {
            // first transform common sources to their more useful nature
            if (source is String) {
                source = new URLRequest(String(source));
            } else if (source is Class) {
                // it's probably a ByteArray from an Embed, but don't cast it
                source = new (source as Class)();
            }
            var l :Loader = new Loader();
            var lc :LoaderContext = new LoaderContext(false, appDom);
            // now we only need handle the two cases
            if (source is URLRequest) {
                l.load(URLRequest(source), lc);
            } else if (source is ByteArray) {
                l.loadBytes(ByteArray(source), lc);
            } else {
                return new Error("Unknown source: " + source);
            }
            return l.contentLoaderInfo;
        };

        var complete :Function = function (retval :Object) :void {
            completeCallback(processProperty(retval, LoaderInfo, "loader"));
        };

        new MultiLoader(sources, generator, complete, forEach);
    }

    /**
     * Coordinate loading some asynchronous objects.
     *
     * @param sources An Array, Dictionary, or Object of sources, or just a single source.
     * @param generatorFunciton a function to call to generate the loaders.
     * @param completeCallack the function to call when complete.
     * @param forEach whether to call the completeCallback for each source, or all-at-once at
     * the end. If forEach is used, keys will never be returned.
     * @param isCompleteCheckFn a function to attempt to call on the dispatcher to see if
     * it's already complete after generation.
     * @param errorTypes an Array of event types that will be dispatched by the loader.
     * If unspecifed, all the normal error event types are used.
     * @param completeType, the event complete type. If unspecifed @default Event.COMPLETE.
     */
    public function MultiLoader (
        sources :Object, generatorFn :Function, completeCallback :Function,
        forEach :Boolean = false, isCompleteCheckFn :String = null,
        errorTypes :Array = null, completeType :String = null)
    {
        if (errorTypes == null) {
            errorTypes = [ ErrorEvent.ERROR, AsyncErrorEvent.ASYNC_ERROR,
                IOErrorEvent.IO_ERROR, SecurityErrorEvent.SECURITY_ERROR ];
        }
        if (completeType == null) {
            completeType = Event.COMPLETE;
        }

        _complete = completeCallback;
        _forEach = forEach;

        var endCheckKey :* = null;
        if (sources is Array) {
            _result = new Array();

        } else if (sources is Dictionary) {
            _result = new Dictionary();

        } else {
            _result = new Object();
            if (!Util.isPlainObject(sources)) {
                // stash the singleton source
                sources = { singleton_key: sources };
                endCheckKey = "singleton_key";
            }
        }

        for (var key :* in sources) {
            var sourceVal :Object = sources[key];
            var val :Object;
            try {
                val = (sourceVal is Array) ? generatorFn.apply(null, sourceVal as Array)
                                           : generatorFn(sourceVal);
            } catch (err :Error) {
                val = err;
            }
            _result[key] = val;
            if ((val is IEventDispatcher) &&
                    (isCompleteCheckFn == null || !val[isCompleteCheckFn]())) {
                var ed :IEventDispatcher = IEventDispatcher(val);
                _remaining++;
                _targetsToKeys[ed] = key;
                ed.addEventListener(completeType, handleComplete);
                for each (var type :String in errorTypes) {
                    ed.addEventListener(type, handleError);
                }
            } else if (_forEach) {
                checkReport(key);
            }
        }

        if (!_forEach) {
            checkReport(endCheckKey);
        }

        // if we're not done at this point, keep a reference to this loader
        if (_remaining > 0) {
            _activeMultiLoaders[this] = true;
        }
    }

    protected function handleError (event :ErrorEvent) :void
    {
        _result[_targetsToKeys[event.target]] = new Error(event.text) // , event.errorID); ???
        handleComplete(event); // the rest is the same as complete
    }

    protected function handleComplete (event :Event) :void
    {
        _remaining--;
        checkReport(_targetsToKeys[event.target]);
    }

    protected function checkReport (key :*) :void
    {
        if (!_forEach && _remaining > 0) {
            return;
        }

        var thisResult :Object = (_forEach || (key === "singleton_key")) ? _result[key] : _result;
        try {
            _complete(thisResult);
        } catch (err :Error) {
            trace("MultiLoader: Error calling completeCallback [result=" + thisResult + "].");
            trace("Cause: " + err.getStackTrace());
        }

        if (_forEach) {
            delete _result[key]; // free the loaded object to assist gc
        }

        // If we're all done, remove the static reference to this loader.
        // Note that this could be called in for-each mode if we haven't yet started to load
        // something asynchronously but have come across an Error or an already-completed load.
        // That's ok, as this will just end up deleting a reference that doesn't exist, and if
        // necessary the reference will still be added at the end of the constructor.
        if (_remaining == 0) {
            delete _activeMultiLoaders[this];
        }
    }

    /**
     * Utility method used in this class.
     */
    protected static function processProperty (
        retval :Object, testClass :Class, prop :String) :Object
    {
        if (retval is testClass) {
            retval = retval[prop];
        } else {
            for (var key :* in retval) {
                var o :Object = retval[key];
                if (o is testClass) {
                    retval[key] = o[prop];
                } // else keep it the same
            }
        }
        return retval;
    }

    protected var _complete :Function;

    protected var _result :Object;

    protected var _targetsToKeys :Dictionary = new Dictionary(true);

    protected var _forEach :Boolean;

    protected var _remaining :int = 0;

    protected static const _activeMultiLoaders :Dictionary = new Dictionary();
}
}

//
// $Id$

package com.threerings.util {

import flash.display.Loader;
import flash.display.LoaderInfo;

import flash.events.AsyncErrorEvent;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;

import flash.net.URLRequest;

import flash.system.ApplicationDomain;
import flash.system.LoaderContext;

import flash.utils.ByteArray;
import flash.utils.Dictionary;

/**
 * Easy loader for many things, including managing multiple downloads.
 * More documentation coming.
 */
public class MultiLoader
{
    /**
     * Load one or more sources and return DisplayObjects.
     *
     * @param sources an Array, Dictionary, or Object containing sources as values, or a single
     * source value. The sources may be Strings (representing urls), URLRequests, ByteArrays,
     * or a Class that can be instantiated to become a URLRequest or ByteArray. Note that
     * the format of your sources Object dictates the format of the return Object.
     * @param completeCallback the function to call when complete. The signature should be:
     * function (value :Object) :void. Note that the structure of the return Object
     * is dictated by the sources parameter. If you pass in an Array, you get your results
     * in an Array. If you use a Dictionary or Object, the results will be returned as the same,
     * with the same keys used in sources now pointing to the results. If your sources parameter
     * was just a single source (like a String) then the result will just be a single result,
     * like a DisplayObject. Each result will be a DisplayObject or an Error
     * describing the problem.
     * @param forEach if true, each value or error will be returned as soon as possible. The values
     * or errors will be returned directly to the completeCallback. Any keys are lost, so you
     * probably only want to use this with an Array sources.
     * @param appDom the ApplicationDomain in which to load the contents, or null to specify
     * that it should load in a child of the current ApplicationDomain.
     *
     * @example Load one embed, add it as a child.
     * <listing version="3.0">
     * MultiLoader.getContents(EMBED_CONSTANT, addChild);
     * </listing>
     *
     * @example Load 3 embeds, add them as children.
     * <listing version="3.0">
     * MultiLoader.getContents([EMBED1, EMBED2, EMBED3], addChild, true);
     * </listing>
     *
     * @example Load multiple URLs, have the contents returned to the result function one at
     * a time.
     * <listing version="3.0">
     * function handleComplete (result :Object) :void {
     *    // process a result here. Result may be a DisplayObject or an Error.
     * };
     *
     * var obj :Object = {
     *     key1: "http://somehost.com/someImage.gif",
     *     key2: "http://somehost.com/someOtherImage.gif"
     * };
     *
     * MultiLoader.getContents(obj, handleComplete, true);
     * </listing>
     *
     * @example Load 3 embeds, wait to handle them until they're all loaded.
     * <listing version="3.0">
     * function handleComplete (results :Array) :void {
     *     // process results here
     * };
     *
     * MultiLoader.getContents([EMBED1, EMBED2, EMBED3], handleComplete);
     * </listing>
     */
    public static function getContents (
        sources :Object, completeCallback :Function, forEach :Boolean = false,
        appDom :ApplicationDomain = null) :void
    {
        var complete :Function = function (retval :Object) :void {
            completeCallback(processProperty(retval, Loader, "content"));
        };
        getLoaders(sources, complete, forEach, appDom);
    }

    /**
     * Exactly like getContents() only it returns the Loader objects rather than their contents.
     *
     * @example Advanced usage: Loading classes.
     * <listing version="3.0">
     * // A holder for new classes, created as a child of the system domain.
     * var appDom :ApplicationDomain = new ApplicationDomain(null);
     *

     * function handleComplete (results :Object) :void {
     *     // now we can retrieve classes
     *     var clazz :Class = appDom.getDefinition("com.package.SomeClass") as Class;
     * }
     *

     * // load all the classes contained in the specified sources
     * MultiLoader.getLoaders([EMBED, "http://site.com/pack.swf"], handleComplete, false, appDom);
     *

     * [Embed(source="resource.swf", mimeType="application/octet-stream")]
     * private static const EMBED :Class;
     * </listing>
     *
     * @see getContents()
     */
    public static function getLoaders (
        sources :Object, completeCallback :Function, forEach :Boolean = false,
        appDom :ApplicationDomain = null) :void
    {
        var generator :Function = function (source :*) :Object {
            // first transform common sources to their more useful nature
            if (source is String) {
                source = new URLRequest(String(source));
            } else if (source is Class) {
                // it's probably a ByteArray from an Embed, but don't cast it
                source = new (source as Class)();
            }
            var l :Loader = new Loader();
            var lc :LoaderContext = new LoaderContext(false, appDom);
            // now we only need handle the two cases
            if (source is URLRequest) {
                l.load(URLRequest(source), lc);
            } else if (source is ByteArray) {
                l.loadBytes(ByteArray(source), lc);
            } else {
                return new Error("Unknown source: " + source);
            }
            return l.contentLoaderInfo;
        };

        var complete :Function = function (retval :Object) :void {
            completeCallback(processProperty(retval, LoaderInfo, "loader"));
        };

        new MultiLoader(sources, complete, generator, forEach);
    }

    /**
     * Coordinate loading some asynchronous objects.
     *
     * @param sources An Array, Dictionary, or Object of sources, or just a single source.
     * @param completeCallack the function to call when complete.
     * @param generatorFunciton a function to call to generate the IEventDispatchers, or
     * null if the source values are already ready to go.
     * @param forEach whether to call the completeCallback for each source, or all-at-once at
     * the end. If forEach is used, keys will never be returned.
     * @param isCompleteCheckFn a function to attempt to call on the dispatcher to see if
     * it's already complete after generation.
     * @param errorTypes an Array of event types that will be dispatched by the loader.
     * If unspecifed, all the normal error event types are used.
     * @param completeType, the event complete type. If unspecifed @default Event.COMPLETE.
     */
    public function MultiLoader (
        sources :Object, completeCallback :Function, generatorFn :Function = null,
        forEach :Boolean = false, isCompleteCheckFn :String = null,
        errorTypes :Array = null, completeType :String = null)
    {
        if (errorTypes == null) {
            errorTypes = [ ErrorEvent.ERROR, AsyncErrorEvent.ASYNC_ERROR,
                IOErrorEvent.IO_ERROR, SecurityErrorEvent.SECURITY_ERROR ];
        }
        if (completeType == null) {
            completeType = Event.COMPLETE;
        }

        _complete = completeCallback;
        _forEach = forEach;

        var endCheckKey :* = null;
        if (sources is Array) {
            _result = new Array();

        } else if (sources is Dictionary) {
            _result = new Dictionary();

        } else {
            _result = new Object();
            if (!Util.isPlainObject(sources)) {
                // stash the singleton source
                sources = { singleton_key: sources };
                endCheckKey = "singleton_key";
            }
        }

        for (var key :* in sources) {
            var val :Object = sources[key];
            if (generatorFn != null) {
                try {
                    val = (val is Array) ? generatorFn.apply(null, val as Array)
                                         : generatorFn(val);
                } catch (err :Error) {
                    val = err;
                }
            }
            _result[key] = val;
            if ((val is IEventDispatcher) &&
                    (isCompleteCheckFn == null || !val[isCompleteCheckFn]())) {
                var ed :IEventDispatcher = IEventDispatcher(val);
                _remaining++;
                _targetsToKeys[ed] = key;
                ed.addEventListener(completeType, handleComplete);
                for each (var type :String in errorTypes) {
                    ed.addEventListener(type, handleError);
                }
            } else if (_forEach) {
                checkReport(key);
            }
        }

        if (!_forEach) {
            checkReport(endCheckKey);
        }

        // if we're not done at this point, keep a reference to this loader
        if (_remaining > 0) {
            _activeMultiLoaders[this] = true;
        }
    }

    protected function handleError (event :ErrorEvent) :void
    {
        _result[_targetsToKeys[event.target]] = new Error(event.text) // , event.errorID); ???
        handleComplete(event); // the rest is the same as complete
    }

    protected function handleComplete (event :Event) :void
    {
        _remaining--;
        checkReport(_targetsToKeys[event.target]);
    }

    protected function checkReport (key :*) :void
    {
        if (!_forEach && _remaining > 0) {
            return;
        }

        var thisResult :Object = (_forEach || (key === "singleton_key")) ? _result[key] : _result;
        try {
            _complete(thisResult);
        } catch (err :Error) {
            trace("MultiLoader: Error calling completeCallback [result=" + thisResult + "].");
            trace("Cause: " + err.getStackTrace());
        }

        if (_forEach) {
            delete _result[key]; // free the loaded object to assist gc
        }

        // If we're all done, remove the static reference to this loader.
        // Note that this could be called in for-each mode if we haven't yet started to load
        // something asynchronously but have come across an Error or an already-completed load.
        // That's ok, as this will just end up deleting a reference that doesn't exist, and if
        // necessary the reference will still be added at the end of the constructor.
        if (_remaining == 0) {
            delete _activeMultiLoaders[this];
        }
    }

    /**
     * Utility method used in this class.
     */
    protected static function processProperty (
        retval :Object, testClass :Class, prop :String) :Object
    {
        if (retval is testClass) {
            retval = retval[prop];
        } else {
            for (var key :* in retval) {
                var o :Object = retval[key];
                if (o is testClass) {
                    retval[key] = o[prop];
                } // else keep it the same
            }
        }
        return retval;
    }

    protected var _complete :Function;

    protected var _result :Object;

    protected var _targetsToKeys :Dictionary = new Dictionary(true);

    protected var _forEach :Boolean;

    protected var _remaining :int = 0;

    protected static const _activeMultiLoaders :Dictionary = new Dictionary();
}
}

Swap the order of the generatorFn and callbackFn args.
Made the generatorFn optional, in case the source values are already ready-to-go,
although 99% of the time it's going to be better to use the generator function
to get things ready to go because MultiLoader will take care of figuring out
the sources structure.

git-svn-id: a1a4b28b82a3276cc491891159dd9963a0a72fae@5022 542714f4-19e9-0310-aa3c-eee0fc999fb1


package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.render.Effect;
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.controller.scene.RenderingController;
	import aerys.minko.scene.node.camera.AbstractCamera;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.DataProvider;
	import aerys.minko.type.enum.DataProviderUsage;

	import flash.display.BitmapData;
	import flash.utils.getTimer;

	/**
	 * Scene objects are the root of any 3D scene.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class Scene extends Group
	{
		use namespace minko_scene;

		minko_scene var _camera		: AbstractCamera;

		private var _renderingCtrl	: RenderingController;

		private var _properties		: DataProvider;
		private var _bindings		: DataBindings;

		private var _enterFrame		: Signal;
		private var _exitFrame		: Signal;

		public function get activeCamera() : AbstractCamera
		{
			return _camera;
		}

		public function get numPasses() : uint
		{
			return _renderingCtrl.numPasses;
		}

		public function get numTriangles() : uint
		{
			return _renderingCtrl.numTriangles;
		}

		public function get postProcessingEffect() : Effect
		{
			return _renderingCtrl.postProcessingEffect;
		}
		public function set postProcessingEffect(value : Effect) : void
		{
			_renderingCtrl.postProcessingEffect = value;
		}

		public function get postProcessingProperties() : DataProvider
		{
			return _renderingCtrl.postProcessingProperties;
		}

		public function get properties() : DataProvider
		{
			return _properties;
		}
		public function set properties(value : DataProvider) : void
		{
			if (_properties != value)
			{
				if (_properties)
					_bindings.removeProvider(_properties);

				_properties = value;

				if (value)
					_bindings.addProvider(value);
			}
		}

		public function get bindings() : DataBindings
		{
			return _bindings;
		}

		/**
		 * The signal executed when the viewport is about to start rendering a frame.
		 * Callback functions for this signal should accept the following arguments:
		 *

		 *
viewport : Viewport, the viewport who starts rendering the frame

		 *

		 * @return
		 *
		 */
		public function get enterFrame() : Signal
		{
			return _enterFrame;
		}

		/**
		 * The signal executed when the viewport is done rendering a frame.
		 * Callback functions for this signal should accept the following arguments:
		 *

		 *
viewport : Viewport, the viewport who just finished rendering the frame

		 *

		 * @return
		 *
		 */
		public function get exitFrame() : Signal
		{
			return _exitFrame;
		}

		public function Scene(...children)
		{
			super();
		}

		override protected function initialize() : void
		{
			_bindings = new DataBindings(this);
			this.properties = new DataProvider(DataProviderUsage.EXCLUSIVE);

			super.initialize();
		}

		override protected function initializeSignals() : void
		{
			super.initializeSignals();

			_enterFrame = new Signal('Scene.enterFrame');
			_exitFrame = new Signal('Scene.exitFrame');
		}

		override protected function initializeSignalHandlers() : void
		{
			added.add(addedHandler);
		}

		override protected function initializeContollers() : void
		{
			super.initializeContollers();

			_renderingCtrl = new RenderingController();
			addController(_renderingCtrl);
		}

		public function render(viewport : Viewport, destination : BitmapData = null) : void
		{
			_enterFrame.execute(this, viewport, destination, getTimer());
			_exitFrame.execute(this, viewport, destination, getTimer());
		}

		private function addedHandler(child : ISceneNode, parent : Group) : void
		{
			throw new Error();
		}
	}
}

package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.render.Effect;
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.controller.scene.RenderingController;
	import aerys.minko.scene.node.camera.AbstractCamera;
	import aerys.minko.type.Signal;
	import aerys.minko.type.binding.DataBindings;
	import aerys.minko.type.binding.DataProvider;
	import aerys.minko.type.enum.DataProviderUsage;

	import flash.display.BitmapData;
	import flash.utils.getTimer;

	/**
	 * Scene objects are the root of any 3D scene.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class Scene extends Group
	{
		use namespace minko_scene;

		minko_scene var _camera		: AbstractCamera;

		private var _renderingCtrl	: RenderingController;

		private var _properties		: DataProvider;
		private var _bindings		: DataBindings;

		private var _enterFrame		: Signal;
		private var _exitFrame		: Signal;

		public function get activeCamera() : AbstractCamera
		{
			return _camera;
		}

		public function get numPasses() : uint
		{
			return _renderingCtrl.numPasses;
		}

		public function get numTriangles() : uint
		{
			return _renderingCtrl.numTriangles;
		}

		public function get postProcessingEffect() : Effect
		{
			return _renderingCtrl.postProcessingEffect;
		}
		public function set postProcessingEffect(value : Effect) : void
		{
			_renderingCtrl.postProcessingEffect = value;
		}

		public function get postProcessingProperties() : DataProvider
		{
			return _renderingCtrl.postProcessingProperties;
		}

		public function get properties() : DataProvider
		{
			return _properties;
		}
		public function set properties(value : DataProvider) : void
		{
			if (_properties != value)
			{
				if (_properties)
					_bindings.removeProvider(_properties);

				_properties = value;

				if (value)
					_bindings.addProvider(value);
			}
		}

		public function get bindings() : DataBindings
		{
			return _bindings;
		}

		/**
		 * The signal executed when the viewport is about to start rendering a frame.
		 * Callback functions for this signal should accept the following arguments:
		 *

		 *
viewport : Viewport, the viewport who starts rendering the frame

		 *

		 * @return
		 *
		 */
		public function get enterFrame() : Signal
		{
			return _enterFrame;
		}

		/**
		 * The signal executed when the viewport is done rendering a frame.
		 * Callback functions for this signal should accept the following arguments:
		 *

		 *
viewport : Viewport, the viewport who just finished rendering the frame

		 *

		 * @return
		 *
		 */
		public function get exitFrame() : Signal
		{
			return _exitFrame;
		}

		public function Scene(...children)
		{
			super();
		}

		override protected function initialize() : void
		{
			_bindings = new DataBindings(this);
			this.properties = new DataProvider(DataProviderUsage.EXCLUSIVE);

			super.initialize();
		}

		override protected function initializeSignals() : void
		{
			super.initializeSignals();

			_enterFrame = new Signal('Scene.enterFrame');
			_exitFrame = new Signal('Scene.exitFrame');
		}

		override protected function initializeSignalHandlers() : void
		{
			super.initializeSignalHandlers();

			added.add(addedHandler);
		}

		override protected function initializeContollers() : void
		{
			super.initializeContollers();

			_renderingCtrl = new RenderingController();
			addController(_renderingCtrl);
		}

		public function render(viewport : Viewport, destination : BitmapData = null) : void
		{
			_enterFrame.execute(this, viewport, destination, getTimer());
			_exitFrame.execute(this, viewport, destination, getTimer());
		}

		private function addedHandler(child : ISceneNode, parent : Group) : void
		{
			throw new Error();
		}
	}
}

fix missing call to super.initializeSignalHandlers()


package aerys.minko.type.loader
{
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	import flash.utils.setTimeout;

	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.Signal;
	import aerys.minko.type.loader.parser.IParser;
	import aerys.minko.type.loader.parser.ParserOptions;

	public class SceneLoader implements ILoader
	{
		private static const REGISTERED_PARSERS : Vector.<Class> = new <Class>[];

		private static const STATE_IDLE		: uint = 0;
		private static const STATE_LOADING	: uint = 1;
		private static const STATE_PARSING	: uint = 2;
		private static const STATE_COMPLETE	: uint = 3;

		private var _currentState			: int;

		private var _progress				: Signal;
		private var _complete				: Signal;
		private var _error					: Signal;

		private var _data					: ISceneNode;
		private var _parser					: IParser;
		private var _numDependencies		: uint;
		private var _dependencies			: Vector.<ILoader>;
		private var _parserOptions			: ParserOptions;

		private var _currentProgress		: Number;
		private var _currentProgressChanged : Boolean;
		private var _isComplete				: Boolean;

		public function get error() : Signal
		{
			return _error;
		}

		public function get progress() : Signal
		{
			return _progress;
		}

		public function get complete() : Signal
		{
			return _complete;
		}

		public function get isComplete() : Boolean
		{
			return _isComplete;
		}

		public function get data() : ISceneNode
		{
			return _data;
		}

		public static function registerParser(parserClass : Class) : void
		{
			if (REGISTERED_PARSERS.indexOf(parserClass) != -1)
				return;

			if (!(new parserClass(new ParserOptions()) is IParser))
				throw new Error('Parsers must implement the IParser interface.');

			REGISTERED_PARSERS.push(parserClass);
		}

		public function SceneLoader(parserOptions : ParserOptions = null)
		{
			_currentState	= STATE_IDLE;

			_error			= new Signal('SceneLoader.error');
			_progress		= new Signal('SceneLoader.progress');
			_complete		= new Signal('SceneLoader.complete');
			_data			= null;
			_isComplete		= false;

			_parserOptions	= parserOptions || new ParserOptions();
		}

		public function load(urlRequest : URLRequest) : void
		{
			if (_currentState != STATE_IDLE)
				throw new Error('This controller is already loading an asset.');

			_currentState = STATE_LOADING;

			var urlLoader : URLLoader = new URLLoader();
			urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
			urlLoader.addEventListener(ProgressEvent.PROGRESS, loadProgressHandler);
			urlLoader.addEventListener(Event.COMPLETE, loadCompleteHandler);
			urlLoader.load(urlRequest);
		}

		private function loadProgressHandler(e : ProgressEvent) : void
		{
			_progress.execute(this, 0.5 * e.bytesLoaded / e.bytesTotal);
		}

		private function loadCompleteHandler(e : Event) : void
		{
			_currentState = STATE_IDLE;

			loadBytes(URLLoader(e.currentTarget).data);
		}

		public function loadClass(classObject : Class) : void
		{
			loadBytes(new classObject());
		}

		public function loadBytes(byteArray : ByteArray) : void
		{
			var startPosition : uint = byteArray.position;

			if (_currentState != STATE_IDLE)
				throw new Error('This loader is already loading an asset.');

			_currentState = STATE_PARSING;
			_progress.execute(this, 0.5);

			if (_parserOptions != null && _parserOptions.parser != null)
			{
				_parser = new (_parserOptions.parser)(_parserOptions);

				if (!_parser.isParsable(byteArray))
					throw new Error('Invalid datatype for this parser.');
			}
			else
			{
				// search a parser by testing registered parsers.
				var numRegisteredParser : uint = REGISTERED_PARSERS.length;
				for (var parserId : uint = 0; parserId < numRegisteredParser; ++parserId)
				{
					_parser = new REGISTERED_PARSERS[parserId](_parserOptions);
					byteArray.position = startPosition;
					if (_parser.isParsable(byteArray))
						break;
				}

				if (parserId == numRegisteredParser)
					throw new Error('No parser could be found for this datatype.');
			}

			byteArray.position = startPosition;

			_dependencies	 = _parser.getDependencies(byteArray);
			_numDependencies = 0;

			if (_dependencies != null)
				for each (var dependency : ILoader in _dependencies)
					if (!dependency.isComplete)
					{
						dependency.error.add(decrementDependencyCounter);
						dependency.complete.add(decrementDependencyCounter);

						_numDependencies++;
					}

			if (_numDependencies == 0)
				parse();
		}

		private function decrementDependencyCounter(...args) : void
		{
			--_numDependencies;

			if (_numDependencies == 0)
				parse();
		}

		private function parse() : void
		{
			_parser.error.add(parseErrorHandler);
			_parser.progress.add(parseProgressHandler);
			_parser.complete.add(parseCompleteHandler);
			_parser.parse();
		}

		private function parseErrorHandler(parser : IParser) : void
		{
			_isComplete = true;
		}

		private function parseProgressHandler(parser : IParser, progress : Number) : void
		{
			_progress.execute(this, 0.5 * (1 + progress));
		}

		private function parseCompleteHandler(parser : IParser, loadedData : ISceneNode) : void
		{
			_isComplete = true;
			_data = loadedData;

			// call later to make sure the loading is always seen as asynchronous
			setTimeout(callLaterComplete, 0, loadedData);
		}

		private function callLaterComplete(loadedData : ISceneNode) : void
		{
			_complete.execute(this, loadedData);
		}
	}
}

package aerys.minko.type.loader
{
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
	import flash.utils.setTimeout;

	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.type.Signal;
	import aerys.minko.type.loader.parser.IParser;
	import aerys.minko.type.loader.parser.ParserOptions;

	public class SceneLoader implements ILoader
	{
		private static const REGISTERED_PARSERS : Vector.<Class> = new <Class>[];

		private static const STATE_IDLE		: uint = 0;
		private static const STATE_LOADING	: uint = 1;
		private static const STATE_PARSING	: uint = 2;
		private static const STATE_COMPLETE	: uint = 3;

		private var _currentState			: int;

		private var _progress				: Signal;
		private var _complete				: Signal;
		private var _error					: Signal;

		private var _data					: ISceneNode;
		private var _parser					: IParser;
		private var _numDependencies		: uint;
		private var _dependencies			: Vector.<ILoader>;
		private var _parserOptions			: ParserOptions;

		private var _currentProgress		: Number;
		private var _currentProgressChanged : Boolean;
		private var _isComplete				: Boolean;

		public function get error() : Signal
		{
			return _error;
		}

		public function get progress() : Signal
		{
			return _progress;
		}

		public function get complete() : Signal
		{
			return _complete;
		}

		public function get isComplete() : Boolean
		{
			return _isComplete;
		}

		public function get data() : ISceneNode
		{
			return _data;
		}

		public static function registerParser(parserClass : Class) : void
		{
			if (REGISTERED_PARSERS.indexOf(parserClass) != -1)
				return;

			if (!(new parserClass(new ParserOptions()) is IParser))
				throw new Error('Parsers must implement the IParser interface.');

			REGISTERED_PARSERS.push(parserClass);
		}

		public function SceneLoader(parserOptions : ParserOptions = null)
		{
			_currentState	= STATE_IDLE;

			_error			= new Signal('SceneLoader.error');
			_progress		= new Signal('SceneLoader.progress');
			_complete		= new Signal('SceneLoader.complete');
			_data			= null;
			_isComplete		= false;

			_parserOptions	= parserOptions || new ParserOptions();
		}

		public function load(urlRequest : URLRequest) : void
		{
			if (_currentState != STATE_IDLE)
				throw new Error('This controller is already loading an asset.');

			_currentState = STATE_LOADING;

			var urlLoader : URLLoader = new URLLoader();
			urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
			urlLoader.addEventListener(ProgressEvent.PROGRESS, loadProgressHandler);
			urlLoader.addEventListener(Event.COMPLETE, loadCompleteHandler);
			urlLoader.load(urlRequest);
		}

		private function loadProgressHandler(e : ProgressEvent) : void
		{
			_progress.execute(this, 0.5 * e.bytesLoaded / e.bytesTotal);
		}

		private function loadCompleteHandler(e : Event) : void
		{
			_currentState = STATE_IDLE;

			loadBytes(URLLoader(e.currentTarget).data);
		}

		public function loadClass(classObject : Class) : void
		{
			loadBytes(new classObject());
		}

		public function loadBytes(byteArray : ByteArray) : void
		{
			var startPosition : uint = byteArray.position;

			if (_currentState != STATE_IDLE)
				throw new Error('This loader is already loading an asset.');

			_currentState = STATE_PARSING;
			_progress.execute(this, 0.5);

			if (_parserOptions != null && _parserOptions.parser != null)
			{
				_parser = new (_parserOptions.parser)(_parserOptions);

				if (!_parser.isParsable(byteArray))
					throw new Error('Invalid datatype for this parser.');
			}
			else
			{
				// search a parser by testing registered parsers.
				var numRegisteredParser : uint = REGISTERED_PARSERS.length;
				for (var parserId : uint = 0; parserId < numRegisteredParser; ++parserId)
				{
					_parser = new REGISTERED_PARSERS[parserId](_parserOptions);
					byteArray.position = startPosition;
					if (_parser.isParsable(byteArray))
						break;
				}

				if (parserId == numRegisteredParser)
					throw new Error('No parser could be found for this datatype.');
			}

			byteArray.position = startPosition;

			_dependencies	 = _parser.getDependencies(byteArray);
			_numDependencies = 0;

			if (_dependencies != null)
				for each (var dependency : ILoader in _dependencies)
					if (!dependency.isComplete)
					{
						dependency.error.add(decrementDependencyCounter);
						dependency.complete.add(decrementDependencyCounter);

						_numDependencies++;
					}

			if (_numDependencies == 0)
				parse();
		}

		private function decrementDependencyCounter(...args) : void
		{
			--_numDependencies;

			if (_numDependencies == 0)
				parse();
		}

		private function parse() : void
		{
			_parser.error.add(parseErrorHandler);
			_parser.progress.add(parseProgressHandler);
			_parser.complete.add(parseCompleteHandler);
			_parser.parse();
		}

		private function parseErrorHandler(parser : IParser) : void
		{
			_isComplete = true;

            _parser = null;
		}

		private function parseProgressHandler(parser : IParser, progress : Number) : void
		{
			_progress.execute(this, 0.5 * (1 + progress));
		}

		private function parseCompleteHandler(parser : IParser, loadedData : ISceneNode) : void
		{
			_isComplete = true;
			_data = loadedData;

            _parser = null;

			// call later to make sure the loading is always seen as asynchronous
			setTimeout(callLaterComplete, 0, loadedData);
		}

		private function callLaterComplete(loadedData : ISceneNode) : void
		{
			_complete.execute(this, loadedData);
		}
	}
}

set SceneLoader._parser = null when loading fails/completes


//
// Flump - Copyright 2012 Three Rings Design

package flump.export {

import flash.display.BitmapData;
import flash.utils.Dictionary;

import flump.SwfTexture;
import flump.display.Movie;
import flump.mold.KeyframeMold;
import flump.mold.LayerMold;
import flump.mold.MovieMold;
import flump.xfl.XflLibrary;
import flump.xfl.XflTexture;

import starling.display.DisplayObject;
import starling.display.Image;
import starling.display.Sprite;
import starling.textures.Texture;

public class DisplayCreator
{
    public function DisplayCreator (lib :XflLibrary) {
        _lib = lib;
    }

    public function loadMovie (name :String) :Movie {
        return new Movie(_lib.getLibrary(name, MovieMold), _lib.frameRate, loadId);
    }

    public function getMemoryUsage (name :String, subtex :Dictionary = null) :int {
        if (name == null) return 0;
        if (FLIPBOOK_TEXTURE.exec(name) != null || _lib.getLibrary(name) is XflTexture) {
            const tex :Texture = getStarlingTexture(name);
            const usage :int = 4 * tex.width * tex.height;
            if (subtex != null && !subtex.hasOwnProperty(name)) {
                subtex[name] = usage;
            }
            return usage;
        }
        const xflMovie :MovieMold = _lib.getLibrary(name, MovieMold);
        if (subtex == null) subtex = new Dictionary();
        for each (var layer :LayerMold in xflMovie.layers) {
            for each (var kf :KeyframeMold in layer.keyframes) {
                getMemoryUsage(kf.id, subtex);
            }
        }
        var subtexUsage :int = 0;
        for (var texName :String in subtex) subtexUsage += subtex[texName];
        return subtexUsage;
    }

    /** Gets the maximum number of pixels drawn in a single frame by the given id. If it's
     * a texture, that's just the number of pixels in the texture. For a movie, it's the frame with
     * the largest set of textures present in its keyframe. For movies inside movies, the frame
     * drawn usage is the maximum that movie can draw. We're trying to get the worst case here.
     */
    public function getMaxDrawn (name :String) :int {
        if (name == null) return 0;
        if (FLIPBOOK_TEXTURE.exec(name) != null || _lib.getLibrary(name) is XflTexture) {
            const tex :Texture = getStarlingTexture(name);
            return tex.width * tex.height;
        }
        const xflMovie :MovieMold = _lib.getLibrary(name, MovieMold);
        var maxDrawn :int = 0;
        for (var ii :int = 0; ii < xflMovie.frames; ii++) {
            var drawn :int = 0;
            for each (var layer :LayerMold in xflMovie.layers) {
                var kf :KeyframeMold = layer.keyframeForFrame(ii);
                if (kf.visible) drawn += getMaxDrawn(kf.id);
            }
            maxDrawn = Math.max(maxDrawn, drawn);
        }
        return maxDrawn;
    }

    private function getStarlingTexture (name :String) :Texture {
        if (!_textures.hasOwnProperty(name)) {
            const match :Object = FLIPBOOK_TEXTURE.exec(name);
            var packed :SwfTexture;
            if (match == null)  {
                packed = SwfTexture.fromTexture(_lib.swf, _lib.getLibrary(name, XflTexture));
            } else {
                const movieName :String = match[1];
                const frame :int = int(match[2]);
                const movie :MovieMold = _lib.getLibrary(movieName, MovieMold);
                if (!movie.flipbook) {
                    throw new Error("Got non-flipbook movie for flipbook texture '" + name + "'");
                }
                packed = SwfTexture.fromFlipbook(_lib.swf, movie, frame);
            }
            _textures[name] = Texture.fromBitmapData(packed.toBitmapData());
            _textureOffsets[name] = packed.offset;
        }
        return _textures[name];
    }

    public function loadTexture (name :String) :DisplayObject {
        const image :Image = new Image(getStarlingTexture(name));
        image.x = _textureOffsets[name].x;
        image.y = _textureOffsets[name].y;
        const holder :Sprite = new Sprite();
        holder.addChild(image);
        return holder;
    }

    public function loadId (id :String) :DisplayObject {
        const match :Object = FLIPBOOK_TEXTURE.exec(id);
        if (match != null) return loadTexture(id);
        const libraryItem :* = _lib.getLibrary(id);
        if (libraryItem is XflTexture) return loadTexture(XflTexture(libraryItem).libraryItem);
        else return loadMovie(MovieMold(libraryItem).libraryItem);
    }

    protected const _textures :Dictionary = new Dictionary();// library name to Texture
    protected const _textureOffsets :Dictionary = new Dictionary();// library name to Point
    protected var _lib :XflLibrary;

    protected static const FLIPBOOK_TEXTURE :RegExp = /^(.*)_flipbook_(\d+)$/;
}
}

//
// Flump - Copyright 2012 Three Rings Design

package flump.export {

import flash.utils.Dictionary;

import flump.SwfTexture;
import flump.display.Movie;
import flump.mold.KeyframeMold;
import flump.mold.LayerMold;
import flump.mold.MovieMold;
import flump.xfl.XflLibrary;
import flump.xfl.XflTexture;

import starling.display.DisplayObject;
import starling.display.Image;
import starling.display.Sprite;
import starling.textures.Texture;

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

public class DisplayCreator
{
    public function DisplayCreator (lib :XflLibrary) {
        _lib = lib;
    }

    public function loadMovie (name :String) :Movie {
        return new Movie(_lib.getLibrary(name, MovieMold), _lib.frameRate, loadId);
    }

    public function getMemoryUsage (name :String, subtex :Dictionary = null) :int {
        if (name == null) return 0;
        if (FLIPBOOK_TEXTURE.exec(name) != null || _lib.getLibrary(name) is XflTexture) {
            const tex :Texture = getStarlingTexture(name);
            const usage :int = 4 * tex.width * tex.height;
            if (subtex != null && !subtex.hasOwnProperty(name)) {
                subtex[name] = usage;
            }
            return usage;
        }
        const xflMovie :MovieMold = _lib.getLibrary(name, MovieMold);
        if (subtex == null) subtex = new Dictionary();
        for each (var layer :LayerMold in xflMovie.layers) {
            for each (var kf :KeyframeMold in layer.keyframes) {
                getMemoryUsage(kf.id, subtex);
            }
        }
        var subtexUsage :int = 0;
        for (var texName :String in subtex) subtexUsage += subtex[texName];
        return subtexUsage;
    }

    /** Gets the maximum number of pixels drawn in a single frame by the given id. If it's
     * a texture, that's just the number of pixels in the texture. For a movie, it's the frame with
     * the largest set of textures present in its keyframe. For movies inside movies, the frame
     * drawn usage is the maximum that movie can draw. We're trying to get the worst case here.
     */
    public function getMaxDrawn (name :String) :int {
        if (name == null) return 0;
        if (FLIPBOOK_TEXTURE.exec(name) != null || _lib.getLibrary(name) is XflTexture) {
            const tex :Texture = getStarlingTexture(name);
            return tex.width * tex.height;
        }
        const xflMovie :MovieMold = _lib.getLibrary(name, MovieMold);
        var maxDrawn :int = 0;
        var calculatedKeyframes :Map = Maps.newMapOf(KeyframeMold);
        for (var ii :int = 0; ii < xflMovie.frames; ii++) {
            var drawn :int = 0;
            for each (var layer :LayerMold in xflMovie.layers) {
                var kf :KeyframeMold = layer.keyframeForFrame(ii);
                if (kf.visible) {
                    if (!calculatedKeyframes.containsKey(kf)) {
                        calculatedKeyframes.put(kf, getMaxDrawn(kf.id));
                    }
                    drawn += calculatedKeyframes.get(kf);
                }
            }
            maxDrawn = Math.max(maxDrawn, drawn);
        }
        return maxDrawn;
    }

    private function getStarlingTexture (name :String) :Texture {
        if (!_textures.hasOwnProperty(name)) {
            const match :Object = FLIPBOOK_TEXTURE.exec(name);
            var packed :SwfTexture;
            if (match == null)  {
                packed = SwfTexture.fromTexture(_lib.swf, _lib.getLibrary(name, XflTexture));
            } else {
                const movieName :String = match[1];
                const frame :int = int(match[2]);
                const movie :MovieMold = _lib.getLibrary(movieName, MovieMold);
                if (!movie.flipbook) {
                    throw new Error("Got non-flipbook movie for flipbook texture '" + name + "'");
                }
                packed = SwfTexture.fromFlipbook(_lib.swf, movie, frame);
            }
            _textures[name] = Texture.fromBitmapData(packed.toBitmapData());
            _textureOffsets[name] = packed.offset;
        }
        return _textures[name];
    }

    public function loadTexture (name :String) :DisplayObject {
        const image :Image = new Image(getStarlingTexture(name));
        image.x = _textureOffsets[name].x;
        image.y = _textureOffsets[name].y;
        const holder :Sprite = new Sprite();
        holder.addChild(image);
        return holder;
    }

    public function loadId (id :String) :DisplayObject {
        const match :Object = FLIPBOOK_TEXTURE.exec(id);
        if (match != null) return loadTexture(id);
        const libraryItem :* = _lib.getLibrary(id);
        if (libraryItem is XflTexture) return loadTexture(XflTexture(libraryItem).libraryItem);
        else return loadMovie(MovieMold(libraryItem).libraryItem);
    }

    protected const _textures :Dictionary = new Dictionary();// library name to Texture
    protected const _textureOffsets :Dictionary = new Dictionary();// library name to Point
    protected var _lib :XflLibrary;

    protected static const FLIPBOOK_TEXTURE :RegExp = /^(.*)_flipbook_(\d+)$/;
}
}

Speed up getMaxDrawn by caching keyframe computations

A deeply nested movie in one of Jon's flas
was causing a single keyframe calculation to run
literally millions of times, which made Flash just hang
forever.


////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You 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
{
    import org.apache.flex.net.JSONItemConverter;

    import samples.flexstore.Product;

    public class ProductJSONItemConverter extends JSONItemConverter
    {
        public function ProductJSONItemConverter()
        {
            super();
        }

        override public function convertItem(data:String):Object
        {
            var obj:Object = super.convertItem(data);
            var product:Product = new Product();
            for (var p:String in obj)
                product[p] = obj[p];
			return product;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You 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
{
    import org.apache.flex.net.JSONItemConverter;

    import samples.flexstore.Product;

    public class ProductJSONItemConverter extends JSONItemConverter
    {
        public function ProductJSONItemConverter()
        {
            super();
        }

        override public function convertItem(data:String):Object
        {
            var obj:Object = super.convertItem(data);
            var product:Product = new Product();
            for (var p:String in obj)
                setProperty(product, p, obj[p]);
			return product;
        }
    }
}

use setProperty (for now at least)

use setProperty (for now at least)


package dolly {
import dolly.core.dolly_internal;
import dolly.core.metadata.MetadataName;

import org.as3commons.reflect.Accessor;
import org.as3commons.reflect.Field;
import org.as3commons.reflect.IMetadataContainer;
import org.as3commons.reflect.Type;
import org.as3commons.reflect.Variable;

use namespace dolly_internal;

public class Copier {

	private static function isVariableCopyable(variable:Variable, skipMetadataChecking:Boolean = true):Boolean {
		return !variable.isStatic && (skipMetadataChecking || variable.hasMetadata(MetadataName.COPYABLE));
	}

	private static function isAccessorCopyable(accessor:Accessor, skipMetadataChecking:Boolean = true):Boolean {
		return !accessor.isStatic && accessor.isReadable() && accessor.isWriteable() &&
				(skipMetadataChecking || accessor.hasMetadata(MetadataName.COPYABLE));
	}

	dolly_internal static function findCopyableFieldsForType(type:Type):Vector.<Field> {
		const result:Vector.<Field> = new Vector.<Field>();

		var variable:Variable;
		var accessor:Accessor;

		const isClassCloneable:Boolean = type.hasMetadata(MetadataName.COPYABLE);
		if (isClassCloneable) {
			for each(variable in type.variables) {
				if (isVariableCopyable(variable)) {
					result.push(variable);
				}
			}
			for each(accessor in type.accessors) {
				if (isAccessorCopyable(accessor)) {
					result.push(accessor);
				}
			}
		} else {
			const metadataContainers:Array = type.getMetadataContainers(MetadataName.COPYABLE);
			for each(var metadataContainer:IMetadataContainer in metadataContainers) {
				if (metadataContainer is Variable) {
					variable = metadataContainer as Variable;
					if (isVariableCopyable(variable, false)) {
						result.push(variable);
					}
				} else if (metadataContainer is Accessor) {
					accessor = metadataContainer as Accessor;
					if (isAccessorCopyable(accessor, false)) {
						result.push(accessor);
					}
				}
			}
		}

		return result;
	}

	public static function copy(source:*):* {
		const type:Type = Type.forInstance(source);
		const copy:* = new (type.clazz)();

		const fieldsToCopy:Vector.<Field> = findCopyableFieldsForType(type);
		var name:String;
		for each(var field:Field in fieldsToCopy) {
			name = field.name;
			copy[name] = source[name];
		}

		return copy;
	}
}
}

package dolly {
import dolly.core.dolly_internal;
import dolly.core.metadata.MetadataName;

import org.as3commons.reflect.Accessor;
import org.as3commons.reflect.Field;
import org.as3commons.reflect.IMetadataContainer;
import org.as3commons.reflect.Type;
import org.as3commons.reflect.Variable;

use namespace dolly_internal;

public class Copier {

	private static function isVariableCopyable(variable:Variable, skipMetadataChecking:Boolean = true):Boolean {
		return !variable.isStatic && (skipMetadataChecking || variable.hasMetadata(MetadataName.COPYABLE));
	}

	private static function isAccessorCopyable(accessor:Accessor, skipMetadataChecking:Boolean = true):Boolean {
		return !accessor.isStatic && accessor.isReadable() && accessor.isWriteable() &&
				(skipMetadataChecking || accessor.hasMetadata(MetadataName.COPYABLE));
	}

	dolly_internal static function findCopyableFieldsForType(type:Type):Vector.<Field> {
		const result:Vector.<Field> = new Vector.<Field>();

		var variable:Variable;
		var accessor:Accessor;

		const isTypeCloneable:Boolean = type.hasMetadata(MetadataName.COPYABLE);
		if (isTypeCloneable) {
			for each(variable in type.variables) {
				if (isVariableCopyable(variable)) {
					result.push(variable);
				}
			}
			for each(accessor in type.accessors) {
				if (isAccessorCopyable(accessor)) {
					result.push(accessor);
				}
			}
		} else {
			const metadataContainers:Array = type.getMetadataContainers(MetadataName.COPYABLE);
			for each(var metadataContainer:IMetadataContainer in metadataContainers) {
				if (metadataContainer is Variable) {
					variable = metadataContainer as Variable;
					if (isVariableCopyable(variable, false)) {
						result.push(variable);
					}
				} else if (metadataContainer is Accessor) {
					accessor = metadataContainer as Accessor;
					if (isAccessorCopyable(accessor, false)) {
						result.push(accessor);
					}
				}
			}
		}

		return result;
	}

	public static function copy(source:*):* {
		const type:Type = Type.forInstance(source);
		const copy:* = new (type.clazz)();

		const fieldsToCopy:Vector.<Field> = findCopyableFieldsForType(type);
		var name:String;
		for each(var field:Field in fieldsToCopy) {
			name = field.name;
			copy[name] = source[name];
		}

		return copy;
	}
}
}

Rename local variable in method Copier.copy: isClassCloneable -> isTypeCloneable


/*
 * 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.campaign
{
    import com.google.analytics.utils.Variables;

    /**
     * Campaign tracker object.
     * Contains all the data associated with a campaign.
     *
     * AdWords:
     * The following variables
     * name, source, medium, term, content
     * are automatically generated for AdWords hits when autotagging
     * is turned on through the AdWords interface.
     *
     *
     * note:
     * we can not use a CampaignInfo because here the serialization
     * to URL have to be injected into __utmz and is then a special case.
     *
     * links:
     *
     * Understanding campaign variables: The five dimensions of campaign tracking
     * http://www.google.com/support/googleanalytics/bin/answer.py?answer=55579&hl=en
     *
     * How does campaign tracking work?
     * http://www.google.com/support/googleanalytics/bin/answer.py?hl=en&answer=55540
     *
     * What is A/B Testing and how can it help me?
     * http://www.google.com/support/googleanalytics/bin/answer.py?answer=55589
     *
     * What information do the filter fields represent?
     * http://www.google.com/support/googleanalytics/bin/answer.py?hl=en&answer=55588
     */
    public class CampaignTracker
    {

        /**
         * @private
         */
        private function _addIfNotEmpty( arr:Array, field:String, value:String ):void
        {
            if( value != "" )
            {
                value = value.split( "+" ).join( "%20" );
                value = value.split( " " ).join( "%20" );
                arr.push( field + value );
            }
        }

        /**
         * The campaign code or id can be used to refer to a campaign lookup table,
         * or chart of referring codes used to define variables in place of multiple request query tags.
         * variable: utmcid
         */
        public var id:String;

        /**
         * The resource that provided the click.
         * Every referral to a web site has an origin, or source.
         * Examples of sources are the Google search engine,
         * the AOL search engine, the name of a newsletter,
         * or the name of a referring web site.
         * Other example: "AdWords".
         *
         * variable: utmcsr
         */
        public var source:String; //required

        /**
        * google ad click id
        *
        * variable: utmgclid
        */
        public var clickId:String;

        /**
        * Usually refers to name given to the marketing campaign or is used to differentiate campaign source.
        * The campaign name differentiates product promotions such as "Spring Ski Sale"
        * or slogan campaigns such as "Get Fit For Summer".
        *
        * alternative:
        * Used for keyword analysis to identify a specific product promotion or strategic campaign.
        *
        * variable: utmccn
        */
        public var name:String; //required

        /**
        * Method of Delivery.
        * The medium helps to qualify the source;
        * together, the source and medium provide specific information
        * about the origin of a referral.
        * For example, in the case of a Google search engine source,
        * the medium might be "cost-per-click", indicating a sponsored link for which the advertiser paid,
        * or "organic", indicating a link in the unpaid search engine results.
        * In the case of a newsletter source, examples of medium include "email" and "print".
        * Other examples: "Organic", "CPC", or "PPC".
        *
        * variable: utmcmd
        */
        public var medium:String; //required

        /**
         * The term or keyword is the word or phrase that a user types into a search engine.
         * Used for paid search.
         * variable: utmctr
         */
        public var term:String;

        /**
         * The content dimension describes the version of an advertisement on which a visitor clicked.
         *
It is used in content-targeted advertising and Content (A/B) Testing to determine which version of an advertisement is most effective at attracting profitable leads.

         *
Alternative: Used for A/B testing and content-targeted ads to differentiate ads or links that point to the same URL.

         *
variable: utmcct

         */
        public var content:String;

        /**
         * Creates a new CampaingTracker instance.
         */
        public function CampaignTracker( id:String = "", source:String = "", clickId:String = "",
                                         name:String = "", medium:String = "", term:String = "", content:String = "" )
        {
            this.id      = id;
            this.source  = source;
            this.clickId = clickId;
            this.name    = name;
            this.medium  = medium;
            this.term    = term;
            this.content = content;
        }

        /**
         * Returns a flag indicating whether this tracker object is valid.
         * A tracker object is considered to be valid if and only if one of id, source or clickId is present.
         */
        public function isValid():Boolean
        {
            if( (id != "") ||
                (source != "") ||
                (clickId != "") )
                {
                    return true;
                }

            return false;
        }

        /**
         * Builds the tracker object from a tracker string.
         *
         * @private
         * @param {String} trackerString Tracker string to parse tracker object from.
         */
        public function fromTrackerString( tracker:String ):void
        {
            /* note:
               we are basically deserializing the utmz.campaignTracking property
            */
            var data:String = tracker.split( CampaignManager.trackingDelimiter ).join( "&" );
            var vars:Variables = new Variables( data );

            if( vars.hasOwnProperty( "utmcid" ) )
            {
                this.id = vars["utmcid"];
            }

            if( vars.hasOwnProperty( "utmcsr" ) )
            {
                this.source = vars["utmcsr"];
            }

            if( vars.hasOwnProperty( "utmccn" ) )
            {
                this.name = vars["utmccn"];
            }

            if( vars.hasOwnProperty( "utmcmd" ) )
            {
                this.medium = vars["utmcmd"];
            }

            if( vars.hasOwnProperty( "utmctr" ) )
            {
                this.term = vars["utmctr"];
            }

            if( vars.hasOwnProperty( "utmcct" ) )
            {
                this.content = vars["utmcct"];
            }

            if( vars.hasOwnProperty( "utmgclid" ) )
            {
                this.clickId = vars["utmgclid"];
            }

        }

        /**
         * Format for tracker have the following fields (seperated by "|"):
         *

         *
utmcid - lookup table id

         *
utmcsr - campaign source

         *
utmgclid - google ad click id

         *
utmccn - campaign name

         *
utmcmd - campaign medium

         *
utmctr - keywords

         *
utmcct - ad content description

         *
         *
         *

         * should be in this order : utmcid=one|utmcsr=two|utmgclid=three|utmccn=four|utmcmd=five|utmctr=six|utmcct=seven
         *
         */
        public function toTrackerString():String
        {
            var data:Array = [];

                /* for each value, append key=value if and only if
                   the value is not empty.
                */
                _addIfNotEmpty( data, "utmcid=",   id );
                _addIfNotEmpty( data, "utmcsr=",   source );
                _addIfNotEmpty( data, "utmgclid=", clickId );
                _addIfNotEmpty( data, "utmccn=",   name );
                _addIfNotEmpty( data, "utmcmd=",   medium );
                _addIfNotEmpty( data, "utmctr=",   term );
                _addIfNotEmpty( data, "utmcct=",   content );

            return data.join( CampaignManager.trackingDelimiter );
        }

    }
}

/*
 * 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.campaign
{
    import com.google.analytics.utils.Variables;

    /* Campaign tracker object.
       Contains all the data associated with a campaign.

       AdWords:
       The following variables
       name, source, medium, term, content
       are automatically generated for AdWords hits when autotagging
       is turned on through the AdWords interface.

       note:
       we can not use a CampaignInfo because here the serialization
       to URL have to be injected into __utmz and is then a special case.

       links:

       Understanding campaign variables: The five dimensions of campaign tracking
       http://www.google.com/support/googleanalytics/bin/answer.py?answer=55579&hl=en

       How does campaign tracking work?
       http://www.google.com/support/googleanalytics/bin/answer.py?hl=en&answer=55540

       What is A/B Testing and how can it help me?
       http://www.google.com/support/googleanalytics/bin/answer.py?answer=55589

       What information do the filter fields represent?
       http://www.google.com/support/googleanalytics/bin/answer.py?hl=en&answer=55588
     */
    public class CampaignTracker
    {

        /**
         * @private
         */
        private function _addIfNotEmpty( arr:Array, field:String, value:String ):void
        {
            if( value != "" )
            {
                value = value.split( "+" ).join( "%20" );
                value = value.split( " " ).join( "%20" );
                arr.push( field + value );
            }
        }

        /**
         * The campaign code or id can be used to refer to a campaign lookup table,
         * or chart of referring codes used to define variables in place of multiple request query tags.
         * variable: utmcid
         */
        public var id:String;

        /**
         * The resource that provided the click.
         * Every referral to a web site has an origin, or source.
         * Examples of sources are the Google search engine,
         * the AOL search engine, the name of a newsletter,
         * or the name of a referring web site.
         * Other example: "AdWords".
         *
         * variable: utmcsr
         */
        public var source:String; //required

        /**
        * google ad click id
        *
        * variable: utmgclid
        */
        public var clickId:String;

        /**
        * Usually refers to name given to the marketing campaign or is used to differentiate campaign source.
        * The campaign name differentiates product promotions such as "Spring Ski Sale"
        * or slogan campaigns such as "Get Fit For Summer".
        *
        * alternative:
        * Used for keyword analysis to identify a specific product promotion or strategic campaign.
        *
        * variable: utmccn
        */
        public var name:String; //required

        /**
        * Method of Delivery.
        * The medium helps to qualify the source;
        * together, the source and medium provide specific information
        * about the origin of a referral.
        * For example, in the case of a Google search engine source,
        * the medium might be "cost-per-click", indicating a sponsored link for which the advertiser paid,
        * or "organic", indicating a link in the unpaid search engine results.
        * In the case of a newsletter source, examples of medium include "email" and "print".
        * Other examples: "Organic", "CPC", or "PPC".
        *
        * variable: utmcmd
        */
        public var medium:String; //required

        /**
         * The term or keyword is the word or phrase that a user types into a search engine.
         * Used for paid search.
         * variable: utmctr
         */
        public var term:String;

        /**
         * The content dimension describes the version of an advertisement on which a visitor clicked.
         *
It is used in content-targeted advertising and Content (A/B) Testing to determine which version of an advertisement is most effective at attracting profitable leads.

         *
Alternative: Used for A/B testing and content-targeted ads to differentiate ads or links that point to the same URL.

         *
variable: utmcct

         */
        public var content:String;

        /**
         * Creates a new CampaingTracker instance.
         */
        public function CampaignTracker( id:String = "", source:String = "", clickId:String = "",
                                         name:String = "", medium:String = "", term:String = "", content:String = "" )
        {
            this.id      = id;
            this.source  = source;
            this.clickId = clickId;
            this.name    = name;
            this.medium  = medium;
            this.term    = term;
            this.content = content;
        }

        /**
         * Returns a flag indicating whether this tracker object is valid.
         * A tracker object is considered to be valid if and only if one of id, source or clickId is present.
         */
        public function isValid():Boolean
        {
            if( (id != "") ||
                (source != "") ||
                (clickId != "") )
                {
                    return true;
                }

            return false;
        }

        /**
         * Builds the tracker object from a tracker string.
         *
         * @private
         * @param {String} trackerString Tracker string to parse tracker object from.
         */
        public function fromTrackerString( tracker:String ):void
        {
            /* note:
               we are basically deserializing the utmz.campaignTracking property
            */
            var data:String = tracker.split( CampaignManager.trackingDelimiter ).join( "&" );
            var vars:Variables = new Variables( data );

            if( vars.hasOwnProperty( "utmcid" ) )
            {
                this.id = vars["utmcid"];
            }

            if( vars.hasOwnProperty( "utmcsr" ) )
            {
                this.source = vars["utmcsr"];
            }

            if( vars.hasOwnProperty( "utmccn" ) )
            {
                this.name = vars["utmccn"];
            }

            if( vars.hasOwnProperty( "utmcmd" ) )
            {
                this.medium = vars["utmcmd"];
            }

            if( vars.hasOwnProperty( "utmctr" ) )
            {
                this.term = vars["utmctr"];
            }

            if( vars.hasOwnProperty( "utmcct" ) )
            {
                this.content = vars["utmcct"];
            }

            if( vars.hasOwnProperty( "utmgclid" ) )
            {
                this.clickId = vars["utmgclid"];
            }

        }

        /**
         * Format for tracker have the following fields (seperated by "|"):
         *

         *
utmcid - lookup table id

         *
utmcsr - campaign source

         *
utmgclid - google ad click id

         *
utmccn - campaign name

         *
utmcmd - campaign medium

         *
utmctr - keywords

         *
utmcct - ad content description

         *
         *
         *

         * should be in this order : utmcid=one|utmcsr=two|utmgclid=three|utmccn=four|utmcmd=five|utmctr=six|utmcct=seven
         *
         */
        public function toTrackerString():String
        {
            var data:Array = [];

                /* for each value, append key=value if and only if
                   the value is not empty.
                */
                _addIfNotEmpty( data, "utmcid=",   id );
                _addIfNotEmpty( data, "utmcsr=",   source );
                _addIfNotEmpty( data, "utmgclid=", clickId );
                _addIfNotEmpty( data, "utmccn=",   name );
                _addIfNotEmpty( data, "utmcmd=",   medium );
                _addIfNotEmpty( data, "utmctr=",   term );
                _addIfNotEmpty( data, "utmcct=",   content );

            return data.join( CampaignManager.trackingDelimiter );
        }

    }
}

update comment for asdoc

update comment for asdoc


/**
 * User: MerlinDS
 * Date: 13.07.2014
 * Time: 1:51
 */
package com.merlinds.miracle_tool.models {
	import com.merlinds.miracle_tool.events.EditorEvent;
	import com.merlinds.miracle_tool.models.vo.AnimationVO;
	import com.merlinds.miracle_tool.models.vo.ElementVO;
	import com.merlinds.miracle_tool.models.vo.SourceVO;
	import com.merlinds.unitls.Resolutions;

	import flash.filesystem.File;
	import flash.geom.Point;

	import org.robotlegs.mvcs.Actor;

	public class ProjectModel extends Actor {

		private var _name:String;
		/**
		 * TODO: MF-29 Make normal comment for referenceSize
		 * This size will be used for scaling of output texture.
		 * It based of target screen resolution and resource screen resolution.
		 * For example: IF reference size (width of screen of the fla. resource ) equals 2048
		 * and will be needed to publish project for 1024 screen width, than scale will be 0.5;
		 **/
		private var _referenceResolution:int;
		/**
		 * Width of the target screen resolution
		 */
		private var _targetResolution:int;
		//TODO MF-28 Bound calculation for every polygon in texture
		private var _boundsOffset:int;

		private var _sources:Vector.<SourceVO>;
		private var _inProgress:int;

		private var _outputSize:int;
		private var _zoom:Number = 1;
		private var _sheetSize:Point;

		private var _saved:Boolean;

		//quick hack for animation saving
		public var tempFile:File;
		//storage for handling previous resolution sources
		private var _memorize:Object;
		//==============================================================================
		//{region							PUBLIC METHODS
		public function ProjectModel(name:String, referenceResolution:int) {
			_name = name;
			_memorize = {};
			_referenceResolution = referenceResolution;
			_targetResolution = referenceResolution;
			_sources = new <SourceVO>[];
			_boundsOffset = 0;
			_sheetSize = new Point();
			super();
		}

		public function addSource(file:File):SourceVO {
			//revert to default sources and delete old backups
			this.revertBackupTo(_referenceResolution);
			_memorize = {};
			//add new source
			var source:SourceVO;
			var n:int = _sources.length;
			for(var i:int = 0; i < n; i++){
				 source = _sources[i];
				if(source.file.nativePath == file.nativePath)break;
			}
			if(i >= n){
				source = new SourceVO(file);
				_sources.push(source);
			}else
			{
				//TODO MF-30 Update swf source if it already added
			}
			_inProgress = i;
			return source;
		}

		public function deleteAnimation(name:String):void {
			var source:SourceVO = this.selected;
			var animation:AnimationVO;
			var n:int = source.animations.length;
			for(var i:int = 0; i < n; i++){
				animation = source.animations[i];
				if(animation.name == name){
					//delete founded animation
					source.animations.splice(i, 1);
					break;
				}
				animation = null;
			}
			//nulled object and add it back for future
			if(animation != null){
				animation.added = false;
				source.animations.push(animation);
			}
		}

		//} endregion PUBLIC METHODS ===================================================

		//==============================================================================
		//{region						PRIVATE\PROTECTED METHODS
		/**
		 * Backup sources for current resolution
		 */
		private function prepareBackup():void {
			if(!_memorize.hasOwnProperty(_targetResolution.toString())){
				_memorize[_targetResolution] = _sources.concat();
			}
		}

		/**
		 * get sources for resolution
		 * @param resolution
		 * @return True if backup was reverted. In other case returns false
		 */
		private function revertBackupTo(resolution:int):Boolean {
			var result:Boolean = _memorize.hasOwnProperty(resolution.toString());
			if(result){
				_sources = _memorize[resolution];
			}
			return result;
		}

		/**
		 * Scale sources for resolution
		 */
		private function scaleSources():void {
			//calculate scale
			var scale:Number = Resolutions.width(_targetResolution) / Resolutions.width(_referenceResolution);
			//clone default sources, that was first for the project and scale elements in it
			var defaultSources:Vector.<SourceVO> = _memorize[_referenceResolution.toString()];
			var sources:Vector.<SourceVO> = new <SourceVO>[];
			var n:int = sources.length = defaultSources.length;
			sources.fixed = true;
			for(var i:int = 0; i < n; i++){
				//cloning
				//TODO do cloning of animation to publish command
				sources[i] = defaultSources[i].clone(scale);
			}
			//end
			sources.fixed = false;
			_sources = sources;
		}
		//} endregion PRIVATE\PROTECTED METHODS ========================================

		//==============================================================================
		//{region							EVENTS HANDLERS
		//} endregion EVENTS HANDLERS ==================================================

		//==============================================================================
		//{region							GETTERS/SETTERS
		public function get name():String {
			return _name;
		}

		public function get sources():Vector.<SourceVO> {
			return _sources;
		}

		public function get sheetSize():Point {
			return _sheetSize;
		}

		public function get saved():Boolean {
			return _saved;
		}

		public function set saved(value:Boolean):void {
			_saved = value;
		}

		public function get selected():SourceVO {
			var source:SourceVO;
			var n:int = _sources.length;
			for(var i:int = 0; i < n; i++){
				source = _sources[i];
				if(source.selected)break;
				source = null;
			}
			return source;
		}

		public function get inProgress():SourceVO{
			return _inProgress < 0 ? null : _sources[ _inProgress ];
		}

		public function get boundsOffset():int {
			return _boundsOffset;
		}

		public function set boundsOffset(value:int):void {
			if(value != _boundsOffset){
				_boundsOffset = value;
				if(_sources.length > 0){
					this.dispatch(new EditorEvent(EditorEvent.UPDATE_PROJECT));
				}
			}
		}

		public function get outputSize():int {
			return _outputSize;
		}

		public function set outputSize(value:int):void {
			_outputSize = value;
			_sheetSize.x = value;
			_sheetSize.y = value;
		}

		public function get zoom():Number {
			return _zoom;
		}

		public function set zoom(value:Number):void {
			_zoom = value;
		}
		//TODO MF-29 Make normal comment for referenceSize
		public function get referenceResolution():int {
			return _referenceResolution;
		}

		public function get targetResolution():int {
			return _targetResolution;
		}

		public function set targetResolution(value:int):void {
			if(value != _targetResolution){
				this.prepareBackup();
				_targetResolution = value;
				if(_sources.length > 0){
					var reverted:Boolean = this.revertBackupTo(_targetResolution);
					if( !reverted ){
						_outputSize = 1;
						this.scaleSources();
					}
					this.dispatch(new EditorEvent(EditorEvent.UPDATE_PROJECT));
				}
			}
		}

//} endregion GETTERS/SETTERS ==================================================
	}
}

/**
 * User: MerlinDS
 * Date: 13.07.2014
 * Time: 1:51
 */
package com.merlinds.miracle_tool.models {
	import com.merlinds.miracle_tool.events.EditorEvent;
	import com.merlinds.miracle_tool.models.vo.AnimationVO;
	import com.merlinds.miracle_tool.models.vo.ElementVO;
	import com.merlinds.miracle_tool.models.vo.SourceVO;
	import com.merlinds.unitls.Resolutions;

	import flash.filesystem.File;
	import flash.geom.Point;

	import org.robotlegs.mvcs.Actor;

	public class ProjectModel extends Actor {

		private var _name:String;
		/**
		 * TODO: MF-29 Make normal comment for referenceSize
		 * This size will be used for scaling of output texture.
		 * It based of target screen resolution and resource screen resolution.
		 * For example: IF reference size (width of screen of the fla. resource ) equals 2048
		 * and will be needed to publish project for 1024 screen width, than scale will be 0.5;
		 **/
		private var _referenceResolution:int;
		/**
		 * Width of the target screen resolution
		 */
		private var _targetResolution:int;
		//TODO MF-28 Bound calculation for every polygon in texture
		private var _boundsOffset:int;

		private var _sources:Vector.<SourceVO>;
		private var _inProgress:int;

		private var _outputSize:int;
		private var _zoom:Number = 1;
		private var _sheetSize:Point;

		private var _saved:Boolean;

		//quick hack for animation saving
		public var tempFile:File;
		//storage for handling previous resolution sources
		private var _memorize:Object;
		//==============================================================================
		//{region							PUBLIC METHODS
		public function ProjectModel(name:String, referenceResolution:int) {
			_name = name;
			_memorize = {};
			_referenceResolution = referenceResolution;
			_targetResolution = referenceResolution;
			_sources = new <SourceVO>[];
			_boundsOffset = 0;
			_sheetSize = new Point();
			super();
		}

		public function addSource(file:File):SourceVO {
			//revert to default sources and delete old backups
			this.revertBackupTo(_referenceResolution);
			_memorize = {};
			//add new source
			var source:SourceVO;
			var n:int = _sources.length;
			for(var i:int = 0; i < n; i++){
				 source = _sources[i];
				if(source.file.nativePath == file.nativePath)break;
			}
			if(i >= n){
				source = new SourceVO(file);
				_sources.push(source);
			}else
			{
				//TODO MF-30 Update swf source if it already added
			}
			_inProgress = i;
			return source;
		}

		public function deleteAnimation(name:String):void {
			var source:SourceVO = this.selected;
			var animation:AnimationVO;
			var n:int = source.animations.length;
			for(var i:int = 0; i < n; i++){
				animation = source.animations[i];
				if(animation.name == name){
					//delete founded animation
					source.animations.splice(i, 1);
					break;
				}
				animation = null;
			}
			//nulled object and add it back for future
			if(animation != null){
				animation.added = false;
				source.animations.push(animation);
			}
		}

		//} endregion PUBLIC METHODS ===================================================

		//==============================================================================
		//{region						PRIVATE\PROTECTED METHODS
		/**
		 * Backup sources for current resolution
		 */
		private function prepareBackup():void {
			if(!_memorize.hasOwnProperty(_targetResolution.toString())){
				_memorize[_targetResolution] = _sources.concat();
			}
		}

		/**
		 * get sources for resolution
		 * @param resolution
		 * @return True if backup was reverted. In other case returns false
		 */
		private function revertBackupTo(resolution:int):Boolean {
			var result:Boolean = _memorize.hasOwnProperty(resolution.toString());
			if(result){
				_sources = _memorize[resolution];
			}
			return result;
		}

		/**
		 * Scale sources for resolution
		 */
		private function scaleSources():void {
			//calculate scale
			var scale:Number = Resolutions.width(_targetResolution) / Resolutions.width(_referenceResolution);
			//clone default sources, that was first for the project and scale elements in it
			var defaultSources:Vector.<SourceVO> = _memorize[_referenceResolution.toString()];
			var sources:Vector.<SourceVO> = new <SourceVO>[];
			var n:int = sources.length = defaultSources.length;
			sources.fixed = true;
			for(var i:int = 0; i < n; i++){
				//cloning
				sources[i] = defaultSources[i].clone(scale);
			}
			//end
			sources.fixed = false;
			_sources = sources;
		}
		//} endregion PRIVATE\PROTECTED METHODS ========================================

		//==============================================================================
		//{region							EVENTS HANDLERS
		//} endregion EVENTS HANDLERS ==================================================

		//==============================================================================
		//{region							GETTERS/SETTERS
		public function get name():String {
			return _name;
		}

		public function get sources():Vector.<SourceVO> {
			return _sources;
		}

		public function get sheetSize():Point {
			return _sheetSize;
		}

		public function get saved():Boolean {
			return _saved;
		}

		public function set saved(value:Boolean):void {
			_saved = value;
		}

		public function get selected():SourceVO {
			var source:SourceVO;
			var n:int = _sources.length;
			for(var i:int = 0; i < n; i++){
				source = _sources[i];
				if(source.selected)break;
				source = null;
			}
			return source;
		}

		public function get inProgress():SourceVO{
			return _inProgress < 0 ? null : _sources[ _inProgress ];
		}

		public function get boundsOffset():int {
			return _boundsOffset;
		}

		public function set boundsOffset(value:int):void {
			if(value != _boundsOffset){
				_boundsOffset = value;
				if(_sources.length > 0){
					this.dispatch(new EditorEvent(EditorEvent.UPDATE_PROJECT));
				}
			}
		}

		public function get outputSize():int {
			return _outputSize;
		}

		public function set outputSize(value:int):void {
			_outputSize = value;
			_sheetSize.x = value;
			_sheetSize.y = value;
		}

		public function get zoom():Number {
			return _zoom;
		}

		public function set zoom(value:Number):void {
			_zoom = value;
		}
		//TODO MF-29 Make normal comment for referenceSize
		public function get referenceResolution():int {
			return _referenceResolution;
		}

		public function get targetResolution():int {
			return _targetResolution;
		}

		public function set targetResolution(value:int):void {
			if(value != _targetResolution){
				this.prepareBackup();
				_targetResolution = value;
				if(_sources.length > 0){
					var reverted:Boolean = this.revertBackupTo(_targetResolution);
					if( !reverted ){
						_outputSize = 1;
						this.scaleSources();
					}
					this.dispatch(new EditorEvent(EditorEvent.UPDATE_PROJECT));
				}
			}
		}

//} endregion GETTERS/SETTERS ==================================================
	}
}

Remove fixed TODO

Remove fixed TODO