//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.util {

import flash.events.NetStatusEvent;
import flash.events.EventDispatcher;

import flash.net.SharedObject;
import flash.net.SharedObjectFlushStatus;

/**
 * Dispatched when this Config object has a value set on it.
 *
 * @eventType com.threerings.util.ConfigValueSetEvent.CONFIG_VALUE_SET
 */
[Event(name="ConfigValSet", type="com.threerings.util.ConfigValueSetEvent")]

public class Config extends EventDispatcher
{
    /**
     * Constructs a new config object which will obtain configuration
     * information from the specified path.
     */
    public function Config (path :String)
    {
        _so = SharedObject.getLocal("config_" + path, "/");
    }

    /**
     * Fetches and returns the value for the specified configuration property.
     */
    public function getValue (name :String, defValue :Object) :Object
    {
        var val :* = _so.data[name];
        return (val === undefined) ? defValue : val;
    }

    /**
     * Returns the value specified.
     */
    public function setValue (name :String, value :Object) :void
    {
        _so.data[name] = value;
        _so.flush(); // flushing is not strictly necessary

        // dispatch an event corresponding
        dispatchEvent(new ConfigValueSetEvent(name, value));
    }

    /**
     * Remove any set value for the specified preference.
     */
    public function remove (name :String) :void
    {
        delete _so.data[name];
        _so.flush(); // flushing is not strictly necessary
    }

    /**
     * Ensure that we can store preferences up to the specified size.
     * Note that calling this method may pop up a dialog to the user, asking
     * them if it's ok to increase the capacity. The result listener may
     * never be called if the user doesn't answer the pop-up.
     *
     * @param rl an optional listener that will be informed as to whether
     * the request succeeded.
     */
    public function ensureCapacity (kilobytes :int, rl :ResultListener) :void
    {
        // flush with the size, see if we're cool
        var result :String = _so.flush(1024 * kilobytes);
        if (rl == null) {
            return;
        }

        // success
        if (result == SharedObjectFlushStatus.FLUSHED) {
            rl.requestCompleted(this);
            return;
        }

        // otherwise we'll hear back in a sec
        var thisConfig :Config = this;
        var listener :Function = function (evt :NetStatusEvent) :void {
                // TODO: There is a bug where the status is always
                // "SharedObject.Flush.Failed", even on success, if the request
                // was for a large enough storage that the player calls it
                // "unlimited".
                trace("================[" + evt.info.code + "]");
                if ("SharedObject.Flush.Success" == evt.info.code) {
                    rl.requestCompleted(thisConfig);
                } else {
                    rl.requestFailed(new Error(String(evt.info.code)));
                }
                _so.removeEventListener(NetStatusEvent.NET_STATUS, listener);
            };

        _so.addEventListener(NetStatusEvent.NET_STATUS, listener);
    }

    /** The shared object that contains our preferences. */
    protected var _so :SharedObject;
}
}

//
// $Id$
//
// Narya library - tools for developing networked games
// Copyright (C) 2002-2007 Three Rings Design, Inc., All Rights Reserved
// http://www.threerings.net/code/narya/
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.threerings.util {

import flash.events.NetStatusEvent;
import flash.events.EventDispatcher;

import flash.net.SharedObject;
import flash.net.SharedObjectFlushStatus;

/**
 * Dispatched when this Config object has a value set on it.
 *
 * @eventType com.threerings.util.ConfigValueSetEvent.CONFIG_VALUE_SET
 */
[Event(name="ConfigValSet", type="com.threerings.util.ConfigValueSetEvent")]

public class Config extends EventDispatcher
{
    /**
     * Constructs a new config object which will obtain configuration
     * information from the specified path.
     */
    public function Config (path :String)
    {
        setPath(path);
    }

    public function setPath (path :String) :void
    {
        _so = (path == null) ? null : SharedObject.getLocal("config_" + path, "/");
        _data = (_so == null) ? {} : _so.data;

        // dispatch events for all settings
        for (var n :String in _data) {
            dispatchEvent(new ConfigValueSetEvent(n, _data[n]));
        }
    }

    /**
     * Fetches and returns the value for the specified configuration property.
     */
    public function getValue (name :String, defValue :Object) :Object
    {
        var val :* = _data[name];
        return (val === undefined) ? defValue : val;
    }

    /**
     * Returns the value specified.
     */
    public function setValue (name :String, value :Object) :void
    {
        _data[name] = value;
        if (_so != null) {
            _so.flush(); // flushing is not strictly necessary
        }

        // dispatch an event corresponding
        dispatchEvent(new ConfigValueSetEvent(name, value));
    }

    /**
     * Remove any set value for the specified preference.
     * This does not dispatch an event because this would only be done to remove an
     * obsolete preference.
     */
    public function remove (name :String) :void
    {
        delete _data[name];
        if (_so != null) {
            _so.flush(); // flushing is not strictly necessary
        }
    }

    /**
     * Ensure that we can store preferences up to the specified size.
     * Note that calling this method may pop up a dialog to the user, asking
     * them if it's ok to increase the capacity. The result listener may
     * never be called if the user doesn't answer the pop-up.
     *
     * @param rl an optional listener that will be informed as to whether
     * the request succeeded.
     */
    public function ensureCapacity (kilobytes :int, rl :ResultListener) :void
    {
        if (_so == null) {
            if (rl != null) {
                rl.requestCompleted(this);
            }
            return;
        }

        // flush with the size, see if we're cool
        var result :String = _so.flush(1024 * kilobytes);
        if (rl == null) {
            return;
        }

        // success
        if (result == SharedObjectFlushStatus.FLUSHED) {
            rl.requestCompleted(this);
            return;
        }

        // otherwise we'll hear back in a sec
        var thisConfig :Config = this;
        var listener :Function = function (evt :NetStatusEvent) :void {
                // TODO: There is a bug where the status is always
                // "SharedObject.Flush.Failed", even on success, if the request
                // was for a large enough storage that the player calls it
                // "unlimited".
                trace("================[" + evt.info.code + "]");
                if ("SharedObject.Flush.Success" == evt.info.code) {
                    rl.requestCompleted(thisConfig);
                } else {
                    rl.requestFailed(new Error(String(evt.info.code)));
                }
                _so.removeEventListener(NetStatusEvent.NET_STATUS, listener);
            };

        _so.addEventListener(NetStatusEvent.NET_STATUS, listener);
    }

    /** The shared object that contains our preferences. */
    protected var _so :SharedObject;

    /** The object in which we store things, usually _so.data. */
    protected var _data :Object;
}
}

Allow to be constructed in, and flipped between, storing
preferences or just providing a runtime place for them to live.

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


package aerys.minko.type.clone
{
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.controller.AnimationController;
	import aerys.minko.scene.controller.TransformController;
	import aerys.minko.scene.controller.camera.CameraController;
	import aerys.minko.scene.controller.light.DirectionalLightController;
	import aerys.minko.scene.controller.light.LightController;
	import aerys.minko.scene.controller.light.PointLightController;
	import aerys.minko.scene.controller.light.SpotLightController;
	import aerys.minko.scene.controller.mesh.MeshController;
	import aerys.minko.scene.controller.mesh.MeshVisibilityController;
	import aerys.minko.scene.controller.mesh.skinning.SkinningController;

	public final class CloneOptions
	{
		private var _clonedControllerTypes		: Vector.<Class>;
		private var _ignoredControllerTypes		: Vector.<Class>;
		private var _reassignedControllerTypes	: Vector.<Class>;
		private var _defaultControllerAction	: uint;

		public function get defaultControllerAction() : uint
		{
			return _defaultControllerAction;
		}
		public function set defaultControllerAction(v : uint) : void
		{
			if (v != ControllerCloneAction.CLONE
				&& v != ControllerCloneAction.IGNORE
				&& v != ControllerCloneAction.REASSIGN)
				throw new Error('Unknown action type.');

			_defaultControllerAction = v;
		}

		public function CloneOptions()
		{
			initialize();
		}

		private function initialize() : void
		{
			_clonedControllerTypes = new <Class>[];
			_ignoredControllerTypes = new <Class>[];
			_reassignedControllerTypes = new <Class>[];
			_defaultControllerAction = ControllerCloneAction.REASSIGN;
		}

		public static function get defaultOptions() : CloneOptions
		{
			var cloneOptions : CloneOptions = new CloneOptions();

			cloneOptions._clonedControllerTypes.push(
				AnimationController,
				SkinningController
			);
			cloneOptions._ignoredControllerTypes.push(
				MeshController,
				MeshVisibilityController,
				CameraController,
				TransformController,
                LightController,
                DirectionalLightController,
                SpotLightController,
                PointLightController
			);
			cloneOptions._defaultControllerAction = ControllerCloneAction.REASSIGN;

			return cloneOptions;
		}

		public static function get cloneAllOptions() : CloneOptions
		{
			var cloneOptions : CloneOptions = new CloneOptions();

			cloneOptions._ignoredControllerTypes.push(
				MeshController,
				MeshVisibilityController,
				CameraController,
				TransformController,
                LightController,
                DirectionalLightController,
                SpotLightController,
                PointLightController
			);
			cloneOptions._defaultControllerAction = ControllerCloneAction.CLONE;

			return cloneOptions;
		}

		public function setActionForControllerClass(controllerClass	: Class,
													action			: uint) : CloneOptions
		{
			var index : int = 0;

			if ((index = _clonedControllerTypes.indexOf(controllerClass)) >= 0)
				_clonedControllerTypes.splice(index, 1);
			else if ((index = _ignoredControllerTypes.indexOf(controllerClass)) >= 0)
				_ignoredControllerTypes.splice(index, 1);
			else if ((index = _reassignedControllerTypes.indexOf(controllerClass)) >= 0)
				_reassignedControllerTypes.splice(index, 1);

			switch (action)
			{
				case ControllerCloneAction.CLONE:
					_clonedControllerTypes.push(controllerClass);
					break;

				case ControllerCloneAction.IGNORE:
					_ignoredControllerTypes.push(controllerClass);
					break;

				case ControllerCloneAction.REASSIGN:
					_reassignedControllerTypes.push(controllerClass);
					break;

				default:
					throw new Error('Unknown action type.');
			}

			return this;
		}

		public function getActionForController(controller : AbstractController) : uint
		{
			var numControllersToClone		: uint = _clonedControllerTypes.length;
			var numControllersToIgnore		: uint = _ignoredControllerTypes.length;
			var numControllersToReassign	: uint = _reassignedControllerTypes.length;
			var controllerId				: uint;

			for (controllerId = 0; controllerId < numControllersToClone; ++controllerId)
				if (controller is _clonedControllerTypes[controllerId])
					return ControllerCloneAction.CLONE;

			for (controllerId = 0; controllerId < numControllersToIgnore; ++controllerId)
				if (controller is _ignoredControllerTypes[controllerId])
					return ControllerCloneAction.IGNORE;

			for (controllerId = 0; controllerId < numControllersToReassign; ++controllerId)
				if (controller is _reassignedControllerTypes[controllerId])
					return ControllerCloneAction.REASSIGN;

			return _defaultControllerAction;
		}

	}
}

