package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.controller.IRebindableController;
	import aerys.minko.scene.controller.TransformController;
	import aerys.minko.scene.data.TransformDataProvider;
	import aerys.minko.type.Signal;
	import aerys.minko.type.clone.CloneOptions;
	import aerys.minko.type.clone.ControllerCloneAction;
	import aerys.minko.type.math.Matrix4x4;

	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;

	use namespace minko_scene;

	/**
	 * The base class to extend in order to create new scene node types.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class AbstractSceneNode implements ISceneNode
	{
		private static var _id			: uint;

		private var _name				: String;
		private var _root				: ISceneNode;
		private var _parent				: Group;

		private var _transform			: Matrix4x4;
		private var _localToWorld		: Matrix4x4;
		private var _worldToLocal		: Matrix4x4;

		private var _controllers		: Vector.<AbstractController>;

		private var _added				: Signal;
		private var _removed			: Signal;
		private var _addedToScene		: Signal;
		private var _removedFromScene	: Signal;
		private var _controllerAdded	: Signal;
		private var _controllerRemoved	: Signal;

		public function get x() : Number
		{
			return _transform.translationX;
		}
		public function set x(value : Number) : void
		{
			_transform.translationX = value;
		}

		public function get y() : Number
		{
			return _transform.translationX;
		}
		public function set y(value : Number) : void
		{
			_transform.translationY = value;
		}

		public function get z() : Number
		{
			return _transform.translationX;
		}
		public function set z(value : Number) : void
		{
			_transform.translationZ = value;
		}

		public function get rotationX() : Number
		{
			return _transform.rotationX;
		}
		public function set rotationX(value : Number) : void
		{
			_transform.rotationX = value;
		}

		public function get rotationY() : Number
		{
			return _transform.rotationY;
		}
		public function set rotationY(value : Number) : void
		{
			_transform.rotationY = value;
		}

		public function get rotationZ() : Number
		{
			return _transform.rotationZ;
		}
		public function set rotationZ(value : Number) : void
		{
			_transform.rotationZ = value;
		}

		public function get scaleX() : Number
		{
			return _transform.scaleX;
		}
		public function set scaleX(value : Number) : void
		{
			_transform.scaleX = value;
		}

		public function get scaleY() : Number
		{
			return _transform.scaleX;
		}
		public function set scaleY(value : Number) : void
		{
			_transform.scaleY = value;
		}

		public function get scaleZ() : Number
		{
			return _transform.scaleZ;
		}
		public function set scaleZ(value : Number) : void
		{
			_transform.scaleZ = value;
		}

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

		public function get parent() : Group
		{
			return _parent;
		}
		public function set parent(value : Group) : void
		{
			if (value == _parent)
				return ;

			// remove child
			if (_parent)
			{
				var oldParent : Group = _parent;

				oldParent._children.splice(
					oldParent.getChildIndex(this),
					1
				);

				parent._numChildren--;
				oldParent.descendantRemoved.execute(oldParent, this);

				_parent = null;
				_removed.execute(this, oldParent);
			}

			// set parent
			_parent = value;

			// add child
			if (_parent)
			{
				_parent._children[_parent.numChildren] = this;
				_parent._numChildren++;
				_parent.descendantAdded.execute(_parent, this);

				_added.execute(this, _parent);
			}
		}

		public function get root() : ISceneNode
		{
			return _root;
		}

		public function get transform() : Matrix4x4
		{
			return _transform;
		}

		public function get localToWorld() : Matrix4x4
		{
			return _localToWorld;
		}

		public function get worldToLocal() : Matrix4x4
		{
			return _worldToLocal;
		}

		public function get added() : Signal
		{
			return _added;
		}

		public function get removed() : Signal
		{
			return _removed;
		}

		public function get addedToScene() : Signal
		{
			return _addedToScene;
		}

		public function get removedFromScene() : Signal
		{
			return _removedFromScene;
		}

		public function get numControllers() : uint
		{
			return _controllers.length;
		}

		public function get controllerAdded() : Signal
		{
			return _controllerAdded;
		}

		public function get controllerRemoved() : Signal
		{
			return _controllerRemoved;
		}

		public function AbstractSceneNode()
		{
			initialize();
		}

		private function initialize() : void
		{
			_name = getDefaultSceneName(this);
			_root = this;

			_transform = new Matrix4x4();
			_localToWorld = new Matrix4x4();
			_worldToLocal = new Matrix4x4();

			_controllers = new <AbstractController>[];

			_added = new Signal('AbstractSceneNode.added');
			_removed = new Signal('AbstractSceneNode.removed');
			_addedToScene = new Signal('AbstractSceneNode.addedToScene');
			_removedFromScene = new Signal('AbstractSceneNode.removedFromScene');
			_controllerAdded = new Signal('AbstractSceneNode.controllerAdded');
			_controllerRemoved = new Signal('AbstractSceneNode.controllerRemoved');

			_added.add(addedHandler);
			_removed.add(removedHandler);
			_addedToScene.add(addedToSceneHandler);
			_removedFromScene.add(removedFromSceneHandler);

			addController(new TransformController());
		}

		protected function addedHandler(child : ISceneNode, parent : Group) : void
		{
			_root = _parent ? _parent.root : this;
			if (_root is Scene)
				_addedToScene.execute(this, _root);
		}

		protected function removedHandler(child : ISceneNode, parent : Group) : void
		{
			// update root
			var oldRoot : ISceneNode = _root;

			_root = _parent ? _parent.root : this;
			if (oldRoot is Scene)
				_removedFromScene.execute(this, oldRoot);
		}

		protected function addedToSceneHandler(child : ISceneNode, scene : Scene) : void
		{
			// nothing
		}

		protected function removedFromSceneHandler(child : ISceneNode, scene : Scene) : void
		{
			// nothing
		}

		public function addController(controller : AbstractController) : ISceneNode
		{
			_controllers.push(controller);

			controller.addTarget(this);
			_controllerAdded.execute(this, controller);

			return this;
		}

		public function removeController(controller : AbstractController) : ISceneNode
		{
			var numControllers	: uint = _controllers.length - 1;

			_controllers[_controllers.indexOf(controller)] = _controllers[numControllers];
			_controllers.length = numControllers;

			controller.removeTarget(this);
			_controllerRemoved.execute(this, controller);

			return this;
		}

		public function removeAllControllers() : ISceneNode
		{
			while (numControllers)
				removeController(getController(0));

			return this;
		}

		public function getController(index : uint) : AbstractController
		{
			return _controllers[index];
		}

		public function getControllersByType(type			: Class,
											 controllers	: Vector.<AbstractController> = null) : Vector.<AbstractController>
		{
			controllers ||= new Vector.<AbstractController>();

			var nbControllers : uint = numControllers;

			for (var i : int = 0; i < nbControllers; ++i)
			{
				var ctrl 	: AbstractController	= getController(i);

				if (ctrl is type)
					controllers.push(ctrl);
			}

			return controllers;
		}

		public static function getDefaultSceneName(scene : ISceneNode) : String
		{
			var className : String = getQualifiedClassName(scene);

			return className.substr(className.lastIndexOf(':') + 1)
				   + '_' + (++_id);
		}

		minko_scene function cloneNode() : AbstractSceneNode
		{
			throw new Error('Must be overriden');
		}

		public final function clone(cloneOptions : CloneOptions = null) : ISceneNode
		{
			cloneOptions ||= CloneOptions.defaultOptions;

			// fill up 2 dics with all nodes and controllers
			var nodeMap			: Dictionary = new Dictionary();
			var controllerMap	: Dictionary = new Dictionary();
			listItems(cloneNode(), nodeMap, controllerMap);

			// clone controllers with respect with instructions
			cloneControllers(controllerMap, cloneOptions);

			// rebind all controller dependencies.
			rebindControllerDependencies(controllerMap, nodeMap, cloneOptions);

			// add cloned/rebinded/original controllers to clones
			for (var objNode : Object in nodeMap)
			{
				var node			: AbstractSceneNode = AbstractSceneNode(objNode);
				var numControllers	: uint = node.numControllers;

				for (var controllerId : uint = 0; controllerId < numControllers; ++controllerId)
				{
					var controller : AbstractController = controllerMap[node.getController(controllerId)];
					if (controller != null)
						nodeMap[node].addController(controller);
				}
			}

			return nodeMap[this];
		}

		private function listItems(clonedRoot	: ISceneNode,
								   nodes		: Dictionary,
								   controllers	: Dictionary) : void
		{
			var numControllers : uint = this.numControllers;
			for (var controllerId : uint = 0; controllerId < numControllers; ++controllerId)
				controllers[getController(controllerId)] = true;

			nodes[this] = clonedRoot;

			if (this is Group)
			{
				var group		: Group = Group(this);
				var clonedGroup	: Group = Group(clonedRoot);
				var numChildren	: uint	= group.numChildren;

				for (var childId : uint = 0; childId < numChildren; ++childId)
				{
					var child		: AbstractSceneNode = AbstractSceneNode(group.getChildAt(childId));
					var clonedChild	: AbstractSceneNode = AbstractSceneNode(clonedGroup.getChildAt(childId));

					child.listItems(clonedChild, nodes, controllers);
				}
			}
		}

		private function cloneControllers(controllerMap : Dictionary, cloneOptions : CloneOptions) : void
		{
			for (var objController : Object in controllerMap)
			{
				var controller	: AbstractController = AbstractController(objController);
				var action		: uint				 = cloneOptions.getActionForController(controller);

				if (action == ControllerCloneAction.CLONE)
					controllerMap[controller] = controller.clone();
				else if (action == ControllerCloneAction.REASSIGN)
					controllerMap[controller] = controller;
				else if (action == ControllerCloneAction.IGNORE)
					controllerMap[controller] = null;
				else
					throw new Error();
			}
		}

		private function rebindControllerDependencies(controllerMap	: Dictionary,
													  nodeMap		: Dictionary,
													  cloneOptions	: CloneOptions) : void
		{
			for (var objController : Object in controllerMap)
			{
				var controller	: AbstractController	= AbstractController(objController);
				var action		: uint					= cloneOptions.getActionForController(controller);

				if (controller is IRebindableController && action == ControllerCloneAction.CLONE)
					IRebindableController(controllerMap[controller]).rebindDependencies(nodeMap, controllerMap);
			}
		}
	}
}

package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.controller.IRebindableController;
	import aerys.minko.scene.controller.TransformController;
	import aerys.minko.scene.data.TransformDataProvider;
	import aerys.minko.type.Signal;
	import aerys.minko.type.clone.CloneOptions;
	import aerys.minko.type.clone.ControllerCloneAction;
	import aerys.minko.type.math.Matrix4x4;

	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;

	use namespace minko_scene;

	/**
	 * The base class to extend in order to create new scene node types.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class AbstractSceneNode implements ISceneNode
	{
		private static var _id			: uint;

		private var _name				: String;
		private var _root				: ISceneNode;
		private var _parent				: Group;

		private var _transform			: Matrix4x4;
		private var _localToWorld		: Matrix4x4;
		private var _worldToLocal		: Matrix4x4;

		private var _controllers		: Vector.<AbstractController>;

		private var _added				: Signal;
		private var _removed			: Signal;
		private var _addedToScene		: Signal;
		private var _removedFromScene	: Signal;
		private var _controllerAdded	: Signal;
		private var _controllerRemoved	: Signal;

		public function get x() : Number
		{
			return _transform.translationX;
		}
		public function set x(value : Number) : void
		{
			_transform.translationX = value;
		}

		public function get y() : Number
		{
			return _transform.translationX;
		}
		public function set y(value : Number) : void
		{
			_transform.translationY = value;
		}

		public function get z() : Number
		{
			return _transform.translationX;
		}
		public function set z(value : Number) : void
		{
			_transform.translationZ = value;
		}

		public function get rotationX() : Number
		{
			return _transform.rotationX;
		}
		public function set rotationX(value : Number) : void
		{
			_transform.rotationX = value;
		}

		public function get rotationY() : Number
		{
			return _transform.rotationY;
		}
		public function set rotationY(value : Number) : void
		{
			_transform.rotationY = value;
		}

		public function get rotationZ() : Number
		{
			return _transform.rotationZ;
		}
		public function set rotationZ(value : Number) : void
		{
			_transform.rotationZ = value;
		}

		public function get scaleX() : Number
		{
			return _transform.scaleX;
		}
		public function set scaleX(value : Number) : void
		{
			_transform.scaleX = value;
		}

		public function get scaleY() : Number
		{
			return _transform.scaleY;
		}
		public function set scaleY(value : Number) : void
		{
			_transform.scaleY = value;
		}

		public function get scaleZ() : Number
		{
			return _transform.scaleZ;
		}
		public function set scaleZ(value : Number) : void
		{
			_transform.scaleZ = value;
		}

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

		public function get parent() : Group
		{
			return _parent;
		}
		public function set parent(value : Group) : void
		{
			if (value == _parent)
				return ;

			// remove child
			if (_parent)
			{
				var oldParent : Group = _parent;

				oldParent._children.splice(
					oldParent.getChildIndex(this),
					1
				);

				parent._numChildren--;
				oldParent.descendantRemoved.execute(oldParent, this);

				_parent = null;
				_removed.execute(this, oldParent);
			}

			// set parent
			_parent = value;

			// add child
			if (_parent)
			{
				_parent._children[_parent.numChildren] = this;
				_parent._numChildren++;
				_parent.descendantAdded.execute(_parent, this);

				_added.execute(this, _parent);
			}
		}

		public function get root() : ISceneNode
		{
			return _root;
		}

		public function get transform() : Matrix4x4
		{
			return _transform;
		}

		public function get localToWorld() : Matrix4x4
		{
			return _localToWorld;
		}

		public function get worldToLocal() : Matrix4x4
		{
			return _worldToLocal;
		}

		public function get added() : Signal
		{
			return _added;
		}

		public function get removed() : Signal
		{
			return _removed;
		}

		public function get addedToScene() : Signal
		{
			return _addedToScene;
		}

		public function get removedFromScene() : Signal
		{
			return _removedFromScene;
		}

		public function get numControllers() : uint
		{
			return _controllers.length;
		}

		public function get controllerAdded() : Signal
		{
			return _controllerAdded;
		}

		public function get controllerRemoved() : Signal
		{
			return _controllerRemoved;
		}

		public function AbstractSceneNode()
		{
			initialize();
		}

		private function initialize() : void
		{
			_name = getDefaultSceneName(this);
			_root = this;

			_transform = new Matrix4x4();
			_localToWorld = new Matrix4x4();
			_worldToLocal = new Matrix4x4();

			_controllers = new <AbstractController>[];

			_added = new Signal('AbstractSceneNode.added');
			_removed = new Signal('AbstractSceneNode.removed');
			_addedToScene = new Signal('AbstractSceneNode.addedToScene');
			_removedFromScene = new Signal('AbstractSceneNode.removedFromScene');
			_controllerAdded = new Signal('AbstractSceneNode.controllerAdded');
			_controllerRemoved = new Signal('AbstractSceneNode.controllerRemoved');

			_added.add(addedHandler);
			_removed.add(removedHandler);
			_addedToScene.add(addedToSceneHandler);
			_removedFromScene.add(removedFromSceneHandler);

			addController(new TransformController());
		}

		protected function addedHandler(child : ISceneNode, parent : Group) : void
		{
			_root = _parent ? _parent.root : this;
			if (_root is Scene)
				_addedToScene.execute(this, _root);
		}

		protected function removedHandler(child : ISceneNode, parent : Group) : void
		{
			// update root
			var oldRoot : ISceneNode = _root;

			_root = _parent ? _parent.root : this;
			if (oldRoot is Scene)
				_removedFromScene.execute(this, oldRoot);
		}

		protected function addedToSceneHandler(child : ISceneNode, scene : Scene) : void
		{
			// nothing
		}

		protected function removedFromSceneHandler(child : ISceneNode, scene : Scene) : void
		{
			// nothing
		}

		public function addController(controller : AbstractController) : ISceneNode
		{
			_controllers.push(controller);

			controller.addTarget(this);
			_controllerAdded.execute(this, controller);

			return this;
		}

		public function removeController(controller : AbstractController) : ISceneNode
		{
			var numControllers	: uint = _controllers.length - 1;

			_controllers[_controllers.indexOf(controller)] = _controllers[numControllers];
			_controllers.length = numControllers;

			controller.removeTarget(this);
			_controllerRemoved.execute(this, controller);

			return this;
		}

		public function removeAllControllers() : ISceneNode
		{
			while (numControllers)
				removeController(getController(0));

			return this;
		}

		public function getController(index : uint) : AbstractController
		{
			return _controllers[index];
		}

		public function getControllersByType(type			: Class,
											 controllers	: Vector.<AbstractController> = null) : Vector.<AbstractController>
		{
			controllers ||= new Vector.<AbstractController>();

			var nbControllers : uint = numControllers;

			for (var i : int = 0; i < nbControllers; ++i)
			{
				var ctrl 	: AbstractController	= getController(i);

				if (ctrl is type)
					controllers.push(ctrl);
			}

			return controllers;
		}

		public static function getDefaultSceneName(scene : ISceneNode) : String
		{
			var className : String = getQualifiedClassName(scene);

			return className.substr(className.lastIndexOf(':') + 1)
				   + '_' + (++_id);
		}

		minko_scene function cloneNode() : AbstractSceneNode
		{
			throw new Error('Must be overriden');
		}

		public final function clone(cloneOptions : CloneOptions = null) : ISceneNode
		{
			cloneOptions ||= CloneOptions.defaultOptions;

			// fill up 2 dics with all nodes and controllers
			var nodeMap			: Dictionary = new Dictionary();
			var controllerMap	: Dictionary = new Dictionary();
			listItems(cloneNode(), nodeMap, controllerMap);

			// clone controllers with respect with instructions
			cloneControllers(controllerMap, cloneOptions);

			// rebind all controller dependencies.
			rebindControllerDependencies(controllerMap, nodeMap, cloneOptions);

			// add cloned/rebinded/original controllers to clones
			for (var objNode : Object in nodeMap)
			{
				var node			: AbstractSceneNode = AbstractSceneNode(objNode);
				var numControllers	: uint = node.numControllers;

				for (var controllerId : uint = 0; controllerId < numControllers; ++controllerId)
				{
					var controller : AbstractController = controllerMap[node.getController(controllerId)];
					if (controller != null)
						nodeMap[node].addController(controller);
				}
			}

			return nodeMap[this];
		}

		private function listItems(clonedRoot	: ISceneNode,
								   nodes		: Dictionary,
								   controllers	: Dictionary) : void
		{
			var numControllers : uint = this.numControllers;
			for (var controllerId : uint = 0; controllerId < numControllers; ++controllerId)
				controllers[getController(controllerId)] = true;

			nodes[this] = clonedRoot;

			if (this is Group)
			{
				var group		: Group = Group(this);
				var clonedGroup	: Group = Group(clonedRoot);
				var numChildren	: uint	= group.numChildren;

				for (var childId : uint = 0; childId < numChildren; ++childId)
				{
					var child		: AbstractSceneNode = AbstractSceneNode(group.getChildAt(childId));
					var clonedChild	: AbstractSceneNode = AbstractSceneNode(clonedGroup.getChildAt(childId));

					child.listItems(clonedChild, nodes, controllers);
				}
			}
		}

		private function cloneControllers(controllerMap : Dictionary, cloneOptions : CloneOptions) : void
		{
			for (var objController : Object in controllerMap)
			{
				var controller	: AbstractController = AbstractController(objController);
				var action		: uint				 = cloneOptions.getActionForController(controller);

				if (action == ControllerCloneAction.CLONE)
					controllerMap[controller] = controller.clone();
				else if (action == ControllerCloneAction.REASSIGN)
					controllerMap[controller] = controller;
				else if (action == ControllerCloneAction.IGNORE)
					controllerMap[controller] = null;
				else
					throw new Error();
			}
		}

		private function rebindControllerDependencies(controllerMap	: Dictionary,
													  nodeMap		: Dictionary,
													  cloneOptions	: CloneOptions) : void
		{
			for (var objController : Object in controllerMap)
			{
				var controller	: AbstractController	= AbstractController(objController);
				var action		: uint					= cloneOptions.getActionForController(controller);

				if (controller is IRebindableController && action == ControllerCloneAction.CLONE)
					IRebindableController(controllerMap[controller]).rebindDependencies(nodeMap, controllerMap);
			}
		}
	}
}

Update src/aerys/minko/scene/node/AbstractSceneNode.as

BUG:scaleY


/**
  * LzXMLTranslator.as
  *
  * @copyright Copyright 2001-2009 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic Kernel
  * @subtopic swf9
  */

