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

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

    use namespace minko_math;

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

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

        private var _target					: ISceneNode;

        private var _invalidList			: Boolean;

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

        public function TransformController()
        {
            super();

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

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

            if (_transforms.length)
                updateLocalToWorld();
        }

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

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

                rootLocalToWorld.copyFrom(rootTransform);

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

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

                rootTransform._hasChanged = false;
                rootFlags = (rootFlags | FLAG_INIT_LOCAL_TO_WORLD) & ~FLAG_INIT_WORLD_TO_LOCAL;

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

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

                    rootWorldToLocal.copyFrom(rootLocalToWorld).invert();
                    rootFlags |= FLAG_INIT_WORLD_TO_LOCAL;

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

                _flags[nodeId] = rootFlags;

                var localToWorldTransformChanged : Signal = root.localToWorldTransformChanged;

                if (root.localToWorldTransformChanged.enabled)
                    root.localToWorldTransformChanged.execute(root, rootLocalToWorld);
            }
        }

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

            updateRootLocalToWorld(nodeId);

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

                localToWorld._hasChanged = false;

                if (lastChildId > subtreeMax)
                    subtreeMax = lastChildId;

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

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

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

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

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

                        childTransform._hasChanged = false;
                        childFlags = (childFlags | FLAG_INIT_LOCAL_TO_WORLD)
                            & ~FLAG_INIT_WORLD_TO_LOCAL;

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

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

                            childWorldToLocal.copyFrom(childLocalToWorld).invert();
                            childFlags |= FLAG_INIT_WORLD_TO_LOCAL;

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

                        _flags[childId] = childFlags;

                        var localToWorldTransformChanged : Signal = child.localToWorldTransformChanged;

                        if (localToWorldTransformChanged.enabled)
                            localToWorldTransformChanged.execute(child, childLocalToWorld);
                    }
                }

				++nodeId;
            }
        }

        private function updateLocalToWorldPath(path : Vector.<uint> = null) : void
        {
            var numNodes 	    : uint 	    = path.length;
            var nodeId          : uint      = path[uint(numNodes - 1)];
            var localToWorld 	: Matrix4x4	= _localToWorldTransforms[nodeId];

            updateRootLocalToWorld(nodeId);

            for (var i : int = numNodes - 2; i >= 0; --i)
            {
                var childId             : uint          = path[i];
                var childTransform		: Matrix4x4		= _transforms[childId];
                var childLocalToWorld	: Matrix4x4		= _localToWorldTransforms[childId];
                var childFlags          : uint          = _flags[childId];
                var child	            : ISceneNode	= _idToNode[childId];

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

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

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

                childTransform._hasChanged = false;
                childFlags = (childFlags | FLAG_INIT_LOCAL_TO_WORLD)
                    & ~FLAG_INIT_WORLD_TO_LOCAL;

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

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

                    childWorldToLocal.copyFrom(childLocalToWorld).invert();
                    childFlags |= FLAG_INIT_WORLD_TO_LOCAL;

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

                _flags[childId] = childFlags;

                var localToWorldTransformChanged : Signal = child.localToWorldTransformChanged;

                if (localToWorldTransformChanged.enabled)
                    localToWorldTransformChanged.execute(child, childLocalToWorld);

                localToWorld = childLocalToWorld;
            }
        }

        private function updateAncestorsAndSelfLocalToWorld(nodeId : uint) : void
        {
            var dirtyRoot   : int           = -1;
            var tmpNodeId   : int           = nodeId;
            var path        : Vector.<uint> = new <uint>[];
            var numNodes    : uint          = 0;

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

                path[numNodes] = tmpNodeId;
                ++numNodes;

                tmpNodeId = _parentId[tmpNodeId];
            }

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

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

            _target = target;
            _invalidList = true;

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

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

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

            target.added.add(addedHandler);
        }

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

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

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

            _invalidList = false;
            _target = null;

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

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

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

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

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

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

            return _nodeToId[node];
        }

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

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

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

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

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

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

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

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

                ++nodeId;
            }

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

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

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            return _localToWorldTransforms[nodeId];
        }

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

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

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            var flags : uint = _flags[nodeId];

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

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

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

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

            return worldToLocalTransform;
        }

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

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

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

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

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

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

    use namespace minko_math;

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

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

        private var _target					: ISceneNode;

        private var _invalidList			: Boolean;

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

        public function TransformController()
        {
            super();

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

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

            if (_transforms.length)
                updateLocalToWorld();
        }

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

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

                rootLocalToWorld.copyFrom(rootTransform);

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

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

                rootTransform._hasChanged = false;
                rootFlags = (rootFlags | FLAG_INIT_LOCAL_TO_WORLD) & ~FLAG_INIT_WORLD_TO_LOCAL;

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

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

                    rootWorldToLocal.copyFrom(rootLocalToWorld).invert();
                    rootFlags |= FLAG_INIT_WORLD_TO_LOCAL;

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

                _flags[nodeId] = rootFlags;

                var localToWorldTransformChanged : Signal = root.localToWorldTransformChanged;

                if (root.localToWorldTransformChanged.enabled)
                    root.localToWorldTransformChanged.execute(root, rootLocalToWorld);
            }
        }

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

            updateRootLocalToWorld(nodeId);

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

                localToWorld._hasChanged = false;

                if (lastChildId > subtreeMax)
                    subtreeMax = lastChildId;

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

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

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

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

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

                        childTransform._hasChanged = false;
                        childFlags = (childFlags | FLAG_INIT_LOCAL_TO_WORLD)
                            & ~FLAG_INIT_WORLD_TO_LOCAL;

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

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

                            childWorldToLocal.copyFrom(childLocalToWorld).invert();
                            childFlags |= FLAG_INIT_WORLD_TO_LOCAL;

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

                        _flags[childId] = childFlags;

                        var localToWorldTransformChanged : Signal = child.localToWorldTransformChanged;

                        if (localToWorldTransformChanged.enabled)
                            localToWorldTransformChanged.execute(child, childLocalToWorld);
                    }
                }

				++nodeId;
            }
        }

        private function updateLocalToWorldPath(path : Vector.<uint> = null) : void
        {
            var numNodes 	    : uint 	    = path.length;
            var nodeId          : uint      = path[uint(numNodes - 1)];
            var localToWorld 	: Matrix4x4	= _localToWorldTransforms[nodeId];

            updateRootLocalToWorld(nodeId);

            for (var i : int = numNodes - 2; i >= 0; --i)
            {
                var childId             : uint          = path[i];
                var childTransform		: Matrix4x4		= _transforms[childId];
                var childLocalToWorld	: Matrix4x4		= _localToWorldTransforms[childId];
                var childFlags          : uint          = _flags[childId];
                var child	            : ISceneNode	= _idToNode[childId];

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

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

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

                childTransform._hasChanged = false;
                childFlags = (childFlags | FLAG_INIT_LOCAL_TO_WORLD)
                    & ~FLAG_INIT_WORLD_TO_LOCAL;

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

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

                    childWorldToLocal.copyFrom(childLocalToWorld).invert();
                    childFlags |= FLAG_INIT_WORLD_TO_LOCAL;

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

                _flags[childId] = childFlags;

                var localToWorldTransformChanged : Signal = child.localToWorldTransformChanged;

                if (localToWorldTransformChanged.enabled)
                    localToWorldTransformChanged.execute(child, childLocalToWorld);

                localToWorld = childLocalToWorld;
            }
        }

        private function updateAncestorsAndSelfLocalToWorld(nodeId : uint) : void
        {
            var dirtyRoot   : int           = -1;
            var tmpNodeId   : int           = nodeId;
            var path        : Vector.<uint> = new <uint>[];
            var numNodes    : uint          = 0;

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

                path[numNodes] = tmpNodeId;
                ++numNodes;

                tmpNodeId = _parentId[tmpNodeId];
            }

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

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

            _target = target;
            _invalidList = true;

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

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

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

            target.added.add(addedHandler);
        }

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

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

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

            _invalidList = false;
            _target = null;

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

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

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

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

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

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

            return _nodeToId[node];
        }

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

            _nodeToId = new Dictionary(true);
            _transforms.length = 0;
            _flags.length = 0;
            _localToWorldTransforms.length = 0;
            _worldToLocalTransforms.length = 0;
            _numChildren.length = 0;
            _firstChildId.length = 0;
            _idToNode.length = 0;
            _parentId.length = 1;
            _parentId[0] = -1;

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

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

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

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

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

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

                ++nodeId;
            }

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

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

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            return _localToWorldTransforms[nodeId];
        }

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

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

            if (forceUpdate)
                updateAncestorsAndSelfLocalToWorld(nodeId);

            var flags : uint = _flags[nodeId];

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

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

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

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

            return worldToLocalTransform;
        }

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

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

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

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