package aerys.minko.type.clone
{
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.controller.animation.AnimationController;
	import aerys.minko.scene.controller.TransformController;
	import aerys.minko.scene.controller.camera.CameraController;
	import aerys.minko.scene.controller.light.DirectionalLightController;
	import aerys.minko.scene.controller.light.LightController;
	import aerys.minko.scene.controller.light.PointLightController;
	import aerys.minko.scene.controller.light.SpotLightController;
	import aerys.minko.scene.controller.mesh.MeshController;
	import aerys.minko.scene.controller.mesh.MeshVisibilityController;
	import aerys.minko.scene.controller.mesh.skinning.SkinningController;

	public final class CloneOptions
	{
		private var _clonedControllerTypes		: Vector.<Class>;
		private var _ignoredControllerTypes		: Vector.<Class>;
		private var _reassignedControllerTypes	: Vector.<Class>;
		private var _defaultControllerAction	: uint;

		public function get defaultControllerAction() : uint
		{
			return _defaultControllerAction;
		}
		public function set defaultControllerAction(v : uint) : void
		{
			if (v != ControllerCloneAction.CLONE
				&& v != ControllerCloneAction.IGNORE
				&& v != ControllerCloneAction.REASSIGN)
				throw new Error('Unknown action type.');

			_defaultControllerAction = v;
		}

		public function CloneOptions()
		{
			initialize();
		}

		private function initialize() : void
		{
			_clonedControllerTypes = new <Class>[];
			_ignoredControllerTypes = new <Class>[];
			_reassignedControllerTypes = new <Class>[];
			_defaultControllerAction = ControllerCloneAction.REASSIGN;
		}

		public static function get defaultOptions() : CloneOptions
		{
			var cloneOptions : CloneOptions = new CloneOptions();

			cloneOptions._clonedControllerTypes.push(
				AnimationController,
				SkinningController
			);
			cloneOptions._ignoredControllerTypes.push(
				MeshController,
				MeshVisibilityController,
				CameraController,
				TransformController,
                LightController,
                DirectionalLightController,
                SpotLightController,
                PointLightController
			);
			cloneOptions._defaultControllerAction = ControllerCloneAction.REASSIGN;

			return cloneOptions;
		}

		public static function get cloneAllOptions() : CloneOptions
		{
			var cloneOptions : CloneOptions = new CloneOptions();

			cloneOptions._ignoredControllerTypes.push(
				MeshController,
				MeshVisibilityController,
				CameraController,
				TransformController,
                LightController,
                DirectionalLightController,
                SpotLightController,
                PointLightController
			);
			cloneOptions._defaultControllerAction = ControllerCloneAction.CLONE;

			return cloneOptions;
		}

		public function setActionForControllerClass(controllerClass	: Class,
													action			: uint) : CloneOptions
		{
			var index : int = 0;

			if ((index = _clonedControllerTypes.indexOf(controllerClass)) >= 0)
				_clonedControllerTypes.splice(index, 1);
			else if ((index = _ignoredControllerTypes.indexOf(controllerClass)) >= 0)
				_ignoredControllerTypes.splice(index, 1);
			else if ((index = _reassignedControllerTypes.indexOf(controllerClass)) >= 0)
				_reassignedControllerTypes.splice(index, 1);

			switch (action)
			{
				case ControllerCloneAction.CLONE:
					_clonedControllerTypes.push(controllerClass);
					break;

				case ControllerCloneAction.IGNORE:
					_ignoredControllerTypes.push(controllerClass);
					break;

				case ControllerCloneAction.REASSIGN:
					_reassignedControllerTypes.push(controllerClass);
					break;

				default:
					throw new Error('Unknown action type.');
			}

			return this;
		}

		public function getActionForController(controller : AbstractController) : uint
		{
			var numControllersToClone		: uint = _clonedControllerTypes.length;
			var numControllersToIgnore		: uint = _ignoredControllerTypes.length;
			var numControllersToReassign	: uint = _reassignedControllerTypes.length;
			var controllerId				: uint;

			for (controllerId = 0; controllerId < numControllersToClone; ++controllerId)
				if (controller is _clonedControllerTypes[controllerId])
					return ControllerCloneAction.CLONE;

			for (controllerId = 0; controllerId < numControllersToIgnore; ++controllerId)
				if (controller is _ignoredControllerTypes[controllerId])
					return ControllerCloneAction.IGNORE;

			for (controllerId = 0; controllerId < numControllersToReassign; ++controllerId)
				if (controller is _reassignedControllerTypes[controllerId])
					return ControllerCloneAction.REASSIGN;

			return _defaultControllerAction;
		}

	}
}

fix broken import of AnimationController


package
{
	import com.*;
	import com.errors.RuntimeError;
	import com.events.*;

	import flash.display.LoaderInfo;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.external.ExternalInterface;
	import flash.system.ApplicationDomain;
	import flash.system.Security;
	import flash.utils.*;

	import mxi.Extend;
	import mxi.Utils;
	import mxi.events.ODataEvent;
	import mxi.events.OErrorEvent;
	import mxi.events.OProgressEvent;

	[SWF(width='500', height='500')]
	public class Moxie extends Sprite
	{
		public static var uid:String;

		private var eventDispatcher:String = "moxie.core.EventTarget.instance.dispatchEvent";

		public static var compFactory:ComponentFactory;

		public static var stageOccupied:Boolean = false; // whether a display facility is already occupied

		/**
		 * Main constructor for the Plupload class.
		 */
		public function Moxie()
		{
			if (stage) {
				_init();
			} else {
				addEventListener(Event.ADDED_TO_STAGE, init);
			}
		}

		/**
		 * Initialization event handler.
		 *
		 * @param e Event object.
		 */
		private function _init(e:Event = null):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, _init);

			// Allow scripting on swf loaded from another domain
			if (MXI::EnableCSS) {
				Security.allowDomain("*");
			}

			// Align and scale stage
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.EXACT_FIT;

			// use only FlashVars, ignore QueryString
			var params:Object, url:String, urlParts:Object, pos:int, query:Object;

			params = root.loaderInfo.parameters;
			pos = root.loaderInfo.url.indexOf('?');
			if (pos !== -1) {
				query = Utils.parseStr(root.loaderInfo.url.substr(pos + 1));

				for (var key:String in params) {
					if (query.hasOwnProperty(Utils.trim(key))) {
						delete params[key];
					}
				}
			}

			// Setup id
			if (!params.hasOwnProperty("uid")) {
				return; // we do not have uid, so we cannot fire error event - lets simply wait until it timeouts
			}

			Moxie.uid = Utils.sanitize(params["uid"]);

			// Event dispatcher
			if (params.hasOwnProperty("target") && /^[\w\.]+$/.test(params["target"])) {
				eventDispatcher = params["target"];
			}

			//ExternalInterface.marshallExceptions = true; // propagate AS exceptions to JS and vice-versa
			ExternalInterface.addCallback('exec', exec);
			ExternalInterface.addCallback('isOccupied', isOccupied);

			// initialize component factory
			Moxie.compFactory = new ComponentFactory;

			_fireEvent(Moxie.uid + "::Init");
		}

		public function exec(uid:String, compName:String, action:String, args:* = null) : *
		{
			// Moxie.log([uid, compName, action, args]);

			uid = Utils.sanitize(uid); // make it safe

			var comp:* = Moxie.compFactory.get(uid);

			// Moxie.log([compName, action]);

			try {
				// initialize corresponding com
				if (!comp) {
					comp = Moxie.compFactory.create(this, uid, compName);
				}

				// execute the action if available
				if (comp.hasOwnProperty(action)) {
					return comp[action].apply(comp, args as Array);
				}
			} catch(err:*) { // re-route exceptions thrown by components (TODO: check marshallExceptions feature)
				_fireEvent(Moxie.uid + "::Exception", {
					name: err.name,
					code: err.errorID,
					message: compName + "::" + action
				});
			}
		}

		public function isOccupied() : Boolean {
			return Moxie.stageOccupied;
		}

		/**
		 * Intercept component events and do some operations if required
		 *
		 * @param uid String unique identifier of the component throwing the event
		 * @param e mixed Event object
		 * @param exType String event type in mOxie format
		 */
		public function onComponentEvent(uid:String, e:*, exType:String) : void
		{
			var evt:Object = {};
			var data:* = e.hasOwnProperty('data') ? e.data : null;

			switch (e.type)
			{
				case ProgressEvent.PROGRESS:
				case OProgressEvent.PROGRESS:
					evt.loaded = e.bytesLoaded;
					evt.total = e.bytesTotal;
					break;

				case OErrorEvent.ERROR:
					data = e.code;
					break;
			}

			evt.type = [uid, exType].join('::');

			_fireEvent(evt, data);
		}

		/**
		 * Fires an event from the flash movie out to the page level JS.
		 *
		 * @param uid String unique identifier of the component throwing the event
		 * @param type Name of event to fire.
		 * @param obj Object with optional data.
		 */
		private function _fireEvent(evt:*, obj:* = null):void {
			try {
				ExternalInterface.call(eventDispatcher, evt, obj);
			} catch(err:*) {
				//_fireEvent("Exception", { name: 'RuntimeError', message: 4 });

				// throwing an exception would be better here
			}
		}

		public static function log(obj:*) : void {
			ExternalInterface.call('console.info', obj);
		}

	}
}