/**
  * @shortdesc: Utility for converting native XML DOM object into LzDataNode tree
  */
public class LzXMLTranslator {

static function copyXML (xmlobj:XML, trimwhitespace:Boolean, nsprefix:Boolean) :LzDataElement {
    var lfcnode:LzDataNodeMixin = copyFlashXML(xmlobj, trimwhitespace, nsprefix);
    if (lfcnode == null) {
        trace('LzXMLTranslator.copyXML: lfcnode.children is null', lfcnode);
    }

    if (lfcnode is LzDataText) {
        return null;
    }
    // create a new, empty ownerDocument (LPP-7537)
    new LzDataElement(null, {}, [lfcnode]);
    return (lfcnode cast LzDataElement);
}

static const whitespaceChars :Object = {' ': true, '\r': true, '\n': true, '\t': true};

/**
  * trim whitespace from start and end of string
  * @access private
  */
static function trim (str:String) :String {
    var whitech:Object = whitespaceChars;
    var len:int = str.length;
    var sindex:int = 0;
    var eindex:int = str.length -1;
    var ch:String;
    while (sindex < len) {
        ch = str.charAt(sindex);
        if (whitech[ch] != true) break;
        sindex++;
    }

    while (eindex > sindex) {
        ch = str.charAt(eindex);
        if (whitech[ch] != true) break;
        eindex--;
    }

    return str.slice(sindex,eindex+1);
}

// Recursively copy a Flash XML(Node) tree into a LzDataElement
// tree. Used by LzDataNode.stringToLzData
/**
  * @param boolean trimwhitespace: trim whitespace from start and end of text nodes
  * @param boolean nsprefix: preserve namespace prefixes on node names and attribute names
  * @access private
  */
static function copyFlashXML (node:XML, trimwhitespace:Boolean, nsprefix:Boolean) :LzDataNodeMixin {
    var lfcnode:LzDataNodeMixin = null;
    // text node?
    if (node.nodeKind() == 'text') {
        var nv:String = node.toString();
        if (trimwhitespace == true) {
            nv = trim(nv);
        }
        lfcnode = new LzDataText(nv);
//PBR Changed to match swf kernel
//    } else if (node.nodeKind() == 'element') {
      } else {

        var nattrs:XMLList = node.attributes();
        var cattrs:Object = {};
        for (var i:int = 0; i < nattrs.length(); i++) {
            var attr:XML = nattrs[i];
            var qattr:QName = attr.name();
            if (nsprefix) {
                var ns:Namespace = attr.namespace();
                var key:String;
                if (ns != null && ns.prefix != "") {
                    key = ns.prefix + ":" + qattr.localName;
                } else {
                    key = qattr.localName;
                }
                cattrs[key] = attr.toString();
            } else {
                cattrs[qattr.localName] = attr.toString();
            }
        }

        var nsDecl:Array = node.namespaceDeclarations();
        for (var i:int = 0; i < nsDecl.length; ++i) {
            var ns:Namespace = nsDecl[i];
            var prefix:String = ns.prefix;
            var key:String = (prefix == "" ? "xmlns" : nsprefix ? "xmlns:" + prefix : prefix);
            cattrs[key] = ns.uri;
        }

        var nname:String;
        var qname:QName = node.name();
        if (nsprefix) {
            var ns:Namespace = node.namespace();
            if (ns != null && ns.prefix != "") {
                nname = ns.prefix + ":" + qname.localName;
            } else {
                nname = qname.localName;
            }
        } else {
            nname = qname.localName;
        }

        lfcnode = new LzDataElement(nname, cattrs);
        var children:XMLList = node.children();
        var newchildren:Array = [];
        for (var i:int  = 0; i < children.length(); i++) {
            var child:XML = children[i];
            var lfcchild:LzDataNodeMixin = copyFlashXML(child, trimwhitespace, nsprefix);
            newchildren[i] = lfcchild;
        }
        (lfcnode cast LzDataElement).$lzc$set_childNodes(newchildren);
    }
    return lfcnode;
}

} // End of LzXMLTranslator

