/**
 * User: MerlinDS
 * Date: 12.07.2014
 * Time: 21:35
 */
package com.merlinds.miracle_tool.views {
	import com.merlinds.miracle_tool.utils.dispatchAction;
	import com.merlinds.miracle_tool.views.components.controls.ActionButton;

	import flash.events.MouseEvent;

	import org.robotlegs.mvcs.Mediator;

	public class AppMenuMediator extends Mediator {

		//==============================================================================
		//{region							PUBLIC METHODS
		public function AppMenuMediator() {
			super();
		}

		override public function onRegister():void {
			this.addViewListener(MouseEvent.CLICK, this.mouseClickHandler);
		}

		override public function onRemove():void {
			this.removeViewListener(MouseEvent.CLICK, this.mouseClickHandler);
		}
		//} endregion PUBLIC METHODS ===================================================

		//==============================================================================
		//{region						PRIVATE\PROTECTED METHODS
		//} endregion PRIVATE\PROTECTED METHODS ========================================

		//==============================================================================
		//{region							EVENTS HANDLERS
		private function mouseClickHandler(event:MouseEvent):void {
			var target:Object = event.target;
			if(target is ActionButton){
				dispatchAction((target as ActionButton).action, this.dispatch);
			}
		}
		//} endregion EVENTS HANDLERS ==================================================

		//==============================================================================
		//{region							GETTERS/SETTERS
		//} endregion GETTERS/SETTERS ==================================================
	}
}

/**
 * User: MerlinDS
 * Date: 12.07.2014
 * Time: 21:35
 */
package com.merlinds.miracle_tool.views {
	import com.merlinds.miracle_tool.events.ActionEvent;
	import com.merlinds.miracle_tool.services.ActionService;
	import com.merlinds.miracle_tool.utils.dispatchAction;
	import com.merlinds.miracle_tool.views.components.controls.ActionButton;

	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;

	import org.robotlegs.mvcs.Mediator;

	public class AppMenuMediator extends Mediator {

		[Inject]
		public var actionService:ActionService;
		//==============================================================================
		//{region							PUBLIC METHODS
		public function AppMenuMediator() {
			super();
		}

		override public function onRegister():void {
			this.contextView.stage.addEventListener(KeyboardEvent.KEY_UP, this.keyboardHandler);
			this.addViewListener(MouseEvent.CLICK, this.mouseClickHandler);
		}

		override public function onRemove():void {
			this.removeViewListener(MouseEvent.CLICK, this.mouseClickHandler);
			this.contextView.stage.removeEventListener(KeyboardEvent.KEY_UP, this.keyboardHandler);
		}
		//} endregion PUBLIC METHODS ===================================================

		//==============================================================================
		//{region						PRIVATE\PROTECTED METHODS
		//} endregion PRIVATE\PROTECTED METHODS ========================================

		//==============================================================================
		//{region							EVENTS HANDLERS
		private function mouseClickHandler(event:MouseEvent):void {
			var target:Object = event.target;
			if(target is ActionButton){
				dispatchAction((target as ActionButton).action, this.dispatch);
			}
		}

		private function keyboardHandler(event:KeyboardEvent):void {
			switch (event.keyCode){
				case 78: // N
					if(event.ctrlKey){
						//new project
						this.dispatch(new ActionEvent(ActionEvent.NEW_PROJECT));
					}
					break;
				case 83: // S
					if(event.ctrlKey){
						//save project
						this.dispatch(new ActionEvent(ActionEvent.SAVE_PROJECT));
					}
					break;
				case 81: // Q
					if(event.ctrlKey){
						//close program
					}
					break;
				case 69: // E
					if(event.ctrlKey){
						//close project
						this.dispatch(new ActionEvent(ActionEvent.CLOSE_PROJECT));
					}
					break;
				case 86: // V
					break;
				case 72: // H
					break;
				case 119: // F8
					break;
				case 120: // F9
					break;
			}
		}
		//} endregion EVENTS HANDLERS ==================================================

		//==============================================================================
		//{region							GETTERS/SETTERS
		//} endregion GETTERS/SETTERS ==================================================
	}
}

Add few shortcuts

Add few shortcuts


//
// $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.net.URLRequest;
//import flash.net.navigateToURL; // function import

public class NetUtil
{
    /**
     * Convenience method to load a web page in the browser window without
     * having to worry about SecurityErrors in various conditions.
     *
     * @return true if the url was able to be loaded.
     */
    public static function navigateToURL (url :String, preferredWindow :String = "_self") :Boolean
    {
        var ureq :URLRequest = new URLRequest(url);
        while (true) {
            try {
                flash.net.navigateToURL(ureq, preferredWindow);
                return true;
            } catch (err :SecurityError) {
                if (preferredWindow != null) {
                    preferredWindow = null; // try again with no preferred window

                } else {
                    Log.getLog(NetUtil).warning("Unable to navigate to URL [e=" + err + "].");
                    break;
                }
            }
        }

        return false; // failure!
    }
}
}

//
// $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.net.URLRequest;
//import flash.net.navigateToURL; // function import

public class NetUtil
{
    /**
     * Convenience method to load a web page in the browser window without
     * having to worry about SecurityErrors in various conditions.
     *
     * @param url a String or a URLRequest.
     * @param preferredWindow the browser tab/window identifier in which to load. If you
     * specify a non-null window and it causes a security error, the request is retried with null.
     *
     * @return true if the url was able to be loaded.
     */
    public static function navigateToURL (url :*, preferredWindow :String = "_self") :Boolean
    {
        var ureq :URLRequest = (url is URLRequest) ? URLRequest(url) : new URLRequest(String(url));
        while (true) {
            try {
                flash.net.navigateToURL(ureq, preferredWindow);
                return true;
            } catch (err :SecurityError) {
                if (preferredWindow != null) {
                    preferredWindow = null; // try again with no preferred window

                } else {
                    Log.getLog(NetUtil).warning("Unable to navigate to URL [e=" + err + "].");
                    break;
                }
            }
        }

        return false; // failure!
    }
}
}

Allow a preconfigured URLRequest to be passed in.

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