package
{
	import com.*;
	import com.errors.RuntimeError;
	import com.events.*;

	import flash.display.LoaderInfo;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.external.ExternalInterface;
	import flash.system.ApplicationDomain;
	import flash.system.Security;
	import flash.utils.*;

	import mxi.Extend;
	import mxi.Utils;
	import mxi.events.ODataEvent;
	import mxi.events.OErrorEvent;
	import mxi.events.OProgressEvent;

	[SWF(width='500', height='500')]
	public class Moxie extends Sprite
	{
		public static var uid:String;

		private var eventDispatcher:String = "moxie.core.EventTarget.instance.dispatchEvent";

		public static var compFactory:ComponentFactory;

		public static var stageOccupied:Boolean = false; // whether a display facility is already occupied

		/**
		 * Main constructor for the Plupload class.
		 */
		public function Moxie()
		{
			if (stage) {
				_init();
			} else {
				addEventListener(Event.ADDED_TO_STAGE, init);
			}
		}

		/**
		 * Initialization event handler.
		 *
		 * @param e Event object.
		 */
		private function _init(e:Event = null):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, _init);

			// Allow scripting on swf loaded from another domain
			if (MXI::EnableCSS) {
				Security.allowDomain("*");
			}

			// Align and scale stage
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.EXACT_FIT;

			// use only FlashVars, ignore QueryString
			var params:Object, url:String, urlParts:Object, pos:int, query:Object;

			params = root.loaderInfo.parameters;

			pos = root.loaderInfo.url.indexOf('?');
			if (pos !== -1) {
				return; // we do not allow anything from query sring, so if there's anything, we - quit
			}

			// Setup id
			if (!params.hasOwnProperty("uid")) {
				return; // we do not have uid, so we cannot fire error event - lets simply wait until it timeouts
			}

			Moxie.uid = Utils.sanitize(params["uid"]);

			// Event dispatcher
			if (params.hasOwnProperty("target") && /^[\w\.]+$/.test(params["target"])) {
				eventDispatcher = params["target"];
			}

			//ExternalInterface.marshallExceptions = true; // propagate AS exceptions to JS and vice-versa
			ExternalInterface.addCallback('exec', exec);
			ExternalInterface.addCallback('isOccupied', isOccupied);

			// initialize component factory
			Moxie.compFactory = new ComponentFactory;

			_fireEvent(Moxie.uid + "::Init");
		}

		public function exec(uid:String, compName:String, action:String, args:* = null) : *
		{
			// Moxie.log([uid, compName, action, args]);

			uid = Utils.sanitize(uid); // make it safe

			var comp:* = Moxie.compFactory.get(uid);

			// Moxie.log([compName, action]);

			try {
				// initialize corresponding com
				if (!comp) {
					comp = Moxie.compFactory.create(this, uid, compName);
				}

				// execute the action if available
				if (comp.hasOwnProperty(action)) {
					return comp[action].apply(comp, args as Array);
				}
			} catch(err:*) { // re-route exceptions thrown by components (TODO: check marshallExceptions feature)
				_fireEvent(Moxie.uid + "::Exception", {
					name: err.name,
					code: err.errorID,
					message: compName + "::" + action
				});
			}
		}

		public function isOccupied() : Boolean {
			return Moxie.stageOccupied;
		}

		/**
		 * Intercept component events and do some operations if required
		 *
		 * @param uid String unique identifier of the component throwing the event
		 * @param e mixed Event object
		 * @param exType String event type in mOxie format
		 */
		public function onComponentEvent(uid:String, e:*, exType:String) : void
		{
			var evt:Object = {};
			var data:* = e.hasOwnProperty('data') ? e.data : null;

			switch (e.type)
			{
				case ProgressEvent.PROGRESS:
				case OProgressEvent.PROGRESS:
					evt.loaded = e.bytesLoaded;
					evt.total = e.bytesTotal;
					break;

				case OErrorEvent.ERROR:
					data = e.code;
					break;
			}

			evt.type = [uid, exType].join('::');

			_fireEvent(evt, data);
		}

		/**
		 * Fires an event from the flash movie out to the page level JS.
		 *
		 * @param uid String unique identifier of the component throwing the event
		 * @param type Name of event to fire.
		 * @param obj Object with optional data.
		 */
		private function _fireEvent(evt:*, obj:* = null):void {
			try {
				ExternalInterface.call(eventDispatcher, evt, obj);
			} catch(err:*) {
				//_fireEvent("Exception", { name: 'RuntimeError', message: 4 });

				// throwing an exception would be better here
			}
		}

		public static function log(obj:*) : void {
			ExternalInterface.call('console.info', obj);
		}

	}
}

Flash: Address the problem with arguments from query string.


//
// Flump - Copyright 2013 Flump Authors

package flump.xfl {

import aspire.util.XmlUtil;

import flump.mold.KeyframeMold;
import flump.mold.LayerMold;

public class XflLayer
{
    public static const NAME :String = "name";
    public static const TYPE :String = "layerType";

    public static const TYPE_GUIDE :String = "guide";
    public static const TYPE_FOLDER :String = "folder";
	public static const TYPE_MASK :String = "mask";

    use namespace xflns;

    public static function parse (lib :XflLibrary, baseLocation :String, xml :XML, flipbook :Boolean) :LayerMold {
        const layer :LayerMold = new LayerMold();
        layer.name = XmlUtil.getStringAttr(xml, NAME);
        layer.flipbook = flipbook;
		// mask
		if (mask!=null) layer.mask = mask;
        const location :String = baseLocation + ":" + layer.name;
        var frameXmlList :XMLList = xml.frames.DOMFrame;
        for each (var frameXml :XML in frameXmlList) {
            layer.keyframes.push(XflKeyframe.parse(lib, location, frameXml, flipbook));
        }
        if (layer.keyframes.length == 0) lib.addError(location, ParseError.INFO, "No keyframes on layer");

        var ii :int;
        var kf :KeyframeMold;
        var nextKf :KeyframeMold;

        // normalize skews, so that we always skew the shortest distance between
        // two angles (we don't want to skew more than Math.PI)
        for (ii = 0; ii < layer.keyframes.length - 1; ++ii) {
            kf = layer.keyframes[ii];
            nextKf = layer.keyframes[ii+1];
            frameXml = frameXmlList[ii];

            if (kf.skewX + Math.PI < nextKf.skewX) {
                nextKf.skewX += -Math.PI * 2;
            } else if (kf.skewX - Math.PI > nextKf.skewX) {
                nextKf.skewX += Math.PI * 2;
            }
            if (kf.skewY + Math.PI < nextKf.skewY) {
                nextKf.skewY += -Math.PI * 2;
            } else if (kf.skewY - Math.PI > nextKf.skewY) {
                nextKf.skewY += Math.PI * 2;
            }
        }

        // apply additional rotations
        var additionalRotation :Number = 0;
        for (ii = 0; ii < layer.keyframes.length - 1; ++ii) {
            kf = layer.keyframes[ii];
            nextKf = layer.keyframes[ii+1];
            frameXml = frameXmlList[ii];

            var motionTweenRotate :String = XmlUtil.getStringAttr(frameXml,
                XflKeyframe.MOTION_TWEEN_ROTATE, XflKeyframe.MOTION_TWEEN_ROTATE_NONE);

            // If a direction is specified, take it into account
            if (motionTweenRotate != XflKeyframe.MOTION_TWEEN_ROTATE_NONE) {
                var direction :Number = (motionTweenRotate == XflKeyframe.MOTION_TWEEN_ROTATE_CLOCKWISE ? 1 : -1);
                // negative scales affect rotation direction
                direction *= sign(nextKf.scaleX) * sign(nextKf.scaleY);

                while (direction < 0 && kf.skewX < nextKf.skewX) {
                    nextKf.skewX -= Math.PI * 2;
                }
                while (direction > 0 && kf.skewX > nextKf.skewX) {
                    nextKf.skewX += Math.PI * 2;
                }
                while (direction < 0 && kf.skewY < nextKf.skewY) {
                    nextKf.skewY -= Math.PI * 2;
                }
                while (direction > 0 && kf.skewY > nextKf.skewY) {
                    nextKf.skewY += Math.PI * 2;
                }

                // additional rotations specified?
                var motionTweenRotateTimes :Number =
                    XmlUtil.getNumberAttr(frameXml, XflKeyframe.MOTION_TWEEN_ROTATE_TIMES, 0);
                var thisRotation :Number = motionTweenRotateTimes * Math.PI * 2 * direction;
                additionalRotation += thisRotation;
            }

            nextKf.rotate(additionalRotation);
        }

        return layer;
    }

    protected static function sign (n :Number) :Number {
        return (n > 0 ? 1 : (n < 0 ? -1 : 0));
    }
}
}

//
// Flump - Copyright 2013 Flump Authors

package flump.xfl {

import aspire.util.XmlUtil;

import flump.mold.KeyframeMold;
import flump.mold.LayerMold;

public class XflLayer
{
    public static const NAME :String = "name";
    public static const TYPE :String = "layerType";

    public static const TYPE_GUIDE :String = "guide";
    public static const TYPE_FOLDER :String = "folder";
	public static const TYPE_MASK :String = "mask";

    use namespace xflns;

    public static function parse (lib :XflLibrary, baseLocation :String, xml :XML, flipbook :Boolean,mask:String=null) :LayerMold {
        const layer :LayerMold = new LayerMold();
        layer.name = XmlUtil.getStringAttr(xml, NAME);
        layer.flipbook = flipbook;
		// mask
		if (mask!=null) layer.mask = mask;
        const location :String = baseLocation + ":" + layer.name;
        var frameXmlList :XMLList = xml.frames.DOMFrame;
        for each (var frameXml :XML in frameXmlList) {
            layer.keyframes.push(XflKeyframe.parse(lib, location, frameXml, flipbook));
        }
        if (layer.keyframes.length == 0) lib.addError(location, ParseError.INFO, "No keyframes on layer");

        var ii :int;
        var kf :KeyframeMold;
        var nextKf :KeyframeMold;

        // normalize skews, so that we always skew the shortest distance between
        // two angles (we don't want to skew more than Math.PI)
        for (ii = 0; ii < layer.keyframes.length - 1; ++ii) {
            kf = layer.keyframes[ii];
            nextKf = layer.keyframes[ii+1];
            frameXml = frameXmlList[ii];

            if (kf.skewX + Math.PI < nextKf.skewX) {
                nextKf.skewX += -Math.PI * 2;
            } else if (kf.skewX - Math.PI > nextKf.skewX) {
                nextKf.skewX += Math.PI * 2;
            }
            if (kf.skewY + Math.PI < nextKf.skewY) {
                nextKf.skewY += -Math.PI * 2;
            } else if (kf.skewY - Math.PI > nextKf.skewY) {
                nextKf.skewY += Math.PI * 2;
            }
        }

        // apply additional rotations
        var additionalRotation :Number = 0;
        for (ii = 0; ii < layer.keyframes.length - 1; ++ii) {
            kf = layer.keyframes[ii];
            nextKf = layer.keyframes[ii+1];
            frameXml = frameXmlList[ii];

            var motionTweenRotate :String = XmlUtil.getStringAttr(frameXml,
                XflKeyframe.MOTION_TWEEN_ROTATE, XflKeyframe.MOTION_TWEEN_ROTATE_NONE);

            // If a direction is specified, take it into account
            if (motionTweenRotate != XflKeyframe.MOTION_TWEEN_ROTATE_NONE) {
                var direction :Number = (motionTweenRotate == XflKeyframe.MOTION_TWEEN_ROTATE_CLOCKWISE ? 1 : -1);
                // negative scales affect rotation direction
                direction *= sign(nextKf.scaleX) * sign(nextKf.scaleY);

                while (direction < 0 && kf.skewX < nextKf.skewX) {
                    nextKf.skewX -= Math.PI * 2;
                }
                while (direction > 0 && kf.skewX > nextKf.skewX) {
                    nextKf.skewX += Math.PI * 2;
                }
                while (direction < 0 && kf.skewY < nextKf.skewY) {
                    nextKf.skewY -= Math.PI * 2;
                }
                while (direction > 0 && kf.skewY > nextKf.skewY) {
                    nextKf.skewY += Math.PI * 2;
                }

                // additional rotations specified?
                var motionTweenRotateTimes :Number =
                    XmlUtil.getNumberAttr(frameXml, XflKeyframe.MOTION_TWEEN_ROTATE_TIMES, 0);
                var thisRotation :Number = motionTweenRotateTimes * Math.PI * 2 * direction;
                additionalRotation += thisRotation;
            }

            nextKf.rotate(additionalRotation);
        }

        return layer;
    }

    protected static function sign (n :Number) :Number {
        return (n > 0 ? 1 : (n < 0 ? -1 : 0));
    }
}
}