/**
  * LzXMLTranslator.as
  *
  * @copyright Copyright 2001-2009 Laszlo Systems, Inc.  All Rights Reserved.
  *            Use is subject to license terms.
  *
  * @topic Kernel
  * @subtopic swf9
  */

/**
  * @shortdesc: Utility for converting native XML DOM object into LzDataNode tree
  */
public class LzXMLTranslator {

    /**
      * LzXMLTranslator interface
      */
    static function copyXML (xmlobj:XML, trimwhitespace:Boolean, nsprefix:Boolean) :LzDataElement {
        var lfcnode:LzDataNodeMixin = copyFlashXML(xmlobj, trimwhitespace, nsprefix);
        return (lfcnode cast LzDataElement);
    }

    private static function nextSibling (node:XML) :XML {
        var p:XML = node.parent();
        return p ? p.children()[node.childIndex() + 1] : null;
    }

    private static function firstChild (node:XML) :XML {
        return node.children()[0];
    }

    /**
      * Translate a single Flash XML(Node) into a LzDataNode.
      */
    static function copyFlashNode (node:XML, trimwhitespace:Boolean, nsprefix:Boolean) :LzDataNodeMixin {
        var kind:String = node.nodeKind();
        if (kind == 'text') {
            var nv:String = node.toString();
            if (trimwhitespace) {
                nv = LzDataElement.trim(nv);
            }
            return new LzDataText(nv);
        } else if (kind == 'element') {
            var nname:String;
            var qname:QName = node.name();
            if (nsprefix) {
                var ns:Namespace = node.namespace();
                if (ns != null && ns.prefix != "") {
                    nname = ns.prefix + ":" + qname.localName;
                } else {
                    nname = qname.localName;
                }
            } else {
                nname = qname.localName;
            }

            var nattrs:XMLList = node.attributes();
            var cattrs:Object = {};
            for (var i:int = 0, len:int = nattrs.length(); i < len; i++) {
                var attr:XML = nattrs[i];
                var qattr:QName = attr.name();
                if (nsprefix) {
                    var ns:Namespace = attr.namespace();
                    var key:String;
                    if (ns != null && ns.prefix != "") {
                        key = ns.prefix + ":" + qattr.localName;
                    } else {
                        key = qattr.localName;
                    }
                    cattrs[key] = attr.toString();
                } else {
                    cattrs[qattr.localName] = attr.toString();
                }
            }

            var nsDecl:Array = node.namespaceDeclarations();
            for (var i:int = 0, len:int = nsDecl.length; i < len; ++i) {
                var ns:Namespace = nsDecl[i];
                var prefix:String = ns.prefix;
                var key:String = (prefix == "" ? "xmlns" : nsprefix ? "xmlns:" + prefix : prefix);
                cattrs[key] = ns.uri;
            }

            var lfcnode:LzDataElement = new LzDataElement(nname);
            // avoid copy of cattrs (see LzDataElement ctor)
            lfcnode.attributes = cattrs;
            return lfcnode;
        } else {
            return null;
        }
    }

    /**
      * Copy a Flash XML(Node) tree into a LzDataElement tree. Used by LzDataElement.stringToLzData
      *
      * @param boolean trimwhitespace: trim whitespace from start and end of text nodes
      * @param boolean nsprefix: preserve namespace prefixes on node names and attribute names
      * @access private
      */
    static function copyFlashXML (xmlnode:XML, trimwhitespace:Boolean, nsprefix:Boolean) :LzDataNodeMixin {
        // create a new, empty ownerDocument (LPP-7537)
        var document:LzDataElement = new LzDataElement(null);
        // handle this separately so you don't need to worry about the
        // case when xmlnode has got siblings
        if (! firstChild(xmlnode)) {
            return document.appendChild(copyFlashNode(xmlnode, trimwhitespace, nsprefix));
        }
        var lfcparent:LzDataElement = document;
        var next:XML, node:XML = xmlnode;
        // traverse DOM tree
        for (;;) {
            var kind:String = node.nodeKind();
            if (kind == 'text') {
                var lfctext:LzDataText = LzDataText(copyFlashNode(node, trimwhitespace, nsprefix));
                // inlined lfcparent.appendChild(lfcnode)
                lfctext.parentNode = lfcparent;
                lfctext.ownerDocument = document;
                lfctext.__LZo = (lfcparent.childNodes.push(lfctext) - 1);
            } else if (kind == 'element') {
                var lfcnode:LzDataElement = LzDataElement(copyFlashNode(node, trimwhitespace, nsprefix));
                // inlined lfcparent.appendChild(lfcnode)
                lfcnode.parentNode = lfcparent;
                lfcnode.ownerDocument = document;
                lfcnode.__LZo = (lfcparent.childNodes.push(lfcnode) - 1);

                // traverse down first
                if ((next = firstChild(node))) {
                    // this node is the new context
                    lfcparent = lfcnode;
                    node = next;
                    continue;
                }
            }
            // select next node
            while (! (next = nextSibling(node))) {
                // no nextSibling, go back in DOM
                node = node.parent();
                lfcparent = lfcparent.parentNode;
                if (node === xmlnode) {
                    // reached top element, copy finished
                    return document.childNodes[0];
                }
            }
            node = next;
        }
        // add return to make flash compiler happy
        return null;
    }

} // End of LzXMLTranslator

Change 20090516-bargull-NOm by bargull@dell--p4--2-53 on 2009-05-16 00:21:34
    in /home/Admin/src/svn/openlaszlo/trunk
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary: copy native xml-tree iteratively (SWF9)

New Features:

Bugs Fixed: LPP-8067 (Improve data performance) (SWF9)

Technical Reviewer: henry
QA Reviewer: (pending)
Doc Reviewer: (pending)

Documentation:

Release Notes:

Details:
Remove recursion in copyFlashXML() when traversing the DOM tree.
This is similar to the changes for the DHTML and SWF8 kernel.

Tests:
smokecheck, alldata in swf9

git-svn-id: d62bde4b5aa582fdf07c8d403e53e0399a7ed285@13927 fa20e4f9-1d0a-0410-b5f3-dc9c16b8b17c


// Editor debug icons

BillboardSet@ debugIconsSetDirectionalLights;
BillboardSet@ debugIconsSetSpotLights;
BillboardSet@ debugIconsSetPointLights;
BillboardSet@ debugIconsSetCameras;
BillboardSet@ debugIconsSetSoundSources;
BillboardSet@ debugIconsSetSoundSources3D;
BillboardSet@ debugIconsSetSoundListeners;
BillboardSet@ debugIconsSetZones;
BillboardSet@ debugIconsSetSplinesPoints;

Node@ debugIconsNode = null;

int stepDebugIconsUpdate = 30; //ms
int timeToNextDebugIconsUpdate = 0;
const int splinePathResolution = 16;
bool debugIconsShow = true;
Vector2 debugIconsSize = Vector2(0.025, 0.025);
Vector2 debugIconsSizeSmall = debugIconsSize / 2.0;

