In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

XML Accordion Tree

Simple XML based multilevel accordion tree
Get Adobe Flash player
by meix 29 Mar 2011
/**
 * Copyright meix ( http://wonderfl.net/user/meix )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/hnoY
 */

/*****************************************************************************************/
/* ACCORDION TREE CLASS                                        
/*****************************************************************************************/
package{
    
    import flash.display.*;
    import flash.text.*;
    import flash.events.*;
    import flash.geom.*;
    
    import gs.TweenLite;
    import gs.easing.Expo;
    import gs.plugins.TweenPlugin;
    import gs.plugins.TintPlugin;
    
    import idv.cjcat.signals.NativeSignal;
    

    public class AccordionTree extends Sprite {
        
        public static var ITEM_WIDTH:int = 260;
        public static var ITEM_HEIGHT:int = 25;
        
        private var menuSprite:Sprite;
        private var contractMask:Shape;
        private var expandMask:Shape;
        
        private var enterFrameSignal:NativeSignal;
        
        private var data:XML = new XML("<content><item>TreeItem 1</item><item>TreeItem 2</item><item>TreeItem 3<item>TreeItem 3-1</item><item>TreeItem 3-2</item><item>TreeItem 3-3</item><item>TreeItem 3-4</item><item>TreeItem 3-5</item><item>TreeItem 3-6</item><item>TreeItem 3-7</item><item>TreeItem 3-8</item><item>TreeItem 3-9</item><item>TreeItem 3-10</item><item>TreeItem 3-11</item></item><item>TreeItem 4</item><item>TreeItem 5<item>TreeItem 5-1<item>TreeItem 5-1-1</item><item>TreeItem 5-1-2</item><item>TreeItem 5-1-3<item>TreeItem 5-1-3-1</item></item><item>TreeItem 5-1-4<item>TreeItem 5-1-4-1</item><item>TreeItem 5-1-4-2</item><item>TreeItem 5-1-4-3<item>TreeItem 5-1-4-3-1</item><item>TreeItem 5-1-4-3-2</item></item><item>TreeItem 5-1-4-4</item></item><item>TreeItem 5-1-5</item></item><item>TreeItem 5-2</item></item><item>TreeItem 6</item><item>TreeItem 7</item><item>TreeItem 8<item>TreeItem 8-1</item><item>TreeItem 8-2</item><item>TreeItem 8-3</item><item>TreeItem 8-4</item></item><item>TreeItem 9</item></content>");
        private var items:Vector.<Object> = new Vector.<Object>();
        private var itemsCopy:Vector.<Object>;

        private var tweenDuration:Number = .5;
        private var tweenEase:Function = Expo.easeOut;
        
        private var selectedIndex:int = -1;
        
        public function AccordionTree(){
            TweenPlugin.activate([TintPlugin]);

            enterFrameSignal = new NativeSignal(this, Event.ENTER_FRAME);

            menuSprite = new Sprite();
            menuSprite.x = menuSprite.y = 10;
            addChild(menuSprite); 

            contractMask = getMask();
            expandMask = getMask();
    
            parse(data);
            initChildAndParentIndices();

            addChildren();

            contractAll();
        }
        
        private function getMask():Shape {
            var s:Shape = new Shape();
            s.graphics.beginFill(0xFF0000, 0);
            s.graphics.drawRect(0, 0, 10, 10);
            s.graphics.endFill();
            return s;
        }
        
        private function parse(node:XML, level:int = -1):void{
            var item:TreeItem = new TreeItem(node, level);
            item.width = ITEM_WIDTH; item.height = ITEM_HEIGHT;
            item.clickSignal.add(onItemClick);
            items.push( { sprite:item, level:level, tweenY:0, parentIndex:-1, childIndices:[], visible:Boolean } );
            node.item.(parse(valueOf(), level + 1));
        }
        
        private function addChildren():void {
            for each (var o:Object in items) {
                menuSprite.addChild(o.sprite);
            }
        }
        
        private function initChildAndParentIndices():void {
            var levelsObject:Object = { };
            var lastLevel:int = -1;
            for (var i:int = 0; i < items.length; i++) {
                var o:Object = items[i];
                if (lastLevel < o.level){
                    o.parentIndex = i - 1;
                    levelsObject["l" + o.level] = i;
                }else
                    o.parentIndex = levelsObject["l" + o.level] - 1;
                
                if(o.parentIndex){
                    var parentSprite:TreeItem = items[o.parentIndex].sprite as TreeItem;
                    if (parentSprite.hasChildren) items[o.parentIndex].childIndices.push(i);
                }
                
                lastLevel = o.level;
            }
        }

        private function draw():void {
            var iy:int = 0;
            for (var i:int = 0; i < items.length; i++) {
                var o:Object = items[i];
                o.tweenY = iy;
                var item:TreeItem = o.sprite;
                if (o.visible) {
                    item.visible = true;
                    TweenLite.to(item, tweenDuration, { alpha:1, y:iy, ease:tweenEase } ); // Tween in visible items
                    iy += ITEM_HEIGHT;
                }else {
                    TweenLite.to(item, tweenDuration, { alpha:1, y:iy, ease:tweenEase, onComplete:hideItemSprite, onCompleteParams:[i] } ); // Set visible to false after tween out
                }
            }
            tweenMasks();
            TweenLite.delayedCall(tweenDuration, afterTween);
        }

        private function afterTween():void {
            for each (var o:Object in items) o.sprite.mask = null;
            if(menuSprite.contains(contractMask)) menuSprite.removeChild(contractMask);
            if(menuSprite.contains(expandMask)) menuSprite.removeChild(expandMask);
        }
        
        private function tweenMasks():void {
            contractMask.width = expandMask.width = ITEM_WIDTH;
            menuSprite.addChild(contractMask); contractMask.y = 0; contractMask.height = 1;
            menuSprite.addChild(expandMask); expandMask.y = 0; expandMask.height = 1;
            
            var expandItems:Array = [];
            var contractItems:Array = [];

            if (itemsCopy) {    
                for (var i:int = 0; i < items.length; i++) {
                    if (items[i].visible != itemsCopy[i].visible) {
                        if (items[i].visible) {
                            expandItems.push(items[i]);
                            items[i].sprite.mask = expandMask;     // Mask expanding items
                        }else {
                            contractItems.push(items[i]);
                            items[i].sprite.mask = contractMask; // Mask contracting items
                        }
                    }
                }

                if (expandItems.length > 0) {    // Tween expand mask
                    expandMask.y = expandItems[0].sprite.y;
                    TweenLite.to(expandMask, tweenDuration, { y:expandItems[0].tweenY, height:expandItems.length * ITEM_HEIGHT, ease:tweenEase } );
                }
                if(contractItems.length>0){        // Tween contract mask
                    contractMask.y = contractItems[0].sprite.y;
                    contractMask.height = contractItems.length * ITEM_HEIGHT;
                    TweenLite.to(contractMask, tweenDuration, { y:contractItems[0].tweenY, height:0, ease:tweenEase } );
                }
            }
        }

        private function hideItemSprite(index:int):void {
            items[index].sprite.visible = false;
        }
        
        private function expandIndex(index:int):void {
            var o:Object = items[index];

            contractLevel(o.level);    // Contract other nodes at the same level
            
            if (o.level > 0) expandIndex(o.parentIndex);    // Expand parent nodes

            for (var i:int = 0; i < o.childIndices.length; i++)
                items[o.childIndices[i]].visible = true;    // Show children on node

            invalidate();
        }
        
        private function contractIndex(index:int):void {
            var o:Object = items[index];
            for (var i:int = 0; i < o.childIndices.length; i++) {    
                items[o.childIndices[i]].visible = false;
                contractIndex(o.childIndices[i]); // Contract all children
            }
            invalidate();
        }

        private function contractLevel(level:int):void {
            for (var i:int = 0; i < items.length; i++) {    
                if (items[i].level == level)
                    contractIndex(i);
            }
            invalidate();
        }
        
        private function contractAll():void {
            for each (var o:Object in items) {
                o.visible = o.level == 0 ? true : false;
            }
            invalidate();
        }
        
        private function deselectAll():void {
            for each (var o:Object in items) {
                o.sprite.selected = false;
            }
            selectedIndex = -1;
            invalidate();
        }
        
        private function selectIndex(index:int):void {
            deselectAll();
            items[index].sprite.selected = true;
            selectedIndex = index;
            expandIndex(index);
        }
        
        private function selectFirstLeaf(index:int):void {
            if (items[index].childIndices.length > 0) {
                var o:Object = items[index];
                var childIndex:int = o.childIndices[0];
                items[childIndex].childIndices.length > 0 ? selectFirstLeaf(childIndex) : selectIndex(childIndex);
            }else {
                selectIndex(index);
            }
        }

        private function copyItems():void {
            itemsCopy = new Vector.<Object>();
            for each (var o:Object in items) {
                itemsCopy.push({item:o.sprite, level:o.level, childIndices:o.childIndices, visible:o.visible } );
            }
        }
        
        private function invalidate():void{
            enterFrameSignal.addOnce(onInvalidate);
        }
        
        /*****************************************************************************************/
        /* EVENT HANDLERS                                            
        /*****************************************************************************************/
        
        private function onInvalidate(e:Event):void{
            enterFrameSignal.remove(onInvalidate);
            draw();
        }
        
        private function onItemClick(e:MouseEvent):void {
            copyItems();    // Copy current items props
            deselectAll();
            for (var i:int = 0; i < items.length; i++) {
                if (items[i].sprite == e.currentTarget) {
                    if (items[i].childIndices.length > 0) {
                        selectFirstLeaf(i); // Branch - open and select first leaf    
                    }else
                        selectIndex(i);        // Leaf - set selected
                }
            }
        }
    }
}    