fix TransformController.updateTransformList() to avoid instanciating new vectors and use Vector.length = 0 instead


package aerys.minko.render.shader.sprite
{
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.ShaderSettings;
	import aerys.minko.render.shader.part.DiffuseShaderPart;
	import aerys.minko.type.enum.Blending;
	import aerys.minko.type.enum.DepthTest;
	import aerys.minko.type.enum.SamplerFiltering;
	import aerys.minko.type.enum.SamplerMipMapping;
	import aerys.minko.type.enum.SamplerWrapping;

	public class SpriteShader extends Shader
	{
        private var _diffuse    : DiffuseShaderPart;

		private var _uv	        : SFloat;

		public function SpriteShader()
		{
			super();

            _diffuse = new DiffuseShaderPart(this);
		}

		override protected function initializeSettings(settings : ShaderSettings) : void
		{
//			settings.blending = Blending.ALPHA;
			settings.depthTest = DepthTest.LESS;
		}

		override protected function getVertexPosition() : SFloat
		{
			var vertexXY	: SFloat	= vertexXY.xy;

			_uv = multiply(add(vertexXY, 0.5), float2(1, -1));

			var depth	: SFloat 	= meshBindings.getParameter('depth', 1);

			var xy		: SFloat	= float2(
				meshBindings.getParameter('x', 1),
				meshBindings.getParameter('y', 1)
			);
			var size	: SFloat	= float2(
				meshBindings.getParameter('width', 1),
				meshBindings.getParameter('height', 1)
			);
			var vpSize	: SFloat	= float2(
				sceneBindings.getParameter('viewportWidth', 1),
				sceneBindings.getParameter('viewportHeight', 1)
			);

			xy = divide(add(xy, divide(size, 2)), vpSize);
			xy = multiply(subtract(xy, 0.5), float2(2, -2));

			vertexXY.scaleBy(divide(size, divide(vpSize, 2)));
			vertexXY.incrementBy(xy);

			return float4(vertexXY, depth, 1);
		}

		override protected function getPixelColor() : SFloat
		{
			return _diffuse.getDiffuseColor(true, _uv);
		}
	}
}