void CreateDebugIcons()
{
    if (editorScene is null) return;

    debugIconsSetDirectionalLights = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetDirectionalLights.material = cache.GetResource("Material", "Materials/Editor/DebugIconLight.xml");
    debugIconsSetDirectionalLights.material.renderOrder = 255;
    debugIconsSetDirectionalLights.sorted = true;
    debugIconsSetDirectionalLights.temporary = true;
    debugIconsSetDirectionalLights.viewMask = 0x80000000;

    debugIconsSetSpotLights = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetSpotLights.material = cache.GetResource("Material", "Materials/Editor/DebugIconSpotLight.xml");
    debugIconsSetSpotLights.material.renderOrder = 255;
    debugIconsSetSpotLights.sorted = true;
    debugIconsSetSpotLights.temporary = true;
    debugIconsSetSpotLights.viewMask = 0x80000000;

    debugIconsSetPointLights = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetPointLights.material = cache.GetResource("Material", "Materials/Editor/DebugIconPointLight.xml");
    debugIconsSetPointLights.material.renderOrder = 255;
    debugIconsSetPointLights.sorted = true;
    debugIconsSetPointLights.temporary = true;
    debugIconsSetPointLights.viewMask = 0x80000000;

    debugIconsSetCameras = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetCameras.material = cache.GetResource("Material", "Materials/Editor/DebugIconCamera.xml");
    debugIconsSetCameras.material.renderOrder = 255;
    debugIconsSetCameras.sorted = true;
    debugIconsSetCameras.temporary = true;
    debugIconsSetCameras.viewMask = 0x80000000;

    debugIconsSetSoundSources = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetSoundSources.material = cache.GetResource("Material", "Materials/Editor/DebugIconSoundSource.xml");
    debugIconsSetSoundSources.material.renderOrder = 255;
    debugIconsSetSoundSources.sorted = true;
    debugIconsSetSoundSources.temporary = true;
    debugIconsSetSoundSources.viewMask = 0x80000000;

    debugIconsSetSoundSources3D = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetSoundSources3D.material = cache.GetResource("Material", "Materials/Editor/DebugIconSoundSource.xml");
    debugIconsSetSoundSources3D.material.renderOrder = 255;
    debugIconsSetSoundSources3D.sorted = true;
    debugIconsSetSoundSources3D.temporary = true;
    debugIconsSetSoundSources3D.viewMask = 0x80000000;

    debugIconsSetSoundListeners = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetSoundListeners.material = cache.GetResource("Material", "Materials/Editor/DebugIconSoundListener.xml");
    debugIconsSetSoundListeners.material.renderOrder = 255;
    debugIconsSetSoundListeners.sorted = true;
    debugIconsSetSoundListeners.temporary = true;
    debugIconsSetSoundListeners.viewMask = 0x80000000;

    debugIconsSetZones = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetZones.material = cache.GetResource("Material", "Materials/Editor/DebugIconZone.xml");
    debugIconsSetZones.material.renderOrder = 255;
    debugIconsSetZones.sorted = true;
    debugIconsSetZones.temporary = true;
    debugIconsSetZones.viewMask = 0x80000000;

    debugIconsSetSplinesPoints = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetSplinesPoints.material = cache.GetResource("Material", "Materials/Editor/DebugIconSplinePathPoint.xml");
    debugIconsSetSplinesPoints.material.renderOrder = 255;
    debugIconsSetSplinesPoints.sorted = true;
    debugIconsSetSplinesPoints.temporary = true;
    debugIconsSetSplinesPoints.viewMask = 0x80000000;

}

void UpdateViewDebugIcons()
{
    if (timeToNextDebugIconsUpdate > time.systemTime) return;

    if (editorScene is null) return;

    debugIconsNode = editorScene.GetChild("DebugIconsContainer", true);

    if (debugIconsNode is null)
    {
        debugIconsNode = editorScene.CreateChild("DebugIconsContainer", LOCAL);
        debugIconsNode.temporary = true;
    }

    // Checkout if debugIconsNode do not have any BBS component, add they at once
    BillboardSet@ bbs = debugIconsNode.GetComponent("BillboardSet");

    if (bbs is null)
    {
        CreateDebugIcons();
    }

    Vector3 camPos = cameraNode.worldPosition;

    if (debugIconsSetPointLights !is null)
    {
        debugIconsSetDirectionalLights.enabled = debugIconsShow;
        debugIconsSetSpotLights.enabled = debugIconsShow;
        debugIconsSetPointLights.enabled = debugIconsShow;

        Array<Node@> nodes = editorScene.GetChildrenWithComponent("Light", true);

        if (nodes.length > 0)
        {
            debugIconsSetDirectionalLights.numBillboards = 0;
            debugIconsSetSpotLights.numBillboards = 0;
            debugIconsSetPointLights.numBillboards = 0;

            debugIconsSetPointLights.Commit();
            debugIconsSetSpotLights.Commit();
            debugIconsSetDirectionalLights.Commit();

            debugIconsSetDirectionalLights.numBillboards = nodes.length;
            debugIconsSetSpotLights.numBillboards = nodes.length;
            debugIconsSetPointLights.numBillboards = nodes.length;

            for (int i=0;i<nodes.length; i++)
            {
                Light@ light = nodes[i].GetComponent("Light");

                if (light.lightType == LIGHT_POINT)
                {
                    Billboard@ bb = debugIconsSetPointLights.billboards[i];
                    bb.position = nodes[i].worldPosition;
                    bb.color = light.effectiveColor;
                    float distance = (camPos - nodes[i].worldPosition).length;
                    bb.size = debugIconsSize * distance;
                    bb.enabled = true;
                }
                else if (light.lightType == LIGHT_DIRECTIONAL)
                {
                    Billboard@ bb = debugIconsSetDirectionalLights.billboards[i];
                    bb.position = nodes[i].worldPosition;
                    bb.color = light.effectiveColor;
                    float distance = (camPos - nodes[i].worldPosition).length;
                    bb.size = debugIconsSize * distance;
                    bb.enabled = true;
                }
                else if (light.lightType == LIGHT_SPOT)
                {
                    Billboard@ bb = debugIconsSetSpotLights.billboards[i];
                    bb.position = nodes[i].worldPosition;
                    bb.color = light.effectiveColor;
                    float distance = (camPos - nodes[i].worldPosition).length;
                    bb.size = debugIconsSize * distance;
                    bb.enabled = true;
                }
            }

            debugIconsSetPointLights.Commit();
            debugIconsSetSpotLights.Commit();
            debugIconsSetDirectionalLights.Commit();

        }
    }

    if (debugIconsSetCameras !is null)
    {
        debugIconsSetCameras.enabled = debugIconsShow;
        Array<Node@> nodes = editorScene.GetChildrenWithComponent("Camera", true);

        if (nodes.length > 0)
        {
            debugIconsSetCameras.numBillboards = nodes.length;
            for (int i=0;i<nodes.length; i++)
            {
                Camera@ camera = nodes[i].GetComponent("Camera");
                Billboard@ bb = debugIconsSetCameras.billboards[i];
                bb.position = nodes[i].worldPosition;

                // if mainCamera enough closer to selected camera then make bb size relative to distance
                float distance = (camPos - nodes[i].worldPosition).length;
                bb.size = debugIconsSize * distance;

                bb.enabled = true;
            }

            debugIconsSetCameras.Commit();
        }
    }

    if (debugIconsSetSoundSources !is null)
    {
        debugIconsSetSoundSources.enabled = debugIconsShow;

        Array<Node@> nodes = editorScene.GetChildrenWithComponent("SoundSource", true);

        if (nodes.length > 0)
        {
            debugIconsSetSoundSources.numBillboards = nodes.length;
            for (int i=0;i<nodes.length; i++)
            {
                Component@ component = nodes[i].GetComponent("SoundSource");
                Billboard@ bb = debugIconsSetSoundSources.billboards[i];
                bb.position = nodes[i].worldPosition;
                float distance = (camPos - nodes[i].worldPosition).length;
                bb.size = debugIconsSize * distance;
                bb.enabled = component.enabled;
            }

            debugIconsSetSoundSources.Commit();
        }
    }

    if (debugIconsSetSoundSources3D !is null)
    {
        debugIconsSetSoundSources3D.enabled = debugIconsShow;

        Array<Node@> nodes = editorScene.GetChildrenWithComponent("SoundSource3D", true);

        if (nodes.length > 0)
        {
            debugIconsSetSoundSources3D.numBillboards = nodes.length;
            for (int i=0;i<nodes.length; i++)
            {
                Component@ component = nodes[i].GetComponent("SoundSource3D");
                Billboard@ bb = debugIconsSetSoundSources3D.billboards[i];
                bb.position = nodes[i].worldPosition;
                float distance = (camPos - nodes[i].worldPosition).length;
                bb.size = debugIconsSize * distance;
                bb.enabled = component.enabled;
            }

            debugIconsSetSoundSources3D.Commit();
        }
    }

    if (debugIconsSetSoundListeners !is null)
    {
        debugIconsSetSoundListeners.enabled = debugIconsShow;
        Array<Node@> nodes = editorScene.GetChildrenWithComponent("SoundListener" , true);

        if (nodes.length > 0)
        {
            debugIconsSetSoundListeners.numBillboards = nodes.length;
            for (int i=0;i<nodes.length; i++)
            {
                Component@ component = nodes[i].GetComponent("SoundListener");
                Billboard@ bb = debugIconsSetSoundListeners.billboards[i];
                bb.position = nodes[i].worldPosition;
                float distance = (camPos - nodes[i].worldPosition).length;
                bb.size = debugIconsSize * distance;
                bb.enabled = component.enabled;
            }

            debugIconsSetSoundListeners.Commit();
        }
    }

    if (debugIconsSetZones !is null)
    {
        debugIconsSetZones.enabled = debugIconsShow;

        // Collect all scene's Zones and add it
        Array<Node@> nodes = editorScene.GetChildrenWithComponent("Zone", true);

        Zone@ zone = editorScene.GetComponent("Zone");
        if (zone !is null)
        {
            debugIconsSetZones.numBillboards = 1;
            Billboard@ bb = debugIconsSetZones.billboards[0];
            bb.position = Vector3(0,0,0);
            float distance = (camPos - bb.position).length;
            bb.size = debugIconsSize * distance;
            //bb.color = zone.ambientColor;
            bb.enabled = zone.enabled;
        }

        if (nodes.length > 0)
        {
            debugIconsSetZones.numBillboards = 1 + nodes.length;
            for (int i=0;i<nodes.length; i++)
            {
                Zone@ zone = nodes[i].GetComponent("Zone");
                Billboard@ bb = debugIconsSetZones.billboards[i+1];
                bb.position = nodes[i].worldPosition;
                float distance = (camPos - nodes[i].worldPosition).length;
                bb.size = debugIconsSize * distance;
                //bb.color = zone.ambientColor;
                bb.enabled = zone.enabled;
            }

            debugIconsSetZones.Commit();
        }
    }

    if (debugIconsSetSplinesPoints !is null)
    {
        debugIconsSetSplinesPoints.enabled = debugIconsShow;

        // Collect all scene's SplinePath and add it
        Array<Node@> nodes = editorScene.GetChildrenWithComponent("SplinePath", true);

        if (nodes.length > 0)
        {
            debugIconsSetSplinesPoints.numBillboards = nodes.length * splinePathResolution;

            float splineStep = 1.0f / splinePathResolution;

            for(int i=0; i < nodes.length; i++)
            {

                SplinePath@ sp = nodes[i].GetComponent("SplinePath");
                if(sp !is null)
                {
                    Vector3 splinePoint;
                    // Create path
                    for(int step=0; step < splinePathResolution; step++)
                    {
                        splinePoint = sp.GetPoint(splineStep * step);

                        float distance = (camPos - splinePoint).length;
                        int index = (i * splinePathResolution) + step;
                        Billboard@ bb = debugIconsSetSplinesPoints.billboards[index];
                        bb.position = splinePoint;
                        bb.size = debugIconsSizeSmall * distance;
                        //bb.color = Color(1,1,0);
                        bb.enabled = sp.enabled;
                    }
                }
            }

            debugIconsSetSplinesPoints.Commit();
        }
    }

    timeToNextDebugIconsUpdate = time.systemTime + stepDebugIconsUpdate;
}

