/**
 * 
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) Open-Xchange Inc., 2006-2011
 * Mail: info@open-xchange.com 
 * 
 * @author Andreas Mayer <andreas.mayer@open-xchange.com>
 * 
 */

  //////////////
 //   Tree   //
//////////////

/**
 * Creates a tree widget inside the specified DOM element.
 * @class A basic tree widget.
 * @param {Object} container The DOM element to which the root nodes of the tree
 * will be added.
 * @param {Object] state Optional state object previously retrieved with
 * {@link getState}.
 */
function Tree(container, state, id) {
    /**
     * @private
     */
    this.container = container;

    /**
     * @private
     */
    this.state = state || new Object();
    
    /**
     * @private
     */
    this.children = {};
    
    /**
     * @private
     */
    this.id = id;
    
    /**
     * @private
     */
    this.hasChildren = false;
    this.events = new Events();
}

Tree.prototype = {
    /**
     * Removes a previously added root node from the tree.
     * @param {TreeNode} node The root node to remove.
     */
    removeChild: function(node) {
        delete this.children[node.id];
        delete this.state[node.id];
        this.container.removeChild(node.div);
    },
    
    /**
     * Returns the open/colsed state of all nodes in the tree as a JavaScript
     * object. Returns the original state if no root nodes were added.
     * @type Object 
     * @return A state object which can be supplied to the constructor later.
     */
    getState: function() {
        if (this.hasChildren) {
            this.state = new Object();
            for (var i in this.children) {
                var childState = this.children[i].getState();
                if (childState) this.state[i] = childState;
            }
        }
        return this.state;
    },
    
    /**
     * @private
     */
    setState: function(state) {
        for(var i in this.state)    
        {
            if(this.children[i] && this.state[this.children[i].id])
            {    
                this.children[i].setState(this.state[this.children[i].id]);
            }
        }
    },
    replaceChild: function(node,oldnode) {
        this.container.replaceChild(node.div,oldnode.div);
        node.tree_id = oldnode.tree_id;
        node.tree = oldnode.tree;
        this.children[node.id] = node;            
        node.setState(oldnode.getState());
        node.setOpen(oldnode.open);
    },
    /**
     * @private
     */
    addChild: function(node) {
        this.hasChildren = true;
        node.tree_id = this.id;
        node.tree = this;
        this.children[node.id] = node;
        if (this.state[node.id]) 
        {        
            node.setState(this.state[node.id]);
            node.setOpen(true);
        }
        this.container.appendChild(node.div);        
    },
    setOpen:function ()
    {
        
    }
};

/**
 * Adds a root node to a tree or a child node to another node.
 * The new node is appended as the last immediate child of the specified parent
 * object.
 * @class A node for the tree widget.
 * @param {Object} parent Either a Tree or a TreeNode object to which the new
 * node will be added.
 * @param {Boolean} hasChildren Specifies whether to display a plus sign before
 * the node. For trees which are built on-demand, this parameter can be used to
 * specify whether the node has children without actually adding any. If this
 * parameter is false and a child is added later, the plus sign is displayed
 * automatically.
 * @param {String} colsedIcon URI of the icon to display when the children are
 * not displayed.
 * @param {String} openIcon URI of the icon to display when the children are
 * displayed.
 * @param {String or Object} text Text to display in the node as a string or as
 * an inline-level DOM node.
 * @param {String} id Unique ID of the node. Used to restore state.
 * @param {Object} data Any additional data to store in the node. the data is
 * later accessible via {@link #data}.
 */