package aerys.minko.scene.controller
{
	import aerys.minko.ns.minko_math;
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.node.Group;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;
	import aerys.minko.type.math.Matrix4x4;

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

	use namespace minko_math;

	/**
	 * The TransformController handles the batched update of all the local to world matrices
	 * of a sub-scene. As such, it will only be active on the root node of a sub-scene and will
	 * automatically disable itself on other nodes.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public final class TransformController extends AbstractController
	{
		private var _target								: ISceneNode;

		private var _invalidList						: Boolean;

		private var _nodeToId							: Dictionary;
		private var _idToNode							: Vector.<ISceneNode>
		private var _transforms							: Vector.<Matrix4x4>;
		private var _localToWorldTransformsInitialized	: Vector.<Boolean>;
		private var _localToWorldTransforms 			: Vector.<Matrix4x4>;
        private var _worldToLocalTransforms 			: Vector.<Matrix4x4>;
		private var _numChildren						: Vector.<uint>;
		private var _firstChildId						: Vector.<uint>;
		private var _parentId							: Vector.<int>;

		public function TransformController()
		{
			super();

			targetAdded.add(targetAddedHandler);
			targetRemoved.add(targetRemovedHandler);
		}

		private function renderingBeginHandler(scene			: Scene,
											   viewport		: Viewport,
											   destination	: BitmapData,
											   time			: Number) : void
		{
			if (_invalidList)
				updateTransformsList();

			if (_transforms.length)
				updateLocalToWorld();
		}

		private function updateLocalToWorld(nodeId : uint = 0) : void
		{
			var numNodes 			: uint 			= _transforms.length;
			var childrenOffset		: uint			= 1;
			var rootLocalToWorld	: Matrix4x4		= _localToWorldTransforms[nodeId];
			var rootTransform		: Matrix4x4		= _transforms[nodeId];
			var root				: ISceneNode	= _idToNode[childId];

			if (rootTransform._hasChanged || !_localToWorldTransformsInitialized[nodeId])
			{
				rootLocalToWorld.copyFrom(rootTransform);

				if (nodeId != 0)
					rootLocalToWorld.append(_localToWorldTransforms[_parentId[nodeId]]);

				rootTransform._hasChanged = false;
				_localToWorldTransformsInitialized[nodeId] = true;
				root.localToWorldTransformChanged.execute(root, rootLocalToWorld);
			}

			for (; nodeId < numNodes; ++nodeId)
			{
				var localToWorld 	: Matrix4x4	= _localToWorldTransforms[nodeId];
				var numChildren		: uint		= _numChildren[nodeId];
				var firstChildId	: uint		= _firstChildId[nodeId];
				var lastChildId		: uint		= firstChildId + numChildren;
				var isDirty			: Boolean	= localToWorld._hasChanged;

                localToWorld._hasChanged = false;

				for (var childId : uint = firstChildId; childId < lastChildId; ++childId)
				{
					var childTransform		: Matrix4x4		= _transforms[childId];
					var childLocalToWorld	: Matrix4x4		= _localToWorldTransforms[childId];
					var childIsDirty		: Boolean		= isDirty || childTransform._hasChanged
						|| !_localToWorldTransformsInitialized[childId];

					if (childIsDirty)
					{
						var child	: ISceneNode	= _idToNode[childId];

						childLocalToWorld
							.copyFrom(childTransform)
							.append(localToWorld);

						childTransform._hasChanged = false;
						_localToWorldTransformsInitialized[childId] = true;
						child.localToWorldTransformChanged.execute(child, childLocalToWorld);
					}
				}
			}
		}

        private function getDirtyRoot(nodeId : int) : int
        {
            var dirtyRoot : int = -1;

            while (nodeId >= 0)
            {
                if ((_transforms[nodeId] as Matrix4x4)._hasChanged ||
					!_localToWorldTransformsInitialized[nodeId])
                    dirtyRoot = nodeId;

                nodeId = _parentId[nodeId];
            }

            return dirtyRoot;
        }

		private function targetAddedHandler(ctrl	: TransformController,
											target	: ISceneNode) : void
		{
			if (_target)
				throw new Error('The TransformController cannot have more than one target.');

			_target = target;
			_invalidList = true;

			if (target is Scene)
			{
				(target as Scene).renderingBegin.add(renderingBeginHandler);
				return;
			}

			if (target is Group)
			{
				var targetGroup : Group = target as Group;

				targetGroup.descendantAdded.add(descendantAddedHandler);
				targetGroup.descendantRemoved.add(descendantRemovedHandler);
			}

			target.added.add(addedHandler);
		}

		private function targetRemovedHandler(ctrl		: TransformController,
											  target	: ISceneNode) : void
		{
			target.added.remove(addedHandler);

			if (target is Group)
			{
				var targetGroup : Group = target as Group;

				targetGroup.descendantAdded.remove(descendantAddedHandler);
				targetGroup.descendantRemoved.remove(descendantRemovedHandler);
			}

			_invalidList = false;
			_target = null;

			_nodeToId = null;
			_transforms = null;
			_localToWorldTransformsInitialized = null;
			_localToWorldTransforms = null;
            _worldToLocalTransforms = null;
			_numChildren = null;
			_idToNode = null;
			_parentId = null;
		}

		private function addedHandler(target : ISceneNode, ancestor : Group) : void
		{
			// the controller will remove itself from the node when it's not its own root anymore
			// but it will watch for the 'removed' signal to add itself back if the node becomes
			// its own root again
			_target.removed.add(removedHandler);
			_target.removeController(this);
		}

		private function removedHandler(target : ISceneNode, ancestor : Group) : void
		{
			if (target.root == target)
			{
				target.removed.remove(removedHandler);
				target.addController(this);
			}
		}

		private function descendantAddedHandler(root		: Group,
												descendant	: ISceneNode) : void
		{
			_invalidList = true;
		}

		private function descendantRemovedHandler(root			: Group,
												  descendant	: ISceneNode) : void
		{
			_invalidList = true;
		}

		private function updateTransformsList() : void
		{
			var root 	: ISceneNode 			= _target.root;
			var nodes 	: Vector.<ISceneNode> 	= new <ISceneNode>[root];
			var nodeId 	: uint 					= 0;

			_nodeToId = new Dictionary(true);
			_transforms = new <Matrix4x4>[];
			_localToWorldTransformsInitialized = new <Boolean>[];
			_localToWorldTransforms = new <Matrix4x4>[];
            _worldToLocalTransforms = new <Matrix4x4>[];
			_numChildren = new <uint>[];
			_firstChildId = new <uint>[];
			_idToNode = new <ISceneNode>[];
			_parentId = new <int>[-1];

			while (nodes.length)
			{
				var node 	: ISceneNode 	= nodes.shift();
				var group 	: Group 		= node as Group;

				_nodeToId[node] = nodeId;
				_idToNode[nodeId] = node;
				_transforms[nodeId] = node.transform;
				_localToWorldTransforms[nodeId] = new Matrix4x4().lock();
				_localToWorldTransformsInitialized[nodeId] = false;

				if (group)
				{
					var numChildren 	: uint = group.numChildren;
					var firstChildId 	: uint = nodeId + nodes.length + 1;

					_numChildren[nodeId] = numChildren;
					_firstChildId[nodeId] = firstChildId;
					for (var childId : uint = 0; childId < numChildren; ++childId)
					{
						_parentId[uint(firstChildId + childId)] = nodeId;
						nodes.push(group.getChildAt(childId));
					}
				}
				else
				{
					_numChildren[nodeId] = 0;
					_firstChildId[nodeId] = 0;
				}

				++nodeId;
			}

            _worldToLocalTransforms.length = _localToWorldTransforms.length;
			_invalidList = false;
		}

		public function getLocalToWorldTransform(node 			: ISceneNode,
												 forceUpdate 	: Boolean = false) : Matrix4x4
		{
			if (_invalidList || _nodeToId[node] == undefined)
				updateTransformsList();

			var nodeId : uint = _nodeToId[node];

			if (forceUpdate)
            {
                var dirtyRoot : int = getDirtyRoot(nodeId);

                if (dirtyRoot >= 0)
                    updateLocalToWorld(dirtyRoot);
            }

			return _localToWorldTransforms[nodeId];
		}

        public function getWorldToLocalTransform(node           : ISceneNode,
                                                 forceUpdate    : Boolean   = false) : Matrix4x4
        {
            if (_invalidList || _nodeToId[node] == undefined)
                updateTransformsList();

            var nodeId                  : uint      = _nodeToId[node];
            var worldToLocalTransform   : Matrix4x4 = _worldToLocalTransforms[nodeId];

            if (!worldToLocalTransform)
            {
                _worldToLocalTransforms[nodeId] = worldToLocalTransform = new Matrix4x4();
                if (!forceUpdate)
                    worldToLocalTransform.copyFrom(_localToWorldTransforms[nodeId]).invert();
            }

            if (forceUpdate)
            {
                var dirtyRoot : int = getDirtyRoot(nodeId);

                if (dirtyRoot >= 0)
                {
                    updateLocalToWorld(dirtyRoot);

                    worldToLocalTransform
                        .copyFrom(_localToWorldTransforms[nodeId])
                        .invert();
                }
            }

            return worldToLocalTransform;
        }
	}
}

package aerys.minko.scene.controller
{
    import aerys.minko.ns.minko_math;
    import aerys.minko.render.Viewport;
    import aerys.minko.scene.node.Group;
    import aerys.minko.scene.node.ISceneNode;
    import aerys.minko.scene.node.Scene;
    import aerys.minko.type.math.Matrix4x4;

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

    use namespace minko_math;

    /**
     * The TransformController handles the batched update of all the local to world matrices
     * of a sub-scene. As such, it will only be active on the root node of a sub-scene and will
     * automatically disable itself on other nodes.
     *
     * @author Jean-Marc Le Roux
     *
     */
    public final class TransformController extends AbstractController
    {
        private static const INIT_NONE                  : uint  = 0;
        private static const INIT_LOCAL_TO_WORLD        : uint  = 1;
        private static const INIT_WORLD_TO_LOCAL        : uint  = 2;

        private var _target								: ISceneNode;

        private var _invalidList						: Boolean;

        private var _nodeToId							: Dictionary;
        private var _idToNode							: Vector.<ISceneNode>
        private var _transforms							: Vector.<Matrix4x4>;
        private var _initialized	                    : Vector.<uint>;
        private var _localToWorldTransforms 			: Vector.<Matrix4x4>;
        private var _worldToLocalTransforms 			: Vector.<Matrix4x4>;
        private var _numChildren						: Vector.<uint>;
        private var _firstChildId						: Vector.<uint>;
        private var _parentId							: Vector.<int>;

        public function TransformController()
        {
            super();

            targetAdded.add(targetAddedHandler);
            targetRemoved.add(targetRemovedHandler);
        }

        private function renderingBeginHandler(scene			: Scene,
                                               viewport		: Viewport,
                                               destination	: BitmapData,
                                               time			: Number) : void
        {
            if (_invalidList)
                updateTransformsList();

            if (_transforms.length)
                updateLocalToWorld();
        }

        private function updateLocalToWorld(nodeId : uint = 0) : void
        {
            var numNodes 			: uint 			= _transforms.length;
            var childrenOffset		: uint			= 1;
            var rootLocalToWorld	: Matrix4x4		= _localToWorldTransforms[nodeId];
            var rootTransform		: Matrix4x4		= _transforms[nodeId];
            var root				: ISceneNode	= _idToNode[childId];

            if (rootTransform._hasChanged || _initialized[nodeId] == INIT_NONE)
            {
                rootLocalToWorld.copyFrom(rootTransform);

                if (nodeId != 0)
                    rootLocalToWorld.append(_localToWorldTransforms[_parentId[nodeId]]);

                rootTransform._hasChanged = false;
                _initialized[nodeId] = INIT_LOCAL_TO_WORLD;
                root.localToWorldTransformChanged.execute(root, rootLocalToWorld);
            }

            for (; nodeId < numNodes; ++nodeId)
            {
                var localToWorld 	: Matrix4x4	= _localToWorldTransforms[nodeId];
                var numChildren		: uint		= _numChildren[nodeId];
                var firstChildId	: uint		= _firstChildId[nodeId];
                var lastChildId		: uint		= firstChildId + numChildren;
                var isDirty			: Boolean	= localToWorld._hasChanged;

                localToWorld._hasChanged = false;

                for (var childId : uint = firstChildId; childId < lastChildId; ++childId)
                {
                    var childTransform		: Matrix4x4		= _transforms[childId];
                    var childLocalToWorld	: Matrix4x4		= _localToWorldTransforms[childId];
                    var childIsDirty		: Boolean		= isDirty || childTransform._hasChanged
                        || !_initialized[childId];

                    if (childIsDirty)
                    {
                        var child	: ISceneNode	= _idToNode[childId];

                        childLocalToWorld
                        .copyFrom(childTransform)
                            .append(localToWorld);

                        childTransform._hasChanged = false;
                        _initialized[childId] = INIT_LOCAL_TO_WORLD;
                        child.localToWorldTransformChanged.execute(child, childLocalToWorld);
                    }
                }
            }
        }

        private function updateAncestorsAndSelfLocalToWorld(nodeId : uint) : void
        {
            var dirtyRoot : int = -1;

            while (nodeId >= 0)
            {
                if ((_transforms[nodeId] as Matrix4x4)._hasChanged ||
                    !_initialized[nodeId])
                    dirtyRoot = nodeId;

                nodeId = _parentId[nodeId];
            }

            if (dirtyRoot >= 0)
                updateLocalToWorld(dirtyRoot);
        }

        private function targetAddedHandler(ctrl	: TransformController,
                                            target	: ISceneNode) : void
        {
            if (_target)
                throw new Error('The TransformController cannot have more than one target.');

            _target = target;
            _invalidList = true;

            if (target is Scene)
            {
                (target as Scene).renderingBegin.add(renderingBeginHandler);
                return;
            }

            if (target is Group)
            {
                var targetGroup : Group = target as Group;

                targetGroup.descendantAdded.add(descendantAddedHandler);
                targetGroup.descendantRemoved.add(descendantRemovedHandler);
            }

            target.added.add(addedHandler);
        }

        private function targetRemovedHandler(ctrl		: TransformController,
                                              target	: ISceneNode) : void
        {
            target.added.remove(addedHandler);

            if (target is Group)
            {
                var targetGroup : Group = target as Group;

                targetGroup.descendantAdded.remove(descendantAddedHandler);
                targetGroup.descendantRemoved.remove(descendantRemovedHandler);
            }

            _invalidList = false;
            _target = null;

            _nodeToId = null;
            _transforms = null;
            _initialized = null;
            _localToWorldTransforms = null;
            _worldToLocalTransforms = null;
            _numChildren = null;
            _idToNode = null;
            _parentId = null;
        }

        private function addedHandler(target : ISceneNode, ancestor : Group) : void
        {
            // the controller will remove itself from the node when it's not its own root anymore
            // but it will watch for the 'removed' signal to add itself back if the node becomes
            // its own root again
            _target.removed.add(removedHandler);
            _target.removeController(this);
        }

        private function removedHandler(target : ISceneNode, ancestor : Group) : void
        {
            if (target.root == target)
            {
                target.removed.remove(removedHandler);
                target.addController(this);
            }
        }

        private function descendantAddedHandler(root		: Group,
                                                descendant	: ISceneNode) : void
        {
            _invalidList = true;
        }

        private function descendantRemovedHandler(root			: Group,
                                                  descendant	: ISceneNode) : void
        {
            _invalidList = true;
        }

        private function updateTransformsList() : void
        {
            var root 	: ISceneNode 			= _target.root;
            var nodes 	: Vector.<ISceneNode> 	= new <ISceneNode>[root];
            var nodeId 	: uint 					= 0;

            _nodeToId = new Dictionary(true);
            _transforms = new <Matrix4x4>[];
            _initialized = new <uint>[];
            _localToWorldTransforms = new <Matrix4x4>[];
            _worldToLocalTransforms = new <Matrix4x4>[];
            _numChildren = new <uint>[];
            _firstChildId = new <uint>[];
            _idToNode = new <ISceneNode>[];
            _parentId = new <int>[-1];

            while (nodes.length)
            {
                var node 	: ISceneNode 	= nodes.shift();
                var group 	: Group 		= node as Group;

                _nodeToId[node] = nodeId;
                _idToNode[nodeId] = node;
                _transforms[nodeId] = node.transform;
                _localToWorldTransforms[nodeId] = new Matrix4x4().lock();
                _initialized[nodeId] = INIT_NONE;

                if (group)
                {
                    var numChildren 	: uint = group.numChildren;
                    var firstChildId 	: uint = nodeId + nodes.length + 1;

                    _numChildren[nodeId] = numChildren;
                    _firstChildId[nodeId] = firstChildId;
                    for (var childId : uint = 0; childId < numChildren; ++childId)
                    {
                        _parentId[uint(firstChildId + childId)] = nodeId;
                        nodes.push(group.getChildAt(childId));
                    }
                }
                else
                {
                    _numChildren[nodeId] = 0;
                    _firstChildId[nodeId] = 0;
                }

                ++nodeId;
            }

            _worldToLocalTransforms.length = _localToWorldTransforms.length;
            _invalidList = false;
        }

        public function getLocalToWorldTransform(node 			: ISceneNode,
                                                 forceUpdate 	: Boolean = false) : Matrix4x4
        {
            if (_invalidList || _nodeToId[node] == undefined)
                updateTransformsList();

            var nodeId : uint = _nodeToId[node];

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            return _localToWorldTransforms[nodeId];
        }

        public function getWorldToLocalTransform(node           : ISceneNode,
                                                 forceUpdate    : Boolean   = false) : Matrix4x4
        {
            if (_invalidList || _nodeToId[node] == undefined)
                updateTransformsList();

            var nodeId                  : uint      = _nodeToId[node];
            var worldToLocalTransform   : Matrix4x4 = _worldToLocalTransforms[nodeId];

            if (!worldToLocalTransform)
            {
                _worldToLocalTransforms[nodeId] = worldToLocalTransform = new Matrix4x4();
                if (!forceUpdate)
                {
                    worldToLocalTransform.copyFrom(_localToWorldTransforms[nodeId]).invert();
                    _initialized[nodeId] |= INIT_WORLD_TO_LOCAL;
                }
            }

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            if (!(_initialized[nodeId] & INIT_WORLD_TO_LOCAL))
            {
                _initialized[nodeId] |= INIT_WORLD_TO_LOCAL;

                worldToLocalTransform
                    .copyFrom(_localToWorldTransforms[nodeId])
                    .invert();
            }

            return worldToLocalTransform;
        }
    }
}