// Editor debug icons

BillboardSet@ debugIconsSetDirectionalLights;
BillboardSet@ debugIconsSetSpotLights;
BillboardSet@ debugIconsSetPointLights;
BillboardSet@ debugIconsSetCameras;
BillboardSet@ debugIconsSetSoundSources;
BillboardSet@ debugIconsSetSoundSources3D;
BillboardSet@ debugIconsSetSoundListeners;
BillboardSet@ debugIconsSetZones;
BillboardSet@ debugIconsSetSplinesPoints;

Node@ debugIconsNode = null;

int stepDebugIconsUpdate = 40; //ms
int timeToNextDebugIconsUpdate = 0;
const int splinePathResolution = 16;
bool debugIconsShow = true;
Vector2 debugIconsSize = Vector2(0.025, 0.025);
Vector2 debugIconsSizeSmall = debugIconsSize / 2.0;

void CreateDebugIcons()
{
    if (editorScene is null) return;

    debugIconsSetDirectionalLights = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetDirectionalLights.material = cache.GetResource("Material", "Materials/Editor/DebugIconLight.xml");
    debugIconsSetDirectionalLights.material.renderOrder = 255;
    debugIconsSetDirectionalLights.sorted = true;
    debugIconsSetDirectionalLights.temporary = true;
    debugIconsSetDirectionalLights.viewMask = 0x80000000;

    debugIconsSetSpotLights = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetSpotLights.material = cache.GetResource("Material", "Materials/Editor/DebugIconSpotLight.xml");
    debugIconsSetSpotLights.material.renderOrder = 255;
    debugIconsSetSpotLights.sorted = true;
    debugIconsSetSpotLights.temporary = true;
    debugIconsSetSpotLights.viewMask = 0x80000000;

    debugIconsSetPointLights = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetPointLights.material = cache.GetResource("Material", "Materials/Editor/DebugIconPointLight.xml");
    debugIconsSetPointLights.material.renderOrder = 255;
    debugIconsSetPointLights.sorted = true;
    debugIconsSetPointLights.temporary = true;
    debugIconsSetPointLights.viewMask = 0x80000000;

    debugIconsSetCameras = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetCameras.material = cache.GetResource("Material", "Materials/Editor/DebugIconCamera.xml");
    debugIconsSetCameras.material.renderOrder = 255;
    debugIconsSetCameras.sorted = true;
    debugIconsSetCameras.temporary = true;
    debugIconsSetCameras.viewMask = 0x80000000;

    debugIconsSetSoundSources = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetSoundSources.material = cache.GetResource("Material", "Materials/Editor/DebugIconSoundSource.xml");
    debugIconsSetSoundSources.material.renderOrder = 255;
    debugIconsSetSoundSources.sorted = true;
    debugIconsSetSoundSources.temporary = true;
    debugIconsSetSoundSources.viewMask = 0x80000000;

    debugIconsSetSoundSources3D = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetSoundSources3D.material = cache.GetResource("Material", "Materials/Editor/DebugIconSoundSource.xml");
    debugIconsSetSoundSources3D.material.renderOrder = 255;
    debugIconsSetSoundSources3D.sorted = true;
    debugIconsSetSoundSources3D.temporary = true;
    debugIconsSetSoundSources3D.viewMask = 0x80000000;

    debugIconsSetSoundListeners = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetSoundListeners.material = cache.GetResource("Material", "Materials/Editor/DebugIconSoundListener.xml");
    debugIconsSetSoundListeners.material.renderOrder = 255;
    debugIconsSetSoundListeners.sorted = true;
    debugIconsSetSoundListeners.temporary = true;
    debugIconsSetSoundListeners.viewMask = 0x80000000;

    debugIconsSetZones = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetZones.material = cache.GetResource("Material", "Materials/Editor/DebugIconZone.xml");
    debugIconsSetZones.material.renderOrder = 255;
    debugIconsSetZones.sorted = true;
    debugIconsSetZones.temporary = true;
    debugIconsSetZones.viewMask = 0x80000000;

    debugIconsSetSplinesPoints = debugIconsNode.CreateComponent("BillboardSet");
    debugIconsSetSplinesPoints.material = cache.GetResource("Material", "Materials/Editor/DebugIconSplinePathPoint.xml");
    debugIconsSetSplinesPoints.material.renderOrder = 255;
    debugIconsSetSplinesPoints.sorted = true;
    debugIconsSetSplinesPoints.temporary = true;
    debugIconsSetSplinesPoints.viewMask = 0x80000000;

}

void UpdateViewDebugIcons()
{
    if (timeToNextDebugIconsUpdate > time.systemTime) return;

    if (editorScene is null) return;

    debugIconsNode = editorScene.GetChild("DebugIconsContainer", true);

    if (debugIconsNode is null)
    {
        debugIconsNode = editorScene.CreateChild("DebugIconsContainer", LOCAL);
        debugIconsNode.temporary = true;
    }

    // Checkout if debugIconsNode do not have any BBS component, add they at once
    BillboardSet@ bbs = debugIconsNode.GetComponent("BillboardSet");

    if (bbs is null)
    {
        CreateDebugIcons();
    }

    Vector3 camPos = cameraNode.worldPosition;

    if (debugIconsSetPointLights !is null)
    {
        debugIconsSetDirectionalLights.enabled = debugIconsShow;
        debugIconsSetSpotLights.enabled = debugIconsShow;
        debugIconsSetPointLights.enabled = debugIconsShow;

        Array<Node@> nodes = editorScene.GetChildrenWithComponent("Light", true);

        if (nodes.length > 0)
        {
            debugIconsSetDirectionalLights.numBillboards = 0;
            debugIconsSetSpotLights.numBillboards = 0;
            debugIconsSetPointLights.numBillboards = 0;

            debugIconsSetPointLights.Commit();
            debugIconsSetSpotLights.Commit();
            debugIconsSetDirectionalLights.Commit();

            debugIconsSetDirectionalLights.numBillboards = nodes.length;
            debugIconsSetSpotLights.numBillboards = nodes.length;
            debugIconsSetPointLights.numBillboards = nodes.length;

            for (int i=0;i<nodes.length; i++)
            {
                Light@ light = nodes[i].GetComponent("Light");

                if (light.lightType == LIGHT_POINT)
                {
                    Billboard@ bb = debugIconsSetPointLights.billboards[i];
                    bb.position = nodes[i].worldPosition;
                    bb.color = light.effectiveColor;
                    float distance = (camPos - nodes[i].worldPosition).length;
                    bb.size = debugIconsSize * distance;
                    bb.enabled = true;
                }
                else if (light.lightType == LIGHT_DIRECTIONAL)
                {
                    Billboard@ bb = debugIconsSetDirectionalLights.billboards[i];
                    bb.position = nodes[i].worldPosition;
                    bb.color = light.effectiveColor;
                    float distance = (camPos - nodes[i].worldPosition).length;
                    bb.size = debugIconsSize * distance;
                    bb.enabled = true;
                }
                else if (light.lightType == LIGHT_SPOT)
                {
                    Billboard@ bb = debugIconsSetSpotLights.billboards[i];
                    bb.position = nodes[i].worldPosition;
                    bb.color = light.effectiveColor;
                    float distance = (camPos - nodes[i].worldPosition).length;
                    bb.size = debugIconsSize * distance;
                    bb.enabled = true;
                }
            }

            debugIconsSetPointLights.Commit();
            debugIconsSetSpotLights.Commit();
            debugIconsSetDirectionalLights.Commit();

        }
    }

    if (debugIconsSetCameras !is null)
    {
        debugIconsSetCameras.enabled = debugIconsShow;
        Array<Node@> nodes = editorScene.GetChildrenWithComponent("Camera", true);

        if (nodes.length > 0)
        {
            debugIconsSetCameras.numBillboards = nodes.length;
            for (int i=0;i<nodes.length; i++)
            {
                Camera@ camera = nodes[i].GetComponent("Camera");
                Billboard@ bb = debugIconsSetCameras.billboards[i];
                bb.position = nodes[i].worldPosition;

                // if mainCamera enough closer to selected camera then make bb size relative to distance
                float distance = (camPos - nodes[i].worldPosition).length;
                bb.size = debugIconsSize * distance;

                bb.enabled = true;
            }

            debugIconsSetCameras.Commit();
        }
    }

    if (debugIconsSetSoundSources !is null)
    {
        debugIconsSetSoundSources.enabled = debugIconsShow;

        Array<Node@> nodes = editorScene.GetChildrenWithComponent("SoundSource", true);

        if (nodes.length > 0)
        {
            debugIconsSetSoundSources.numBillboards = nodes.length;
            for (int i=0;i<nodes.length; i++)
            {
                Component@ component = nodes[i].GetComponent("SoundSource");
                Billboard@ bb = debugIconsSetSoundSources.billboards[i];
                bb.position = nodes[i].worldPosition;
                float distance = (camPos - nodes[i].worldPosition).length;
                bb.size = debugIconsSize * distance;
                bb.enabled = component.enabled;
            }

            debugIconsSetSoundSources.Commit();
        }
    }

    if (debugIconsSetSoundSources3D !is null)
    {
        debugIconsSetSoundSources3D.enabled = debugIconsShow;

        Array<Node@> nodes = editorScene.GetChildrenWithComponent("SoundSource3D", true);

        if (nodes.length > 0)
        {
            debugIconsSetSoundSources3D.numBillboards = nodes.length;
            for (int i=0;i<nodes.length; i++)
            {
                Component@ component = nodes[i].GetComponent("SoundSource3D");
                Billboard@ bb = debugIconsSetSoundSources3D.billboards[i];
                bb.position = nodes[i].worldPosition;
                float distance = (camPos - nodes[i].worldPosition).length;
                bb.size = debugIconsSize * distance;
                bb.enabled = component.enabled;
            }

            debugIconsSetSoundSources3D.Commit();
        }
    }

    if (debugIconsSetSoundListeners !is null)
    {
        debugIconsSetSoundListeners.enabled = debugIconsShow;
        Array<Node@> nodes = editorScene.GetChildrenWithComponent("SoundListener" , true);

        if (nodes.length > 0)
        {
            debugIconsSetSoundListeners.numBillboards = nodes.length;
            for (int i=0;i<nodes.length; i++)
            {
                Component@ component = nodes[i].GetComponent("SoundListener");
                Billboard@ bb = debugIconsSetSoundListeners.billboards[i];
                bb.position = nodes[i].worldPosition;
                float distance = (camPos - nodes[i].worldPosition).length;
                bb.size = debugIconsSize * distance;
                bb.enabled = component.enabled;
            }

            debugIconsSetSoundListeners.Commit();
        }
    }

    if (debugIconsSetZones !is null)
    {
        debugIconsSetZones.enabled = debugIconsShow;

        // Collect all scene's Zones and add it
        Array<Node@> nodes = editorScene.GetChildrenWithComponent("Zone", true);

        Zone@ zone = editorScene.GetComponent("Zone");
        if (zone !is null)
        {
            debugIconsSetZones.numBillboards = 1;
            Billboard@ bb = debugIconsSetZones.billboards[0];
            bb.position = Vector3(0,0,0);
            float distance = (camPos - bb.position).length;
            bb.size = debugIconsSize * distance;
            //bb.color = zone.ambientColor;
            bb.enabled = zone.enabled;
        }

        if (nodes.length > 0)
        {
            debugIconsSetZones.numBillboards = 1 + nodes.length;
            for (int i=0;i<nodes.length; i++)
            {
                Zone@ zone = nodes[i].GetComponent("Zone");
                Billboard@ bb = debugIconsSetZones.billboards[i+1];
                bb.position = nodes[i].worldPosition;
                float distance = (camPos - nodes[i].worldPosition).length;
                bb.size = debugIconsSize * distance;
                //bb.color = zone.ambientColor;
                bb.enabled = zone.enabled;
            }

            debugIconsSetZones.Commit();
        }
    }

    if (debugIconsSetSplinesPoints !is null)
    {
        debugIconsSetSplinesPoints.enabled = debugIconsShow;

        // Collect all scene's SplinePath and add it
        Array<Node@> nodes = editorScene.GetChildrenWithComponent("SplinePath", true);

        if (nodes.length > 0)
        {
            debugIconsSetSplinesPoints.numBillboards = nodes.length * splinePathResolution;

            float splineStep = 1.0f / splinePathResolution;

            for(int i=0; i < nodes.length; i++)
            {

                SplinePath@ sp = nodes[i].GetComponent("SplinePath");
                if(sp !is null)
                {
                    Vector3 splinePoint;
                    // Create path
                    for(int step=0; step < splinePathResolution; step++)
                    {
                        splinePoint = sp.GetPoint(splineStep * step);

                        float distance = (camPos - splinePoint).length;
                        int index = (i * splinePathResolution) + step;
                        Billboard@ bb = debugIconsSetSplinesPoints.billboards[index];
                        bb.position = splinePoint;
                        bb.size = debugIconsSizeSmall * distance;

                        if (step == 0)
                        {
                            bb.color = Color(1,1,0);
                            bb.size = debugIconsSize * distance;
                        }
                        else if ((step+1) >= (splinePathResolution - splineStep))
                        {
                            bb.color = Color(0,1,0);
                            bb.size = debugIconsSize * distance;
                        }
                        else
                        {
                            bb.color = Color(1,1,1);
                            bb.size = debugIconsSizeSmall * distance;
                        }
                        bb.enabled = sp.enabled;
                    }
                }
            }

            debugIconsSetSplinesPoints.Commit();
        }
    }

    timeToNextDebugIconsUpdate = time.systemTime + stepDebugIconsUpdate;
}