function TreeNode(parent, hasChildren, closedIcon, openIcon, text, id, data,afterlabel) {
    /**
     * Specifies whether this node has children.
     */
    this.hasChildren = hasChildren;
    this.link = false;
    /**
     * URI of the icon to display when the children are not displayed.
     */
    this.closedIcon = closedIcon;

    /**
     * URI of the icon to display when the children are displayed.
     */
    this.openIcon = openIcon;
    
    /**
     * Unique ID of the node.
     */
    this.id = id;
    
    /**
     * Additional data specified as the last parameter of the constructor.
     */
    this.data = data;
    
    /**
     * Specifies whether the children are displayed.
     */
    this.open = false;
    
    /**
     * @private
     */
    this.children = {};
    
    /**
    * @parent node
    */
    this.parent = parent;
    /**
     * @private
     */
    this.count = 0;

    this.events = new Events();
    
    /**
     * @private
     */
    this.state = new Object();
    this.tree_id;    
    this.tree;        
    
    /**
     * @private
     */
    this.decoration = [];

    var afterlabeltext = afterlabel;
    /**
     * This and further fields can be used to customize the behaviour of
     * the tree.
     */

    this.table_Node = newnode("table",{borderCollapse:"collapse",borderWidth:"0",borderSpacing:"0",width:"100%"}
                                    ,0,[
                            newnode("tbody",{width:"100%"},0,[
                                        newnode("tr",{width:"100%"},0,[
                                                    newnode("td",0,0,[
        newnode("div",0,0,[this.plus = newnode("img", 0, {className: hasChildren ? "tree-children" : "tree-nochildren",
                src: hasChildren ? getFullImgSrc("img/plus.gif") : getFullImgSrc("img/noplus.gif")})])
                                                                        ]),
                                                    newnode("td",0,0,[
        newnode("div", { position: "relative" },0,[this.icon = newnode("img", 0,
                {className: "tree-icon", src: this.closedIcon})])]),
                                                    newnode("td",{paddingLeft:"0",width:"100%"},0,[                
        this.text = newnode("div", 0, {className: "tree-text border-color-content-default"}, [
                this.textnode = typeof(text) != "string" ? text    : document.createTextNode(text),                                            
                    this.afterLabel = newnode("span", 0, {className: "tree-afterlabel"}, [
                            this.afterlabel_textnode = typeof(afterlabeltext) != "string" ? afterlabeltext
                                : document.createTextNode(afterlabeltext)])
                    ])                
                                                    /*, newnode("td",0,0,[                        
        this.afterLabel = newnode("span", 0, {className: "tree-afterlabel"}, [
                this.afterlabel_textnode = typeof(afterlabeltext) != "string" ? afterlabeltext
                    : this.afterlabel_textnode = document.createTextNode(afterlabeltext)])]) */
                                                                            ])
        
                                                                    ])                                                                                                                                                                

                                                              ])
                                                              
                                                    ]);

    this.div = newnode("div", 0, {className: "tree-div"}, [
        this.line = newnode("span", 0, {className: "tree-line"}, [
            this.table_Node
            ,this.container = newnode("div", {marginLeft: "18px", whiteSpace: "nowrap", display: "none"})])]);
    var Self = this;
    
    addDOMEvent(this.plus, "click", function(e) {
        Self.setOpen(!Self.open);
        cancelDefault(e);        
        Self.tree.events.trigger("OX_Tree_State_Changed",Self.tree_id);        
    });

    addDOMEvent(this.icon, "click", function(e) {
        cancelDefault(e);
        Self.tree.events.trigger("OX_Tree_Node_Click",Self,Self.tree_id);
    });

    addDOMEvent(this.text, "click", function(e) {
        cancelDefault(e);
        Self.tree.events.trigger("OX_Tree_Node_Click",Self,Self.tree_id);
    });
    
}

