var TFVS_IDToObject;
if (TFVS_IDToObject == null || TFVS_IDToObject == undefined)
    TFVS_IDToObject = new Object();

var TFVS_StatusUpdateList;
if (TFVS_StatusUpdateList == null || TFVS_StatusUpdateList == undefined)
    TFVS_StatusUpdateList = new TFVS_StatusUpdates();

// This global boolean indicates if we have pressed and are dragging the grabber on any
// one of our trees. We can only drag one at a time, so we can do this just fine. 
var TFVS_Dragging = false;
    
function TFVS_StatusUpdates() {
    this.store = new Object();
    this.Add = function(obj) {
        this.store[obj.id] = obj;
    }
    this.Retrieve = function(id) {
        var ret = this.store[id];
        this.store[id] = null;
        if (ret == undefined || ret == null)
            ret = null;

        return ret;
    }
}

// Used for calling Timeout and Interval calls in their correct scope.
function TFVS_TimerEnd(id, call) {

    var obj = TFVS_IDToObject[id];
    with (obj) {
        var func = call + "();";
        eval(func);
    }
}

// Checks to make sure the container that our status structure is generated 
// in has a sibling which is our verification link, that it is well formed, 
// and visible.
function TFVS_verifySecurityLinkExists(showDiv) {

    var mainDiv = null;
    var link = null;

    var abort = false;
    if (showDiv == null)
        abort = true;
    else {
        // Grab our root div.
        mainDiv = showDiv.parentNode;
    }

    if (mainDiv == null)
        abort = true;
    else {
        // Grab our link div.
        if (mainDiv.childNodes[1].className == "TypeFragLink")
            link = mainDiv.childNodes[1]; // I.e. it should appear to be in the right spot
        else
            link = mainDiv.childNodes[3]; // Firefox assums a text node at every other index.
    }

    if (link == null)
        abort = true;
    else {
        if (!(link.parentNode == mainDiv
           && link.nodeName == "A"
           && (link.getAttribute("href").toLowerCase() == "http://www.typefrag.com/"
               || link.getAttribute("href").toLowerCase() == "http://www.typefrag.com")
           && (link.innerText == "Ventrilo by TypeFrag.com"
               || link.textContent == "Ventrilo by TypeFrag.com")
           && (link.getAttribute("rel") == null || link.getAttribute("rel").toLowerCase().indexOf("nofollow") == -1)
           && link.visibility != "hidden" && link.style.visibility != "hidden")) {
            abort = true;
        }
    }

    // If we need to abort for any reason, do so.
    if (abort) {
        alert("This page does not meet the acceptable criteria to display the TypeFrag Server Viewer.");
        throw ("This page does not meet the acceptable criteria to display the TypeFrag Server Viewer.");
    }
}