/*****************************************************************************************/
/* TREE ITEM CLASS                                        
/*****************************************************************************************/

import flash.display.*;
import flash.events.*;
import flash.text.*;

import gs.TweenLite;
import gs.easing.Expo;
import gs.plugins.TweenPlugin;
import gs.plugins.TintPlugin;
    
import idv.cjcat.signals.NativeSignal;
    
class TreeItem extends Sprite {

    private var _width:int = 250;
    private var _height:int = 25;
    private var _margin:int = 10;
    private var _indent:int = 25;
    private var _data:XML;
    private var _level:int;
    private var _selected:Boolean;
    private var _bgColor:uint = 0xCCCCCC;
    private var _frameColor:uint = 0xFFFFFF;
    private var _normalColor:uint = 0x666666;
    private var _overColor:uint = 0x888888;
    private var _selectedColor:uint = 0xCC0000;
    private var _selectedOverColor:uint = 0xFF0000;

    public var clickSignal:NativeSignal;
    private var enterFrameSignal:NativeSignal;

    private var tf:TextField;
    private var bg:Shape;
    private var frame:Shape;
    
    public function TreeItem(data:XML, level:int):void {
        mouseChildren = false; mouseEnabled = true; buttonMode = true; useHandCursor = true;
        
        enterFrameSignal = new NativeSignal(this, Event.ENTER_FRAME);
        clickSignal = new NativeSignal(this, MouseEvent.CLICK); clickSignal.add(onClick);
        new NativeSignal(this, MouseEvent.MOUSE_OVER).add(onMouseOver);
        new NativeSignal(this, MouseEvent.MOUSE_OUT).add(onMouseOut);

        frame = new Shape(); addChild(frame);
        
        bg = new Shape(); addChild(bg);
        
        tf = new TextField();
        tf.x = _margin + level * _indent;
        tf.autoSize = TextFieldAutoSize.LEFT;
        tf.selectable = false;
        tf.defaultTextFormat = new TextFormat("Arial", 14, _normalColor, true);
        addChild(tf);
        
        this.data = data;
    }
    