TreeNode.prototype = {
    /**
     * Returns the current text of the node.
     * @type String or Object
     * @return The current text of the node as a string or DOM node.
     */
    getText: function() {
        return this.textnode.nodeType == 3 ? this.textnode.data : this.textnode;
    },
    
    /**
     * Sets the text of the node.
     * @param {String or Object} text The new text of the node as a string or
     * as an inline-level DOM node.
     */
    setText: function(text) {
        if (typeof(text) != "string") {
            this.text.replaceChild(text, this.textnode);
            this.textnode = text;
        } else if (this.textnode.nodeType == 3)
        {
            this.textnode.data = text;
        }
        else {
            var textNode = document.createTextNode(text);
            this.text.removeChild(this.textnode);
            this.text.appendChild(textNode);
            this.textnode = textNode;
        }
    },

    updateIcon: function () { this.icon.src = open ? this.openIcon : this.closedIcon;},
    
    /**
     * Removes a previously added child node from the tree.
     * @param {TreeNode} node The child node to remove.
     */
    removeChild: function(node) {
        if (!this.children[node.id]) return;        
        if (this.children[node.id].tree_id != this.tree_id) return;                
        if (!--this.count) {
            this.open = false;
            this.hasChildren = false;
            this.plus.src = getFullImgSrc("img/noplus.gif");
        }
        delete this.children[node.id];
        if(this.state!=undefined)
            delete this.state[node.id];
        this.container.removeChild(node.div);
    },
    
    /**
     * Displays or hides all children.
     * @param {Boolean} open Specifies whether to display children.
     */
    setOpen: function(open,fn_opt) {
        var Self = this;
        if(open && !this.open && this.hasChildren)
        {
            this.tree.events.trigger("OX_Tree_Expand",Self,fn_opt);
        }        
        else if (fn_opt) 
                fn_opt();
        this.open = open;
        if(open)
            this.events.trigger("NodeOpen",Self);
        else
            this.events.trigger("NodeClose",Self);        
        this.plus.src = this.hasChildren ? (open ? getFullImgSrc("img/minus.gif")
                                                 : getFullImgSrc("img/plus.gif"))
                                         : getFullImgSrc("img/noplus.gif");
        this.plus.className = this.hasChildren ? "tree-children" : "tree-nochildren";
        this.icon.src = open ? this.openIcon : this.closedIcon;
        this.container.style.display = open ? "block" : "none";
    },
    
    /**
     * Sets the decoration icons for this node.
     * @param {Array} decoration An array of image URLs which are displayed on
     * top of the icon. The order of array elements determines the z index.
     * The last element is displayed on top.
     */
    setDecoration: function(decoration) {
        var min = Math.min(this.decoration.length, decoration.length);
        var max = Math.max(this.decoration.length, decoration.length);
        for (var i = 0; i < min; i++) {
            this.decoration[i].src = decoration[i];
        }
        for (var i = min; i < decoration.length; i++) {
            var div = newnode("img", 0,
                { className: "tree-decoration", src: decoration[i] });
            this.decoration[i] = div;
            this.icon.parentNode.appendChild(div);
        }
        for (var i = decoration.length; i < max; i++) {
            this.icon.parentNode.removeChild(this.decoration[i]);
        }
        this.decoration.length = decoration.length;
    },
    
    /**
     * @private
     */
    addChild: function(node) {
        if (!this.count++) {
            this.hasChildren = true;
            this.plus.src = this.open ? getFullImgSrc("img/minus.gif") : getFullImgSrc("img/plus.gif");
        }
        node.tree_id = this.tree_id;
        node.tree = this.tree;        
        this.children[node.id] = node;        
        if (this.state && this.state[node.id]) {
            node.setState(this.state[node.id]);
            node.setOpen(true);
        }
        this.container.appendChild(node.div);
    },
    replaceChild: function(node,oldnode) {
        try {
          this.container.replaceChild(node.div,oldnode.div);
        } catch (e) {
            // for some reason this sometimes leads into an error
        }
        node.tree_id = oldnode.tree_id;
        node.tree = oldnode.tree;    
        node.parent = this;
        this.children[node.id] = node;
        node.setState(oldnode.getState());
        node.setOpen(node.open);
    },
    /**
     * @private
     */
    setState: function(state) {
        this.state = state;
        for(var i in this.state)    
        {
            if(this.children[i] && this.state[this.children[i].id])
            {                
                this.children[i].setState(this.state[this.children[i].id]);
                this.children[i].setOpen(true);                                                
            }
        }
    },
    
    /**
     * @private
     */
    getState: function() {
        if (this.count || !this.hasChildren) {
            this.state = new Object();
            for (var i in this.children) {
                var childState = this.children[i].getState();
                if (childState) this.state[i] = childState;
            }
        }
        return this.open ? this.state : undefined;
    }
};
fileloaded();