add begin and end of splinepath coloring


//------------------------------------------------------------------------------
//  Copyright (c) 2011 the original author or authors. All Rights Reserved.
//
//  NOTICE: You are permitted you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//------------------------------------------------------------------------------

package org.robotlegs.v2.view.impl
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.events.Event;
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;
	import org.robotlegs.v2.view.api.IContainerBinding;
	import org.robotlegs.v2.view.api.IViewHandler;
	import org.robotlegs.v2.view.api.IViewWatcher;

	public class StageWatcher implements IViewWatcher
	{

		/*============================================================================*/
		/* Private Properties                                                         */
		/*============================================================================*/

		private const _bindingsByContainer:Dictionary = new Dictionary(false);

		private const _confirmedHandlersByFQCN:Dictionary = new Dictionary(false);

		private const _removeHandlersByTarget:Dictionary = new Dictionary(true);

		/*============================================================================*/
		/* Constructor                                                                */
		/*============================================================================*/

		public function StageWatcher()
		{
			// This page intentionally left blank
		}

		/*============================================================================*/
		/* Public Functions                                                           */
		/*============================================================================*/

		public function addHandler(handler:IViewHandler, container:DisplayObjectContainer):void
		{
			if (handler.interests == 0)
				throw new ArgumentError('A view handler must be interested in something.');

			const binding:IContainerBinding = _bindingsByContainer[container] ||= createBindingFor(container);
			binding.addHandler(handler);
		}

		public function removeHandler(handler:IViewHandler, container:DisplayObjectContainer):void
		{
			const binding:IContainerBinding = _bindingsByContainer[container];
			if (!binding)
				return;

			binding.removeHandler(handler);

			// No point in a binding with no handlers!
			if (binding.handlers.length == 0)
				removeBinding(binding);
		}

		/*============================================================================*/
		/* Private Functions                                                          */
		/*============================================================================*/

		private function addRootBinding(binding:IContainerBinding):void
		{
			// The magical, but extremely expensive, capture-phase ADDED_TO_STAGE listener
			binding.container.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, true);
		}

		private function createBindingFor(container:DisplayObjectContainer):IContainerBinding
		{
			const binding:IContainerBinding = new ContainerBinding(container);
			binding.parent = findParentBindingFor(container);

			// If the new binding doesn't have a parent it is a Root
			if (binding.parent == null)
			{
				addRootBinding(binding);
			}

			// Reparent any bindings which are contained within the new binding AND
			// A. Don't have a parent, OR
			// B. Have a parent that is not contained within the new binding
			for each (var childBinding:IContainerBinding in _bindingsByContainer)
			{
				if (container.contains(childBinding.container))
				{
					if (!childBinding.parent)
					{
						removeRootBinding(childBinding);
						childBinding.parent = binding;
					}
					else if (!container.contains(childBinding.parent.container))
					{
						childBinding.parent = binding;
					}
				}
			}

			return binding;
		}

		private function ensureRemoveHandler(target:DisplayObject, handler:IViewHandler):void
		{
			var removeHandlers:Vector.<IViewHandler> = _removeHandlersByTarget[target];
			if (!removeHandlers)
			{
				removeHandlers = new Vector.<IViewHandler>;
				_removeHandlersByTarget[target] = removeHandlers;
				// Just a normal, target-phase REMOVED_FROM_STAGE listener per target
				target.addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
			}
			removeHandlers.push(handler);
		}

		private function findParentBindingFor(target:DisplayObject):IContainerBinding
		{
			var parent:DisplayObjectContainer = target.parent;
			while (parent)
			{
				var binding:IContainerBinding = _bindingsByContainer[parent];
				if (binding)
				{
					return binding;
				}
				parent = parent.parent;
			}
			return null;
		}

		private function handleFreshView(
			target:DisplayObject,
			targetFQCN:String,
			confirmedHandlers:Vector.<IViewHandler>):void
		{
			var handlerResponse:uint = 0;
			var combinedResponse:uint = 0;
			var handler:IViewHandler;
			var handlers:Vector.<IViewHandler>;
			var binding:IContainerBinding = findParentBindingFor(target);
			// Walk upwards from the nearest binding
			while (binding)
			{
				handlers = binding.handlers;
				var totalHandlers:uint = handlers.length;
				for (var i:uint = 0; i < totalHandlers; i++)
				{
					handler = handlers[i];

					// Have our interests been blocked by a previous handler?
					// note: this leads to an interesting thought:
					// Should handlers really be able to have multiple interests (as currently implemented)?
					// How does blocking work then?
					if (!((combinedResponse & 0xAAAAAAAA) ^ (handler.interests << 1)))
						continue;

					handlerResponse = handler.handleViewAdded(target, null);
					combinedResponse |= handlerResponse;

					if (handlerResponse)
					{
						ensureRemoveHandler(target, handler);
						confirmedHandlers.push(handler);
					}
				}

				binding = binding.parent;
			}
		}

		private function handleKnownView(
			target:DisplayObject,
			targetFQCN:String,
			confirmedHandlers:Vector.<IViewHandler>):void
		{
			var handlerResponse:uint = 0;
			var combinedResponse:uint = 0;
			var handler:IViewHandler;
			const totalHandlers:uint = confirmedHandlers.length;
			for (var i:uint = 0; i < totalHandlers; i++)
			{
				handler = confirmedHandlers[i];
				if (!((combinedResponse & 0xAAAAAAAA) ^ (handler.interests << 1)))
				{
					trace('warning: a confirmed handler was blocked - cache purging did not take place when it should have.');
					continue;
				}

				handlerResponse = handler.handleViewAdded(target, null);
				combinedResponse |= handlerResponse;

				if (handlerResponse)
				{
					ensureRemoveHandler(target, handler);
				}
				else
				{
					trace('warning: a confirmed handler did not handle a view - cache purging did not take place when it should have.');
				}
			}
		}

		private function onAddedToStage(event:Event):void
		{
			const target:DisplayObject = event.target as DisplayObject;
			const targetFQCN:String = getQualifiedClassName(target);

			// note: all vectors should actually be linked lists
			// to prevent mid-iteration errors
			var confirmedHandlers:Vector.<IViewHandler> = _confirmedHandlersByFQCN[targetFQCN];

			if (confirmedHandlers)
			{
				handleKnownView(target, targetFQCN, confirmedHandlers);
			}
			else
			{
				confirmedHandlers = new Vector.<IViewHandler>;
				_confirmedHandlersByFQCN[targetFQCN] = confirmedHandlers;
				handleFreshView(target, targetFQCN, confirmedHandlers);
			}
		}

		private function onRemovedFromStage(event:Event):void
		{
			// This listener only fires for targets that got picked up by handlers
			const target:DisplayObject = event.target as DisplayObject;
			target.removeEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);

			var handler:IViewHandler;
			const handlers:Vector.<IViewHandler> = _removeHandlersByTarget[target];
			const totalHandlers:uint = handlers.length;
			for (var i:uint = 0; i < totalHandlers; i++)
			{
				handler = handlers[i];
				handler.handleViewRemoved(target);
			}
			delete _removeHandlersByTarget[target];
		}

		private function removeBinding(binding:IContainerBinding):void
		{
			delete _bindingsByContainer[binding.container];

			if (!binding.parent)
			{
				// This binding didn't have a parent, so it was a Root
				removeRootBinding(binding);
			}

			// Re-parent the bindings
			for each (var childBinding:IContainerBinding in _bindingsByContainer)
			{
				if (childBinding.parent == binding)
				{
					childBinding.parent = binding.parent;
					if (!childBinding.parent)
					{
						// This binding used to have a parent,
						// but no longer does, so it is now a Root
						addRootBinding(childBinding);
					}
				}
			}
		}

		private function removeRootBinding(binding:IContainerBinding):void
		{
			binding.container.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage, true);
		}
	}
}