fix world to local computation/update when the local to world is uptodate and the world to local has already been instanciated


package aerys.minko.scene.controller
{
    import aerys.minko.ns.minko_math;
    import aerys.minko.render.Viewport;
    import aerys.minko.scene.node.Group;
    import aerys.minko.scene.node.ISceneNode;
    import aerys.minko.scene.node.Scene;
    import aerys.minko.type.math.Matrix4x4;

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

    import mx.olap.aggregators.MaxAggregator;

    use namespace minko_math;

    /**
     * The TransformController handles the batched update of all the local to world matrices
     * of a sub-scene. As such, it will only be active on the root node of a sub-scene and will
     * automatically disable itself on other nodes.
     *
     * @author Jean-Marc Le Roux
     *
     */
    public final class TransformController extends AbstractController
    {

        private static const FLAG_NONE                      : uint  = 0;
        private static const FLAG_INIT_LOCAL_TO_WORLD       : uint  = 1;
        private static const FLAG_INIT_WORLD_TO_LOCAL       : uint  = 2;
        private static const FLAG_SYNCHRONIZE_TRANSFORMS    : uint  = 4;
        private static const FLAG_LOCK_TRANSFORMS           : uint  = 8;

        private var _target					: ISceneNode;

        private var _invalidList			: Boolean;

        private var _nodeToId				: Dictionary;
        private var _idToNode				: Vector.<ISceneNode>
        private var _transforms				: Vector.<Matrix4x4>;
        private var _flags	                : Vector.<uint>;
        private var _localToWorldTransforms : Vector.<Matrix4x4>;
        private var _worldToLocalTransforms : Vector.<Matrix4x4>;
        private var _numChildren			: Vector.<uint>;
        private var _firstChildId			: Vector.<uint>;
        private var _parentId				: Vector.<int>;

        public function TransformController()
        {
            super();

            targetAdded.add(targetAddedHandler);
            targetRemoved.add(targetRemovedHandler);
        }

        private function renderingBeginHandler(scene			: Scene,
                                               viewport		: Viewport,
                                               destination	: BitmapData,
                                               time			: Number) : void
        {
            if (_invalidList)
                updateTransformsList();

            if (_transforms.length)
                updateLocalToWorld();
        }

        private function updateRootLocalToWorld(nodeId : uint = 0) : void
        {
            var rootLocalToWorld	: Matrix4x4		= _localToWorldTransforms[nodeId];
            var rootTransform		: Matrix4x4		= _transforms[nodeId];
            var root				: ISceneNode	= _idToNode[nodeId];
            var rootFlags           : uint          = _flags[nodeId];

            if (rootTransform._hasChanged || !(rootFlags & FLAG_INIT_LOCAL_TO_WORLD))
            {
                if (rootFlags & FLAG_LOCK_TRANSFORMS)
                    rootLocalToWorld.lock();

                rootLocalToWorld.copyFrom(rootTransform);

                if (nodeId != 0)
                    rootLocalToWorld.append(_localToWorldTransforms[_parentId[nodeId]]);

                if (rootFlags & FLAG_LOCK_TRANSFORMS)
                    rootLocalToWorld.unlock();

                rootTransform._hasChanged = false;
                _flags[nodeId] |= FLAG_INIT_LOCAL_TO_WORLD;
                root.localToWorldTransformChanged.execute(root, rootLocalToWorld);

                if (rootFlags & FLAG_SYNCHRONIZE_TRANSFORMS)
                {
                    var rootWorldToLocal : Matrix4x4 = _worldToLocalTransforms[nodeId]
                        || (_worldToLocalTransforms[nodeId] = new Matrix4x4());

                    if (rootFlags & FLAG_LOCK_TRANSFORMS)
                        rootWorldToLocal.lock();

                    rootWorldToLocal.copyFrom(rootLocalToWorld).invert();

                    if (rootFlags & FLAG_LOCK_TRANSFORMS)
                        rootWorldToLocal.unlock();
                }
            }
        }

        private function updateLocalToWorld(nodeId : uint = 0, subtreeOnly : Boolean = false) : void
        {
            var numNodes 			: uint 			= _transforms.length;
            var subtreeMax          : uint          = nodeId;

            updateRootLocalToWorld(nodeId);

            while (nodeId < numNodes)
            {
                var localToWorld 	: Matrix4x4	= _localToWorldTransforms[nodeId];
                var numChildren		: uint		= _numChildren[nodeId];
                var firstChildId	: uint		= _firstChildId[nodeId];
                var lastChildId		: uint		= firstChildId + numChildren;
                var isDirty			: Boolean	= localToWorld._hasChanged;

                localToWorld._hasChanged = false;

                if (lastChildId > subtreeMax)
                    subtreeMax = lastChildId;

                for (var childId : uint = firstChildId; childId < lastChildId; ++childId)
                {
                    var childTransform		: Matrix4x4		= _transforms[childId];
                    var childLocalToWorld	: Matrix4x4		= _localToWorldTransforms[childId];
                    var childFlags          : uint          = _flags[childId];
                    var childIsDirty		: Boolean		= isDirty || childTransform._hasChanged
                        || !(childFlags & FLAG_INIT_LOCAL_TO_WORLD);

                    if (childIsDirty)
                    {
                        var child	: ISceneNode	= _idToNode[childId];

                        if (childFlags & FLAG_LOCK_TRANSFORMS)
                            childLocalToWorld.lock();

                        childLocalToWorld
                            .copyFrom(childTransform)
                            .append(localToWorld);

                        if (childFlags & FLAG_LOCK_TRANSFORMS)
                            childLocalToWorld.unlock();

                        childTransform._hasChanged = false;
                        _flags[childId] |= FLAG_INIT_LOCAL_TO_WORLD;
                        child.localToWorldTransformChanged.execute(child, childLocalToWorld);

                        if (childFlags & FLAG_SYNCHRONIZE_TRANSFORMS)
                        {
                            var childWorldToLocal : Matrix4x4 = _worldToLocalTransforms[childId]
                                || (_worldToLocalTransforms[childId] = new Matrix4x4());

                            if (childFlags & FLAG_LOCK_TRANSFORMS)
                                childWorldToLocal.lock();

                            childWorldToLocal.copyFrom(childLocalToWorld).invert();

                            if (childFlags & FLAG_LOCK_TRANSFORMS)
                                childWorldToLocal.unlock();
                        }
                    }
                }

                if (subtreeOnly && nodeId && nodeId >= subtreeMax)
                {
                    // jump to the first brother who has children
                    var parentId : uint = _parentId[nodeId];

                    nodeId = _firstChildId[parentId];
                    while (!_numChildren[nodeId] && nodeId < subtreeMax)
                        ++nodeId;

                    if (nodeId >= subtreeMax)
                        return ;

                    nodeId = _firstChildId[nodeId];
                }
                else
                    ++nodeId;
            }
        }

        private function updateAncestorsAndSelfLocalToWorld(nodeId : int) : void
        {
            var dirtyRoot : int = -1;

            while (nodeId >= 0)
            {
                if ((_transforms[nodeId] as Matrix4x4)._hasChanged
                    || !(_flags[nodeId] & FLAG_INIT_LOCAL_TO_WORLD))
                    dirtyRoot = nodeId;

                nodeId = _parentId[nodeId];
            }

            if (dirtyRoot >= 0)
                updateLocalToWorld(dirtyRoot, true);
        }

        private function targetAddedHandler(ctrl	: TransformController,
                                            target	: ISceneNode) : void
        {
            if (_target)
                throw new Error('The TransformController cannot have more than one target.');

            _target = target;
            _invalidList = true;

            if (target is Scene)
            {
                (target as Scene).renderingBegin.add(renderingBeginHandler);
                return;
            }

            if (target is Group)
            {
                var targetGroup : Group = target as Group;

                targetGroup.descendantAdded.add(descendantAddedHandler);
                targetGroup.descendantRemoved.add(descendantRemovedHandler);
            }

            target.added.add(addedHandler);
        }

        private function targetRemovedHandler(ctrl		: TransformController,
                                              target	: ISceneNode) : void
        {
            target.added.remove(addedHandler);

            if (target is Group)
            {
                var targetGroup : Group = target as Group;

                targetGroup.descendantAdded.remove(descendantAddedHandler);
                targetGroup.descendantRemoved.remove(descendantRemovedHandler);
            }

            _invalidList = false;
            _target = null;

            _nodeToId = null;
            _transforms = null;
            _flags = null;
            _localToWorldTransforms = null;
            _worldToLocalTransforms = null;
            _numChildren = null;
            _idToNode = null;
            _parentId = null;
        }

        private function addedHandler(target : ISceneNode, ancestor : Group) : void
        {
            // the controller will remove itself from the node when it's not its own root anymore
            // but it will watch for the 'removed' signal to add itself back if the node becomes
            // its own root again
            _target.removed.add(removedHandler);
            _target.removeController(this);
        }

        private function removedHandler(target : ISceneNode, ancestor : Group) : void
        {
            if (target.root == target)
            {
                target.removed.remove(removedHandler);
                target.addController(this);
            }
        }

        private function descendantAddedHandler(root		: Group,
                                                descendant	: ISceneNode) : void
        {
            _invalidList = true;
        }

        private function descendantRemovedHandler(root			: Group,
                                                  descendant	: ISceneNode) : void
        {
            _invalidList = true;
        }

        private function getNodeId(node : ISceneNode) : uint
        {
            if (_invalidList || !(node in _nodeToId))
                updateTransformsList();

            return _nodeToId[node];
        }

        private function updateTransformsList() : void
        {
            var root    	                : ISceneNode 			= _target.root;
            var nodes   	                : Vector.<ISceneNode> 	= new <ISceneNode>[root];
            var nodeId  	                : uint 					= 0;
            var oldNodeToId                 : Dictionary            = _nodeToId;
            var oldInitialized              : Vector.<uint>         = _flags;
            var oldLocalToWorldTransforms   : Vector.<Matrix4x4>    = _localToWorldTransforms;
            var oldWorldToLocalTransform    : Vector.<Matrix4x4>    = _worldToLocalTransforms;

            _nodeToId = new Dictionary(true);
            _transforms = new <Matrix4x4>[];
            _flags = new <uint>[];
            _localToWorldTransforms = new <Matrix4x4>[];
            _worldToLocalTransforms = new <Matrix4x4>[];
            _numChildren = new <uint>[];
            _firstChildId = new <uint>[];
            _idToNode = new <ISceneNode>[];
            _parentId = new <int>[-1];

            while (nodes.length)
            {
                var node 	: ISceneNode 	= nodes.shift();
                var group   : Group 		= node as Group;

                _nodeToId[node] = nodeId;
                _idToNode[nodeId] = node;
                _transforms[nodeId] = node.transform;

                if (oldNodeToId && node in oldNodeToId)
                {
                    var oldNodeId   : uint  = oldNodeToId[node];

                    _localToWorldTransforms[nodeId] = oldLocalToWorldTransforms[oldNodeId];
                    _worldToLocalTransforms[nodeId] = oldWorldToLocalTransform[oldNodeId];
                    _flags[nodeId] = oldInitialized[oldNodeId];
                }
                else
                {
                    _localToWorldTransforms[nodeId] = new Matrix4x4().lock();
                    _worldToLocalTransforms[nodeId] = null;
                    _flags[nodeId] = FLAG_NONE;
                }

                if (group)
                {
                    var numChildren 	: uint = group.numChildren;
                    var firstChildId 	: uint = nodeId + nodes.length + 1;

                    _numChildren[nodeId] = numChildren;
                    _firstChildId[nodeId] = firstChildId;
                    for (var childId : uint = 0; childId < numChildren; ++childId)
                    {
                        _parentId[uint(firstChildId + childId)] = nodeId;
                        nodes.push(group.getChildAt(childId));
                    }
                }
                else
                {
                    _numChildren[nodeId] = 0;
                    _firstChildId[nodeId] = 0;
                }

                ++nodeId;
            }

            _worldToLocalTransforms.length = _localToWorldTransforms.length;
            _invalidList = false;
        }

        public function getLocalToWorldTransform(node 			: ISceneNode,
                                                 forceUpdate 	: Boolean = false) : Matrix4x4
        {
            var nodeId : uint = getNodeId(node);

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            return _localToWorldTransforms[nodeId];
        }

        public function getWorldToLocalTransform(node           : ISceneNode,
                                                 forceUpdate    : Boolean   = false) : Matrix4x4
        {
            var nodeId                  : uint      = getNodeId(node);
            var worldToLocalTransform   : Matrix4x4 = _worldToLocalTransforms[nodeId];

            if (!worldToLocalTransform)
            {
                _worldToLocalTransforms[nodeId] = worldToLocalTransform = new Matrix4x4();
                if (!forceUpdate)
                {
                    worldToLocalTransform.copyFrom(_localToWorldTransforms[nodeId]).invert();
                    _flags[nodeId] |= FLAG_INIT_WORLD_TO_LOCAL;
                }
            }

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            var flags : uint = _flags[nodeId];

            if (!(flags & FLAG_INIT_WORLD_TO_LOCAL))
            {
                _flags[nodeId] |= FLAG_INIT_WORLD_TO_LOCAL;

                if (flags & FLAG_LOCK_TRANSFORMS)
                    worldToLocalTransform.lock();

                worldToLocalTransform
                    .copyFrom(_localToWorldTransforms[nodeId])
                    .invert();

                if (flags & FLAG_LOCK_TRANSFORMS)
                    worldToLocalTransform.unlock();
            }

            return worldToLocalTransform;
        }

        public function setSharedLocalToWorldTransformReference(node      : ISceneNode,
                                                                matrix    : Matrix4x4) : void
        {
            var nodeId : uint = getNodeId(node);

            if (_flags[nodeId] & FLAG_INIT_LOCAL_TO_WORLD)
                matrix.copyFrom(_localToWorldTransforms[nodeId]);
            _localToWorldTransforms[nodeId] = matrix;
        }

        public function setSharedWorldToLocalTransformReference(node      : ISceneNode,
                                                                matrix    : Matrix4x4) : void
        {
            var nodeId : uint = getNodeId(node);

            if (_flags[nodeId] & FLAG_INIT_WORLD_TO_LOCAL)
                matrix.copyFrom(_worldToLocalTransforms[nodeId]);
            _worldToLocalTransforms[nodeId] = matrix;
        }

        public function synchronizeTransforms(node : ISceneNode, enabled : Boolean) : void
        {
            var nodeId : uint = getNodeId(node);

            _flags[nodeId] = enabled
                ? _flags[nodeId] | FLAG_SYNCHRONIZE_TRANSFORMS
                : _flags[nodeId] & ~FLAG_SYNCHRONIZE_TRANSFORMS;
        }

        public function lockTransformsBeforeUpdate(node : ISceneNode, enabled : Boolean) : void
        {
            var nodeId : uint = getNodeId(node);

            _flags[nodeId] = enabled
                ? _flags[nodeId] | FLAG_LOCK_TRANSFORMS
                : _flags[nodeId] & ~FLAG_LOCK_TRANSFORMS;
        }
    }
}