    public function draw():void {
        tf.text = _data && _data.text() ? _data.text() : "---";
        if (hasChildren) tf.text = "+ " + tf.text;
        tf.y = height / 2 - tf.height / 2;
        
        var tfm:TextFormat = tf.defaultTextFormat;
        tfm.color = selected ? _selectedColor : _normalColor;
        tf.setTextFormat(tfm);
        
        frame.graphics.clear();
        frame.graphics.beginFill(_frameColor, 1);
        frame.graphics.drawRect(0, 0, width, height);
        frame.graphics.endFill();
        
        bg.graphics.clear();
        bg.graphics.beginFill(_bgColor, 1);
        bg.graphics.drawRoundRect(1, 1, width-2, height-2, 8);
        bg.graphics.endFill();
    }
    
    private function invalidate():void{
        enterFrameSignal.addOnce(onInvalidate);
    }
    
    /*****************************************************************************************/
    /* EVENT HANDLERS                                            
    /*****************************************************************************************/
    
    private function onInvalidate(e:Event):void{
        enterFrameSignal.remove(onInvalidate);
        draw();
    }
    
    private function onClick(e:MouseEvent):void{
        new TweenLite(tf, .2, {tint:null});
    }

    private function onMouseOver(e:MouseEvent):void{
        new TweenLite(tf, .2, {tint:selected ? _selectedOverColor : _overColor});
    }

    private function onMouseOut(e:MouseEvent):void{
        new TweenLite(tf, .2, {tint:null});
    }
    
    /*****************************************************************************************/
    /* GETTERS/SETTERS                                            
    /*****************************************************************************************/
    
    override public function set width(value:Number):void {
        _width = value;
        invalidate();
    }
    override public function get width():Number { return _width; }
    
    override public function set height(value:Number):void {
        _height = value;
        invalidate();
    }
    override public function get height():Number { return _height; }
    
    public function set data(value:XML):void {
        _data = value;
        invalidate();
    }
    public function get data():XML { return _data; }
    
    public function set selected(value:Boolean):void {
        _selected = value;
        invalidate();
    }
    public function get selected():Boolean { return _selected; }

    public function get hasChildren():Boolean { 
        return data.item.length() > 0; 
    }
}