package aerys.minko.render.shader.sprite
{
	import aerys.minko.render.material.basic.BasicProperties;
	import aerys.minko.render.shader.SFloat;
	import aerys.minko.render.shader.Shader;
	import aerys.minko.render.shader.ShaderSettings;
	import aerys.minko.render.shader.part.DiffuseShaderPart;
	import aerys.minko.type.enum.Blending;
	import aerys.minko.type.enum.BlendingSource;
	import aerys.minko.type.enum.DepthTest;

	public class SpriteShader extends Shader
	{
        private var _diffuse    : DiffuseShaderPart;

		private var _uv	        : SFloat;

		public function SpriteShader()
		{
			super();

            _diffuse = new DiffuseShaderPart(this);
		}

		override protected function initializeSettings(settings : ShaderSettings) : void
		{
			var blending : uint = meshBindings.getProperty(
				BasicProperties.BLENDING, Blending.OPAQUE
			);
			settings.depthWriteEnabled = meshBindings.getProperty(
				BasicProperties.DEPTH_WRITE_ENABLED, true
			);
			settings.depthTest = meshBindings.getProperty(
				BasicProperties.DEPTH_TEST, DepthTest.LESS
			);

			if ((blending & 0xff) == BlendingSource.SOURCE_ALPHA)
			{
				settings.priority -= 0.5;
				settings.depthSortDrawCalls = true;
			}

			settings.blending			= blending;
			settings.enabled			= true;
			settings.scissorRectangle	= null;
		}

		override protected function getVertexPosition() : SFloat
		{
			var vertexXY	: SFloat	= vertexXY.xy;

			_uv = multiply(add(vertexXY, 0.5), float2(1, -1));

			var depth	: SFloat 	= meshBindings.getParameter('depth', 1);

			var xy		: SFloat	= float2(
				meshBindings.getParameter('x', 1),
				meshBindings.getParameter('y', 1)
			);
			var size	: SFloat	= float2(
				meshBindings.getParameter('width', 1),
				meshBindings.getParameter('height', 1)
			);
			var vpSize	: SFloat	= float2(
				sceneBindings.getParameter('viewportWidth', 1),
				sceneBindings.getParameter('viewportHeight', 1)
			);

			xy = divide(add(xy, divide(size, 2)), vpSize);
			xy = multiply(subtract(xy, 0.5), float2(2, -2));

			vertexXY.scaleBy(divide(size, divide(vpSize, 2)));
			vertexXY.incrementBy(xy);

			return float4(vertexXY, depth, 1);
		}

		override protected function getPixelColor() : SFloat
		{
			return _diffuse.getDiffuseColor(true, _uv);
		}
	}
}

Add alpha properties on spriteShader

Add alpha properties on spriteShader


package {
	import net.flashpunk.Engine;
	import net.flashpunk.FP;
	import worlds.World1;

	public class Main extends Engine {
		public function Main() {
			super(800, 640, 100, false);
		}

		override public function init():void {
			FP.screen.color = 0xffffff;
			FP.world = new World1;
			FP.console.enable();
		}
	}
}

package {
	import net.flashpunk.Engine;
	import net.flashpunk.FP;
	import worlds.World1;

	public class Main extends Engine {
		public function Main() {
			super(800, 640, 100, false);
		}

		override public function init():void {
			FP.screen.color = 0xffffff;
			FP.world = new World1;

		}
	}
}

remove console

remove console


