﻿var _indentWidth = 10; //the width of the indent of each node level
var _expandNodeImage = "Resources/Images/treeExpand.gif"; //set the + image for expanding nodes
var _collapseNodeImage = "Resources/Images/treeCollapse.gif"; //set the - image for collapsing nodes
var _noExpandImage = "Resources/Images/treeEmpty.gif"; //set the image of neither and expand or collapse (when the node has no children)
var _selectedNodeColour = "#aaaaaa";

var _ParentNode = null;
var BEHAVIOUR_NONE = 1;
var BEHAVIOUR_EXPAND_CHILDREN = 2;
var BEHAVIOUR_CUSTOM_ONCLICK = 3;

var NODETYPE_FOLDER = 5;
var NODETYPE_LEAF = 6;
var NODETYPE_CUSTOM = 7;

function TreeView(parentDivToAppend) {
    
    var _currentID = 0;  //incremented internally everytime a node is added, to ensure that all nodes have unique ids
    _ParentNode = new TreeViewNode(null, "", "", NODETYPE_FOLDER); //the parentnode is what all other nodes attach to, and isn't rendered
    var ParentDivToAppend = parentDivToAppend;
    var ParentTable = createTable();
    var selectedNode = null;
    
    var my = this;
        
    this.Render = Render;
    this.AddChild = AddChild;
    this.GetSelectedNodeName = GetSelectedNodeName;
    
    function Render() {
        emptyNode(ParentDivToAppend);
        for (var i = 0; i < _ParentNode.Children.length; i++) {
            //create a new row for each child, and render the child.
            //the result from render is appended to the cell of the row.
		    var objRow = ParentTable.insertRow(ParentTable.rows.length);
		    var objCell = objRow.insertCell(objRow.cells.length);
		    objCell.style.width = "100%";
		    objCell.style.paddingLeft = _indentWidth + "px";
            objCell.appendChild(_ParentNode.Children[i].Render()); 
        }
        ParentDivToAppend.appendChild(ParentTable);
    }
    
    function AddChild(imageSrc, text, behaviour, nodeType, onclick) {
        return _ParentNode.AddChild(imageSrc, text, behaviour, nodeType, onclick);
    }


    function GetSelectedNodeName() {
        if (this.selectedNode != null)
            return selectedNode.Text;
        return "";
    }

    //private functions - helpers for this class and the inner class TreeViewNode
    //---------------------------------------------------------------------------
    function expandNode(event) {
		var srcDiv;

		if (window.event) {
			srcDiv = window.event.srcElement;
		}
		else if (event.target)
			srcDiv = event.target;

		while ((srcDiv != null) && (srcDiv.tagName != "DIV"))
			srcDiv = srcDiv.parentNode;
			
        //get the treeViewNode object by its id
        var treeNode = _ParentNode.SearchForNode(srcDiv.id);
        if (treeNode != null) {
            //found the node, now expand/collapse it and render it if it has children
            if (treeNode.HasChildren()) {
                treeNode.Expanded = !treeNode.Expanded;
            }
        }
    }
    
    function setSelected(event) {
		var srcDiv;

		if (window.event) {
			srcDiv = window.event.srcElement;
		}
		else if (event.target)
			srcDiv = event.target;

		while ((srcDiv != null) && (srcDiv.tagName != "DIV"))
			srcDiv = srcDiv.parentNode;	

        if (my.selectedNode != null)
            my.selectedNode.ClearStyle();
                     

        //get the treeViewNode object by its id
        var treeNode = _ParentNode.SearchForNode(srcDiv.id);
        if (treeNode != null) {
            my.selectedNode = treeNode;
            my.selectedNode.SetSelectedStyle();
        }    
        return false;
    }
        
    function createTable() {
		var table = document.createElement('table');
		table.setAttribute("border", "0");
		table.setAttribute("cellpadding", "0");
		table.setAttribute("cellspacing", "0");		
		table.style.width = "100%";
		return table;
	}
	
	
	//empties all the children from the argument element
	function emptyNode(objPanel) {
	    if (objPanel != undefined) 
            while (objPanel.childNodes.length > 0)
		        objPanel.removeChild(objPanel.childNodes[0]);
	}
	//---------------------------------------------------------------------------
	
	
	
	
	
	//Tree View Node class
    function TreeViewNode(parentNode, imageSrc, text, nodeType, onclick) {
        //public vars
        //-----------------------------
        this.ID = _currentID++;
        this.ParentNode = parentNode;
        this.ImageSrc = imageSrc;
        this.Text = text;
        this.Children = new Array();
        this.Expanded = false;
        this.NodeType = nodeType;
        //-----------------------------
                
        
        //private vars
        //-----------------------------
        var DivToAppend = document.createElement('DIV');
        var Table = createTable();
        var textNode = null;
        var OnClickEventHandler = new OnClickHandler();
        OnClickEventHandler.AddEvent(onclick);
        if (this.NodeType != NODETYPE_CUSTOM)
            OnClickEventHandler.AddEvent(setSelected);
        if (this.NodeType == NODETYPE_FOLDER)
            OnClickEventHandler.AddEvent(Render);
        var my = this;
        //-----------------------------
        
        
        //public functions
        //-----------------------------
        this.Render = Render;
        if (this.NodeType == NODETYPE_FOLDER)
            this.AddChild = AddChild;
        this.HasChildren = HasChildren;
        this.SearchForNode = SearchForNode;
        this.ClearStyle = ClearStyle;
        this.SetSelectedStyle = SetSelectedStyle;
        //-----------------------------
        
        
        //Public functions
        //---------------------------------------------------------------------
        
        //create the necessary elements for this node, and return them in a div
        function Render() {
            initDivNode(DivToAppend);
            emptyNode(Table);

            if (my.Expanded) {
                //build table of all children
                for (var i = 0; i < my.Children.length; i++) {
                    //create a new row for each child, and render the child.
                    //the result from render is appended to the cell of the row.
		            var objRow = Table.insertRow(Table.rows.length);
		            var objCell = objRow.insertCell(objRow.cells.length);
		            objCell.style.width = "100%";
		            objCell.style.paddingLeft = _indentWidth + "px";
                    objCell.appendChild(my.Children[i].Render());
                }
            }

            DivToAppend.appendChild(Table);

            return DivToAppend;
        }
        
        //add a node below this node, and return it
        function AddChild(imageSrc, text, behaviour, nodeType, onclick) {
            if (behaviour == BEHAVIOUR_NONE) {
                onclick = "";
            }
            else if ((behaviour == BEHAVIOUR_EXPAND_CHILDREN) && (nodeType == NODETYPE_FOLDER)) {
                onclick = expandNode;
            }
            else if (behaviour == BEHAVIOUR_CUSTOM_ONCLICK) {
                //do nothing - leave onclick as user has defined it
            } 
            
            var node = new TreeViewNode(my, imageSrc, text, nodeType, onclick);
            this.Children[my.Children.length] = node;
            return node;
        }
        
        function HasChildren() {
            return this.Children.length > 0;
        }

        function SearchForNode(id) {
            if (my.ID == id)
                return this;
            for (var i = 0; i < my.Children.length; i++) {
                node = my.Children[i].SearchForNode(id);
                if (node != null)
                    return node;
            }
            return null;
        }
        
        //set the style of this node as standard
        function ClearStyle() {
            if (textNode != null)
                textNode.style.backgroundColor = "#ffffff";
        }
        
        //set the style of this node is selected
        function SetSelectedStyle() {
            if (my.NodeType == NODETYPE_LEAF) {
                if (textNode != null)
                    textNode.style.backgroundColor = _selectedNodeColour;
            }
        }
        //-----------------------------------------------------------------------------------
        
        
        //private functions
        //-----------------------------------------------------------------------------------
        
        //remove all nodes on this div, and re-add this nodes image and text to the passed node
        function initDivNode(div) {
            emptyNode(div);
            var img = document.createElement('img');
            img.src = my.ImageSrc;
            img.style.border = "";
            img.align = "absmiddle";
            
            var expandImg = document.createElement('img');
            if (my.Children.length > 0) {
                if (my.Expanded)
                    expandImg.src = _collapseNodeImage;
                else
                    expandImg.src = _expandNodeImage;
            }
            else
                expandImg.src = _noExpandImage;
            
            var clickableDiv = document.createElement('div');
            clickableDiv.appendChild(expandImg);
            clickableDiv.appendChild(document.createTextNode(" "));
            clickableDiv.appendChild(img);
            clickableDiv.appendChild(document.createTextNode(" "));
            textNode = document.createElement('span');
            textNode.appendChild(document.createTextNode(my.Text));
            clickableDiv.appendChild(textNode);
            clickableDiv.onclick = OnClickEventHandler.RunHandler;
            div.appendChild(clickableDiv);
            clickableDiv.id = my.ID;
        }

        //-----------------------------------------------------------------------------------
        
        
        //an onclick handler class that supports multiple onclick events
        function OnClickHandler() {
            var events = new Array();
            
            this.AddEvent = AddEvent;
            this.RunHandler = RunHandler;
            
            function AddEvent(newEvent) {
                if (newEvent != undefined)
                    events[events.length] = newEvent;
            }
            
            function RunHandler(event) {
                for (var i = 0; i < events.length; i++) {
                    //to tell the difference between a callback, and a user defined function, we check for the text 'function'...
                    //if 'function' exists, we done need to pass the event because its a user defined function that has previously defined arguments...
                    try {
                        eval(events[i](event));
                    }
                    catch (err) {
                        eval(events[i]);
                    }
                        
                }
            }
        }
    }	
}