fix XflLayer parse signature

fix XflLayer parse signature


package goplayer
{
  import flash.utils.getTimer

  import streamio.Stat

  public class Player
    implements FlashNetConnectionListener, FlashNetStreamListener
  {
    private const DEFAULT_VOLUME : Number = .8

    private const START_BUFFER : Duration = Duration.seconds(3)
    private const SMALL_BUFFER : Duration = Duration.seconds(5)
    private const LARGE_BUFFER : Duration = Duration.seconds(60)
    private const SEEK_GRACE_TIME : Duration = Duration.seconds(2)

    private const finishingListeners : Array = []

    private var connection : FlashNetConnection
    private var _movie : Movie
    private var bitratePolicy : BitratePolicy
    private var enableRTMP : Boolean

    private var _started : Boolean = false
    private var _finished : Boolean = false
    private var triedStandardRTMP : Boolean = false
    private var _usingRTMP : Boolean = false

    private var measuredBandwidth : Bitrate = null
    private var measuredLatency : Duration = null

    private var stream : FlashNetStream = null
    private var metadata : Object = null

    private var _volume : Number = DEFAULT_VOLUME
    private var savedVolume : Number = 0
    private var _paused : Boolean = false
    private var _buffering : Boolean = false
    private var seekStopwatch : Stopwatch = new Stopwatch

    public function Player
      (connection : FlashNetConnection,
       movie : Movie,
       bitratePolicy : BitratePolicy,
       enableRTMP : Boolean)
    {
      this.connection = connection
      _movie = movie
      this.bitratePolicy = bitratePolicy
      this.enableRTMP = enableRTMP

      Stat.view(movie.id)

      connection.listener = this
    }

    public function get movie() : Movie
    { return _movie }

    public function addFinishingListener
      (value : PlayerFinishingListener) : void
    { finishingListeners.push(value) }

    public function get started() : Boolean
    { return _started }

    public function get running() : Boolean
    { return started && !finished }

    public function get finished() : Boolean
    { return _finished }

    // -----------------------------------------------------

    public function start() : void
    {
      _started = true

      if (enableRTMP && movie.rtmpURL != null)
        connectUsingRTMP()
      else
        connectUsingHTTP()

      Stat.play(movie.id)
    }

    public function handleConnectionFailed() : void
    {
      if (movie.rtmpURL.hasPort && !triedStandardRTMP)
        handleCustomRTMPUnavailable()
      else
        handleRTMPUnavailable()
    }

    private function handleCustomRTMPUnavailable() : void
    {
      debug("Trying to connect using default RTMP ports.")
      connectUsingStandardRTMP()
    }

    private function handleRTMPUnavailable() : void
    {
      debug("Could not connect using RTMP; trying plain HTTP.")
      connectUsingHTTP()
    }

    public function get usingRTMP() : Boolean
    { return _usingRTMP }

    private function connectUsingRTMP() : void
    { connection.connect(movie.rtmpURL) }

    private function connectUsingStandardRTMP() : void
    {
      triedStandardRTMP = true
      connection.connect(movie.rtmpURL.withoutPort)
    }

    private function connectUsingHTTP() : void
    {
      _usingRTMP = false
      connection.dontConnect()
      startPlaying()
    }

    public function handleConnectionClosed() : void
    {}

    // -----------------------------------------------------

    public function handleConnectionEstablished() : void
    {
      _usingRTMP = true

      if (bandwidthDeterminationNeeded)
        connection.determineBandwidth()
      else
        startPlaying()
    }

    private function get bandwidthDeterminationNeeded() : Boolean
    { return bitratePolicy == BitratePolicy.BEST }

    public function handleBandwidthDetermined
      (bandwidth : Bitrate, latency : Duration) : void
    {
      measuredBandwidth = bandwidth
      measuredLatency = latency

      startPlaying()
    }

    private function get bandwidthDetermined() : Boolean
    { return measuredBandwidth != null }

    private function startPlaying() : void
    {
      stream = connection.getNetStream()
      stream.listener = this
      stream.volume = volume

      useStartBuffer()

      if (usingRTMP)
        playRTMPStream()
      else
        playHTTPStream()

      if (paused)
        stream.paused = true
    }

    private function playRTMPStream() : void
    { stream.playRTMP(streamPicker.first, streamPicker.all) }

    private function playHTTPStream() : void
    { stream.playHTTP(movie.httpURL) }

    private function get streamPicker() : RTMPStreamPicker
    {
      return new RTMPStreamPicker
        (movie.rtmpStreams, bitratePolicy, measuredBandwidth)
    }

    // -----------------------------------------------------

    public function handleNetStreamMetadata(data : Object) : void
    {
      metadata = data

      if (!usingRTMP)
        debug("Video file size: " + stream.httpFileSize)
    }

    public function handleStreamingStarted() : void
    {
      useStartBuffer()
      _buffering = true
      _finished = false
    }

    public function handleBufferFilled() : void
    { handleBufferingFinished() }

    private function handleBufferingFinished() : void
    {
      _buffering = false

      // Give the backend a chance to resume playback before we go
      // ahead and raise the buffer size further.
      later($handleBufferingFinished)
    }

    private function $handleBufferingFinished() : void
    {
      // Make sure playback was not interrupted.
      if (!buffering && !finished)
        useLargeBuffer()
    }

    public function handleBufferEmptied() : void
    {
      if (finishedPlaying)
        handleFinishedPlaying()
      else
        handleBufferingStarted()
    }

    private function handleBufferingStarted() : void
    {
      useSmallBuffer()
      _buffering = true
      _finished = false
    }

    private function useStartBuffer() : void
    { stream.bufferTime = START_BUFFER }

    private function useSmallBuffer() : void
    { stream.bufferTime = SMALL_BUFFER }

    private function useLargeBuffer() : void
    { stream.bufferTime = LARGE_BUFFER }

    public function get buffering() : Boolean
    { return _buffering }

    public function get bufferingUnexpectedly() : Boolean
    { return buffering && !justSeeked }

    private function get justSeeked() : Boolean
    { return seekStopwatch.within(SEEK_GRACE_TIME) }

    public function handleStreamingStopped() : void
    {
      if (finishedPlaying)
        handleFinishedPlaying()
    }

    private function handleFinishedPlaying() : void
    {
      debug("Finished playing.")
      _finished = true

      for each (var listener : PlayerFinishingListener in finishingListeners)
        listener.handleMovieFinishedPlaying()
    }

    private function get finishedPlaying() : Boolean
    { return timeRemaining.seconds < 1 }

    private function get timeRemaining() : Duration
    { return streamLength.minus(playheadPosition) }

    // -----------------------------------------------------

    public function get paused() : Boolean
    { return _paused }

    public function set paused(value : Boolean) : void
    {
      // Allow pausing before the stream has been created.
      _paused = value

      if (stream)
        stream.paused = value
    }

    public function togglePaused() : void
    {
      // Forbid pausing before the stream has been created.
      if (stream)
        paused = !paused
    }

    // -----------------------------------------------------

    public function get playheadPosition() : Duration
    {
      return stream != null && stream.playheadPosition != null
        ? stream.playheadPosition : Duration.ZERO
    }

    public function get playheadRatio() : Number
    { return getRatio(playheadPosition.seconds, streamLength.seconds) }

    public function get bufferRatio() : Number
    { return getRatio(bufferPosition.seconds, streamLength.seconds) }

    private function getRatio
      (numerator : Number, denominator : Number) : Number
    { return Math.min(1, numerator / denominator) }

    private function get bufferPosition() : Duration
    { return playheadPosition.plus(bufferLength) }

    public function set playheadPosition(value : Duration) : void
    {
      if (stream)
        $playheadPosition = value
    }

    private function set $playheadPosition(value : Duration) : void
    {
      _finished = false
      seekStopwatch.start()
      useStartBuffer()
      stream.playheadPosition = value
    }

    public function set playheadRatio(value : Number) : void
    { playheadPosition = streamLength.scaledBy(value) }

    public function seekBy(delta : Duration) : void
    { playheadPosition = playheadPosition.plus(delta) }

    public function rewind() : void
    { playheadPosition = Duration.ZERO }

    public function get playing() : Boolean
    { return stream != null && !paused && !finished }

    // -----------------------------------------------------

    public function get volume() : Number
    { return _volume }

    public function set volume(value : Number) : void
    {
      _volume = Math.round(clamp(value, 0, 1) * 100) / 100

      if (stream)
        stream.volume = value
    }

    public function changeVolumeBy(delta : Number) : void
    { volume = volume + delta }

    public function mute() : void
    {
      savedVolume = volume
      volume = 0
    }

    public function unmute() : void
    { volume = savedVolume == 0 ? DEFAULT_VOLUME : savedVolume }

    public function get muted() : Boolean
    { return volume == 0 }

    // -----------------------------------------------------

    public function get aspectRatio() : Number
    { return movie.aspectRatio }

    public function get bufferTime() : Duration
    {
      return stream != null && stream.bufferTime != null
        ? stream.bufferTime : START_BUFFER
    }

    public function get bufferLength() : Duration
    {
      return stream != null && stream.bufferLength != null
        ? stream.bufferLength : Duration.ZERO
    }

    public function get bufferFillRatio() : Number
    { return getRatio(bufferLength.seconds, bufferTime.seconds) }

    public function get streamLength() : Duration
    {
      return metadata != null
        ? Duration.seconds(metadata.duration)
        : movie.duration
    }

    public function get currentBitrate() : Bitrate
    {
      return stream != null && stream.bitrate != null
        ? stream.bitrate : Bitrate.ZERO
    }

    public function get currentBandwidth() : Bitrate
    {
      return stream != null && stream.bandwidth != null
        ? stream.bandwidth : Bitrate.ZERO
    }

    public function get highQualityDimensions() : Dimensions
    {
      var result : Dimensions = Dimensions.ZERO

      for each (var stream : RTMPStream in movie.rtmpStreams)
        if (stream.dimensions.isGreaterThan(result))
          result = stream.dimensions

      return result
    }
  }
}