package aerys.minko.scene.controller
{
    import aerys.minko.ns.minko_math;
    import aerys.minko.render.Viewport;
    import aerys.minko.scene.node.Group;
    import aerys.minko.scene.node.ISceneNode;
    import aerys.minko.scene.node.Scene;
    import aerys.minko.type.math.Matrix4x4;

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

    import mx.olap.aggregators.MaxAggregator;

    use namespace minko_math;

    /**
     * The TransformController handles the batched update of all the local to world matrices
     * of a sub-scene. As such, it will only be active on the root node of a sub-scene and will
     * automatically disable itself on other nodes.
     *
     * @author Jean-Marc Le Roux
     *
     */
    public final class TransformController extends AbstractController
    {

        private static const FLAG_NONE                      : uint  = 0;
        private static const FLAG_INIT_LOCAL_TO_WORLD       : uint  = 1;
        private static const FLAG_INIT_WORLD_TO_LOCAL       : uint  = 2;
        private static const FLAG_SYNCHRONIZE_TRANSFORMS    : uint  = 4;
        private static const FLAG_LOCK_TRANSFORMS           : uint  = 8;

        private var _target					: ISceneNode;

        private var _invalidList			: Boolean;

        private var _nodeToId				: Dictionary;
        private var _idToNode				: Vector.<ISceneNode>
        private var _transforms				: Vector.<Matrix4x4>;
        private var _flags	                : Vector.<uint>;
        private var _localToWorldTransforms : Vector.<Matrix4x4>;
        private var _worldToLocalTransforms : Vector.<Matrix4x4>;
        private var _numChildren			: Vector.<uint>;
        private var _firstChildId			: Vector.<uint>;
        private var _parentId				: Vector.<int>;

        public function TransformController()
        {
            super();

            targetAdded.add(targetAddedHandler);
            targetRemoved.add(targetRemovedHandler);
        }

        private function renderingBeginHandler(scene		: Scene,
                                               viewport		: Viewport,
                                               destination	: BitmapData,
                                               time			: Number) : void
        {
            if (_invalidList)
                updateTransformsList();

            if (_transforms.length)
                updateLocalToWorld();
        }

        private function updateRootLocalToWorld(nodeId : uint = 0) : void
        {
            var rootLocalToWorld	: Matrix4x4		= _localToWorldTransforms[nodeId];
            var rootTransform		: Matrix4x4		= _transforms[nodeId];
            var root				: ISceneNode	= _idToNode[nodeId];
            var rootFlags           : uint          = _flags[nodeId];

            if (rootTransform._hasChanged || !(rootFlags & FLAG_INIT_LOCAL_TO_WORLD))
            {
                if (rootFlags & FLAG_LOCK_TRANSFORMS)
                    rootLocalToWorld.lock();

                rootLocalToWorld.copyFrom(rootTransform);

                if (nodeId != 0)
                    rootLocalToWorld.append(_localToWorldTransforms[_parentId[nodeId]]);

                if (rootFlags & FLAG_LOCK_TRANSFORMS)
                    rootLocalToWorld.unlock();

                rootTransform._hasChanged = false;
                _flags[nodeId] |= FLAG_INIT_LOCAL_TO_WORLD;
                root.localToWorldTransformChanged.execute(root, rootLocalToWorld);

                if (rootFlags & FLAG_SYNCHRONIZE_TRANSFORMS)
                {
                    var rootWorldToLocal : Matrix4x4 = _worldToLocalTransforms[nodeId]
                        || (_worldToLocalTransforms[nodeId] = new Matrix4x4());

                    if (rootFlags & FLAG_LOCK_TRANSFORMS)
                        rootWorldToLocal.lock();

                    rootWorldToLocal.copyFrom(rootLocalToWorld).invert();

                    if (rootFlags & FLAG_LOCK_TRANSFORMS)
                        rootWorldToLocal.unlock();
                }
            }
        }

        private function updateLocalToWorld(nodeId : uint = 0, targetNodeId : int = -1) : void
        {
            var numNodes 	: uint 	= _transforms.length;
            var subtreeMax  : uint  = nodeId;

            updateRootLocalToWorld(nodeId);

            if (nodeId == targetNodeId)
                return;

            while (nodeId < numNodes)
            {
                var localToWorld 	: Matrix4x4	= _localToWorldTransforms[nodeId];
                var numChildren		: uint		= _numChildren[nodeId];
                var firstChildId	: uint		= _firstChildId[nodeId];
                var lastChildId		: uint		= firstChildId + numChildren;
                var isDirty			: Boolean	= localToWorld._hasChanged;

                localToWorld._hasChanged = false;

                if (lastChildId > subtreeMax)
                    subtreeMax = lastChildId;

                for (var childId : uint = firstChildId; childId < lastChildId; ++childId)
                {
                    var childTransform		: Matrix4x4		= _transforms[childId];
                    var childLocalToWorld	: Matrix4x4		= _localToWorldTransforms[childId];
                    var childFlags          : uint          = _flags[childId];
                    var childIsDirty		: Boolean		= isDirty || childTransform._hasChanged
                        || !(childFlags & FLAG_INIT_LOCAL_TO_WORLD);

                    if (childIsDirty)
                    {
                        var child	: ISceneNode	= _idToNode[childId];

                        if (childFlags & FLAG_LOCK_TRANSFORMS)
                            childLocalToWorld.lock();

                        childLocalToWorld
                            .copyFrom(childTransform)
                            .append(localToWorld);

                        if (childFlags & FLAG_LOCK_TRANSFORMS)
                            childLocalToWorld.unlock();

                        childTransform._hasChanged = false;
                        _flags[childId] |= FLAG_INIT_LOCAL_TO_WORLD;
                        child.localToWorldTransformChanged.execute(child, childLocalToWorld);

                        if (childFlags & FLAG_SYNCHRONIZE_TRANSFORMS)
                        {
                            var childWorldToLocal : Matrix4x4 = _worldToLocalTransforms[childId]
                                || (_worldToLocalTransforms[childId] = new Matrix4x4());

                            if (childFlags & FLAG_LOCK_TRANSFORMS)
                                childWorldToLocal.lock();

                            childWorldToLocal.copyFrom(childLocalToWorld).invert();

                            if (childFlags & FLAG_LOCK_TRANSFORMS)
                                childWorldToLocal.unlock();
                        }

                        if (childId == targetNodeId)
                            return;
                    }
                }

                if (targetNodeId >= 0 && nodeId && nodeId >= subtreeMax)
                {
                    // jump to the first brother who has children
                    var parentId : uint = _parentId[nodeId];

                    nodeId = _firstChildId[parentId];
                    while (!_numChildren[nodeId] && nodeId < subtreeMax)
                        ++nodeId;

                    if (nodeId >= subtreeMax)
                        return ;

                    nodeId = _firstChildId[nodeId];
                }
                else
                    ++nodeId;
            }
        }

        private function updateAncestorsAndSelfLocalToWorld(nodeId : uint) : void
        {
            var dirtyRoot : int = -1;
            var tmpNodeId : int = nodeId;

            while (tmpNodeId >= 0)
            {
                if ((_transforms[tmpNodeId] as Matrix4x4)._hasChanged
                    || !(_flags[nodeId] & FLAG_INIT_LOCAL_TO_WORLD))
                    dirtyRoot = tmpNodeId;

                tmpNodeId = _parentId[tmpNodeId];
            }

            if (dirtyRoot >= 0)
                updateLocalToWorld(dirtyRoot, nodeId);
        }

        private function targetAddedHandler(ctrl	: TransformController,
                                            target	: ISceneNode) : void
        {
            if (_target)
                throw new Error('The TransformController cannot have more than one target.');

            _target = target;
            _invalidList = true;

            if (target is Scene)
            {
                (target as Scene).renderingBegin.add(renderingBeginHandler);
                return;
            }

            if (target is Group)
            {
                var targetGroup : Group = target as Group;

                targetGroup.descendantAdded.add(descendantAddedHandler);
                targetGroup.descendantRemoved.add(descendantRemovedHandler);
            }

            target.added.add(addedHandler);
        }

        private function targetRemovedHandler(ctrl		: TransformController,
                                              target	: ISceneNode) : void
        {
            target.added.remove(addedHandler);

            if (target is Group)
            {
                var targetGroup : Group = target as Group;

                targetGroup.descendantAdded.remove(descendantAddedHandler);
                targetGroup.descendantRemoved.remove(descendantRemovedHandler);
            }

            _invalidList = false;
            _target = null;

            _nodeToId = null;
            _transforms = null;
            _flags = null;
            _localToWorldTransforms = null;
            _worldToLocalTransforms = null;
            _numChildren = null;
            _idToNode = null;
            _parentId = null;
        }

        private function addedHandler(target : ISceneNode, ancestor : Group) : void
        {
            // the controller will remove itself from the node when it's not its own root anymore
            // but it will watch for the 'removed' signal to add itself back if the node becomes
            // its own root again
            _target.removed.add(removedHandler);
            _target.removeController(this);
        }

        private function removedHandler(target : ISceneNode, ancestor : Group) : void
        {
            if (target.root == target)
            {
                target.removed.remove(removedHandler);
                target.addController(this);
            }
        }

        private function descendantAddedHandler(root		: Group,
                                                descendant	: ISceneNode) : void
        {
            _invalidList = true;
        }

        private function descendantRemovedHandler(root			: Group,
                                                  descendant	: ISceneNode) : void
        {
            _invalidList = true;
        }

        private function getNodeId(node : ISceneNode) : uint
        {
            if (_invalidList || !(node in _nodeToId))
                updateTransformsList();

            return _nodeToId[node];
        }

        private function updateTransformsList() : void
        {
            var root    	                : ISceneNode 			= _target.root;
            var nodes   	                : Vector.<ISceneNode> 	= new <ISceneNode>[root];
            var nodeId  	                : uint 					= 0;
            var oldNodeToId                 : Dictionary            = _nodeToId;
            var oldInitialized              : Vector.<uint>         = _flags;
            var oldLocalToWorldTransforms   : Vector.<Matrix4x4>    = _localToWorldTransforms;
            var oldWorldToLocalTransform    : Vector.<Matrix4x4>    = _worldToLocalTransforms;

            _nodeToId = new Dictionary(true);
            _transforms = new <Matrix4x4>[];
            _flags = new <uint>[];
            _localToWorldTransforms = new <Matrix4x4>[];
            _worldToLocalTransforms = new <Matrix4x4>[];
            _numChildren = new <uint>[];
            _firstChildId = new <uint>[];
            _idToNode = new <ISceneNode>[];
            _parentId = new <int>[-1];

            while (nodes.length)
            {
                var node 	: ISceneNode 	= nodes.shift();
                var group   : Group 		= node as Group;

                _nodeToId[node] = nodeId;
                _idToNode[nodeId] = node;
                _transforms[nodeId] = node.transform;

                if (oldNodeToId && node in oldNodeToId)
                {
                    var oldNodeId   : uint  = oldNodeToId[node];

                    _localToWorldTransforms[nodeId] = oldLocalToWorldTransforms[oldNodeId];
                    _worldToLocalTransforms[nodeId] = oldWorldToLocalTransform[oldNodeId];
                    _flags[nodeId] = oldInitialized[oldNodeId];
                }
                else
                {
                    _localToWorldTransforms[nodeId] = new Matrix4x4().lock();
                    _worldToLocalTransforms[nodeId] = null;
                    _flags[nodeId] = FLAG_NONE;
                }

                if (group)
                {
                    var numChildren 	: uint = group.numChildren;
                    var firstChildId 	: uint = nodeId + nodes.length + 1;

                    _numChildren[nodeId] = numChildren;
                    _firstChildId[nodeId] = firstChildId;
                    for (var childId : uint = 0; childId < numChildren; ++childId)
                    {
                        _parentId[uint(firstChildId + childId)] = nodeId;
                        nodes.push(group.getChildAt(childId));
                    }
                }
                else
                {
                    _numChildren[nodeId] = 0;
                    _firstChildId[nodeId] = 0;
                }

                ++nodeId;
            }

            _worldToLocalTransforms.length = _localToWorldTransforms.length;
            _invalidList = false;
        }

        public function getLocalToWorldTransform(node 			: ISceneNode,
                                                 forceUpdate 	: Boolean = false) : Matrix4x4
        {
            var nodeId : uint = getNodeId(node);

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            return _localToWorldTransforms[nodeId];
        }

        public function getWorldToLocalTransform(node           : ISceneNode,
                                                 forceUpdate    : Boolean   = false) : Matrix4x4
        {
            var nodeId                  : uint      = getNodeId(node);
            var worldToLocalTransform   : Matrix4x4 = _worldToLocalTransforms[nodeId];

            if (!worldToLocalTransform)
            {
                _worldToLocalTransforms[nodeId] = worldToLocalTransform = new Matrix4x4();
                if (!forceUpdate)
                {
                    worldToLocalTransform.copyFrom(_localToWorldTransforms[nodeId]).invert();
                    _flags[nodeId] |= FLAG_INIT_WORLD_TO_LOCAL;
                }
            }

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            var flags : uint = _flags[nodeId];

            if (!(flags & FLAG_INIT_WORLD_TO_LOCAL))
            {
                _flags[nodeId] |= FLAG_INIT_WORLD_TO_LOCAL;

                if (flags & FLAG_LOCK_TRANSFORMS)
                    worldToLocalTransform.lock();

                worldToLocalTransform
                    .copyFrom(_localToWorldTransforms[nodeId])
                    .invert();

                if (flags & FLAG_LOCK_TRANSFORMS)
                    worldToLocalTransform.unlock();
            }

            return worldToLocalTransform;
        }

        public function setSharedLocalToWorldTransformReference(node      : ISceneNode,
                                                                matrix    : Matrix4x4) : void
        {
            var nodeId : uint = getNodeId(node);

            if (_flags[nodeId] & FLAG_INIT_LOCAL_TO_WORLD)
                matrix.copyFrom(_localToWorldTransforms[nodeId]);
            _localToWorldTransforms[nodeId] = matrix;
        }

        public function setSharedWorldToLocalTransformReference(node      : ISceneNode,
                                                                matrix    : Matrix4x4) : void
        {
            var nodeId : uint = getNodeId(node);

            if (_flags[nodeId] & FLAG_INIT_WORLD_TO_LOCAL)
                matrix.copyFrom(_worldToLocalTransforms[nodeId]);
            _worldToLocalTransforms[nodeId] = matrix;
        }

        public function synchronizeTransforms(node : ISceneNode, enabled : Boolean) : void
        {
            var nodeId : uint = getNodeId(node);

            _flags[nodeId] = enabled
                ? _flags[nodeId] | FLAG_SYNCHRONIZE_TRANSFORMS
                : _flags[nodeId] & ~FLAG_SYNCHRONIZE_TRANSFORMS;
        }

        public function lockTransformsBeforeUpdate(node : ISceneNode, enabled : Boolean) : void
        {
            var nodeId : uint = getNodeId(node);

            _flags[nodeId] = enabled
                ? _flags[nodeId] | FLAG_LOCK_TRANSFORMS
                : _flags[nodeId] & ~FLAG_LOCK_TRANSFORMS;
        }
    }
}