//------------------------------------------------------------------------------
//  Copyright (c) 2011 the original author or authors. All Rights Reserved.
//
//  NOTICE: You are permitted you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//------------------------------------------------------------------------------

package org.robotlegs.v2.view.impl
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.events.Event;
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;
	import org.as3commons.logging.api.ILogger;
	import org.as3commons.logging.api.getLogger;
	import org.robotlegs.v2.view.api.IContainerBinding;
	import org.robotlegs.v2.view.api.IViewHandler;
	import org.robotlegs.v2.view.api.IViewWatcher;

	public class StageWatcher implements IViewWatcher
	{

		/*============================================================================*/
		/* Private Static Properties                                                  */
		/*============================================================================*/

		private static const logger:ILogger = getLogger(StageWatcher);

		/*============================================================================*/
		/* Private Properties                                                         */
		/*============================================================================*/

		private const _bindingsByContainer:Dictionary = new Dictionary(false);

		private const _confirmedHandlersByFQCN:Dictionary = new Dictionary(false);

		private const _removeHandlersByTarget:Dictionary = new Dictionary(true);

		/*============================================================================*/
		/* Constructor                                                                */
		/*============================================================================*/

		public function StageWatcher()
		{
			// This page intentionally left blank
		}

		/*============================================================================*/
		/* Public Functions                                                           */
		/*============================================================================*/

		public function addHandler(handler:IViewHandler, container:DisplayObjectContainer):void
		{
			if (handler.interests == 0)
				throw new ArgumentError('A view handler must be interested in something.');

			const binding:IContainerBinding = _bindingsByContainer[container] ||= createBindingFor(container);
			binding.addHandler(handler);
		}

		public function removeHandler(handler:IViewHandler, container:DisplayObjectContainer):void
		{
			const binding:IContainerBinding = _bindingsByContainer[container];
			if (!binding)
				return;

			binding.removeHandler(handler);

			// No point in a binding with no handlers!
			if (binding.handlers.length == 0)
				removeBinding(binding);
		}

		/*============================================================================*/
		/* Private Functions                                                          */
		/*============================================================================*/

		private function addRootBinding(binding:IContainerBinding):void
		{
			// The magical, but extremely expensive, capture-phase ADDED_TO_STAGE listener
			binding.container.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, true);
		}

		private function createBindingFor(container:DisplayObjectContainer):IContainerBinding
		{
			const binding:IContainerBinding = new ContainerBinding(container);
			binding.parent = findParentBindingFor(container);

			// If the new binding doesn't have a parent it is a Root
			if (binding.parent == null)
			{
				addRootBinding(binding);
			}

			// Reparent any bindings which are contained within the new binding AND
			// A. Don't have a parent, OR
			// B. Have a parent that is not contained within the new binding
			for each (var childBinding:IContainerBinding in _bindingsByContainer)
			{
				if (container.contains(childBinding.container))
				{
					if (!childBinding.parent)
					{
						removeRootBinding(childBinding);
						childBinding.parent = binding;
					}
					else if (!container.contains(childBinding.parent.container))
					{
						childBinding.parent = binding;
					}
				}
			}

			return binding;
		}

		private function ensureRemoveHandler(target:DisplayObject, handler:IViewHandler):void
		{
			var removeHandlers:Vector.<IViewHandler> = _removeHandlersByTarget[target];
			if (!removeHandlers)
			{
				removeHandlers = new Vector.<IViewHandler>;
				_removeHandlersByTarget[target] = removeHandlers;
				// Just a normal, target-phase REMOVED_FROM_STAGE listener per target
				target.addEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);
			}
			removeHandlers.push(handler);
		}

		private function findParentBindingFor(target:DisplayObject):IContainerBinding
		{
			var parent:DisplayObjectContainer = target.parent;
			while (parent)
			{
				var binding:IContainerBinding = _bindingsByContainer[parent];
				if (binding)
				{
					return binding;
				}
				parent = parent.parent;
			}
			return null;
		}

		private function handleFreshView(
			target:DisplayObject,
			targetFQCN:String,
			confirmedHandlers:Vector.<IViewHandler>):void
		{
			var handlerResponse:uint = 0;
			var combinedResponse:uint = 0;
			var handler:IViewHandler;
			var handlers:Vector.<IViewHandler>;
			var binding:IContainerBinding = findParentBindingFor(target);
			// Walk upwards from the nearest binding
			while (binding)
			{
				handlers = binding.handlers;
				var totalHandlers:uint = handlers.length;
				for (var i:uint = 0; i < totalHandlers; i++)
				{
					handler = handlers[i];

					// Have our interests been blocked by a previous handler?
					// note: this leads to an interesting thought:
					// Should handlers really be able to have multiple interests (as currently implemented)?
					// How does blocking work then?
					if (!((combinedResponse & 0xAAAAAAAA) ^ (handler.interests << 1)))
						continue;

					handlerResponse = handler.handleViewAdded(target, null);
					combinedResponse |= handlerResponse;

					if (handlerResponse)
					{
						ensureRemoveHandler(target, handler);
						confirmedHandlers.push(handler);
					}
				}

				binding = binding.parent;
			}
		}

		private function handleKnownView(
			target:DisplayObject,
			targetFQCN:String,
			confirmedHandlers:Vector.<IViewHandler>):void
		{
			var handlerResponse:uint = 0;
			var combinedResponse:uint = 0;
			var handler:IViewHandler;
			const totalHandlers:uint = confirmedHandlers.length;
			for (var i:uint = 0; i < totalHandlers; i++)
			{
				handler = confirmedHandlers[i];
				if (!((combinedResponse & 0xAAAAAAAA) ^ (handler.interests << 1)))
				{
					logger.warn('a confirmed handler was blocked - cache purging did not take place when it should have');
					continue;
				}

				handlerResponse = handler.handleViewAdded(target, null);
				combinedResponse |= handlerResponse;

				if (handlerResponse)
				{
					ensureRemoveHandler(target, handler);
				}
				else
				{
					logger.warn('a confirmed handler did not handle a view - cache purging did not take place when it should have');
				}
			}
		}

		private function onAddedToStage(event:Event):void
		{
			const target:DisplayObject = event.target as DisplayObject;
			const targetFQCN:String = getQualifiedClassName(target);

			// note: all vectors should actually be linked lists
			// to prevent mid-iteration errors
			var confirmedHandlers:Vector.<IViewHandler> = _confirmedHandlersByFQCN[targetFQCN];

			if (confirmedHandlers)
			{
				handleKnownView(target, targetFQCN, confirmedHandlers);
			}
			else
			{
				confirmedHandlers = new Vector.<IViewHandler>;
				_confirmedHandlersByFQCN[targetFQCN] = confirmedHandlers;
				handleFreshView(target, targetFQCN, confirmedHandlers);
			}
		}

		private function onRemovedFromStage(event:Event):void
		{
			// This listener only fires for targets that got picked up by handlers
			const target:DisplayObject = event.target as DisplayObject;
			target.removeEventListener(Event.REMOVED_FROM_STAGE, onRemovedFromStage);

			var handler:IViewHandler;
			const handlers:Vector.<IViewHandler> = _removeHandlersByTarget[target];
			const totalHandlers:uint = handlers.length;
			for (var i:uint = 0; i < totalHandlers; i++)
			{
				handler = handlers[i];
				handler.handleViewRemoved(target);
			}
			delete _removeHandlersByTarget[target];
		}

		private function removeBinding(binding:IContainerBinding):void
		{
			delete _bindingsByContainer[binding.container];

			if (!binding.parent)
			{
				// This binding didn't have a parent, so it was a Root
				removeRootBinding(binding);
			}

			// Re-parent the bindings
			for each (var childBinding:IContainerBinding in _bindingsByContainer)
			{
				if (childBinding.parent == binding)
				{
					childBinding.parent = binding.parent;
					if (!childBinding.parent)
					{
						// This binding used to have a parent,
						// but no longer does, so it is now a Root
						addRootBinding(childBinding);
					}
				}
			}
		}

		private function removeRootBinding(binding:IContainerBinding):void
		{
			binding.container.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage, true);
		}
	}
}

put the logger back

put the logger back