function TFVS_VentStatus(id, path, expanded) {
    // Assign an ID to us
    this.id = id;
    // Store the ID for global access
    TFVS_IDToObject[this.id] = this;
    
    // The URI to where we can get our XML file.
    this.xmlDocPath = path;
    // The global boolean for if we want this tree initially expanded
    this.expanded = expanded;
    // The node in our head that we used for getting our current XML data.
    this.oldScriptNode = null;
    // The variable that contains the xml data as a string that will create our doc.
    this.xmlData = null;
    // The root element of our XML document
    this.server = null;
    // The XML Document object
    this.xmlDoc = null;
    // Stores the ID of each channel that is currently expanded.
    this.expandedArray = new Array();
    // Used for when refreshing the tree.
    this.oldExpandArray = new Array();
    // Keeps track of the item currently selected, for deselecting purposes
    this.selectedItem = null;
    // ID for interval object used for auto-scrolling (mouse down on the arrows)
    this.scrollInterval = null;
    // ID for the timeout object used for auto-scrolling (mouse down on the arrows)
    this.scrollTimeout = null;
    // Indicates that the grabber is being pressed. Completely unnecessary at this point.
    this.GrabberIsDown = false;
    // What the offset of the grabber is, precalculated.
    this.GrabOffset = 0;
    // Global holds reference to selected element
    this.selectedObj = null;
    // ID for the interval timer used to wait for XML data return and tree update.
    this.refreshInterval = null;
    // A value to keep track of how many times we check our refresh interval. If it reaches 0, we timeout.
    this.refreshTimeout = 0;
    // Keeps track of the ID of the selected node when we refresh.
    this.selectedID = 0;
    // Indicates if we're over the info button.
    this.overInfo = false;

    // The treeWrap HTML div element for this status view.
    this.treeWrap = null;
    // The scrollGrabber HTML div element for this status view.
    this.scrollGrabber = null;
    // The treeBackground HTML div element for this status view.
    this.treeBackground = null;
    // The hoverInfo HTML div element for this status view.
    this.hoverInfo = null;
    // The hoverInfo-content HTMl div element for this status view.
    this.hoverInfo_content = null;

    // Places a script node into our header, in order to initiate a new request of our data
    // Removes the previous node if it exists.
    this.getXMLData = function() {
        var xmlScript = document.createElement("script");
        xmlScript.setAttribute("type", "text/javascript");
        xmlScript.type = "text/javascript";
        var d = new Date();
        xmlScript.setAttribute("src", this.xmlDocPath + "&ID=" + this.id + "&junk=" + d);
        xmlScript.src = this.xmlDocPath + "&ID=" + this.id + "&junk=" + d;

        var head = document.getElementsByTagName("head")[0];
        // Remove the old one.
        if (this.oldScriptNode != null) {
            head.removeChild(this.oldScriptNode);
        }
        this.oldScriptNode = xmlScript;
        head.appendChild(this.oldScriptNode);
    }
    // Attempt to read in the data, and use it to initiate construction of our status view.
    // If we have no data, refresh.
    this.setupServerViewer = function() {
        if (window.ActiveXObject) // code for IE
        {
            this.xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
            this.xmlDoc.async = "false";

        }
        else if (document.implementation.createDocument) // code for Firefox, Mozilla, Opera, etc.
        {
            this.xmlDoc = document.implementation.createDocument("", "", null);
        }
        this.showServerViewer();

        // Initial refresh. Wait half a sec so we don't lock down the page.
        setTimeout("TFVS_TimerEnd('" + this.id + "','Refresh');", 1000);
    }

    // Initiate the construction of the viewer.
    this.showServerViewer = function() {
        // Grab our display div.
        var showDiv = document.getElementById(this.id);
        
        // Verify that our link exists
        //this.verifySecurityLinkExists(showDiv);

        // Generate the container.
        this.showFrame(showDiv);

        this.initDrag();

        return true;
    }

    // Construct the frame and all it's static prettiness
    this.showFrame = function(htmlParent) {
        // MAIN CONTAINER
        var wrap = document.createElement("div");
        wrap.setAttribute("class", "TFVS-Wrap");
        wrap.className = "TFVS-Wrap";
        wrap.ownerId = this.id;
        htmlParent.appendChild(wrap);

        var item
        var container

        // HOVER CONTAINER
        item = document.createElement("div");
        item.setAttribute("class", "TFVS-HoverInfo");
        item.className = "TFVS-HoverInfo";
        item.style.visibility = "Hidden";
        item.style.position = "absolute";
        item.style.top = 0;
        item.style.left = 0;
        item.ownerId = this.id;
        //    wrap.appendChild(item); // Add last
        this.hoverInfo = item;

        container = item;
        var hovCont = container;

        // HOVER CONTAINER: CORNERS
        item = document.createElement("div");
        item.setAttribute("class", "TFVS-HoverInfo-TopLeft");
        item.className = "TFVS-HoverInfo-TopLeft";
        item.ownerId = this.id;
        container.appendChild(item);

        item = document.createElement("div");
        item.setAttribute("class", "TFVS-HoverInfo-TopRight");
        item.className = "TFVS-HoverInfo-TopRight";
        item.ownerId = this.id;
        container.appendChild(item);

        item = document.createElement("div");
        item.setAttribute("class", "TFVS-HoverInfo-BottomLeft");
        item.className = "TFVS-HoverInfo-BottomLeft";
        item.ownerId = this.id;
        container.appendChild(item);

        item = document.createElement("div");
        item.setAttribute("class", "TFVS-HoverInfo-BottomRight");
        item.className = "TFVS-HoverInfo-BottomRight";
        item.ownerId = this.id;
        container.appendChild(item);


        item = document.createElement("div");
        item.setAttribute("class", "TFVS-HoverInfo-Header");
        item.className = "TFVS-HoverInfo-Header";
        item.ownerId = this.id;
        container.appendChild(item);

        item.appendChild(document.createComment(" "));

        item = document.createElement("div");
        item.setAttribute("class", "TFVS-HoverInfo-LeftSide");
        item.className = "TFVS-HoverInfo-LeftSide";
        item.ownerId = this.id;
        container.appendChild(item);

        container = item;

        item = document.createElement("div");
        item.setAttribute("class", "TFVS-HoverInfo-RightSide");
        item.className = "TFVS-HoverInfo-RightSide";
        item.ownerId = this.id;
        container.appendChild(item);

        container = item;


        item = document.createElement("div");
        item.setAttribute("class", "TFVS-HoverInfo-Content");
        item.className = "TFVS-HoverInfo-Content";
        item.ownerId = this.id;
        container.appendChild(item);
        this.hoverInfo_content = item;

        // HOVER CONTAINER: FOOTER
        item = document.createElement("div");
        item.setAttribute("class", "TFVS-HoverInfo-Footer");
        item.className = "TFVS-HoverInfo-Footer";
        item.ownerId = this.id;
        hovCont.appendChild(item);

        item.appendChild(document.createComment(" "));


        // CONTAINER CORNERS
        item = document.createElement("div");
        item.setAttribute("class", "TFVS-TopLeft");
        item.className = "TFVS-TopLeft";
        item.ownerId = this.id;
        wrap.appendChild(item);

        item = document.createElement("div");
        item.setAttribute("class", "TFVS-TopRight");
        item.className = "TFVS-TopRight";
        item.ownerId = this.id;
        wrap.appendChild(item);

        item = document.createElement("div");
        item.setAttribute("class", "TFVS-BottomLeft");
        item.className = "TFVS-BottomLeft";
        item.ownerId = this.id;
        wrap.appendChild(item);

        item = document.createElement("div");
        item.setAttribute("class", "TFVS-BottomRight");
        item.className = "TFVS-BottomRight";
        item.ownerId = this.id;
        wrap.appendChild(item);

        // HEADER
        var header = document.createElement("div");
        header.setAttribute("class", "TFVS-Header");
        header.className = "TFVS-Header";
        header.ownerId = this.id;
        wrap.appendChild(header);

        item = document.createElement("h3");
        item.ownerId = this.id;
        item.appendChild(document.createTextNode("Killer Old Farts"));
        header.appendChild(item);

        container = document.createElement("div");
        container.setAttribute("class", "TFVS-LeftSide");
        container.className = "TFVS-LeftSide";
        container.ownerId = this.id;
        wrap.appendChild(container);

        item = document.createElement("div");
        item.setAttribute("class", "TFVS-RightSide");
        item.className = "TFVS-RightSide";
        item.ownerId = this.id;
        container.appendChild(item);

        container = item;

        // CONTENT CONTAINER
        item = document.createElement("div");
        item.setAttribute("class", "TFVS-Content");
        item.className = "TFVS-Content";
        item.ownerId = this.id;
        container.appendChild(item);
        container = item;

        var arrow = document.createElement("div");
        arrow.setAttribute("class", "TFVS-ScrollUp");
        arrow.className = "TFVS-ScrollUp";
        arrow.ownerId = this.id;
        arrow.onmousedown = EXT_StartScrollingUp;
        arrow.onmouseup = EXT_StopScrollingUp;
        arrow.onmouseout = EXT_StopScrollingUp;
        container.appendChild(arrow);

        arrow = document.createElement("div");
        arrow.setAttribute("class", "TFVS-ScrollDown");
        arrow.className = "TFVS-ScrollDown";
        arrow.ownerId = this.id;
        arrow.onmousedown = EXT_StartScrollingDown;
        arrow.onmouseup = EXT_StopScrollingDown;
        arrow.onmouseout = EXT_StopScrollingDown;
        container.appendChild(arrow);

        arrow = document.createElement("div");
        arrow.setAttribute("class", "TFVS-ScrollGrabber");
        arrow.className = "TFVS-ScrollGrabber";
        arrow.ownerId = this.id;
        container.appendChild(arrow);


        item = document.createElement("div");
        item.setAttribute("class", "TFVS-TreeBackground");
        item.className = "TFVS-TreeBackground";
        item.ownerId = this.id;
        this.treeBackground = item;
        container.appendChild(item);
        container = item;

        this.scrollGrabber = arrow;

        if (this.server != null)
            this.showServer(container); // FILL IN SERVER STUFF HERE.
        else
            this.showLoading(container); // Always show loading first. This will be replaced with unavailable when the timeout happens.
//        this.showUnavailable(container);
        
        // FOOTER
        container = document.createElement("div");
        container.setAttribute("class", "TFVS-Footer");
        container.className = "TFVS-Footer";
        container.ownerId = this.id;
        wrap.appendChild(container);

        item = document.createElement("div");
        item.setAttribute("class", "TFVS-ButtonWrap");
        item.className = "TFVS-ButtonWrap";
        item.ownerId = this.id;
        container.appendChild(item);

        container = item;


        // REFRESH BUTTON
        item = document.createElement("a");
        item.setAttribute("class", "TFVS-Refresh");
        item.setAttribute("href", "://");
        item.className = "TFVS-Refresh";
        item.ownerId = this.id;
        item.onclick = EXT_Refresh;
        item.appendChild(document.createTextNode("Refresh"));
        container.appendChild(item);

        // CONNECT BUTTON
        item = document.createElement("a");
        item.setAttribute("class", "TFVS-Connect");
        item.setAttribute("href", "://");
        item.className = "TFVS-Connect";
        item.ownerId = this.id;
        item.onclick = EXT_JoinServer;
        item.appendChild(document.createTextNode("Connect"));
        container.appendChild(item);

        // Add hover container last.
        wrap.appendChild(hovCont);
    }

    this.showUnavailable = function(htmlParent) {
        var ua = document.createElement("div");
        ua.setAttribute("class", "TFVS-Unavailable");
        ua.className = "TFVS-Unavailable";
        ua.ownerId = this.id;

        var item = document.createElement("h1");
        item.appendChild(document.createTextNode("Unavailable"));
        ua.appendChild(item);

        htmlParent.appendChild(ua);
    }

    this.showLoading = function(htmlParent) {
        var ua = document.createElement("div");
        ua.setAttribute("class", "TFVS-Loading");
        ua.className = "TFVS-Loading";
        ua.ownerId = this.id;
        
        htmlParent.appendChild(ua);
    }

    // Now initiate the construction of the server-structure.
    this.showServer = function(htmlParent) {
        var treeWrap = document.createElement("div");
        treeWrap.setAttribute("class", "TFVS-TreeWrap");
        treeWrap.className = "TFVS-TreeWrap";
        treeWrap.ownerId = this.id;
        htmlParent.appendChild(treeWrap);
        this.treeWrap = treeWrap;

        // Create an unordered list
        var list = document.createElement("ul");
        list.setAttribute("class", "TFVS-RootNode");
        list.className = "TFVS-RootNode";
        list.ownerId = this.id;
        treeWrap.appendChild(list);

        // Create a new list item for the server.
        var item = document.createElement("li");
        item.ownerId = this.id;
        list.appendChild(item);
        var li = item;

        var container = item;
        item = document.createElement("div");
        item.setAttribute("class", "TFVS-None");
        item.ownerId = this.id;
        item.className = "TFVS-None";
        container.appendChild(item);

        container = item;
        item = document.createElement("div");
        item.setAttribute("class", "TFVS-Server");
        item.ownerId = this.id;
        item.className = "TFVS-Server";
        container.appendChild(item);

        container = item;

        var name = this.server.getAttribute("N");
        var comment = this.server.getAttribute("COMM");
        var totalClients = this.server.getAttribute("TC");
        var maxClients = this.server.getAttribute("MC");
        var hostName = this.server.getAttribute("HN");
        var port = this.server.getAttribute("P");

        var hidden = document.createElement("div");
        hidden.ownerId = this.id;
        var p = document.createElement("p");
        p.ownerId = this.id;
        hidden.appendChild(p);
        var b = document.createElement("strong");
        b.ownerId = this.id;
        b.appendChild(document.createTextNode("Address: "));
        p.appendChild(b);
        p.appendChild(document.createTextNode(hostName));
        p = document.createElement("p");
        p.ownerId = this.id;
        hidden.appendChild(p);
        b = document.createElement("strong");
        b.ownerId = this.id;
        b.appendChild(document.createTextNode("Port: "));
        p.appendChild(b);
        p.appendChild(document.createTextNode(port));
        p = document.createElement("p");
        p.ownerId = this.id;
        hidden.appendChild(p);
        b = document.createElement("strong");
        b.ownerId = this.id;
        b.appendChild(document.createTextNode("Users: "));
        p.appendChild(b);
        p.appendChild(document.createTextNode(totalClients));
        b = document.createElement("strong");
        b.ownerId = this.id;
        b.appendChild(document.createTextNode("/"));
        p.appendChild(b);
        p.appendChild(document.createTextNode(maxClients));
        hidden.style.display = 'none';

        // Mark us as protected if we are.
        var protectedMode = this.server.getAttribute("PM");
        if (protectedMode == 1) {
            item.setAttribute("class", "TFVS-ServerLocked");
            item.ownerId = this.id;
            item.className = "TFVS-ServerLocked";
            p = document.createElement("p");
            p.ownerId = this.id;
            hidden.appendChild(p);
            p.appendChild(document.createTextNode("This server is Password Protected"));
        }
        else if (protectedMode == 2) {
            item.setAttribute("class", "TFVS-ServerAuthorization");
            item.ownerId = this.id;
            item.className = "TFVS-ServerAuthorization";
            p = document.createElement("p");
            p.ownerId = this.id;
            hidden.appendChild(p);
            p.appendChild(document.createTextNode("This server requies User Authorization"));
        }

        // Create a clickable text display for the content. Clicking will show/hide the above div.
        var lnk = document.createElement("a");
        lnk.ownerId = this.id;
        lnk.setAttribute("href", "://");
        lnk.setAttribute("class", "TFVS-Info");
        lnk.className = "TFVS-Info";
        // Hook up the show/hide
        lnk.onclick = EXT_itemClicked;

        // Set if they're selected.
        if (this.selectedID == "server") {
            this.selectedItem = lnk;
            lnk.className = "TFVS-SelectedInfo";
        }

        var span = document.createElement("span");
        span.ownerId = this.id;
        span.onmousemove = this.EXT_updateMouseMove;
        span.onmouseover = this.EXT_updateMouseMove;
        span.onmouseout = this.EXT_updateMouseLeave;

        // Add in the channel text.
        var content = name;
        if (comment != null && comment != "")
            content += " (" + comment + ")";
        span.appendChild(document.createTextNode(content));
        lnk.appendChild(span);
        // Add the link into our parent container.
        container.appendChild(lnk);

        // Show all the server's children.
        // Create an unordered list
        var list = document.createElement("ul");
        list.ownerId = this.id;
        list.setAttribute("class", "TFVS-NoIndent");
        list.className = "TFVS-NoIndent";

        // Add in all the users and channels.    			   
        for (var i = 0; i < this.server.childNodes.length; i++) {
            var child = this.server.childNodes[i];
            // US is users, other is channels
            if (child.nodeName == "US")
                this.showUsers(child, list);
            else
                this.showChannels(child, list);
        }

        // If our list element is empty, then we didn't have anything to put in it.
        // That means we don't have to add it to our parent.
        if (list.hasChildNodes()) {
            // Append the list to our parent.
            li.appendChild(list);
        }

        // Add in our hidden node.
        li.appendChild(hidden);


        // Clear our passed in value.
        this.expanded = false;
    }

    // Shows all the users in our list.
    this.showUsers = function(usersList, htmlParent) {
        // htmlParent is a UL

        for (var i = 0; i < usersList.childNodes.length; i++) {
            var child = usersList.childNodes[i];
            // Make sure the user has attributes. If it doesn't, we don't want to print anything.
            if (child.attributes != null && child.attributes.length > 0) {
                // Create a new list item for each user.
                var item = document.createElement("li");
                item.ownerId = this.id;
                admin = child.getAttribute("A");
                phantom = child.getAttribute("P");
                ping = child.getAttribute("PI");
                timeOnline = child.getAttribute("TO");
                if (ping < 0)
                    ping = "-"

                var hidden = document.createElement("div");
                item.ownerId = this.id;
                p = document.createElement("p");
                p.ownerId = this.id;
                hidden.appendChild(p);
                var b = document.createElement("strong");
                b.ownerId = this.id;
                b.appendChild(document.createTextNode("Ping: "));
                p.appendChild(b);
                p.appendChild(document.createTextNode(ping));
                p = document.createElement("p");
                p.ownerId = this.id;
                hidden.appendChild(p);
                b = document.createElement("strong");
                b.ownerId = this.id;
                b.appendChild(document.createTextNode("Time Online: "));
                p.appendChild(b);
                p.appendChild(document.createTextNode(this.ConvertTimeSpan(timeOnline)));
                hidden.style.display = 'none';

                // Determine if they're an Admin, a Phantom User, a Phantom Admin,
                // or just a normal user.
                if (admin == 1) {
                    p = document.createElement("p");
                    p.ownerId = this.id;
                    hidden.appendChild(p);
                    p.appendChild(document.createTextNode("This user is an Administrator"));
                }
                if (phantom == 1) {
                    p = document.createElement("p");
                    p.ownerId = this.id;
                    hidden.appendChild(p);
                    p.appendChild(document.createTextNode("This user is a Phantom"));
                }


                // Set the user's ID.
                var id = htmlParent.getAttribute("id") + "/" + child.getAttribute("ID");
                item.setAttribute("id", id);
                item.id = id;

                // Show this user's info.
                this.showUser(child, item);
                // Add the list item to our parent.
                htmlParent.appendChild(item);

                // Add in our hidden node.
                item.appendChild(hidden);
            }
        }
    }
    // Shows all the channels in our list.
    this.showChannels = function(channelsList, htmlParent) {
        for (var i = 0; i < channelsList.childNodes.length; i++) {
            var child = channelsList.childNodes[i];
            // Make sure the channel has attributes. If it doesn't, we don't want to print anything.
            if (child.attributes != null && child.attributes.length > 0) {
                // Create a new list item for each channel.
                var item = document.createElement("li");
                item.ownerId = this.id;

                // Set the Channel's ID.
                var id = htmlParent.getAttribute("id") + "/" + child.getAttribute("N");
                item.setAttribute("id", id);
                item.id = id;
                // Show this channel's info.
                this.showChannel(child, item);
                // Add the list item to our parent.
                htmlParent.appendChild(item);
            }
        }
    }
    // Prints out our user as a single line.
    this.showUser = function(user, htmlParent) {
        // Grab the attributes
        var name = user.getAttribute("N");
        var comment = user.getAttribute("COMM");
        var admin = user.getAttribute("A");
        var phantom = user.getAttribute("P");

        // Create some extra containers.
        var item = document.createElement("div");
        item.ownerId = this.id;
        item.setAttribute("class", "TFVS-Empty");
        item.className = "TFVS-Empty";
        htmlParent.appendChild(item);

        var container = item;

        item = document.createElement("div");
        item.ownerId = this.id;
        container.appendChild(item);

        container = item;

        // Determine if they're an Admin, a Phantom User, a Phantom Admin,
        // or just a normal user.
        if (admin == 1) {
            item.setAttribute("class", "TFVS-Admin");
            item.className = "TFVS-Admin";
        }
        else {
            item.setAttribute("class", "TFVS-User");
            item.className = "TFVS-User";
        }
        if (phantom == 1) {
            item.setAttribute("class", item.getAttribute("class") + " TFVS-Phantom");
            item.className = item.className + " TFVS-Phantom";
        }

        var lnk = document.createElement("a");
        lnk.setAttribute("href", "://");
        lnk.ownerId = this.id;
        lnk.setAttribute("class", "TFVS-Info");
        lnk.className = "TFVS-Info";
        // Hook up the show/hide
        lnk.onclick = EXT_itemClicked;
        container.appendChild(lnk);

        // Set if they're selected.
        if (htmlParent.getAttribute("id") == this.selectedID) {
            this.selectedItem = lnk;
            lnk.className = "TFVS-SelectedInfo";
        }

        var span = document.createElement("span");
        span.ownerId = this.id;
        span.onmousemove = this.EXT_updateMouseMove;
        span.onmouseover = this.EXT_updateMouseMove;
        span.onmouseout = this.EXT_updateMouseLeave;
        lnk.appendChild(span);

        // Add in the text node for the info.
        var content = name;
        if (comment != null && comment != "")
            content += " (" + comment + ")";
        var txt = document.createTextNode(content);
        // Append the text node.
        span.appendChild(txt);
    }
    // Prints out our channel as a single line.
    this.showChannel = function(channel, htmlParent) {
        // Grab the attributes
        var name = channel.getAttribute("N");
        var comment = channel.getAttribute("COMM");
        var protectedMode = channel.getAttribute("PM");

        var toggle = document.createElement("div");
        toggle.setAttribute("class", "TFVS-Empty");
        toggle.ownerId = this.id;
        toggle.className = "TFVS-Empty";
        htmlParent.appendChild(toggle);

        var item = document.createElement("div");
        item.setAttribute("class", "TFVS-Channel");
        item.ownerId = this.id;
        item.className = "TFVS-Channel";
        toggle.appendChild(item);


        // Mark us as protected if we are.
        if (protectedMode == 1) {
            item.setAttribute("class", "TFVS-ChannelLocked");
            item.className = "TFVS-ChannelLocked";
        }
        else if (protectedMode == 2) {
            item.setAttribute("class", "TFVS-ChannelAuthorization");
            item.className = "TFVS-ChannelAuthorization";
        }

        // Create a clickable text display for the content. Clicking will show/hide the above div.
        var lnk = document.createElement("a");
        lnk.setAttribute("href", "://");
        lnk.ownerId = this.id;
        // Hook up the show/hide
        lnk.onclick = EXT_itemClicked;

        // Set if they're selected.
        if (htmlParent.getAttribute("id") == this.selectedID) {
            this.selectedItem = lnk;
            lnk.className += "TFVS-Selected";
        }

        var span = document.createElement("span");
        span.ownerId = this.id;

        // Add in the channel text.
        var content = name;
        if (comment != null && comment != "")
            content += " (" + comment + ")";
        span.appendChild(document.createTextNode(content));

        lnk.appendChild(span);
        item.appendChild(lnk);

        // Now show any children in this channel.
        // Create an unordered list
        var list = document.createElement("ul");
        list.ownerId = this.id;
        list.setAttribute("id", htmlParent.getAttribute("id"));
        list.id = htmlParent.getAttribute("id");

        // If our global expanded variable is true, then make sure everything is expanded.
        // Otherwise, let the other guys decide
        if (this.expanded) {
            // We're supposed to be expanded (so show the - box, and add us to our expanded array list.)
            this.expandedArray.push(htmlParent.getAttribute("id"));
            toggle.setAttribute("class", "TFVS-Collapse");
            toggle.className = "TFVS-Collapse";
        } else {
            // If we're not contained in the oldExpandArray list...
            if (this.getIndexOf(this.oldExpandArray, htmlParent.getAttribute("id")) == -1) {
                // We're supposed to be collapsed (so show the + box)
                list.style.display = 'none';
                toggle.setAttribute("class", "TFVS-Expand");
                toggle.className = "TFVS-Expand";
            } else {
                // We're supposed to be expanded (so show the - box, and add us to our expanded array list.)
                this.expandedArray.push(htmlParent.getAttribute("id"));
                toggle.setAttribute("class", "TFVS-Collapse");
                toggle.className = "TFVS-Collapse";
            }
        }
        toggle.onmousedown = EXT_showHide;

        // Add in all the users and channels.    			   
        for (var i = 0; i < channel.childNodes.length; i++) {
            var child = channel.childNodes[i];
            // US is users, other is channels 
            if (child.nodeName == "US")
                this.showUsers(child, list);
            else
                this.showChannels(child, list);
        }

        // If our list element is empty, then we didn't have anything to put in it.
        // That means we don't have to add it to our parent.
        if (list.hasChildNodes()) {
            // Append the list to our parent.
            htmlParent.appendChild(list);
        } else {
            toggle.setAttribute("class", "TFVS-Empty");
            toggle.className = "TFVS-Empty";
        }
    }

    function EXT_showHide(e) {
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }

        var obj = null;

        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            obj.showHide(item);
        }
    }
    // Hides or shows the active channel.
    this.showHide = function(item) {

        // Grab the LI that contains the node we clicked on.
        var li = item.parentNode;
        // Grab the dive that we want to show/hide.
        var hideUL = li.childNodes[1];
        if (hideUL != null) {
            // If it's currently hidden, we need to expand it.
            if (hideUL.style.display == 'none') {
                hideUL.style.display = '';
                // Add us to our expandedArray so we know which are expanded.
                this.expandedArray.push(li.getAttribute("id"));
                item.className = "TFVS-Collapse";
            }
            // If we're Expanded, collapse us.
            else {
                hideUL.style.display = 'none';
                // Remove us from the expandedArray
                this.expandedArray.splice(this.getIndexOf(this.expandedArray, li.getAttribute("id")), 1);
                item.className = "TFVS-Expand";
            }
            this.UpdateGrabber(false);
        }
    }

    // Returns the index of the specified item in the array, or -1 if it isn't in the array.
    this.getIndexOf = function(array, item) {
        var ret = -1;
        for (var x = 0; x < array.length; x++) {
            if (array[x] == item) {
                ret = x;
                break;
            }
        }
        return ret;
    }

    // Returns true if this item has a classname attributed to a Channel, or false if it doesn't.
    this.isChannel = function(item) {
        var ret = false;
        if (item.className == "TFVS-Channel"
        || item.className == "TFVS-ChannelLocked"
        || item.className == "TFVS-ChannelAuthorization") {
            ret = true;
        }
        return ret;
    }

    // Returns true if this item has a classname attributed to a User, or false if it doesn't.
    this.isUser = function(item) {
        var ret = false;
        if (item.className == "TFVS-User"
        || item.className == "TFVS-Admin"
        || item.className == "TFVS-User Phantom"
        || item.className == "TFVS-Admin Phantom") {
            ret = true;
        }
        return ret;
    }

    // Returns true if this item has a classname attributed to a Server, or false if it doesn't.
    this.isServer = function(item) {
        var ret = false;
        if (item.className == "TFVS-Server"
        || item.className == "TFVS-ServerLocked"
        || item.className == "TFVS-ServerAuthorization") {
            ret = true;
        }
        return ret;
    }
    function EXT_itemClicked(e) {
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }

        var obj = null;

        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            obj.itemClicked(item);
        }
        return false;
    }
    // Selects the item that is clicked on.
    this.itemClicked = function(item) {
        var el = item.parentNode;
        // If it's selected already, deselect it.
        if (el.className == "TFVS-SelectedInfo") {
            if (!this.overInfo) {
                el.className = "TFVS-Info";
                this.selectedItem = null;
            }
        }
        // Same, but for channels.
        else if (el.className == "TFVS-Selected") {
            el.className = "";
            this.selectedItem = null;
        }
        // Otherwise select it.
        else {
            // Deselect the currently selected one first.
            if (this.selectedItem != null) {
                if (this.selectedItem.className == "TFVS-SelectedInfo")
                    this.selectedItem.className = "TFVS-Info";
                else
                    this.selectedItem.className = "";
            }
            // Then select the new one.
            this.selectedItem = el;
            if (el.className == "TFVS-Info")
                el.className = "TFVS-SelectedInfo";
            else
                el.className = "TFVS-Selected";
        }

        if (this.overInfo) {
            this.ShowHoverInfo(el);
        }
    }
    function EXT_StartScrollingUp(e) {
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }

        var obj = null;

        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            obj.StartScrollingUp(e);
        }
    }
    // Called when the scroll up button is pressed.
    this.StartScrollingUp = function() {
        // Tells any previous up scrolling to stop.
        this.StopScrollingUp();
        // Tells any previous up scrolling to stop.
        this.StopScrollingDown();
        // Handles granular scrolling for single clicking
        this.ScrollUp();
        // In 300 milliseconds, start auto-scrolling
        this.scrollTimeout = setTimeout("TFVS_TimerEnd('" + this.id + "','subStartScrollUp');", 300);
    }

    // Called from our timeout, as long as it wasn't cancled.
    this.subStartScrollUp = function() {
        // Simply starts the interval call every 10 milliseconds
        this.scrollInterval = setInterval("TFVS_TimerEnd('" + this.id + "','ScrollUp');", 10); 
    }

    // Scrolls our container up by 3 pixels
    this.ScrollUp = function() {
        // Grab our server node.
        var item = this.treeWrap;
        if (item == null) return;
        // Change by 3 pixels our scrollTop
        var scrollToMe = item.scrollTop - 3;
        // As long as we arn't at the top let,
        if (scrollToMe >= 0) {
            // Set us to our new value
            item.scrollTop = scrollToMe;
            // Update our grabber based on our TreeWrap's view location/total size ration
            this.UpdateGrabber(item.scrollTop / (item.scrollHeight - item.offsetHeight));
        }
        else {
            // Set us to the top
            item.scrollTop = 0;
            // Update our grabber based on our TreeWrap's view location/total size ration
            this.UpdateGrabber(item.scrollTop / (item.scrollHeight - item.offsetHeight));
            // Stop the auto scrolling if it's occuring.
            this.StopScrollingUp();
        }
    }
    
    function EXT_StopScrollingUp(e) {
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }

        var obj = null;

        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            obj.StopScrollingUp(e);
        }
    }

    // Stops the auto scroll. (Does the same as stop down)
    this.StopScrollingUp = function() {
        // Stop the interval call
        if (this.scrollInterval != null) {
            clearInterval(this.scrollInterval);
            this.scrollInterval = null;
        }
        // Stop the timout call.
        if (this.scrollTimeout != null) {
            clearTimeout(this.scrollTimeout);
            this.scrollTimeout = null;
        }
    }

    function EXT_StartScrollingDown(e) {
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }

        var obj = null;

        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            obj.StartScrollingDown(e);
        }
    }
    // Called when the scroll down button is pressed.
    this.StartScrollingDown = function() {
        // Tells any previous down scrolling to stop.
        this.StopScrollingUp();
        // Tells any previous down scrolling to stop.
        this.StopScrollingDown();
        // Handles granular scrolling for single clicking
        this.ScrollDown();
        // In 300 milliseconds, start auto-scrolling
        this.scrollTimeout = setTimeout("TFVS_TimerEnd('" + this.id + "','subStartScrollDown');", 300);
    }

    // Called from our timeout, as long as it wasn't cancled.
    this.subStartScrollDown = function() {
        // Simply starts the interval call every 10 milliseconds
        this.scrollInterval = setInterval("TFVS_TimerEnd('" + this.id + "','ScrollDown');", 10);
    }

    // Scrolls our container down by 3 pixels
    this.ScrollDown = function() {
        // Grab our server node.
        var item = this.treeWrap;
        if (item == null) return;
        // Change by 3 pixels our scrollTop
        var scrollToMe = item.scrollTop + 3;

        // Adjust by our button's size.
        var scrollHeight = (item.scrollHeight - item.offsetHeight);

        // As long as we arn't at the bottom yet,
        if (scrollToMe <= scrollHeight) {
            // Set us to our new value
            item.scrollTop = scrollToMe;
            // Update our grabber based on our TreeWrap's view location/total size ration
            this.UpdateGrabber(item.scrollTop / scrollHeight);
        }
        else {
            // Set us to the bottom
            item.scrollTop = item.scrollHeight;
            // Update our grabber based on our TreeWrap's view location/total size ration
            this.UpdateGrabber(item.scrollTop / scrollHeight);
            // Stop the auto scrolling if it's occuring.
            this.StopScrollingDown();
        }
    }

    function EXT_StopScrollingDown(e) {
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }

        var obj = null;

        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            obj.StopScrollingDown(e);
        }
    }
    // Stops the auto scroll. (Does the same as stop up)
    this.StopScrollingDown = function() {
        // Stop the interval call
        if (this.scrollInterval != null) {
            clearInterval(this.scrollInterval);
            this.scrollInterval = null;
        }
        // Stop the timout call.
        if (this.scrollTimeout != null) {
            clearTimeout(this.scrollTimeout);
            this.scrollTimeout = null;
        }
    }

    // Sets the selected element to the variable this.selectedObj for use.
    this.setSelectedElem = function(evt) {
        // Gets the target, for either browser type.
        var target = (evt.target) ? evt.target : evt.srcElement;
        // Sets the target to the correct target instead of to something worthless like a textnode.
        target = (target.nodeType && target.nodeType == 3) ? target.parentNode : target;
        // Find out if this is our scroll grabber.
        if (target == this.scrollGrabber) {
            // It was, so set it correctly.
            this.selectedObj = this.scrollGrabber;
        } else {
            // Didn't find it correctly
            this.selectedObj = null;
        }
    }

    function EXT_ScrollGrabbingDown(e) {
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }

        TFVS_Dragging = true; // Turn on global so we know when the release happens no matter where.

        var obj = null;

        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            // Disable FireFox's default "Drag image" behavior.
            if (obj.ScrollGrabbingDown(e) && e.preventDefault)
            {
                e.preventDefault();
            }
        }
    }
    
    // Called when we press down on the grabber.
    this.ScrollGrabbingDown = function(evt) {
        // grab our selectedObj
        evt = (evt) ? evt : event;
        this.setSelectedElem(evt);

        if (this.selectedObj) {
            // Stop any autoscrolling.
            this.StopScrollingUp();
            this.StopScrollingDown();

            var selectedObj = this.selectedObj;

            // Hookup our events.
            if (document.body && document.body.setCapture) {
                // engage event capture in IE/Win
                document.body.setCapture();
            }
            // Set our offsets.
            if (evt.pageY) {
                this.GrabOffset = evt.pageY - ((selectedObj.offsetTop) ?
                      selectedObj.offsetTop : selectedObj.top);
            } else if (typeof evt.clientY != "undefined") {
                this.GrabOffset = evt.clientY - ((selectedObj.offsetTop) ?
                      selectedObj.offsetTop : 0);
            }
            this.GrabberIsDown = true;
            return true;
        }
        return false;
    }

    function EXT_ScrollGrabbingUp(e) {
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }

        TFVS_Dragging = false;

        var obj = null;

        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            // Disable FireFox's default "Drag image" behavior.
            if (obj.ScrollGrabbingUp(e) && e.preventDefault)
            {
                e.preventDefault();
            }
        }
    }
    
    // Called when we release our grabber
    this.ScrollGrabbingUp = function() {
        this.GrabberIsDown = false;
        if (this.selectedObj) {
            // unhook our events.
            if (document.body && document.body.releaseCapture) {
                // stop event capture in IE/Win
                document.body.releaseCapture();
            }
            this.selectedObj = null;
            return true;
        }
        return false;
    }
    function EXT_ScrollGrabbingMove(e) {
        // If we have no buttons pressed, then we should stop this drag operation
        if (!TFVS_Dragging)
            return;
            
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }
        var obj = null;

        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            // Disable FireFox's default "Drag image" behavior.
            if (obj.ScrollGrabbingMove(e) && e.preventDefault)
            {
                e.preventDefault();
            }
        }
    }
    
    // Called when we move our mouse while grabbing
    this.ScrollGrabbingMove = function(e) {
        // Grab the element we've clicked on
        var item
        e = (e) ? e : event;
        
        if (this.selectedObj && this.GrabberIsDown) {
            item = this.selectedObj;
            // Find the mouse's Y position
            var y = 0;
            if (e.pageY) {
                y = e.pageY;
            }
            else {
                y = e.clientY;
            }
            // Calculate where we want to move the grabber to based on our mouse's position and
            // The grabber's offset.
            var moveGrabberTo = y - this.GrabOffset;
            //        window.status = item.parentNode.style.top;

            // Bounds check the grabber so we don't throw it out of the track.
            var top = (item.parentNode.childNodes[0].offsetHeight);
            var bottom = (item.parentNode.childNodes[1].offsetTop - item.offsetHeight);
            moveGrabberTo = (moveGrabberTo >= top) ? moveGrabberTo : top;
            moveGrabberTo = (moveGrabberTo <= bottom) ? moveGrabberTo : bottom;

            // Set it's position
            item.style.top = moveGrabberTo + "px";

            // Update the scroll area.
            this.UpdateScroll((moveGrabberTo - top) / (bottom - top));

            return true;
        }
        return false;
    }

    // Updates the TreeWrap's scrolling view to match the grabber.
    this.UpdateScroll = function(percShow) {
        // Get our TreeWrap
        var item = this.treeWrap
        if (item == null) return;
        // Calculate where we should scroll to in our treeWrap based on our precShow value.
        var scrollToMe = Math.floor((item.scrollHeight - item.offsetHeight) * percShow);
//        status = scrollToMe + " from Math.floor((" + item.scrollHeight + " - " + item.offsetHeight + ") * " + percShow + "";
        // Bounds check and set.
        if (scrollToMe >= 0 && scrollToMe <= item.scrollHeight) {
            item.scrollTop = scrollToMe;
        } else if (scrollToMe < 0) {
            item.scrollTop = 0;
        } else if (scrollToMe > item.scrollHeight) {
            item.scrollTop = item.scrollHeight;
        }
    }

    // Refreshes our TreeWrap's scrolling view to match the grabber. Done after we refresh/rebuild the tree.
    this.RefreshScroll = function() {

        // Get our grabber, and calculate our track's top and bottom
        var item = this.scrollGrabber
        var top = (item.parentNode.childNodes[0].offsetHeight);
        var bottom = (item.parentNode.childNodes[1].offsetTop - item.offsetHeight);

        // Calc our percentage showing.
        var percShow = (item.offsetTop - top) / (bottom - top);
        // Call the refresh based on a percentage location of the grabber.
        this.UpdateScroll(percShow);
    }


    // Updates our Grabber to match our view.
    this.UpdateGrabber = function(percShow) {
        // If we didn't pass in a valid percShow, we need to calculate it.
        if (!percShow) {
            // Grab the TreeWrap value
            var scroll = this.treeWrap;
            if (scroll == null) return;
            // Calculate and bounds check our precShow
            percShow = scroll.scrollTop / (scroll.scrollHeight - scroll.offsetHeight);
            if (percShow > 1)
                percShow = 1;
        }
        // Get our grabber, and calculate our track's top and bottom
        var item = this.scrollGrabber;
        var top = (item.parentNode.childNodes[0].offsetHeight);
        var bottom = (item.parentNode.childNodes[1].offsetTop - item.offsetHeight);

        // Set our grabber's position based on the precShow.
        var moveGrabberTo = top;
        if (percShow) {
            moveGrabberTo = Math.floor(percShow * (bottom - top) + top);
        }
        item.style.top = moveGrabberTo + "px";
    }

    // Assign event handlers used by both Navigator and IE
    this.initDrag = function() {
        if (document.layers) {
            // turn on event capture for these events in NN4 event model
            document.captureEvents(Event.MOUSEDOWN | Event.MOUSEMOVE | Event.MOUSEUP);
            return;
        }
        else if (document.body & document.body.addEventListener) {
            // turn on event capture for these events in W3C DOM event model
            document.addEventListener("mousedown", engage, true);
            document.addEventListener("mousemove", dragIt, true);
            document.addEventListener("mouseup", release, true);
            return;
        }
        
        document.onmousedown = EXT_ScrollGrabbingDown;
        document.onmousemove = EXT_ScrollGrabbingMove;
        document.onmouseup = EXT_ScrollGrabbingUp;
        return;
    }

    this.WaitForReturn = function() {
        // Try to grab our xmlData
        var updateObj = TFVS_StatusUpdateList.Retrieve(this.id);
        if (updateObj != null)
            this.xmlData = updateObj.xmlData;

        // If we finally have data, or if we reached our timeout, kill the interval and update.
        if (this.xmlData != null || this.refreshTimeout >= 100) {
            if (this.refreshInterval != null) {
                clearInterval(this.refreshInterval);
                this.refreshInterval = null;
            }
            this.ReplaceData();
        }
        this.refreshTimeout++;
    }

    function EXT_Refresh(e) {
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }

        var obj = null;
        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            obj.Refresh(e);
        }
        return false;
    }

    // Refreshes the contents of the tree.
    this.Refresh = function() {
        this.xmlData = null;
        // Reload our XML file.
        this.getXMLData();

        this.refreshTimeout = 0;
        if (this.refreshInterval == null) {
            this.refreshInterval = setInterval("TFVS_TimerEnd('" + this.id + "','WaitForReturn');", 100);
        }
    }
    
    this.ReplaceData = function() {
    
        this.server = null;
        if (window.ActiveXObject) // code for IE
        {
            this.xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
            this.xmlDoc.async = "false";

            // make sure we got something back
            if (this.xmlData != null && this.xmlData != "") {
                this.xmlDoc.loadXML(this.xmlData);
                this.server = this.xmlDoc.documentElement;
            }
        }
        else if (document.implementation.createDocument) // code for Firefox, Mozilla, Opera, etc.
        {
            if (this.xmlData != null && this.xmlData != "") {
                var parser = new DOMParser();
                this.xmlDoc = parser.parseFromString(this.xmlData, "text/xml");
                this.server = this.xmlDoc.documentElement;
            }
            else {
                this.xmlDoc = document.implementation.createDocument("", "", null);
            }
        }

        // Update the server's info.
        this.updateServer();
    }

    // Updates the server state of the tree when we refresh
    this.updateServer = function() {
        // If we have a selected item, we need to remember it.
        if (this.selectedItem != null) {
            // If the server is selected, assign a "fake" id for it.
            // Otherwise save our actual ID.
            if (this.isServer(this.selectedItem.parentNode))
                this.selectedID = "server";
            else
                this.selectedID = this.selectedItem.parentNode.parentNode.parentNode.getAttribute("id");
            this.selectedItem = null;
        }
        else {
            this.selectedID = null;
        }


        // Grab our server node.
        var item = this.treeBackground;

        // Find TreeWrap and clear it out.
        for (var x = item.childNodes.length - 1; x >= 0; x--) {
            if (item.childNodes[x].className == "TFVS-TreeWrap"
           || item.childNodes[x].className == "TFVS-Unavailable"
           || item.childNodes[x].className == "TFVS-Loading")
                item.removeChild(item.childNodes[x]);
        }

        // Rebuild the server.

        if (this.server != null) {
            // Store our old array of expanded IDs, and reset it to empty.
            this.oldExpandArray = this.expandedArray;
            this.expandedArray = new Array();
            this.showServer(item);
            this.RefreshScroll();
        }
        else
            this.showUnavailable(item);
    }

    function EXT_JoinServer(e) {
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }

        var obj = null;

        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            obj.JoinServer(e);
        }
        return false;
    }

    // Opens Ventrilo and connects the user to the server represented here.
    this.JoinServer = function() {
        // Grab our server info.
        if (this.server != null) {
            var hostName = this.server.getAttribute("HN");
            var port = this.server.getAttribute("P");
            var name = this.server.getAttribute("N");
            var channel = "";

            // Open Vent.
            window.location.href = "ventrilo://" + hostName + ":" + port + "/servername=" + name;
        }
    }

    this.EXT_updateMouseLeave = function(e) {
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }

        var obj = null;

        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            obj.updateMouseLeave(item);
        }
    }

    // Track the info button stuff, and change our cursor appropriately.
    this.updateMouseLeave = function(item) {

        // Mark as no longer hovering.
        this.overInfo = false;
        this.HideHoverInfo();
        // Change mouse back.
        item.style.cursor = "default";
    }

    this.EXT_updateMouseMove = function(e) {
        // Grab the item we were hovering over.
        var item
        if (!e) // Internet Explorer
        {
            e = event;
            item = e.srcElement;
        } else // FireFox
        {
            item = e.target;
        }

        var obj = null;

        if (item != null && item != undefined && item.ownerId != undefined && item.ownerId != null) {
            obj = TFVS_IDToObject[item.ownerId];
            obj.updateMouseMove(item, e);
        }
    }
    // Track the info button stuff, and change our cursor appropriately.
    this.updateMouseMove = function(item, e) {
        var pos = this.findPos(item, false);
        // Find out where our mouse is, and if it's over our hotspot.
        if (((pos[0] + item.offsetWidth) - e.clientX - document.documentElement.scrollLeft + 6) < 20) // We'll guestimate it is here at 20 pixels in.
        {
            // Mark us as hovering, and change our mouse.
            this.overInfo = true;
            item.style.cursor = "pointer";
        } else {
            // Mark us as no longer hovering, and change our mouse back.
            this.overInfo = false;
            this.HideHoverInfo();
            item.style.cursor = "default";
        }
    }

    // Show the hover information contained in the node.
    this.ShowHoverInfo = function(hoverItem) {
        // Grab our hidden hover info section that we'll display things in.
        var hover = this.hoverInfo;

        // Grab the container that we scroll in, so we can get the scroll offset.
        var scrollDiv = this.treeWrap;

        // Set the position of the hover-over popup
        var offset = this.findPos(hoverItem, true);
        hover.style.left = (offset[0]) + "px";
        hover.style.top = (offset[1] + hoverItem.offsetHeight - scrollDiv.scrollTop) + "px";
        hover.style.width = hoverItem.offsetWidth + "px";
        hover.style.zIndex = 1000; // Just to make sure it's over everything...

        // Grab the content node where we'll put the actual info objects into.
        var content = this.hoverInfo_content;
        // If anything is in there right now, it needs to be removed.
        if (content.hasChildNodes()) {
            content.removeChild(content.firstChild);
        }

        // Grab the info from this item we're hovering over.
        var infoNode = hoverItem.parentNode.parentNode.parentNode.lastChild;
        // Take it out of the object we're hovering over so we can copy.
        infoNode.parentNode.removeChild(infoNode);
        // Make a copy of it.. (It can be inside of something when we copy it seems)
        var copyNode = infoNode.cloneNode(true);
        // Put it back in where it was 
        hoverItem.parentNode.parentNode.parentNode.appendChild(infoNode);
        // Set its display to default so we can see it
        copyNode.style.display = '';

        // Add it into our popup.
        content.appendChild(copyNode);

        // Set popup to visible
        hover.style.visibility = "Visible";
    }

    // Hids the hover-over popup.
    this.HideHoverInfo = function() {
        // Grab our root hover object
        var hover = this.hoverInfo;
        // And set it to not be visible.
        hover.style.visibility = "Hidden";
    }

    // Grabs the real position of this node based on its parents, leading only up to our root parent.
    // Returns a 2 element array of [leftOffset, topOffset]
    this.findPos = function(obj, toWrap) {
        var curString = "";
        // The distance from the left, and distance from the top.
        var curtop = 0;
        var curleft = 0;
        // As long as this object has a parent,
        if (obj.offsetParent) {
            // Keep adding in parent objects' left offsets and top offsets to our cur values.
            do {
                curleft += obj.offsetLeft;
                curtop += obj.offsetTop;
                // Stop when we don't have a parent anymore, or our parent is TFVS-Wrap.
            } while ((obj = obj.offsetParent) && (!toWrap || obj.getAttribute("class") != "TFVS-Wrap"));
        }
        // If we don't have a parent, just add in our x/y value.
        else if (obj.x) {
            curString += "{" + obj.getAttribute("class") + ": " + obj.x + ", " + obj.y + "} ";
            curleft += obj.x;
            curtop += obj.y;
        }
        // Return an array with our two values.
        return [curleft, curtop];
    }

    // Convers the given string to a descriptive timespan.
    this.ConvertTimeSpan = function(timeOnline) {
        // Create a regular-expression to capture the different values.
        var ret = "";
        var regex = new RegExp("(\\d+)?.?(\\d+)?:?(\\d+)?:?(\\d+)?");
        var results = regex.exec(timeOnline);
        var x = 0;
        // For each value captured, cycle through and assign seconds, minutes, hours, days to it.
        for (var i = results.length - 1; i > 0; i--) {
            // If this value isn't any good, skip it.
            if (results[i] != "" && results[i] != null) {
                var v = "";
                var val = results[i];
                // Strip off any leading 0s
                var regex2 = new RegExp("[0]*(\\d+)");
                var results2 = regex2.exec(val);
                if (results2 != null && results2[1] != "" && results2[1] != null)
                    val = results2[1];

                // Increment to the next time-type.
                x++;
                // Add on our time type withotu pluralization, as long as there's a time for it.
                if (x == 1 && val != "0")
                    v = val + " Second";
                else if (x == 2 && val != "0")
                    v = val + " Minute";
                else if (x == 3 && val != "0")
                    v = val + " Hour";
                else if (x == 4 && val != "0")
                    v = val + " Day";

                // As long as we assigned something,
                if (v != "") {
                    // Make it plural if it isn't one
                    if (val != "1")
                        v += "s";

                    // And append it appropriately to our return string.
                    if (ret != "")
                        ret = v + ", " + ret;
                    else
                        ret = v;
                }
            }
        }
        return ret;
    }
}

TFVS_VentStatus.prototype.verifySecurityLinkExists = TFVS_verifySecurityLinkExists;