optimize TransformController.updateLocalToWorld() to stop updating as soon as the node with id == targetNodeId is updated (if provided)


package net.manaca.loaderqueue.inspector
{
import com.bit101.components.CheckBox;
import com.bit101.components.ScrollPane;
import com.bit101.components.Style;

import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.system.Capabilities;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.ui.Keyboard;
import flash.utils.Dictionary;

import net.manaca.loaderqueue.ILoaderAdapter;
import net.manaca.loaderqueue.ILoaderQueue;
import net.manaca.loaderqueue.LoaderAdapterState;
import net.manaca.loaderqueue.LoaderQueueEvent;

/**
 * 提供一个可视化界面用户查看加载队列进行情况.
 * @author wersling
 *
 */
public class LoaderInspector extends Sprite
{
    //==========================================================================
    //  Constructor
    //==========================================================================
    /**
     * 构造函数.
     * @param loaderQueue 需要监视的加载队列.
     *
     */
    public function LoaderInspector(loaderQueue:ILoaderQueue = null)
    {
        super();

        this.loaderQueue = loaderQueue;

        initDisplay();
    }

    //==========================================================================
    //  Variables
    //==========================================================================
    private var scrollPane:ScrollPane;
    private var container:DisplayObjectContainer;
    private var onlyButton:CheckBox;
    private var itemsMap:Dictionary = new Dictionary(true);

    private var parentContainer:DisplayObjectContainer;
    //==========================================================================
    //  Properties
    //==========================================================================
    private var _loaderQueue:ILoaderQueue;
    /**
     * 需要监视的加载队列.
     * @return
     *
     */
    public function get loaderQueue():ILoaderQueue
    {
        return _loaderQueue;
    }

    public function set loaderQueue(value:ILoaderQueue):void
    {
        if(_loaderQueue && _loaderQueue != value)
        {
            removeEvents();
            clearAllTask();
        }

        _loaderQueue = value;
        if(_loaderQueue)
        {
            addEvents();
        }
    }

    private var _height:Number = 0;
    /**
     * @private
     */
    override public function get height():Number
    {
        return _height;
    }
    override public function set height(value:Number):void
    {
        _height = value;
        layout();
    }

    private var _width:Number = 0;
    /**
     * @private
     */
    override public function get width():Number
    {
        return _width;
    }

    override public function set width(value:Number):void
    {
        _width = value;
        layout();
    }

    //----------------------------------
    //  freeze
    //----------------------------------
    private var _freeze:Boolean = true;
    /**
     * 是否冻结，如果冻结，则不更新界面.
     * @return
     *
     */
    protected function get freeze():Boolean
    {
        return _freeze;
    }

    protected function set freeze(value:Boolean):void
    {
        if(_freeze != value)
        {
            _freeze = value;
            for each (var item:InspectorItem in itemsMap)
            {
                item.freeze = freeze;
            }

            if(!freeze)
            {
                layout();
            }
        }
    }

    //==========================================================================
    //  Methods
    //==========================================================================
    /**
     * 初始化界面
     *
     */
    private function initDisplay():void
    {
        addEventListener(Event.ADDED_TO_STAGE,
            addedToStageHandler);

        var titleLabel:TextField = new TextField();
        titleLabel.autoSize = "left";
        titleLabel.selectable = false;
        titleLabel.text = "LoaderInspector v0.1";
        titleLabel.x = 5;
        titleLabel.y = 3;
        var tf:TextFormat = new TextFormat(Style.fontName, 12, 0x000000, true);
        titleLabel.setTextFormat(tf);
        addChild(titleLabel);

        container = new Sprite();
        addChild(container);
        onlyButton = new CheckBox(this, 20, 8,
            "Only Progressing", onlyProgressingItems);
        scrollPane = new ScrollPane(this, 5, 25);
        scrollPane.content.addChild(container);
        scrollPane.autoHideScrollBar = true;
    }

    /**
     * 添加队列事件
     *
     */
    private function addEvents():void
    {
        loaderQueue.addEventListener(LoaderQueueEvent.TASK_ADDED,
            loaderQueue_taskAddedHandler);
        loaderQueue.addEventListener(LoaderQueueEvent.TASK_REMOVED,
            loaderQueue_taskRemovedHandler);
    }

    /**
     * 删除队列事件
     *
     */
    private function removeEvents():void
    {
        loaderQueue.removeEventListener(LoaderQueueEvent.TASK_ADDED,
            loaderQueue_taskAddedHandler);
        loaderQueue.removeEventListener(LoaderQueueEvent.TASK_REMOVED,
            loaderQueue_taskRemovedHandler);
    }

    /**
     * 重新布局，调整UI
     *
     */
    private function layout():void
    {
        if(freeze)
        {
            return;
        }
        graphics.clear();
        graphics.beginFill(0x141E26);
        graphics.drawRect(0, 0, width, height);
        graphics.drawRect(1, 1, width - 2, height - 2);
        graphics.beginFill(0xC3E8FC, 1);
        graphics.drawRect(1, 1, width - 2, height - 2);

        for each (var item:InspectorItem in itemsMap)
        {
            item.width = width - 20;
        }
        sortItemY();

        onlyButton.x = width - 140;
        scrollPane.width = width - 10;
        scrollPane.height = height - 30;
        scrollPane.update();
    }

    /**
     * 对队列中的加载对象进行排序
     *
     */
    private function sortItemY():void
    {
        if(freeze)
        {
            return;
        }
        var cy:int = 5;
        var items:Array = [];
        for each (var item:InspectorItem in itemsMap)
        {
            //判断是否只显示正在加载的对象
            if(onlyButton.selected)
            {
                if(item.loaderAdapter.state == LoaderAdapterState.STARTED)
                {
                    items.push(item);
                }
                else if(container.contains(item))
                {
                    container.removeChild(item);
                }
            }
            else
            {
                items.push(item);
            }
        }

        //排序
        items.sortOn(["priority", "index"]);

        //调整位置
        for each (item in items)
        {
            container.addChild(item);
            item.y = cy;
            cy += 35;
        }
    }

    /**
     * 销毁该对象
     *
     */
    public function dispose():void
    {
        if(loaderQueue)
        {
            removeEvents();
            clearAllTask();
        }
    }

    /**
     * 清除所有加载对象
     *
     */
    private function clearAllTask():void
    {
        for each (var item:InspectorItem in itemsMap)
        {
            removeTask(item.loaderAdapter);
        }
    }

    /**
     * 添加一个加载对象
     * @param loaderAdapter
     *
     */
    private function addTask(loaderAdapter:ILoaderAdapter):void
    {
        if(loaderAdapter)
        {
            var item:InspectorItem = new InspectorItem(loaderAdapter);
            item.addEventListener(Event.CHANGE,
                item_completeHandler);
            item.width = width - 20;
            item.freeze = freeze;
            itemsMap[loaderAdapter] = item;

            sortItemY();
            scrollPane.update();
        }
    }

    /**
     * 删除一个加载对象
     * @param loaderAdapter
     *
     */
    private function removeTask(loaderAdapter:ILoaderAdapter):void
    {
        if(loaderAdapter && itemsMap[loaderAdapter])
        {
            var item:InspectorItem = itemsMap[loaderAdapter];
            item.removeEventListener(Event.CHANGE,
                item_completeHandler);
            item.dispose();
            itemsMap[loaderAdapter] = null;

            sortItemY();
            scrollPane.update();
        }
    }
    //==========================================================================
    //  Event Handlers
    //==========================================================================
    protected function addedToStageHandler(event:Event):void
    {
        freeze = false;
        parentContainer = parent;
        stage.addEventListener(Event.RESIZE, resizeHandler);
        stage.addEventListener(Event.REMOVED_FROM_STAGE,
            removeFromStageHandler);
        stage.addEventListener(KeyboardEvent.KEY_UP, shortcutKey);
        resizeHandler(null);
    }

    private function removeFromStageHandler():void
    {
        freeze = true;
        stage.removeEventListener(Event.RESIZE, resizeHandler);
        stage.removeEventListener(Event.REMOVED_FROM_STAGE,
            removeFromStageHandler);
    }

    protected function resizeHandler(event:Event):void
    {
        width = Math.min(stage.stageWidth, 600);
        height = stage.stageHeight;
        x = int((stage.stageWidth - width)/2);
    }

    private function loaderQueue_taskAddedHandler(event:LoaderQueueEvent):void
    {
        addTask(ILoaderAdapter(event.customData));
    }

    private function loaderQueue_taskRemovedHandler(event:LoaderQueueEvent):void
    {
        removeTask(ILoaderAdapter(event.customData));
    }

    private function item_completeHandler(event:Event):void
    {
        if(onlyButton.selected)
        {
            sortItemY();
        }
    }

    /**
     * 更新是否仅仅显示真正加载的对象.
     * @param event
     *
     */
    private function onlyProgressingItems(event:Event):void
    {
        sortItemY();
        scrollPane.update();
    }

    /**
     * shortcutKey handler.
     * @param e
     */
    private function shortcutKey(event:KeyboardEvent):void
    {
        var effective:Boolean = false;
        if (event.shiftKey && event.altKey && event.ctrlKey &&
            event.keyCode == Keyboard.L)
        {
            effective = true;
        }

        if (Capabilities.os.toLowerCase().indexOf("mac") != -1)
        {
            if (event.shiftKey && event.ctrlKey && event.keyCode == Keyboard.L)
            {
                effective = true;
            }
        }

        if (effective)
        {
            if (parent && parent.contains(this))
            {
                parent.removeChild(this);
            }
            else if(parentContainer)
            {
                parentContainer.addChild(this);
            }
        }
    }
}
}