package aerys.minko.scene.controller
{
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;

	import flash.display.BitmapData;

	/**
	 * EnterFrameController are controllers triggered whenever the Scene.enterFrame
	 * signal is executed.
	 *
	 * The best way to
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class EnterFrameController extends AbstractController
	{
		private var _targetsInScene	: uint	= 0;

		public function EnterFrameController(targetType	: Class = null)
		{
			super(targetType);

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

		protected function targetAddedHandler(ctrl		: EnterFrameController,
											  target	: ISceneNode) : void
		{
			if (target.root is Scene)
				targetAddedToSceneHandler(target, target.root as Scene);
			else
				target.addedToScene.add(targetAddedToSceneHandler);
		}

		protected function targetRemovedHandler(ctrl	: EnterFrameController,
												target	: ISceneNode) : void
		{
			if (target.root is Scene)
				target.removedFromScene.remove(targetRemovedFromSceneHandler);
			else
				target.addedToScene.remove(targetAddedToSceneHandler);
		}

		protected function targetAddedToSceneHandler(target	: ISceneNode,
													 scene	: Scene) : void
		{
			if (target.addedToScene.hasCallback(targetAddedToSceneHandler))
				target.addedToScene.remove(targetAddedToSceneHandler);

			target.removedFromScene.add(targetRemovedFromSceneHandler);

			++_targetsInScene;

			if (_targetsInScene == 1)
				scene.enterFrame.add(sceneEnterFrameHandler);
		}

		protected function targetRemovedFromSceneHandler(target	: ISceneNode,
														 scene	: Scene) : void
		{
			--_targetsInScene;

			target.removedFromScene.remove(targetRemovedFromSceneHandler);
			target.addedToScene.add(targetAddedToSceneHandler);

			if (_targetsInScene == 0)
				scene.enterFrame.remove(sceneEnterFrameHandler);
		}

		protected function sceneEnterFrameHandler(scene			: Scene,
												  viewport		: Viewport,
												  destination	: BitmapData,
												  time			: Number) : void
		{
			throw new Error(
				'The method EnterFrameController.sceneEnterFrameHandler must be overriden.'
			);
		}
	}
}

package aerys.minko.scene.controller
{
	import aerys.minko.render.Viewport;
	import aerys.minko.scene.node.ISceneNode;
	import aerys.minko.scene.node.Scene;

	import flash.display.BitmapData;

	/**
	 * EnterFrameController are controllers triggered whenever the Scene.enterFrame
	 * signal is executed.
	 *
	 * The best way to
	 *
	 * @author Jean-Marc Le Roux
	 *
	 */
	public class EnterFrameController extends AbstractController
	{
		private var _targetsInScene	: uint	= 0;

		public function get numTargetsInScene() : uint
		{
			return _targetsInScene;
		}

		public function EnterFrameController(targetType	: Class = null)
		{
			super(targetType);

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

		protected function targetAddedHandler(ctrl		: EnterFrameController,
											  target	: ISceneNode) : void
		{
			if (target.root is Scene)
				targetAddedToSceneHandler(target, target.root as Scene);
			else
				target.addedToScene.add(targetAddedToSceneHandler);
		}

		protected function targetRemovedHandler(ctrl	: EnterFrameController,
												target	: ISceneNode) : void
		{
			if (target.root is Scene)
				target.removedFromScene.remove(targetRemovedFromSceneHandler);
			else
				target.addedToScene.remove(targetAddedToSceneHandler);
		}

		protected function targetAddedToSceneHandler(target	: ISceneNode,
													 scene	: Scene) : void
		{
			if (target.addedToScene.hasCallback(targetAddedToSceneHandler))
				target.addedToScene.remove(targetAddedToSceneHandler);

			target.removedFromScene.add(targetRemovedFromSceneHandler);

			++_targetsInScene;

			if (_targetsInScene == 1)
				scene.enterFrame.add(sceneEnterFrameHandler);
		}

		protected function targetRemovedFromSceneHandler(target	: ISceneNode,
														 scene	: Scene) : void
		{
			--_targetsInScene;

			target.removedFromScene.remove(targetRemovedFromSceneHandler);
			target.addedToScene.add(targetAddedToSceneHandler);

			if (_targetsInScene == 0)
				scene.enterFrame.remove(sceneEnterFrameHandler);
		}

		protected function sceneEnterFrameHandler(scene			: Scene,
												  viewport		: Viewport,
												  destination	: BitmapData,
												  time			: Number) : void
		{
			throw new Error(
				'The method EnterFrameController.sceneEnterFrameHandler must be overriden.'
			);
		}
	}
}

add EnterFrameController.numTargetsInScene property


////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mybeads
{
	import flash.display.DisplayObject;

	import org.apache.flex.core.IBead;
	import org.apache.flex.core.IDataGridModel;
	import org.apache.flex.core.IItemRenderer;
	import org.apache.flex.core.IItemRendererParent;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.core.UIBase;
	import org.apache.flex.events.Event;
	import org.apache.flex.events.IEventDispatcher;
	import org.apache.flex.html.List;
	import org.apache.flex.html.beads.DataGridView;
	import org.apache.flex.html.beads.IListView;

	public class RowHeightBead implements IBead
	{
		public function RowHeightBead()
		{
		}

		private var _strand:IStrand;
		public function set strand(value:IStrand):void
		{
			_strand = value;

			IEventDispatcher(_strand).addEventListener("layoutComplete",handleLayoutComplete);
		}

		private var _rowHeight:Number = 30;
		public function get rowHeight():Number
		{
			return _rowHeight;
		}
		public function set rowHeight(value:Number):void
		{
			_rowHeight = value;
			// dispatch some event which will trigger layout recalculation
		}

		private var _minRowHeight:Number = 30;
		public function get minRowHeight():Number
		{
			return _minRowHeight;
		}
		public function set minRowHeight(value:Number):void
		{
			_minRowHeight = value;
			// dispatch some event which will trigger layout recalculation
		}

		private var _variableRowHeight:Boolean = false;
		public function get variableRowHeight():Boolean
		{
			return _variableRowHeight;
		}
		public function set variableRowHeight(value:Boolean):void
		{
			_variableRowHeight = value;
			// dispatch some event which will trigger a layout recalculation
		}

		private function handleLayoutComplete(event:Event):void
		{
			if (variableRowHeight) {
				makeAllRowsVariableHeight(minRowHeight);
			}
			else {
				makeAllRowsSameHeight(rowHeight);
			}
		}

		private function makeAllRowsSameHeight(newHeight:Number):void
		{
			// this function forces every cell in the DataGrid to be the same height
			var view:DataGridView = _strand.getBeadByType(DataGridView) as DataGridView;
			var lists:Array = view.getColumnLists();

			for(var i:int=0; i < lists.length; i++)
			{
				var list:List = lists[i] as List;
				var listView:IListView = list.getBeadByType(IListView) as IListView;
				var p:IItemRendererParent = listView.dataGroup;
				var n:Number = (list.dataProvider as Array).length;

				for(var j:int=0; j < n; j++)
				{
					var c:UIBase = p.getItemRendererForIndex(j) as UIBase;
					c.height = newHeight;
				}

				IEventDispatcher(list).dispatchEvent( new Event("layoutNeeded") );
			}
		}

		private function makeAllRowsVariableHeight(minHeight:Number):void
		{
			// this function makes every cell in a row the same height
			// (at least minHeight) but all the rows can have different
			// heights
			var view:DataGridView = _strand.getBeadByType(DataGridView) as DataGridView;
			var lists:Array = view.getColumnLists();

			// future: maybe IDataGridModel.dataProvider should implement IDataProvider which
			// can have a length property and not assume that the .dataProvider is an Array.
			var n:Number = ((_strand.getBeadByType(IDataGridModel) as IDataGridModel).dataProvider as Array).length;

			for(var i:int=0; i < n; i++)
			{
				var maxHeight:Number = minHeight;
				for(var j:int=0; j < lists.length; j++)
				{
					var list:List = lists[j] as List;
					var listView:IListView = list.getBeadByType(IListView) as IListView;
					var p:IItemRendererParent = listView.dataGroup;
					var c:UIBase = p.getItemRendererForIndex(i) as UIBase;
					maxHeight = Math.max(maxHeight,c.height);
				}
				for(j=0; j < lists.length; j++)
				{
					list = lists[j] as List;
					listView = list.getBeadByType(IListView) as IListView;
					p = listView.dataGroup;
					c = p.getItemRendererForIndex(i) as UIBase;
					c.height = maxHeight;
				}
			}

			for(j=0; j < lists.length; j++)
			{
				list = lists[j] as List;
				IEventDispatcher(list).dispatchEvent( new Event("layoutNeeded") );
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package mybeads
{

	import org.apache.flex.core.IBead;
	import org.apache.flex.core.IDataGridModel;
	import org.apache.flex.core.IItemRendererParent;
	import org.apache.flex.core.IStrand;
	import org.apache.flex.core.UIBase;
	import org.apache.flex.events.Event;
	import org.apache.flex.events.IEventDispatcher;
	import org.apache.flex.html.List;
	import org.apache.flex.html.beads.DataGridView;
	import org.apache.flex.html.beads.IListView;

	public class RowHeightBead implements IBead
	{
		public function RowHeightBead()
		{
		}

		private var _strand:IStrand;
		public function set strand(value:IStrand):void
		{
			_strand = value;

			IEventDispatcher(_strand).addEventListener("layoutComplete",handleLayoutComplete);
		}

		private var _rowHeight:Number = 30;
		public function get rowHeight():Number
		{
			return _rowHeight;
		}
		public function set rowHeight(value:Number):void
		{
			_rowHeight = value;
			// dispatch some event which will trigger layout recalculation
		}

		private var _minRowHeight:Number = 30;
		public function get minRowHeight():Number
		{
			return _minRowHeight;
		}
		public function set minRowHeight(value:Number):void
		{
			_minRowHeight = value;
			// dispatch some event which will trigger layout recalculation
		}

		private var _variableRowHeight:Boolean = false;
		public function get variableRowHeight():Boolean
		{
			return _variableRowHeight;
		}
		public function set variableRowHeight(value:Boolean):void
		{
			_variableRowHeight = value;
			// dispatch some event which will trigger a layout recalculation
		}

		private function handleLayoutComplete(event:Event):void
		{
			if (variableRowHeight) {
				makeAllRowsVariableHeight(minRowHeight);
			}
			else {
				makeAllRowsSameHeight(rowHeight);
			}
		}

		private function makeAllRowsSameHeight(newHeight:Number):void
		{
			// this function forces every cell in the DataGrid to be the same height
			var view:DataGridView = _strand.getBeadByType(DataGridView) as DataGridView;
			var lists:Array = view.getColumnLists();

			for(var i:int=0; i < lists.length; i++)
			{
				var list:List = lists[i] as List;
				var listView:IListView = list.getBeadByType(IListView) as IListView;
				var p:IItemRendererParent = listView.dataGroup;
				var n:Number = (list.dataProvider as Array).length;

				for(var j:int=0; j < n; j++)
				{
					var c:UIBase = p.getItemRendererForIndex(j) as UIBase;
					c.height = newHeight;
				}

				IEventDispatcher(list).dispatchEvent( new Event("layoutNeeded") );
			}
		}

		private function makeAllRowsVariableHeight(minHeight:Number):void
		{
			// this function makes every cell in a row the same height
			// (at least minHeight) but all the rows can have different
			// heights
			var view:DataGridView = _strand.getBeadByType(DataGridView) as DataGridView;
			var lists:Array = view.getColumnLists();

			// future: maybe IDataGridModel.dataProvider should implement IDataProvider which
			// can have a length property and not assume that the .dataProvider is an Array.
			var n:Number = ((_strand.getBeadByType(IDataGridModel) as IDataGridModel).dataProvider as Array).length;

			for(var i:int=0; i < n; i++)
			{
				var maxHeight:Number = minHeight;
				for(var j:int=0; j < lists.length; j++)
				{
					var list:List = lists[j] as List;
					var listView:IListView = list.getBeadByType(IListView) as IListView;
					var p:IItemRendererParent = listView.dataGroup;
					var c:UIBase = p.getItemRendererForIndex(i) as UIBase;
					maxHeight = Math.max(maxHeight,c.height);
				}
				for(j=0; j < lists.length; j++)
				{
					list = lists[j] as List;
					listView = list.getBeadByType(IListView) as IListView;
					p = listView.dataGroup;
					c = p.getItemRendererForIndex(i) as UIBase;
					c.height = maxHeight;
				}
			}

			for(j=0; j < lists.length; j++)
			{
				list = lists[j] as List;
				IEventDispatcher(list).dispatchEvent( new Event("layoutNeeded") );
			}
		}
	}
}

Remove unused imports

Remove unused imports


/* -*- c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine.].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

// JSON according to the ECMAScript 5 specification.
// Based on ECMA-262-5 and Errata as of 31 July 2010 (no JSON errata at that time).
//
// Deliberate deviations from the specification
//  - There are additional type checks on JSON.parse and JSON.stringify - that fits AS3 better.
//  - Property enumeration order is indeterminate in AS3, so there is no attempt to extract
//    keys in any particular order when enumerating properties during filtering.
//
// Compiling it
//  - This code must be compilable with -strict.
//  - The code ought to work even if compiled with -AS3 because public:: qualifiers are
//    used where they matter.

package
{

    include "api-versions.as"

    [API(CONFIG::SWF_13)]
    [native(cls="JSONClass", classgc="exact", methods="auto", construct="none")]
    public final class JSON
    {
        private static const as3ns:Namespace = AS3;

        private native
        static function parseCore(text:String):Object;

        // specialized stringify procedure
        private native
        static function stringifySpecializedToString(value:Object,
                                                     replacerArray:Array,
                                                     replacerFunction:Function,
                                                     gap:String):String;

        public static function parse(text:String,
                                     reviver:Function = null):Object
        {
            if (text === null || text === undefined) {
                Error.throwError( SyntaxError, 1132 /*kJSONInvalidParseInput*/ );
            }

            // at this point, we should be assured that text is String.
            var unfiltered: Object = parseCore(text);

            if (reviver === null)
                return unfiltered;
            return (new Walker(reviver)).walk({ "": unfiltered }, "");
        }

        public static function stringify(value:Object,
                                         replacer=null,
                                         space=null):String
        {
            // We deliberately deviate from ECMA-262 and throw on
            // invalid replacer parameter.
            if (!(replacer === null || replacer is Function || replacer is Array))
                Error.throwError( TypeError, 1131 /*kJSONInvalidReplacer*/ );

            // We follow ECMA-262 and silently ignore invalid space parameter.
            if (!(space === null || space is String || space is Number))
                space = null;

            var gap = "";

            if (space is String)
                gap = space.length > 10 ? space.AS3::substring(0,10) : space;
            else if (space is Number)
                gap = "          ".AS3::substring(0,Math.min(10,Math.floor(space)));

            if (replacer === null) {
                return stringifySpecializedToString(value, null, null, gap);
            } else if (replacer is Array) {
                return stringifySpecializedToString(value, computePropertyList(replacer), null, gap);
            } else { // replacer is Function
                return stringifySpecializedToString(value, null, replacer, gap);
            }
        }

        // ECMA-262 5th ed, section 15.12.3 stringify, step 4.b
        private static function computePropertyList(r:Array):Array {
            var propertyList = [];
            var alreadyAdded = {};
            for (var i:uint=0, ilim:uint = r.length; i < ilim; i++) {
                if (!r.AS3::hasOwnProperty(i))
                    continue;
                var v = r[i];
                var item: String = null;
                if (v is String)
                    item = v;
                else if (v is Number)
                    item = String(v);
                if (item !== null && !alreadyAdded[item]) {
                    alreadyAdded[item] = true;
                    propertyList[propertyList.length] = item;
                }
            }
            return propertyList;
        }
    }

    internal final class Walker
    {
        function Walker(reviver:Function) {
            this.reviver = reviver;
        }

        function walk(holder:Object, name:String):* {
            var val:Object = holder[name];
            if (val is Array) {
                var v:Array = val as Array;
                for (var i:uint=0, limit:uint=v.length; i < limit; i++) {
                    var newElement:* = walk(v, String(i));
                    if (newElement === undefined)
                        delete v[i];
                    else
                        v[i] = newElement;
                }
            }
            else if (val !== null && !(val is Boolean) && !(val is Number) && !(val is String)) {

                //
                // See earlier note about unsafe for-in enumeration in AS3.

                for (var p:String in val) {
                    if (!val.AS3::hasOwnProperty(p))
                        break;
                    var newElement:* = walk(val, p);
                    if (newElement === undefined)
                        delete val[p];
                    else
                        val[p] = newElement;
                }
            }
            return reviver.AS3::call(holder, name, val);
        }

        var reviver:Function;
    }
}