package goplayer
{
  import flash.utils.getTimer
  import flash.net.SharedObject

  import streamio.Stat

  public class Player
    implements FlashNetConnectionListener, FlashNetStreamListener
  {
    private const DEFAULT_VOLUME : Number = .8

    private const START_BUFFER : Duration = Duration.seconds(3)
    private const SMALL_BUFFER : Duration = Duration.seconds(5)
    private const LARGE_BUFFER : Duration = Duration.seconds(60)
    private const SEEK_GRACE_TIME : Duration = Duration.seconds(2)

    private const finishingListeners : Array = []

    private var sharedObject : SharedObject = null

    private var connection : FlashNetConnection
    private var _movie : Movie
    private var bitratePolicy : BitratePolicy
    private var enableRTMP : Boolean

    private var _started : Boolean = false
    private var _finished : Boolean = false
    private var triedStandardRTMP : Boolean = false
    private var _usingRTMP : Boolean = false

    private var measuredBandwidth : Bitrate = null
    private var measuredLatency : Duration = null

    private var stream : FlashNetStream = null
    private var metadata : Object = null

    private var _volume : Number = NaN
    private var savedVolume : Number = 0
    private var _paused : Boolean = false
    private var _buffering : Boolean = false
    private var seekStopwatch : Stopwatch = new Stopwatch

    public function Player
      (connection : FlashNetConnection,
       movie : Movie,
       bitratePolicy : BitratePolicy,
       enableRTMP : Boolean)
    {
      this.connection = connection
      _movie = movie
      this.bitratePolicy = bitratePolicy
      this.enableRTMP = enableRTMP

      connection.listener = this

      Stat.view(movie.id)

      try
        { sharedObject = SharedObject.getLocal("player") }
      catch (error : Error)
        {}

      if (sharedObject && sharedObject.size > 0)
        volume = sharedObject.data["volume"]
      else
        volume = DEFAULT_VOLUME
    }

    public function get movie() : Movie
    { return _movie }

    public function addFinishingListener
      (value : PlayerFinishingListener) : void
    { finishingListeners.push(value) }

    public function get started() : Boolean
    { return _started }

    public function get running() : Boolean
    { return started && !finished }

    public function get finished() : Boolean
    { return _finished }

    // -----------------------------------------------------

    public function start() : void
    {
      _started = true

      if (enableRTMP && movie.rtmpURL != null)
        connectUsingRTMP()
      else
        connectUsingHTTP()

      Stat.play(movie.id)
    }

    public function handleConnectionFailed() : void
    {
      if (movie.rtmpURL.hasPort && !triedStandardRTMP)
        handleCustomRTMPUnavailable()
      else
        handleRTMPUnavailable()
    }

    private function handleCustomRTMPUnavailable() : void
    {
      debug("Trying to connect using default RTMP ports.")
      connectUsingStandardRTMP()
    }

    private function handleRTMPUnavailable() : void
    {
      debug("Could not connect using RTMP; trying plain HTTP.")
      connectUsingHTTP()
    }

    public function get usingRTMP() : Boolean
    { return _usingRTMP }

    private function connectUsingRTMP() : void
    { connection.connect(movie.rtmpURL) }

    private function connectUsingStandardRTMP() : void
    {
      triedStandardRTMP = true
      connection.connect(movie.rtmpURL.withoutPort)
    }

    private function connectUsingHTTP() : void
    {
      _usingRTMP = false
      connection.dontConnect()
      startPlaying()
    }

    public function handleConnectionClosed() : void
    {}

    // -----------------------------------------------------

    public function handleConnectionEstablished() : void
    {
      _usingRTMP = true

      if (bandwidthDeterminationNeeded)
        connection.determineBandwidth()
      else
        startPlaying()
    }

    private function get bandwidthDeterminationNeeded() : Boolean
    { return bitratePolicy == BitratePolicy.BEST }

    public function handleBandwidthDetermined
      (bandwidth : Bitrate, latency : Duration) : void
    {
      measuredBandwidth = bandwidth
      measuredLatency = latency

      startPlaying()
    }

    private function get bandwidthDetermined() : Boolean
    { return measuredBandwidth != null }

    private function startPlaying() : void
    {
      stream = connection.getNetStream()
      stream.listener = this
      stream.volume = volume

      useStartBuffer()

      if (usingRTMP)
        playRTMPStream()
      else
        playHTTPStream()

      if (paused)
        stream.paused = true
    }

    private function playRTMPStream() : void
    { stream.playRTMP(streamPicker.first, streamPicker.all) }

    private function playHTTPStream() : void
    { stream.playHTTP(movie.httpURL) }

    private function get streamPicker() : RTMPStreamPicker
    {
      return new RTMPStreamPicker
        (movie.rtmpStreams, bitratePolicy, measuredBandwidth)
    }

    // -----------------------------------------------------

    public function handleNetStreamMetadata(data : Object) : void
    {
      metadata = data

      if (!usingRTMP)
        debug("Video file size: " + stream.httpFileSize)
    }

    public function handleStreamingStarted() : void
    {
      useStartBuffer()
      _buffering = true
      _finished = false
    }

    public function handleBufferFilled() : void
    { handleBufferingFinished() }

    private function handleBufferingFinished() : void
    {
      _buffering = false

      // Give the backend a chance to resume playback before we go
      // ahead and raise the buffer size further.
      later($handleBufferingFinished)
    }

    private function $handleBufferingFinished() : void
    {
      // Make sure playback was not interrupted.
      if (!buffering && !finished)
        useLargeBuffer()
    }

    public function handleBufferEmptied() : void
    {
      if (finishedPlaying)
        handleFinishedPlaying()
      else
        handleBufferingStarted()
    }

    private function handleBufferingStarted() : void
    {
      useSmallBuffer()
      _buffering = true
      _finished = false
    }

    private function useStartBuffer() : void
    { stream.bufferTime = START_BUFFER }

    private function useSmallBuffer() : void
    { stream.bufferTime = SMALL_BUFFER }

    private function useLargeBuffer() : void
    { stream.bufferTime = LARGE_BUFFER }

    public function get buffering() : Boolean
    { return _buffering }

    public function get bufferingUnexpectedly() : Boolean
    { return buffering && !justSeeked }

    private function get justSeeked() : Boolean
    { return seekStopwatch.within(SEEK_GRACE_TIME) }

    public function handleStreamingStopped() : void
    {
      if (finishedPlaying)
        handleFinishedPlaying()
    }

    private function handleFinishedPlaying() : void
    {
      debug("Finished playing.")
      _finished = true

      for each (var listener : PlayerFinishingListener in finishingListeners)
        listener.handleMovieFinishedPlaying()
    }

    private function get finishedPlaying() : Boolean
    { return timeRemaining.seconds < 1 }

    private function get timeRemaining() : Duration
    { return streamLength.minus(playheadPosition) }

    // -----------------------------------------------------

    public function get paused() : Boolean
    { return _paused }

    public function set paused(value : Boolean) : void
    {
      // Allow pausing before the stream has been created.
      _paused = value

      if (stream)
        stream.paused = value
    }

    public function togglePaused() : void
    {
      // Forbid pausing before the stream has been created.
      if (stream)
        paused = !paused
    }

    // -----------------------------------------------------

    public function get playheadPosition() : Duration
    {
      return stream != null && stream.playheadPosition != null
        ? stream.playheadPosition : Duration.ZERO
    }

    public function get playheadRatio() : Number
    { return getRatio(playheadPosition.seconds, streamLength.seconds) }

    public function get bufferRatio() : Number
    { return getRatio(bufferPosition.seconds, streamLength.seconds) }

    private function getRatio
      (numerator : Number, denominator : Number) : Number
    { return Math.min(1, numerator / denominator) }

    private function get bufferPosition() : Duration
    { return playheadPosition.plus(bufferLength) }

    public function set playheadPosition(value : Duration) : void
    {
      if (stream)
        $playheadPosition = value
    }

    private function set $playheadPosition(value : Duration) : void
    {
      _finished = false
      seekStopwatch.start()
      useStartBuffer()
      stream.playheadPosition = value
    }

    public function set playheadRatio(value : Number) : void
    { playheadPosition = streamLength.scaledBy(value) }

    public function seekBy(delta : Duration) : void
    { playheadPosition = playheadPosition.plus(delta) }

    public function rewind() : void
    { playheadPosition = Duration.ZERO }

    public function get playing() : Boolean
    { return stream != null && !paused && !finished }

    // -----------------------------------------------------

    public function get volume() : Number
    { return _volume }

    public function set volume(value : Number) : void
    {
      _volume = Math.round(clamp(value, 0, 1) * 100) / 100

      if (sharedObject)
        sharedObject.data["volume"] = volume

      if (stream)
        stream.volume = volume
    }

    public function changeVolumeBy(delta : Number) : void
    { volume = volume + delta }

    public function mute() : void
    {
      savedVolume = volume
      volume = 0
    }

    public function unmute() : void
    { volume = savedVolume == 0 ? DEFAULT_VOLUME : savedVolume }

    public function get muted() : Boolean
    { return volume == 0 }

    // -----------------------------------------------------

    public function get aspectRatio() : Number
    { return movie.aspectRatio }

    public function get bufferTime() : Duration
    {
      return stream != null && stream.bufferTime != null
        ? stream.bufferTime : START_BUFFER
    }

    public function get bufferLength() : Duration
    {
      return stream != null && stream.bufferLength != null
        ? stream.bufferLength : Duration.ZERO
    }

    public function get bufferFillRatio() : Number
    { return getRatio(bufferLength.seconds, bufferTime.seconds) }

    public function get streamLength() : Duration
    {
      return metadata != null
        ? Duration.seconds(metadata.duration)
        : movie.duration
    }

    public function get currentBitrate() : Bitrate
    {
      return stream != null && stream.bitrate != null
        ? stream.bitrate : Bitrate.ZERO
    }

    public function get currentBandwidth() : Bitrate
    {
      return stream != null && stream.bandwidth != null
        ? stream.bandwidth : Bitrate.ZERO
    }

    public function get highQualityDimensions() : Dimensions
    {
      var result : Dimensions = Dimensions.ZERO

      for each (var stream : RTMPStream in movie.rtmpStreams)
        if (stream.dimensions.isGreaterThan(result))
          result = stream.dimensions

      return result
    }
  }
}