package net.manaca.loaderqueue.inspector
{
import com.bit101.components.CheckBox;
import com.bit101.components.ScrollPane;
import com.bit101.components.Style;

import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.system.Capabilities;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.ui.Keyboard;
import flash.utils.Dictionary;

import net.manaca.loaderqueue.ILoaderAdapter;
import net.manaca.loaderqueue.ILoaderQueue;
import net.manaca.loaderqueue.LoaderAdapterState;
import net.manaca.loaderqueue.LoaderQueueEvent;

/**
 * 提供一个可视化界面用户查看加载队列进行情况.
 * @author wersling
 *
 */
public class LoaderInspector extends Sprite
{
    //==========================================================================
    //  Constructor
    //==========================================================================
    /**
     * 构造函数.
     * @param loaderQueue 需要监视的加载队列.
     *
     */
    public function LoaderInspector(loaderQueue:ILoaderQueue = null)
    {
        super();

        this.loaderQueue = loaderQueue;

        initDisplay();
    }

    //==========================================================================
    //  Variables
    //==========================================================================
    private var scrollPane:ScrollPane;
    private var container:DisplayObjectContainer;
    private var onlyButton:CheckBox;
    private var itemsMap:Dictionary = new Dictionary(true);

    private var parentContainer:DisplayObjectContainer;
    //==========================================================================
    //  Properties
    //==========================================================================
    private var _loaderQueue:ILoaderQueue;
    /**
     * 需要监视的加载队列.
     * @return
     *
     */
    public function get loaderQueue():ILoaderQueue
    {
        return _loaderQueue;
    }

    public function set loaderQueue(value:ILoaderQueue):void
    {
        if(_loaderQueue && _loaderQueue != value)
        {
            removeEvents();
            clearAllTask();
        }

        _loaderQueue = value;
        if(_loaderQueue)
        {
            addEvents();
        }
    }

    private var _height:Number = 0;
    /**
     * @private
     */
    override public function get height():Number
    {
        return _height;
    }
    override public function set height(value:Number):void
    {
        _height = value;
        layout();
    }

    private var _width:Number = 0;
    /**
     * @private
     */
    override public function get width():Number
    {
        return _width;
    }

    override public function set width(value:Number):void
    {
        _width = value;
        layout();
    }

    //----------------------------------
    //  freeze
    //----------------------------------
    private var _freeze:Boolean = true;
    /**
     * 是否冻结，如果冻结，则不更新界面.
     * @return
     *
     */
    protected function get freeze():Boolean
    {
        return _freeze;
    }

    protected function set freeze(value:Boolean):void
    {
        if(_freeze != value)
        {
            _freeze = value;
            for each (var item:InspectorItem in itemsMap)
            {
                item.freeze = freeze;
            }

            if(!freeze)
            {
                layout();
            }
        }
    }

    //==========================================================================
    //  Methods
    //==========================================================================
    /**
     * 初始化界面
     *
     */
    private function initDisplay():void
    {
        addEventListener(Event.ADDED_TO_STAGE,
            addedToStageHandler);

        var titleLabel:TextField = new TextField();
        titleLabel.autoSize = "left";
        titleLabel.selectable = false;
        titleLabel.text = "LoaderInspector v0.1";
        titleLabel.x = 5;
        titleLabel.y = 3;
        var tf:TextFormat = new TextFormat(Style.fontName, 12, 0x000000, true);
        titleLabel.setTextFormat(tf);
        addChild(titleLabel);

        container = new Sprite();
        addChild(container);
        onlyButton = new CheckBox(this, 20, 8,
            "Only Progressing", onlyProgressingItems);
        scrollPane = new ScrollPane(this, 5, 25);
        scrollPane.content.addChild(container);
        scrollPane.autoHideScrollBar = true;
    }

    /**
     * 添加队列事件
     *
     */
    private function addEvents():void
    {
        loaderQueue.addEventListener(LoaderQueueEvent.TASK_ADDED,
            loaderQueue_taskAddedHandler);
        loaderQueue.addEventListener(LoaderQueueEvent.TASK_REMOVED,
            loaderQueue_taskRemovedHandler);
    }

    /**
     * 删除队列事件
     *
     */
    private function removeEvents():void
    {
        loaderQueue.removeEventListener(LoaderQueueEvent.TASK_ADDED,
            loaderQueue_taskAddedHandler);
        loaderQueue.removeEventListener(LoaderQueueEvent.TASK_REMOVED,
            loaderQueue_taskRemovedHandler);
    }

    /**
     * 重新布局，调整UI
     *
     */
    private function layout():void
    {
        if(freeze)
        {
            return;
        }
        graphics.clear();
        graphics.beginFill(0x141E26);
        graphics.drawRect(0, 0, width, height);
        graphics.drawRect(1, 1, width - 2, height - 2);
        graphics.beginFill(0xC3E8FC, 1);
        graphics.drawRect(1, 1, width - 2, height - 2);

        for each (var item:InspectorItem in itemsMap)
        {
            item.width = width - 20;
        }
        sortItemY();

        onlyButton.x = width - 140;
        scrollPane.width = width - 10;
        scrollPane.height = height - 30;
        scrollPane.update();
    }

    /**
     * 对队列中的加载对象进行排序
     *
     */
    private function sortItemY():void
    {
        if(freeze)
        {
            return;
        }
        var cy:int = 5;
        var items:Array = [];
        for each (var item:InspectorItem in itemsMap)
        {
            //判断是否只显示正在加载的对象
            if(onlyButton.selected)
            {
                if(item.loaderAdapter.state == LoaderAdapterState.STARTED)
                {
                    items.push(item);
                }
                else if(container.contains(item))
                {
                    container.removeChild(item);
                }
            }
            else
            {
                items.push(item);
            }
        }

        //排序
        items.sortOn(["priority", "index"]);

        //调整位置
        for each (item in items)
        {
            container.addChild(item);
            item.y = cy;
            cy += 35;
        }
    }

    /**
     * 销毁该对象
     *
     */
    public function dispose():void
    {
        if(loaderQueue)
        {
            removeEvents();
            clearAllTask();
        }
        if(container)
        {
            scrollPane.content.removeChild(container);
            container = null;
        }

        if(scrollPane)
        {
            scrollPane = null;
        }

        if(onlyButton)
        {
            removeChild(onlyButton);
            onlyButton = null;
        }

        parentContainer = null;
    }

    /**
     * 清除所有加载对象
     *
     */
    private function clearAllTask():void
    {
        for each (var item:InspectorItem in itemsMap)
        {
            removeTask(item.loaderAdapter);
        }
    }

    /**
     * 添加一个加载对象
     * @param loaderAdapter
     *
     */
    private function addTask(loaderAdapter:ILoaderAdapter):void
    {
        if(loaderAdapter)
        {
            var item:InspectorItem = new InspectorItem(loaderAdapter);
            item.addEventListener(Event.CHANGE,
                item_completeHandler);
            item.width = width - 20;
            item.freeze = freeze;
            itemsMap[loaderAdapter] = item;

            sortItemY();
            scrollPane.update();
        }
    }

    /**
     * 删除一个加载对象
     * @param loaderAdapter
     *
     */
    private function removeTask(loaderAdapter:ILoaderAdapter):void
    {
        if(loaderAdapter && itemsMap[loaderAdapter])
        {
            var item:InspectorItem = itemsMap[loaderAdapter];
            item.removeEventListener(Event.CHANGE,
                item_completeHandler);
            item.dispose();
            itemsMap[loaderAdapter] = null;

            sortItemY();
            scrollPane.update();
        }
    }
    //==========================================================================
    //  Event Handlers
    //==========================================================================
    protected function addedToStageHandler(event:Event):void
    {
        freeze = false;
        parentContainer = parent;
        stage.addEventListener(Event.RESIZE, resizeHandler);
        stage.addEventListener(Event.REMOVED_FROM_STAGE,
            removeFromStageHandler);
        stage.addEventListener(KeyboardEvent.KEY_UP, shortcutKey);
        resizeHandler(null);
    }

    private function removeFromStageHandler():void
    {
        freeze = true;
        stage.removeEventListener(Event.RESIZE, resizeHandler);
        stage.removeEventListener(Event.REMOVED_FROM_STAGE,
            removeFromStageHandler);
    }

    protected function resizeHandler(event:Event):void
    {
        width = Math.min(stage.stageWidth, 600);
        height = stage.stageHeight;
        x = int((stage.stageWidth - width)/2);
    }

    private function loaderQueue_taskAddedHandler(event:LoaderQueueEvent):void
    {
        addTask(ILoaderAdapter(event.customData));
    }

    private function loaderQueue_taskRemovedHandler(event:LoaderQueueEvent):void
    {
        removeTask(ILoaderAdapter(event.customData));
    }

    private function item_completeHandler(event:Event):void
    {
        if(onlyButton.selected)
        {
            sortItemY();
        }
    }

    /**
     * 更新是否仅仅显示真正加载的对象.
     * @param event
     *
     */
    private function onlyProgressingItems(event:Event):void
    {
        sortItemY();
        scrollPane.update();
    }

    /**
     * shortcutKey handler.
     * @param e
     */
    private function shortcutKey(event:KeyboardEvent):void
    {
        var effective:Boolean = false;
        if (event.shiftKey && event.altKey && event.ctrlKey &&
            event.keyCode == Keyboard.L)
        {
            effective = true;
        }

        if (Capabilities.os.toLowerCase().indexOf("mac") != -1)
        {
            if (event.shiftKey && event.ctrlKey && event.keyCode == Keyboard.L)
            {
                effective = true;
            }
        }

        if (effective)
        {
            if (parent && parent.contains(this))
            {
                parent.removeChild(this);
            }
            else if(parentContainer)
            {
                parentContainer.addChild(this);
            }
        }
    }
}
}