/* -*- c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine.].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

// JSON according to the ECMAScript 5 specification.
// Based on ECMA-262-5 and Errata as of 31 July 2010 (no JSON errata at that time).
//
// Deliberate deviations from the specification
//  - There are additional type checks on JSON.parse and JSON.stringify - that fits AS3 better.
//  - Property enumeration order is indeterminate in AS3, so there is no attempt to extract
//    keys in any particular order when enumerating properties during filtering.
//
// Compiling it
//  - This code must be compilable with -strict.
//  - The code ought to work even if compiled with -AS3 because public:: qualifiers are
//    used where they matter.

package
{

    include "api-versions.as"
    /**
    * The JSON class lets developers import and export data to and from JavaScript Object Notation (JSON) format. JSON is an industry standard data-interchange
    * format that is described at http://www.json.org.
    *
    *
This class supports most ActionScript classes. ActionScript classes that do not support JSON typically return their class name as a string.

    * @playerversion Flash 11
    * @playerversion AIR 3.0
    * @langversion 3.0
    */

    [API(CONFIG::SWF_13)]
    [native(cls="JSONClass", classgc="exact", methods="auto", construct="none")]
    public final class JSON
    {
        private static const as3ns:Namespace = AS3;

        private native
        static function parseCore(text:String):Object;

        // specialized stringify procedure
        private native
        static function stringifySpecializedToString(value:Object,
                                                     replacerArray:Array,
                                                     replacerFunction:Function,
                                                     gap:String):String;

        /**
        * Accepts a JSON-formatted String and returns an Actionscript Object that represents that value. JSON objects,
        * arrays, strings, numbers, booleans, and null map to corresponding Actionscript values, as shown below:
        *

        *

        *
JSON type	ActionScript type

        *
array	Array

        *
string	String

        *
number	Number

        *
boolean	Boolean

        *
null	null

        *

        *

        *
        *
The reviver parameter is a function that takes two parameters: a key and a value. You can use this
        * function to transform or filter each key/value pair as it is parsed. If you supply a reviver function, your transformed
        * or filtered value for each pair, rather than the default parsing, is returned in the parse() function output. If
        * the reviver function returns undefined for any pair, the property is deleted from the final result.
        *

        *
        *
If the parse() function encounters duplicate name strings within the object being parsed, the value of the duplicate
        * key encountered last becomes the value for all preceding occurrences of that key.
        *

        *
        * @param text The JSON string to be parsed
        * @param reviver (Optional) A function that transforms each key/value pair that is parsed
        (
        * @playerversion Flash 11
        * @playerversion AIR 3.0
        * @langversion 3.0
        */
        public static function parse(text:String,
                                     reviver:Function = null):Object
        {
            if (text === null || text === undefined) {
                Error.throwError( SyntaxError, 1132 /*kJSONInvalidParseInput*/ );
            }

            // at this point, we should be assured that text is String.
            var unfiltered: Object = parseCore(text);

            if (reviver === null)
                return unfiltered;
            return (new Walker(reviver)).walk({ "": unfiltered }, "");
        }

        /**
        * Returns a String, in JSON format, that represents an Actionscript value. The stringify method can take three parameters.
        *
        *
The value parameter is required. This parameter is an Actionscript value. Typically, it is an Object or Array,
        * but it can also be a String, Boolean, Number, or null.
        *

        *
        *
The optional replacer parameter can be either a function or an array of strings or numbers. If it is a function,
        * the function takes two parameters: a key and a value. You can use this
        * function to transform or filter each key/value pair as it is parsed. If you supply a replacer function, your transformed
        * or filtered value for each pair, rather than the default parsing, is returned in the parse() function output. If
        * the replacer function returns undefined for any pair, the property is deleted from the final result. If replacer
        * is an array, it is used as a filter for designating which properties should be included in the output.
        *

        *
        *
The optional space parameter is a String or Number that allows white space to be injected into the returned string to improve human readability.
        * Entries in generated JSON objects and JSON arrays are separated by a gap derived from the space parameter. This gap is
        * always between 0 and 10 characters wide. If space is a string, then the derived gap is the first ten characters of that string. If space
        * is a non-negative number x, then the gap is x space characters, to a maximum of ten spaces.
        *

        *
        * @param value The ActionScript value to be converted into a JSON string
        * @param replacer (Optional) A function or an array that transforms or filters key/value pairs in the stringify output
        * @param space (Optional) A string or number that controls added white space in the returned String
        *
        * @playerversion Flash 11
        * @playerversion AIR 3.0
        * @langversion 3.0
        */
        public static function stringify(value:Object,
                                         replacer=null,
                                         space=null):String
        {
            // We deliberately deviate from ECMA-262 and throw on
            // invalid replacer parameter.
            if (!(replacer === null || replacer is Function || replacer is Array))
                Error.throwError( TypeError, 1131 /*kJSONInvalidReplacer*/ );

            // We follow ECMA-262 and silently ignore invalid space parameter.
            if (!(space === null || space is String || space is Number))
                space = null;

            var gap = "";

            if (space is String)
                gap = space.length > 10 ? space.AS3::substring(0,10) : space;
            else if (space is Number)
                gap = "          ".AS3::substring(0,Math.min(10,Math.floor(space)));

            if (replacer === null) {
                return stringifySpecializedToString(value, null, null, gap);
            } else if (replacer is Array) {
                return stringifySpecializedToString(value, computePropertyList(replacer), null, gap);
            } else { // replacer is Function
                return stringifySpecializedToString(value, null, replacer, gap);
            }
        }

        // ECMA-262 5th ed, section 15.12.3 stringify, step 4.b
        private static function computePropertyList(r:Array):Array {
            var propertyList = [];
            var alreadyAdded = {};
            for (var i:uint=0, ilim:uint = r.length; i < ilim; i++) {
                if (!r.AS3::hasOwnProperty(i))
                    continue;
                var v = r[i];
                var item: String = null;
                if (v is String)
                    item = v;
                else if (v is Number)
                    item = String(v);
                if (item !== null && !alreadyAdded[item]) {
                    alreadyAdded[item] = true;
                    propertyList[propertyList.length] = item;
                }
            }
            return propertyList;
        }
    }

    internal final class Walker
    {
        function Walker(reviver:Function) {
            this.reviver = reviver;
        }

        function walk(holder:Object, name:String):* {
            var val:Object = holder[name];
            if (val is Array) {
                var v:Array = val as Array;
                for (var i:uint=0, limit:uint=v.length; i < limit; i++) {
                    var newElement:* = walk(v, String(i));
                    if (newElement === undefined)
                        delete v[i];
                    else
                        v[i] = newElement;
                }
            }
            else if (val !== null && !(val is Boolean) && !(val is Number) && !(val is String)) {

                //
                // See earlier note about unsafe for-in enumeration in AS3.

                for (var p:String in val) {
                    if (!val.AS3::hasOwnProperty(p))
                        break;
                    var newElement:* = walk(val, p);
                    if (newElement === undefined)
                        delete val[p];
                    else
                        val[p] = newElement;
                }
            }
            return reviver.AS3::call(holder, name, val);
        }

        var reviver:Function;
    }
}

Update asdocs in JSON.as (no bug, r=stejohns)