Save volume in local shared object.

Save volume in local shared object.


package aerys.minko.render.shader.compiler.graph.nodes.leaf
{
	import aerys.minko.render.resource.texture.ITextureResource;
	import aerys.minko.render.resource.texture.TextureResource;
	import aerys.minko.render.shader.compiler.CRC32;
	import aerys.minko.render.shader.compiler.graph.nodes.AbstractNode;

	/**
	 * @private
	 * @author Romain Gilliotte
	 *
	 */
	public class Sampler extends AbstractSampler
	{
		/**
		 * This vector is only used to compute the hash of the Sampler.
		 */
		private static const RESOURCES : Vector.<ITextureResource> = new Vector.<ITextureResource>();

		private var _textureResource : ITextureResource;

		public function get textureResource() : ITextureResource
		{
			return _textureResource;
		}

		public function Sampler(textureResource	: ITextureResource,
                                format          : uint  = 0, // SamplerFormat.RGBA
								filter			: uint  = 1, // SamplerFilter.LINEAR
								mipmap			: uint  = 0, // SamplerMipmap.DISABLE
								wrapping		: uint  = 1, // SamplerWrapping.REPEAT
								dimension		: uint  = 0) // SamplerDimension.FLAT
		{
			_textureResource = textureResource;

			super(format, filter, mipmap, wrapping, dimension);
		}

		override protected function computeHash() : uint
		{
			var textureResourceId : int = RESOURCES.indexOf(_textureResource);

			if (textureResourceId === -1)
			{
				RESOURCES.push(textureResource);
				textureResourceId = RESOURCES.length - 1;
			}

			return CRC32.computeForString('Sampler' + textureResourceId)
		}

		override public function toString() : String
		{
			return 'Sampler';
		}

		override public function clone() : AbstractNode
		{
			return new Sampler(_textureResource, format, filter, mipmap, wrapping, dimension);
		}

	}
}

package aerys.minko.render.shader.compiler.graph.nodes.leaf
{
	import aerys.minko.render.resource.texture.ITextureResource;
	import aerys.minko.render.resource.texture.TextureResource;
	import aerys.minko.render.shader.compiler.CRC32;
	import aerys.minko.render.shader.compiler.graph.nodes.AbstractNode;

	import flash.utils.Dictionary;

	/**
	 * @private
	 * @author Romain Gilliotte
	 *
	 */
	public class Sampler extends AbstractSampler
	{
		/**
		 * This vector is only used to compute the hash of the Sampler.
		 */
		private static const RESOURCE_ID  : Dictionary    = new Dictionary(true);

        private static var _lastId          : uint          = 0;

		private var _textureResource : ITextureResource;

		public function get textureResource() : ITextureResource
		{
			return _textureResource;
		}

		public function Sampler(textureResource	: ITextureResource,
                                format          : uint  = 0, // SamplerFormat.RGBA
								filter			: uint  = 1, // SamplerFilter.LINEAR
								mipmap			: uint  = 0, // SamplerMipmap.DISABLE
								wrapping		: uint  = 1, // SamplerWrapping.REPEAT
								dimension		: uint  = 0) // SamplerDimension.FLAT
		{
			_textureResource = textureResource;

			super(format, filter, mipmap, wrapping, dimension);
		}

		override protected function computeHash() : uint
		{
			var textureResourceId : uint = RESOURCE_ID[_textureResource];

			if (!textureResourceId)
			{
				RESOURCE_ID[_textureResource] = ++_lastId;
				textureResourceId = _lastId;
			}

			return CRC32.computeForString('Sampler' + textureResourceId)
		}

		override public function toString() : String
		{
			return 'Sampler';
		}

		override public function clone() : AbstractNode
		{
			return new Sampler(_textureResource, format, filter, mipmap, wrapping, dimension);
		}

	}
}

fix memory leak in the Shader ASG node

fix memory leak in the Shader ASG node


//
// Flump - Copyright 2013 Flump Authors

package flump.export {

import flash.filesystem.File;
import flash.utils.ByteArray;
import flash.utils.IDataOutput;

import deng.fzip.FZip;
import deng.fzip.FZipFile;

import flump.display.LibraryLoader;
import flump.xfl.XflLibrary;

public class JSONZipFormat extends PublishFormat
{
    public static const NAME :String = "JSONZip";

    public var outputFile :File;

    public function JSONZipFormat (destDir :File, libs :Vector.<XflLibrary>, conf :ExportConf,
            projectName :String) {
        super(destDir, libs, conf, projectName);
        if (conf.name != null) {
            outputFile = _destDir.resolvePath(conf.name + "/" + location + ".zip");
        } else {
            outputFile = _destDir.resolvePath(location + ".zip");
        }
    }

    override public function get modified () :Boolean {
        if (!outputFile.exists) return true;

        const zip :FZip = new FZip();
        zip.loadBytes(Files.read(outputFile));
        const md5File :FZipFile = zip.getFileByName("md5");
        const md5 :String = md5File.content.readUTFBytes(md5File.content.length);
        return md5 != md5;
    }

    override public function publish() :void {
        const zip :FZip = new FZip();

        function addToZip(name :String, contentWriter :Function) :void {
            const bytes :ByteArray = new ByteArray();
            contentWriter(bytes);
            zip.addFile(name, bytes);
        }

        const atlases :Vector.<Atlas> = createAtlases();
        for each (var atlas :Atlas in atlases) {
            addToZip(atlas.filename, function (b :ByteArray) :void { AtlasUtil.writePNG(atlas, b); });
        }
        addToZip(LibraryLoader.LIBRARY_LOCATION, function (b :ByteArray) :void {
            b.writeUTFBytes(toJSONString(createMold(atlases)));
        });
        addToZip(LibraryLoader.MD5_LOCATION,
            function (b :ByteArray) :void { b.writeUTFBytes(md5); });
        addToZip(LibraryLoader.VERSION_LOCATION,
            function (b :ByteArray) :void { b.writeUTFBytes(LibraryLoader.VERSION); });

        Files.write(outputFile, function (out :IDataOutput) :void {
            zip.serialize(out, /*includeAdler32=*/true);
        });
    }

}
}

//
// Flump - Copyright 2013 Flump Authors

package flump.export {

import flash.filesystem.File;
import flash.utils.ByteArray;
import flash.utils.IDataOutput;

import deng.fzip.FZip;
import deng.fzip.FZipFile;

import flump.display.LibraryLoader;
import flump.xfl.XflLibrary;

public class JSONZipFormat extends PublishFormat
{
    public static const NAME :String = "JSONZip";

    public var outputFile :File;

    public function JSONZipFormat (destDir :File, libs :Vector.<XflLibrary>, conf :ExportConf,
            projectName :String) {
        super(destDir, libs, conf, projectName);
        if (conf.name != null) {
            outputFile = _destDir.resolvePath(conf.name + "/" + location + ".zip");
        } else {
            outputFile = _destDir.resolvePath(location + ".zip");
        }
    }

    override public function get modified () :Boolean {
        if (!outputFile.exists) return true;

        const zip :FZip = new FZip();
        zip.loadBytes(Files.read(outputFile));
        const md5File :FZipFile = zip.getFileByName("md5");
        const md5 :String = md5File.content.readUTFBytes(md5File.content.length);
        return md5 != this.md5;
    }

    override public function publish() :void {
        const zip :FZip = new FZip();

        function addToZip(name :String, contentWriter :Function) :void {
            const bytes :ByteArray = new ByteArray();
            contentWriter(bytes);
            zip.addFile(name, bytes);
        }

        const atlases :Vector.<Atlas> = createAtlases();
        for each (var atlas :Atlas in atlases) {
            addToZip(atlas.filename, function (b :ByteArray) :void { AtlasUtil.writePNG(atlas, b); });
        }
        addToZip(LibraryLoader.LIBRARY_LOCATION, function (b :ByteArray) :void {
            b.writeUTFBytes(toJSONString(createMold(atlases)));
        });
        addToZip(LibraryLoader.MD5_LOCATION,
            function (b :ByteArray) :void { b.writeUTFBytes(md5); });
        addToZip(LibraryLoader.VERSION_LOCATION,
            function (b :ByteArray) :void { b.writeUTFBytes(LibraryLoader.VERSION); });

        Files.write(outputFile, function (out :IDataOutput) :void {
            zip.serialize(out, /*includeAdler32=*/true);
        });
    }

}
}

Fix the modified column check for combined JSONZip formats.

I'm surprised this wasn't a compiler warning, but even IDEA wasn't complaining about it.