Update LoaderInspector.dispose for best GC.


package battlecode.client.viewer.render {

	public class ImageAssets {

		// control bar background
		[Embed('/img/client/background.png')] public static const GRADIENT_BACKGROUND:Class;
		[Embed('/img/client/hud_unit_underlay.png')] public static const HUD_ARCHON_BACKGROUND:Class;

		// button icons
		[Embed('/img/client/icons/control_play.png')] public static const PLAY_ICON:Class;
		[Embed('/img/client/icons/control_pause.png')] public static const PAUSE_ICON:Class;
		[Embed('/img/client/icons/control_start.png')] public static const START_ICON:Class;
		[Embed('/img/client/icons/control_fastforward.png')] public static const FASTFORWARD_ICON:Class;
		[Embed('/img/client/icons/resultset_next.png')] public static const NEXT_ICON:Class;
		[Embed('/img/client/icons/resultset_previous.png')] public static const PREVIOUS_ICON:Class;
		[Embed('/img/client/icons/control_fastforward_blue.png')] public static const STEP_FORWARD_ICON:Class;
		[Embed('/img/client/icons/control_rewind_blue.png')] public static const STEP_BACKWARD_ICON:Class;
		[Embed('/img/client/icons/arrow_out.png')] public static const ENTER_FULLSCREEN_ICON:Class;
		[Embed('/img/client/icons/arrow_in.png')] public static const EXIT_FULLSCREEN_ICON:Class;

        // unit avatars
        [Embed('/img/units/hq0.png')] public static const HQ_NEUTRAL:Class;
		[Embed('/img/units/hq1.png')] public static const HQ_A:Class;
        [Embed('/img/units/hq2.png')] public static const HQ_B:Class;
		[Embed('/img/units/soldier0.png')] public static const SOLDIER_NEUTRAL:Class;
		[Embed('/img/units/soldier1.png')] public static const SOLDIER_A:Class;
		[Embed('/img/units/soldier2.png')] public static const SOLDIER_B:Class;
		[Embed('/img/units/medbay0.png')] public static const PLACEHOLDER:Class; // TODO

		// explosions images
		[Embed('/img/explode/explode64_f01.png')] public static const EXPLODE_1:Class;
		[Embed('/img/explode/explode64_f02.png')] public static const EXPLODE_2:Class;
		[Embed('/img/explode/explode64_f03.png')] public static const EXPLODE_3:Class;
		[Embed('/img/explode/explode64_f04.png')] public static const EXPLODE_4:Class;
		[Embed('/img/explode/explode64_f05.png')] public static const EXPLODE_5:Class;
		[Embed('/img/explode/explode64_f06.png')] public static const EXPLODE_6:Class;
		[Embed('/img/explode/explode64_f07.png')] public static const EXPLODE_7:Class;
		[Embed('/img/explode/explode64_f08.png')] public static const EXPLODE_8:Class;

		public function ImageAssets() { }

	}

}