package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.data.TransformDataProvider;
	import aerys.minko.type.Signal;
	import aerys.minko.type.math.Matrix4x4;

	import flash.utils.getQualifiedClassName;

	/**
	 * The base class to extend in order to create new scene node types.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class AbstractSceneNode implements ISceneNode
	{
		use namespace minko_scene;

		private static var _id			: uint							= 0;

		private var _name				: String						= null;
		private var _root				: ISceneNode					= null;
		private var _parent				: Group							= null;

		private var _transformData		: TransformDataProvider			= new TransformDataProvider();
		private var _transform			: Matrix4x4						= new Matrix4x4();

		private var _controllers		: Vector.<AbstractController>	= new <AbstractController>[];

		private var _added				: Signal						= new Signal('AbstractSceneNode.added');
		private var _removed			: Signal						= new Signal('AbstractSceneNode.removed');
		private var _addedToScene		: Signal						= new Signal('AbstractSceneNode.addedToScene');
		private var _removedFromScene	: Signal						= new Signal('AbstractSceneNode.removedFromScene');
		private var _controllerAdded	: Signal						= new Signal('AbstractSceneNode.controllerAdded');
		private var _controllerRemoved	: Signal						= new Signal('AbstractSceneNode.controllerRemoved');

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

		public function get parent() : Group
		{
			return _parent;
		}
		public function set parent(value : Group) : void
		{
			// remove child
			if (_parent)
			{
				var oldParent : Group = _parent;

				oldParent._children.splice(
					oldParent.getChildIndex(this),
					1
				);

				parent._numChildren--;
				oldParent.descendantRemoved.execute(oldParent, this);

				_parent = null;
				_removed.execute(this, oldParent);
			}

			// set parent
			_parent = value;

			// add child
			if (_parent)
			{
				_parent._children[_parent.numChildren] = this;
				_parent._numChildren++;
				_parent.descendantAdded.execute(_parent, this);

				_added.execute(this, _parent);
			}
		}

		public function get root() : ISceneNode
		{
			return _root;
		}

		public function get transform() : Matrix4x4
		{
			return _transform;
		}

		public function get localToWorld() : Matrix4x4
		{
			return _transformData.localToWorld;
		}

		public function get worldToLocal() : Matrix4x4
		{
			return _transformData.worldToLocal;
		}

		public function get added() : Signal
		{
			return _added;
		}

		public function get removed() : Signal
		{
			return _removed;
		}

		public function get addedToScene() : Signal
		{
			return _addedToScene;
		}

		public function get removedFromScene() : Signal
		{
			return _removedFromScene;
		}

		public function get numControllers() : uint
		{
			return _controllers.length;
		}

		public function get controllerAdded() : Signal
		{
			return _controllerAdded;
		}

		public function get controllerRemoved() : Signal
		{
			return _controllerRemoved;
		}

		protected function get transformData() : TransformDataProvider
		{
			return _transformData;
		}

		public function AbstractSceneNode()
		{
			initialize();
		}

		private function initialize() : void
		{
			_name = getDefaultSceneName(this);
			_root = this;

			_added.add(addedHandler);
			_removed.add(removedHandler);
			_addedToScene.add(addedToSceneHandler);
			_removedFromScene.add(removedFromSceneHandler);

			_transform.changed.add(transformChangedHandler);
		}

		protected function addedHandler(child : ISceneNode, parent : Group) : void
		{
			_root = _parent ? _parent.root : this;
			if (_root is Scene)
				_addedToScene.execute(this, _root);

			if (child === this)
			{
				_parent.localToWorld.changed.add(transformChangedHandler);
				transformChangedHandler(_parent.transform, null);
			}
		}

		protected function removedHandler(child : ISceneNode, parent : Group) : void
		{
			// update root
			var oldRoot : ISceneNode = _root;

			_root = _parent ? _parent.root : this;
			if (oldRoot is Scene)
				_removedFromScene.execute(this, oldRoot);

			if (child === this)
				parent.localToWorld.changed.remove(transformChangedHandler);
		}

		protected function addedToSceneHandler(child : ISceneNode, scene : Scene) : void
		{
			// nothing
		}

		protected function removedFromSceneHandler(child : ISceneNode, scene : Scene) : void
		{
			// nothing
		}

		protected function transformChangedHandler(transform	: Matrix4x4,
												   propertyName	: String) : void
		{
			if (_parent)
			{
				localToWorld.lock()
					.copyFrom(_transform)
					.append(_parent.localToWorld)
					.unlock();
			}
			else
				localToWorld.copyFrom(_transform);

			worldToLocal.lock()
				.copyFrom(localToWorld)
				.invert()
				.unlock();
		}

		public function addController(controller : AbstractController) : ISceneNode
		{
			_controllers.push(controller);

			controller.addTarget(this);
			_controllerAdded.execute(this, controller);

			return this;
		}

		public function removeController(controller : AbstractController) : ISceneNode
		{
			var numControllers	: uint = _controllers.length - 1;

			_controllers[_controllers.indexOf(controller)] = _controllers[numControllers];
			_controllers.length = numControllers;

			controller.removeTarget(this);
			_controllerRemoved.execute(this, controller);

			return this;
		}

		public function removeAllControllers() : ISceneNode
		{
			while (numControllers)
				removeController(getController(0));

			return this;
		}

		public function getController(index : uint) : AbstractController
		{
			return _controllers[index];
		}

		public function getControllersByType(type			: Class,
											 controllers	: Vector.<AbstractController> = null) : Vector.<AbstractController>
		{
			controllers ||= new Vector.<AbstractController>();

			var nbControllers : uint = numControllers;

			for (var i : int = 0; i < nbControllers; ++i)
			{
				var ctrl 	: AbstractController	= getController(i);

				if (ctrl is type)
					controllers.push(ctrl);
			}

			return controllers;
		}

		public static function getDefaultSceneName(scene : ISceneNode) : String
		{
			var className : String = getQualifiedClassName(scene);

			return className.substr(className.lastIndexOf(':') + 1)
				   + '_' + (++_id);
		}

		public function clone(cloneControllers : Boolean = false) : ISceneNode
		{
			throw new Error('The method AbstractSceneNod.clone() must be overriden.');
		}

		protected function copyControllersFrom(source 			: ISceneNode,
											   target			: ISceneNode,
											   cloneControllers	: Boolean) : void
		{
			var numControllers : uint = target.numControllers;

			while (numControllers)
				target.removeController(target.getController(--numControllers));

			numControllers = source.numControllers;
			for (var controllerId : uint = 0; controllerId < numControllers; ++controllerId)
			{
				var controller : AbstractController = source.getController(controllerId);

				if (cloneControllers)
					controller = controller.clone();

				target.addController(controller);
			}
		}
	}
}

package aerys.minko.scene.node
{
	import aerys.minko.ns.minko_scene;
	import aerys.minko.scene.controller.AbstractController;
	import aerys.minko.scene.data.TransformDataProvider;
	import aerys.minko.type.Signal;
	import aerys.minko.type.math.Matrix4x4;

	import flash.utils.getQualifiedClassName;

	/**
	 * The base class to extend in order to create new scene node types.
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class AbstractSceneNode implements ISceneNode
	{
		use namespace minko_scene;

		private static var _id			: uint							= 0;

		private var _name				: String						= null;
		private var _root				: ISceneNode					= null;
		private var _parent				: Group							= null;

		private var _transformData		: TransformDataProvider			= new TransformDataProvider();
		private var _transform			: Matrix4x4						= new Matrix4x4();

		private var _controllers		: Vector.<AbstractController>	= new <AbstractController>[];

		private var _added				: Signal						= new Signal('AbstractSceneNode.added');
		private var _removed			: Signal						= new Signal('AbstractSceneNode.removed');
		private var _addedToScene		: Signal						= new Signal('AbstractSceneNode.addedToScene');
		private var _removedFromScene	: Signal						= new Signal('AbstractSceneNode.removedFromScene');
		private var _controllerAdded	: Signal						= new Signal('AbstractSceneNode.controllerAdded');
		private var _controllerRemoved	: Signal						= new Signal('AbstractSceneNode.controllerRemoved');

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

		public function get parent() : Group
		{
			return _parent;
		}
		public function set parent(value : Group) : void
		{
			// remove child
			if (_parent)
			{
				var oldParent : Group = _parent;

				oldParent._children.splice(
					oldParent.getChildIndex(this),
					1
				);

				parent._numChildren--;
				oldParent.descendantRemoved.execute(oldParent, this);

				_parent = null;
				_removed.execute(this, oldParent);
			}

			// set parent
			_parent = value;

			// add child
			if (_parent)
			{
				_parent._children[_parent.numChildren] = this;
				_parent._numChildren++;
				_parent.descendantAdded.execute(_parent, this);

				_added.execute(this, _parent);
			}
		}

		public function get root() : ISceneNode
		{
			return _root;
		}

		public function get transform() : Matrix4x4
		{
			return _transform;
		}

		public function get localToWorld() : Matrix4x4
		{
			return _transformData.localToWorld;
		}

		public function get worldToLocal() : Matrix4x4
		{
			return _transformData.worldToLocal;
		}

		public function get added() : Signal
		{
			return _added;
		}

		public function get removed() : Signal
		{
			return _removed;
		}

		public function get addedToScene() : Signal
		{
			return _addedToScene;
		}

		public function get removedFromScene() : Signal
		{
			return _removedFromScene;
		}

		public function get numControllers() : uint
		{
			return _controllers.length;
		}

		public function get controllerAdded() : Signal
		{
			return _controllerAdded;
		}

		public function get controllerRemoved() : Signal
		{
			return _controllerRemoved;
		}

		protected function get transformData() : TransformDataProvider
		{
			return _transformData;
		}

		public function AbstractSceneNode()
		{
			initialize();
		}

		private function initialize() : void
		{
			_name = getDefaultSceneName(this);
			_root = this;

			_added.add(addedHandler);
			_removed.add(removedHandler);
			_addedToScene.add(addedToSceneHandler);
			_removedFromScene.add(removedFromSceneHandler);

			_transform.changed.add(transformChangedHandler);
		}

		protected function addedHandler(child : ISceneNode, parent : Group) : void
		{
			_root = _parent ? _parent.root : this;
			if (_root is Scene)
				_addedToScene.execute(this, _root);

			if (child === this)
			{
				_parent.localToWorld.changed.add(transformChangedHandler);
				transformChangedHandler(_parent.transform, null);
			}
		}

		protected function removedHandler(child : ISceneNode, parent : Group) : void
		{
			// update root
			var oldRoot : ISceneNode = _root;

			_root = _parent ? _parent.root : this;
			if (oldRoot is Scene)
				_removedFromScene.execute(this, oldRoot);

			if (child === this)
				parent.localToWorld.changed.remove(transformChangedHandler);
		}

		protected function addedToSceneHandler(child : ISceneNode, scene : Scene) : void
		{
			// nothing
		}

		protected function removedFromSceneHandler(child : ISceneNode, scene : Scene) : void
		{
			// nothing
		}

		protected function transformChangedHandler(transform	: Matrix4x4,
												   propertyName	: String) : void
		{
			if (_parent)
			{
				localToWorld.lock()
					.copyFrom(_transform)
					.append(_parent.localToWorld)
					.unlock();
			}
			else
				localToWorld.copyFrom(_transform);

			worldToLocal.lock()
				.copyFrom(localToWorld)
				.invert()
				.unlock();
		}

		public function addController(controller : AbstractController) : ISceneNode
		{
			_controllers.push(controller);

			controller.addTarget(this);
			_controllerAdded.execute(this, controller);

			return this;
		}

		public function removeController(controller : AbstractController) : ISceneNode
		{
			var numControllers	: uint = _controllers.length - 1;

			_controllers[_controllers.indexOf(controller)] = _controllers[numControllers];
			_controllers.length = numControllers;

			controller.removeTarget(this);
			_controllerRemoved.execute(this, controller);

			return this;
		}

		public function removeAllControllers() : ISceneNode
		{
			while (numControllers)
				removeController(getController(0));

			return this;
		}

		public function getController(index : uint) : AbstractController
		{
			return _controllers[index];
		}

		public function getControllersByType(type			: Class,
											 controllers	: Vector.<AbstractController> = null) : Vector.<AbstractController>
		{
			controllers ||= new Vector.<AbstractController>();

			var nbControllers : uint = numControllers;

			for (var i : int = 0; i < nbControllers; ++i)
			{
				var ctrl 	: AbstractController	= getController(i);

				if (ctrl is type)
					controllers.push(ctrl);
			}

			return controllers;
		}

		public static function getDefaultSceneName(scene : ISceneNode) : String
		{
			var className : String = getQualifiedClassName(scene);

			return className.substr(className.lastIndexOf(':') + 1)
				   + '_' + (++_id);
		}

		public function clone(cloneControllers : Boolean = false) : ISceneNode
		{
			throw new Error('The method AbstractSceneNode.clone() must be overriden.');
		}

		protected function copyControllersFrom(source 			: ISceneNode,
											   target			: ISceneNode,
											   cloneControllers	: Boolean) : void
		{
			var numControllers : uint = target.numControllers;

			while (numControllers)
				target.removeController(target.getController(--numControllers));

			numControllers = source.numControllers;
			for (var controllerId : uint = 0; controllerId < numControllers; ++controllerId)
			{
				var controller : AbstractController = source.getController(controllerId);

				if (cloneControllers)
					controller = controller.clone();

				target.addController(controller);
			}
		}
	}
}

Fix typo.

Fix typo.