package aerys.minko.scene.node.group
{
	import aerys.minko.scene.action.IAction;
	import aerys.minko.scene.action.group.GroupAction;
	import aerys.minko.scene.node.AbstractScene;
	import aerys.minko.scene.node.IScene;
	import aerys.minko.scene.node.ISearchableScene;
	import aerys.minko.scene.visitor.ISceneVisitor;
	import aerys.minko.scene.visitor.RenderingVisitor;

	import flash.utils.Proxy;
	import flash.utils.flash_proxy;
	import flash.utils.getQualifiedClassName;

	/**
	 * The Group3D provides a basic support for scene building.
	 * A Group3D can contain any object implementing the IScene3D interface.
	 *
	 * Group3D objects do not affect their children with any specific behaviour.
	 *
	 * @author Jean-Marc Le Roux
	 */
	public dynamic class Group extends Proxy implements IGroup
	{
		private static var _id		: uint				= 0;

		private var _name			: String			= null;

		private var _children		: Vector.<IScene>	= null;
		private var _numChildren	: int				= 0;

		private var _actions		: Vector.<IAction>	= Vector.<IAction>([GroupAction.groupAction]);

		public function get actions()	: Vector.<IAction>	{ return _actions; }
		public function get name()		: String			{ return _name; }

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

		protected function get rawChildren()	: Vector.<IScene> 	{ return _children; }

		protected function set rawChildren(value : Vector.<IScene>) : void
		{
			_children = value;
			_numChildren = _children.length;
		}

		/**
		 * The number of children.
		 */
		public function get numChildren() : uint
		{
			return _numChildren;
		}

		public function Group(...children)
		{
			super();

			_name = AbstractScene.getDefaultSceneName(this);

			initialize(children);
		}

		private function initialize(children : Array) : void
		{
			while (children.length == 1 && children[0] is Array)
				children = children[0];

			_numChildren = children.length;
			_children = _numChildren ? Vector.<IScene>(children)
									 : new Vector.<IScene>();
		}

		public function contains(scene : IScene) : Boolean
		{
			return getChildIndex(scene) >= 0;
		}

		public function getChildIndex(child : IScene) : int
		{
			for (var i : int = 0; i < _numChildren; i++)
				if (_children[i] === child)
					return i;

			return -1;
		}

		public function getChildByName(name : String) : IScene
		{
			for (var i : int = 0; i < _numChildren; i++)
				if (_children[i].name === name)
					return _children[i];

			return null;
		}

		/**
		 * Add a child to the container.
		 *
		 * @param	scene The child to add.
		 */
		public function addChild(scene : IScene) : IGroup
		{
			if (!scene)
				throw new Error("Parameter child must be non-null.");

			_children.push(scene);
			++_numChildren;

			//scene.added(this);

			return this;
		}

		public function addChildAt(scene : IScene, position : uint) : IGroup
		{
			if (!scene)
				throw new Error("Parameter child must be non-null.");

			var numChildren : int = _children.length;

			if (position >= numChildren)
				return addChild(scene);

			for (var i : int = numChildren; i > position; --i)
				_children[i] = _children[int(i - 1)];
			_children[position] = scene;

			++_numChildren;

			return this;
		}

		/**
		 * Remove a child from the container.
		 *
		 * @param	myChild The child to remove.
		 * @return Whether the child was actually removed or not.
		 */
		public function removeChild(child : IScene) : IGroup
		{
			var numChildren : int = _children.length;
			var i : int	= 0;

			while (i < numChildren && _children[i] !== child)
				++i;

			if (i >= numChildren)
				return null;

			return removeChildAt(i);
		}

		public function removeChildAt(position : uint) : IGroup
		{
			var removed 	: IScene 	= null;

			if (position < _numChildren)
			{
				removed = _children[position];

				while (position < _numChildren - 1)
					_children[position] = _children[int(++position)];
				_children.length = --_numChildren;
			}

			return this;
		}

		public function removeAllChildren() : IGroup
		{
			_children.length = 0;
			_numChildren = 0;

			return this;
		}

		public function getChildAt(position : uint) : IScene
		{
			return position < _numChildren ? _children[position] : null;
		}

		public function swapChildren(child1	: IScene,
									 child2	: IScene) : IGroup
		{
			var id1	: int 	= getChildIndex(child1);
			var id2 : int	= getChildIndex(child2);

			if (id1 == -1 || id2 == -1)
				return this;

			var tmp : IScene = _children[id2];

			_children[id2] = _children[id1];
			_children[id1] = tmp;

			return this;
		}

		public function getDescendantByName(name : String) : IScene
		{
			var descendant 	: IScene 	= getChildByName(name);
			var numChildren	: int 		= numChildren;

			for (var i : int = 0; i < numChildren && !descendant; ++i)
			{
				var searchable : ISearchableScene = _children[i] as ISearchableScene;

				if (searchable)
					descendant = searchable.getDescendantByName(name);
			}

			return descendant;
		}

		public function toString() : String
		{
			return "[" + getQualifiedClassName(this) + " " + name + "]";
		}

		override flash_proxy function getProperty(name : *) : *
		{
			var index : int = parseInt(name);

			return index == name ? getChildAt(index) : getChildByName(name as String);
		}

		override flash_proxy function setProperty(name : *, value : *) : void
		{
			var index : int = parseInt(name);

			if (index == name)
			{
				if (index < numChildren)
				{
					removeChildAt(index);
					addChildAt(value, index);
				}
			}
			else
			{
				var old : IScene = getChildByName(name);

				addChild(value);

				if (old)
				{
					swapChildren(value, old);
					_children.length = _children.length - 1;
				}
			}
		}

		override flash_proxy function getDescendants(name : *) : *
		{
			return getDescendantByName(name);
		}

		override flash_proxy function nextNameIndex(index : int) : int
		{
			return index < numChildren ? index + 1 : 0;
		}

		override flash_proxy function nextName(index : int) : String
		{
			return String(index - 1);
		}

		override flash_proxy function nextValue(index : int) : *
		{
			return _children[int(index - 1)];
		}
	}
}

package aerys.minko.scene.node.group
{
	import aerys.minko.scene.action.IAction;
	import aerys.minko.scene.action.group.GroupAction;
	import aerys.minko.scene.node.AbstractScene;
	import aerys.minko.scene.node.IScene;
	import aerys.minko.scene.node.ISearchableScene;
	import aerys.minko.scene.visitor.ISceneVisitor;
	import aerys.minko.scene.visitor.RenderingVisitor;

	import flash.utils.Proxy;
	import flash.utils.flash_proxy;
	import flash.utils.getQualifiedClassName;

	/**
	 * The Group3D provides a basic support for scene building.
	 * A Group3D can contain any object implementing the IScene3D interface.
	 *
	 * Group3D objects do not affect their children with any specific behaviour.
	 *
	 * @author Jean-Marc Le Roux
	 */
	public dynamic class Group extends Proxy implements IGroup
	{
		private static var _id		: uint				= 0;

		private var _name			: String			= null;

		private var _children		: Vector.<IScene>	= null;
		private var _numChildren	: int				= 0;

		private var _actions		: Vector.<IAction>	= Vector.<IAction>([GroupAction.groupAction]);

		public function get actions()	: Vector.<IAction>	{ return _actions; }
		public function get name()		: String			{ return _name; }

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

		protected function get rawChildren()	: Vector.<IScene> 	{ return _children; }

		protected function set rawChildren(value : Vector.<IScene>) : void
		{
			_children = value;
			_numChildren = _children.length;
		}

		/**
		 * The number of children.
		 */
		public function get numChildren() : uint
		{
			return _numChildren;
		}

		public function Group(...children)
		{
			super();

			_name = AbstractScene.getDefaultSceneName(this);

			initialize(children);
		}

		private function initialize(children : Array) : void
		{
			_children = new Vector.<IScene>();

			while (children.length == 1 && children[0] is Array)
				children = children[0];

			var childrenNum : uint = children.length;
			for (var childrenIndex : uint = 0; childrenIndex < childrenNum; ++childrenIndex)
			{
				var child : IScene = children[childrenIndex] as IScene;

				if (!child)
					throw new Error("Constructor parameters must be IScene objects.");

				addChild(child);
			}
		}

		public function contains(scene : IScene) : Boolean
		{
			return getChildIndex(scene) >= 0;
		}

		public function getChildIndex(child : IScene) : int
		{
			for (var i : int = 0; i < _numChildren; i++)
				if (_children[i] === child)
					return i;

			return -1;
		}

		public function getChildByName(name : String) : IScene
		{
			for (var i : int = 0; i < _numChildren; i++)
				if (_children[i].name === name)
					return _children[i];

			return null;
		}

		/**
		 * Add a child to the container.
		 *
		 * @param	scene The child to add.
		 */
		public function addChild(scene : IScene) : IGroup
		{
			if (!scene)
				throw new Error("Parameter child must be non-null.");

			_children.push(scene);
			++_numChildren;

			//scene.added(this);

			return this;
		}

		public function addChildAt(scene : IScene, position : uint) : IGroup
		{
			if (!scene)
				throw new Error("Parameter child must be non-null.");

			var numChildren : int = _children.length;

			if (position >= numChildren)
				return addChild(scene);

			for (var i : int = numChildren; i > position; --i)
				_children[i] = _children[int(i - 1)];
			_children[position] = scene;

			++_numChildren;

			return this;
		}

		/**
		 * Remove a child from the container.
		 *
		 * @param	myChild The child to remove.
		 * @return Whether the child was actually removed or not.
		 */
		public function removeChild(child : IScene) : IGroup
		{
			var numChildren : int = _children.length;
			var i : int	= 0;

			while (i < numChildren && _children[i] !== child)
				++i;

			if (i >= numChildren)
				return null;

			return removeChildAt(i);
		}

		public function removeChildAt(position : uint) : IGroup
		{
			var removed 	: IScene 	= null;

			if (position < _numChildren)
			{
				removed = _children[position];

				while (position < _numChildren - 1)
					_children[position] = _children[int(++position)];
				_children.length = --_numChildren;
			}

			return this;
		}

		public function removeAllChildren() : IGroup
		{
			_children.length = 0;
			_numChildren = 0;

			return this;
		}

		public function getChildAt(position : uint) : IScene
		{
			return position < _numChildren ? _children[position] : null;
		}

		public function swapChildren(child1	: IScene,
									 child2	: IScene) : IGroup
		{
			var id1	: int 	= getChildIndex(child1);
			var id2 : int	= getChildIndex(child2);

			if (id1 == -1 || id2 == -1)
				return this;

			var tmp : IScene = _children[id2];

			_children[id2] = _children[id1];
			_children[id1] = tmp;

			return this;
		}

		public function getDescendantByName(name : String) : IScene
		{
			var descendant 	: IScene 	= getChildByName(name);
			var numChildren	: int 		= numChildren;

			for (var i : int = 0; i < numChildren && !descendant; ++i)
			{
				var searchable : ISearchableScene = _children[i] as ISearchableScene;

				if (searchable)
					descendant = searchable.getDescendantByName(name);
			}

			return descendant;
		}

		public function toString() : String
		{
			return "[" + getQualifiedClassName(this) + " " + name + "]";
		}

		override flash_proxy function getProperty(name : *) : *
		{
			var index : int = parseInt(name);

			return index == name ? getChildAt(index) : getChildByName(name as String);
		}

		override flash_proxy function setProperty(name : *, value : *) : void
		{
			var index : int = parseInt(name);

			if (index == name)
			{
				if (index < numChildren)
				{
					removeChildAt(index);
					addChildAt(value, index);
				}
			}
			else
			{
				var old : IScene = getChildByName(name);

				addChild(value);

				if (old)
				{
					swapChildren(value, old);
					_children.length = _children.length - 1;
				}
			}
		}

		override flash_proxy function getDescendants(name : *) : *
		{
			return getDescendantByName(name);
		}

		override flash_proxy function nextNameIndex(index : int) : int
		{
			return index < numChildren ? index + 1 : 0;
		}

		override flash_proxy function nextName(index : int) : String
		{
			return String(index - 1);
		}

		override flash_proxy function nextValue(index : int) : *
		{
			return _children[int(index - 1)];
		}
	}
}