package battlecode.client.viewer.render {

	public class ImageAssets {

		// control bar background
		[Embed('/img/client/background.png')] public static const GRADIENT_BACKGROUND:Class;
		[Embed('/img/client/hud_unit_underlay.png')] public static const HUD_ARCHON_BACKGROUND:Class;

		// button icons
		[Embed('/img/client/icons/control_play.png')] public static const PLAY_ICON:Class;
		[Embed('/img/client/icons/control_pause.png')] public static const PAUSE_ICON:Class;
		[Embed('/img/client/icons/control_start.png')] public static const START_ICON:Class;
		[Embed('/img/client/icons/control_fastforward.png')] public static const FASTFORWARD_ICON:Class;
		[Embed('/img/client/icons/resultset_next.png')] public static const NEXT_ICON:Class;
		[Embed('/img/client/icons/resultset_previous.png')] public static const PREVIOUS_ICON:Class;
		[Embed('/img/client/icons/control_fastforward_blue.png')] public static const STEP_FORWARD_ICON:Class;
		[Embed('/img/client/icons/control_rewind_blue.png')] public static const STEP_BACKWARD_ICON:Class;
		[Embed('/img/client/icons/arrow_out.png')] public static const ENTER_FULLSCREEN_ICON:Class;
		[Embed('/img/client/icons/arrow_in.png')] public static const EXIT_FULLSCREEN_ICON:Class;

        // unit avatars
        [Embed('/img/units/hq0.png')] public static const HQ_NEUTRAL:Class;
		[Embed('/img/units/hq1.png')] public static const HQ_A:Class;
        [Embed('/img/units/hq2.png')] public static const HQ_B:Class;
		[Embed('/img/units/soldier0.png')] public static const SOLDIER_NEUTRAL:Class;
		[Embed('/img/units/soldier1.png')] public static const SOLDIER_A:Class;
		[Embed('/img/units/soldier2.png')] public static const SOLDIER_B:Class;
        [Embed('/img/units/medbay0.png')] public static const MEDBAY_NEUTRAL:Class;
        [Embed('/img/units/medbay1.png')] public static const MEDBAY_A:Class;
        [Embed('/img/units/medbay2.png')] public static const MEDBAY_B:Class;
        [Embed('/img/units/shields0.png')] public static const SHIELDS_NEUTRAL:Class;
        [Embed('/img/units/shields1.png')] public static const SHIELDS_A:Class;
        [Embed('/img/units/shields2.png')] public static const SHIELDS_B:Class;
        [Embed('/img/units/artillery0.png')] public static const ARTILLERY_NEUTRAL:Class;
        [Embed('/img/units/artillery1.png')] public static const ARTILLERY_A:Class;
        [Embed('/img/units/artillery2.png')] public static const ARTILLERY_B:Class;
        [Embed('/img/units/generator0.png')] public static const GENERATOR_NEUTRAL:Class;
        [Embed('/img/units/generator1.png')] public static const GENERATOR_A:Class;
        [Embed('/img/units/generator2.png')] public static const GENERATOR_B:Class;
        [Embed('/img/units/supplier0.png')] public static const SUPPLIER_NEUTRAL:Class;
        [Embed('/img/units/supplier1.png')] public static const SUPPLIER_A:Class;
        [Embed('/img/units/supplier2.png')] public static const SUPPLIER_B:Class;

		// explosions images
		[Embed('/img/explode/explode64_f01.png')] public static const EXPLODE_1:Class;
		[Embed('/img/explode/explode64_f02.png')] public static const EXPLODE_2:Class;
		[Embed('/img/explode/explode64_f03.png')] public static const EXPLODE_3:Class;
		[Embed('/img/explode/explode64_f04.png')] public static const EXPLODE_4:Class;
		[Embed('/img/explode/explode64_f05.png')] public static const EXPLODE_5:Class;
		[Embed('/img/explode/explode64_f06.png')] public static const EXPLODE_6:Class;
		[Embed('/img/explode/explode64_f07.png')] public static const EXPLODE_7:Class;
		[Embed('/img/explode/explode64_f08.png')] public static const EXPLODE_8:Class;

		public function ImageAssets() { }

	}

}

add missing unit image bindings

add missing unit image bindings