Add type checking in Group constructor (warn when an object is not an IScene).


////////////////////////////////////////////////////////////////////////////////
//
//  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 org.apache.flex.utils
{
	import org.apache.flex.core.IPopUpHost;
	import org.apache.flex.core.UIBase;

	/**
	 *  The UIUtils class is a collection of static functions that provide utility
	 *  features to UIBase objects.
	 *
	 *  @langversion 3.0
	 *  @playerversion Flash 10.2
	 *  @playerversion AIR 2.6
	 *  @productversion FlexJS 0.0
	 */
	public class UIUtils
	{
		/**
		 * @private
		 */
		public function UIUtils()
		{
			throw new Error("UIUtils should not be instantiated.");
		}

		/**
		 *  Centers the given item relative to another item. Typically the item being centered is
		 *  a child or sibling of the second item.
		 *
		 *  @param item The component item being centered.
		 *  @param relativeTo The component used as reference for the centering.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public static function center( item:UIBase, relativeTo:UIBase ):void
		{
			var xpos:Number = (relativeTo.width - item.width)/2;
			var ypos:Number = (relativeTo.height - item.height)/2;

			item.x = xpos;
			item.y = ypos;
		}

		/**
		 *  Given a component starting point, this function walks up the parent chain
		 *  looking for a component that implements the IPopUpHost interface. The function
		 *  either returns that component or null if no IPopUpHost can be found.
		 *
		 *  @param start A component to start the search.
		 *  @return A component that implements IPopUpHost or null.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public static function findPopUpHost(start:UIBase):IPopUpHost
		{
			while( start != null && !(start is IPopUpHost) ) {
				start = start.parent as UIBase;
			}

			return start as IPopUpHost;
		}

		/**
		 *  Removes the given component from the IPopUpHost.
		 *
		 *  @param start A component to start the search.
		 *  @return A component that implements IPopUpHost or null.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public static function removePopUp(popUp:UIBase):void
		{
			var host:IPopUpHost = popUp.parent as IPopUpHost;
			host.removeElement(popUp);
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  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 org.apache.flex.utils
{
	import org.apache.flex.core.IPopUpHost;
    import org.apache.flex.core.IChild;
	import org.apache.flex.core.IUIBase;

	/**
	 *  The UIUtils class is a collection of static functions that provide utility
	 *  features to UIBase objects.
	 *
	 *  @langversion 3.0
	 *  @playerversion Flash 10.2
	 *  @playerversion AIR 2.6
	 *  @productversion FlexJS 0.0
	 */
	public class UIUtils
	{
		/**
		 * @private
		 */
		public function UIUtils()
		{
			throw new Error("UIUtils should not be instantiated.");
		}

		/**
		 *  Centers the given item relative to another item. Typically the item being centered is
		 *  a child or sibling of the second item.
		 *
		 *  @param item The component item being centered.
		 *  @param relativeTo The component used as reference for the centering.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public static function center( item:IUIBase, relativeTo:IUIBase ):void
		{
			var xpos:Number = (relativeTo.width - item.width)/2;
			var ypos:Number = (relativeTo.height - item.height)/2;

			item.x = xpos;
			item.y = ypos;
		}

		/**
		 *  Given a component starting point, this function walks up the parent chain
		 *  looking for a component that implements the IPopUpHost interface. The function
		 *  either returns that component or null if no IPopUpHost can be found.
		 *
		 *  @param start A component to start the search.
		 *  @return A component that implements IPopUpHost or null.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public static function findPopUpHost(start:IUIBase):IPopUpHost
		{
			while( start != null && !(start is IPopUpHost) && start is IChild ) {
				start = IChild(start).parent as IUIBase;
			}

			return start as IPopUpHost;
		}

		/**
		 *  Removes the given component from the IPopUpHost.
		 *
		 *  @param start A component to start the search.
		 *  @return A component that implements IPopUpHost or null.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10.2
		 *  @playerversion AIR 2.6
		 *  @productversion FlexJS 0.0
		 */
		public static function removePopUp(popUp:IChild):void
		{
			var host:IPopUpHost = popUp.parent as IPopUpHost;
			host.removeElement(popUp);
		}
	}
}

switch to interfaces so it works on Buttons


//
// Flump - Copyright 2012 Three Rings Design

package flump {

import flash.desktop.NativeApplication;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.InvokeEvent;
import flash.filesystem.File;

import deng.fzip.FZip;
import deng.fzip.FZipFile;

import flump.xfl.XflAnimation;
import flump.xfl.XflLibrary;

import starling.core.Starling;

import com.threerings.util.F;
import com.threerings.util.Log;
import com.threerings.util.StringUtil;

public class Flump extends Sprite
{
    public static const NA :NativeApplication = NativeApplication.nativeApplication;

    public function Flump () {
        NA.addEventListener(InvokeEvent.INVOKE, onInvoke);
        _starling = new Starling(Preview, stage)
        _starling.start();
    }

    protected function onInvoke (invoke :InvokeEvent) :void {
        const file :File = new File(invoke.arguments[0]);
        if (!file.exists) {
            log.error("Given file doesn't exist", "path", file.nativePath);
            NA.exit(1);
            return;
        }
        loadFlashDocument(file);
    }

    protected function loadFlashDocument (file :File) :void {
        if (StringUtil.endsWith(file.nativePath, ".xfl")) file = file.parent;
        if (file.isDirectory) new XflLoader().load(file).succeeded.add(function (lib :XflLibrary) :void {
            PngExporter.dumpTextures(file, lib);
            Preview(_starling.stage.getChildAt(0)).displayAnimation(file, lib, lib.animations[0]);
        });
        else loadFla(file);
    }

    protected function loadFla (file :File) :void {
        log.info("Loading fla", "path", file.nativePath);
        Files.load(file).succeeded.add(function (file :File) :void {
            const zip :FZip = new FZip();
            zip.loadBytes(file.data);
            const files :Array = [];
            for (var ii :int = 0; ii < zip.getFileCount(); ii++) files.push(zip.getFileAt(ii));
            const xmls :Array = F.filter(files, function (fz :FZipFile) :Boolean {
                return StringUtil.endsWith(fz.filename, ".xml");
            });
            const anims :Array = F.filter(xmls, function (fz :FZipFile) :Boolean {
                return StringUtil.startsWith(fz.filename, "LIBRARY/Animations/");
            });
            const textures :Array = F.filter(xmls, function (fz :FZipFile) :Boolean {
                return StringUtil.startsWith(fz.filename, "LIBRARY/Textures/");
            });
            function toFn (fz :FZipFile) :String { return fz.filename };
            log.info("Loaded", "bytes", file.data.length, "anims", F.map(anims, toFn),
                "textures", F.map(textures, toFn));
            for each (var fz :FZipFile in anims) {
                new XflAnimation(bytesToXML(fz.content));
            }
            NA.exit(0);
        });
    }

    protected var _starling :Starling;
    private static const log :Log = Log.getLog(Flump);
}}

//
// Flump - Copyright 2012 Three Rings Design

package flump {

import flash.desktop.NativeApplication;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.InvokeEvent;
import flash.filesystem.File;

import deng.fzip.FZip;
import deng.fzip.FZipFile;

import flump.xfl.XflAnimation;
import flump.xfl.XflLibrary;

import starling.core.Starling;

import com.threerings.util.F;
import com.threerings.util.Log;
import com.threerings.util.StringUtil;

public class Flump extends Sprite
{
    public static const NA :NativeApplication = NativeApplication.nativeApplication;

    public function Flump () {
        NA.addEventListener(InvokeEvent.INVOKE, onInvoke);
        stage.align = StageAlign.TOP_LEFT;
        stage.scaleMode = StageScaleMode.NO_SCALE;
        _starling = new Starling(Preview, stage)
        _starling.start();
    }

    protected function onInvoke (invoke :InvokeEvent) :void {
        const file :File = new File(invoke.arguments[0]);
        if (!file.exists) {
            log.error("Given file doesn't exist", "path", file.nativePath);
            NA.exit(1);
            return;
        }
        loadFlashDocument(file);
    }

    protected function loadFlashDocument (file :File) :void {
        if (StringUtil.endsWith(file.nativePath, ".xfl")) file = file.parent;
        if (file.isDirectory) new XflLoader().load(file).succeeded.add(function (lib :XflLibrary) :void {
            PngExporter.dumpTextures(file, lib);
            Preview(_starling.stage.getChildAt(0)).displayAnimation(file, lib, lib.animations[0]);
        });
        else loadFla(file);
    }

    protected function loadFla (file :File) :void {
        log.info("Loading fla", "path", file.nativePath);
        Files.load(file).succeeded.add(function (file :File) :void {
            const zip :FZip = new FZip();
            zip.loadBytes(file.data);
            const files :Array = [];
            for (var ii :int = 0; ii < zip.getFileCount(); ii++) files.push(zip.getFileAt(ii));
            const xmls :Array = F.filter(files, function (fz :FZipFile) :Boolean {
                return StringUtil.endsWith(fz.filename, ".xml");
            });
            const anims :Array = F.filter(xmls, function (fz :FZipFile) :Boolean {
                return StringUtil.startsWith(fz.filename, "LIBRARY/Animations/");
            });
            const textures :Array = F.filter(xmls, function (fz :FZipFile) :Boolean {
                return StringUtil.startsWith(fz.filename, "LIBRARY/Textures/");
            });
            function toFn (fz :FZipFile) :String { return fz.filename };
            log.info("Loaded", "bytes", file.data.length, "anims", F.map(anims, toFn),
                "textures", F.map(textures, toFn));
            for each (var fz :FZipFile in anims) {
                new XflAnimation(bytesToXML(fz.content));
            }
            NA.exit(0);
        });
    }

    protected var _starling :Starling;
    private static const log :Log = Log.getLog(Flump);
}}

Align the stage properly

Align the stage properly