function CategoryTree(settings)
{
    var self = this;

    this.treeExpandId = settings.treeExpandId;

    if(settings.selectedPath){
        this.categoryId = settings.categoryId;
    }
    else{
        this.categoryId = settings.time + "-" + settings.categoryId;
    }


    // Set a new tree id
    var newTreeId = 'li_' + this.categoryId;
    this.treeId   = newTreeId
    document.getElementById(settings.treeId).setAttribute('id', newTreeId);
    //

    this.expansions = new Array();
    this.expandedIds = new Array();

    this.groupByLetter           = settings.groupByLetter != undefined ? settings.groupByLetter : false;
    this.groupByLetterMinResults = settings.groupByLetterMinResults != undefined && this.groupByLetter ? settings.groupByLetterMinResults : 10;
    this.selectedPath            = settings.selectedPath != undefined ? settings.selectedPath : false;

    this.isExpanded = false;
    this.registeredTrees = new Array(); // holds all root level trees on the page

    this.preSearchNodes = new Array();

    this.pendingChildrenRequest = false;

    /***************************/
    /***** Event Listeners *****/
    /***************************/

    /*
     * Triggered when expanding a category
     *
     * @param categoryId {int}      category id we're expanding
     *
     */
     this.onExpand = function(categoryId)
     {
        if (self.pendingChildrenRequest == true)
            return false;

        // If expansion doesn't exist we're expanding for the first time so do an httprequest
        if (!self.expansions[categoryId])
        {
            var group = self.groupByLetter && self.categoryId == categoryId;
            self.toggleExpandChar(categoryId);
            $.getJSON("/forums/query/get_category_children.php", { cid : categoryId, groupResults : group }, self.onExpand_Callback);

            self.pendingChildrenRequest = true;

            if (categoryId == self.categoryId)
                self.isExpanded = true;
        }
        // Expansion exists
        else
        {
            var categoryRef = document.getElementById('li_' + categoryId);

            // collapse (remove)
            if (self.expandedIds[categoryId])
            {
                self.toggleExpandChar(categoryId);
                $('#ul_' + categoryId).remove();
                self.expandedIds[categoryId] = false;

                if (categoryId == self.categoryId)
                    self.isExpanded = false;
            }
            // expand
            else
            {
                self.toggleExpandChar(categoryId);

                if(self.expansions[categoryId].swapNode)   // must be IE
                {
                    var d = document.getElementById('li_' + categoryId);

                    d.parentNode.insertBefore(self.expansions[categoryId], d);
                    self.expansions[categoryId].swapNode(d);
                }
                else
                {
                    $('#li_' + categoryId).after(self.expansions[categoryId]);
                }

                self.expandedIds[categoryId] = true;

                if (categoryId == self.categoryId)
                    self.isExpanded = true;
            }
        }
    }

    this.expandOtherTrees = function()
    {
        // close other expanded root level categories
        for (var i = 0; i < this.registeredTrees.length; i++)
        {
            if (this.registeredTrees[i].categoryId != this.categoryId)
            {
                if (this.registeredTrees[i].isExpanded)
                this.registeredTrees[i].onExpand(this.registeredTrees[i].categoryId);
            }
        }
    }

    /**************************/
    /***** Bind Listeners *****/
    /**************************/

    if(document.attachEvent)
    {
        document.getElementById(this.treeId).attachEvent('onclick', function() { self.expandOtherTrees(); self.onExpand(self.categoryId) }, true);
    }
    else
    {
        document.getElementById(this.treeId).addEventListener('click', function() { self.expandOtherTrees(); self.onExpand(self.categoryId) }, true);
    }

    /******************************/
    /***** Callback Functions *****/
    /******************************/

    this.onExpand_Callback = function(json)
    {
        var children   = json.children;
        var categoryId = json.request_id;
        var indent = 1;
        var categoryRef = document.getElementById('li_' + categoryId);

        // find indent
        ref = categoryRef;

        while (ref.parentNode)
        {
             if(ref.getAttribute('id'))
             {
                var s = new String(ref.getAttribute('id'));

                if (s.match(/^(c|s)_li_/))
                {
                    indent++;
                }
             }

             ref = ref.parentNode;
        }

        // build a list of children
        var newExpansion = self.buildList(children, categoryId, indent, json.grouped);

        // save the html created for the new children
        self.expansions[categoryId] = newExpansion;
        self.expandedIds[categoryId] = true;

        if (newExpansion.swapNode)   // must be IE
        {
            categoryRef.parentNode.insertBefore(newExpansion, categoryRef);
            newExpansion.swapNode(categoryRef);
        }
        else
        {
            $(categoryRef).after(newExpansion);
        }

        self.pendingChildrenRequest = false;
    }



    /*********************/
    /***** Functions *****/
    /*********************/

     this.openSelectedPath = function(categoryId)
     {
         if (!self.selectedPath)
             return false;

         var thisBranch = null;

         // get the category id to display the children for
         categoryId = (categoryId == undefined) ? self.selectedPath.category_id : categoryId;

         // get the category branch object
         var branch = self.selectedPath
         while (thisBranch == null) {
             if (categoryId == branch.category_id) {
                 thisBranch = branch;
                 break;
             }

             if (branch.children.length) {
                 for (i = 0; i < branch.children.length; i++) {
                     if (categoryId == branch.children[i].category_id) {
                         thisBranch = branch.children[i];
                         break;
                     }
                 }

                 branch = branch.children[0];
             } else {
                 return;
             }
         }

         var children = branch.children;
         var grouped = branch.grouped;
         var indent = 1;
         var categoryRef = document.getElementById('li_' + categoryId);

         // build a list of children
         var newExpansion = self.buildList(children, categoryId, indent, grouped);

         // save the html created for the new children
         self.expansions[categoryId] = newExpansion;
         self.expandedIds[categoryId] = true;

         if(newExpansion.swapNode)   // must be IE
         {
             categoryRef.parentNode.insertBefore(newExpansion, categoryRef);
             newExpansion.swapNode(categoryRef);
         }
         else
         {
             $(categoryRef).after(newExpansion);
         }

         if (categoryId == self.categoryId) {
             self.isExpanded = true;
         }

         self.toggleExpandChar(categoryId);

         if (branch.children[0].children.length) {
             self.openSelectedPath(branch.children[0].category_id);
         }
     }

    this.clearCategories = function()
    {
         // clone all nodes except first (search)
         var ref = document.getElementById('ul_' + this.categoryId);
         var nodes = ref.childNodes[0].childNodes;
         var listBuilt = false;

         var i = 1;
         while (ref.childNodes[0].childNodes.length > 2)
         {
            listBuilt = true;
            var id = ref.childNodes[0].lastChild.getAttribute('id');

            this.preSearchNodes[i - 1] = ref.childNodes[0].lastChild.cloneNode(true);

            // cloneNode doesn't clone onclick functions (yayy javascript) so copy it over
            this.preSearchNodes[i - 1].childNodes[0].onclick = ref.childNodes[0].lastChild.childNodes[0].onclick;

            ref.childNodes[0].removeChild(document.getElementById(id));
            i++;
         }

         // reverse array
         if (listBuilt) {
             var t = new Array();

             for (var i = 0; i < this.preSearchNodes.length; i++)
             {
                t[i] = this.preSearchNodes[this.preSearchNodes.length - 1 - i];
             }

             this.preSearchNodes = t;
         }
    }

    this.unsearch = function ()
    {
        // remove search result nodes
        var ref = document.getElementById('ul_' + this.categoryId).childNodes[0].childNodes[1];
        ref.innerHTML = '';
        ref.className = 'ieSearchFixNoResults';

        // restore nodes
        var ref = document.getElementById('ul_' + this.categoryId).childNodes[0];

        for (var i = 0; i < this.preSearchNodes.length; i++)
        {
            var id = this.preSearchNodes[i].getAttribute('id');

            ref.appendChild(this.preSearchNodes[i]);
        }
    }

    this.registerTrees = function(categories)
    {
        this.registeredTrees = categories;
    }

    this.buildSearchBox = function(settings)
    {
        var list = document.createElement('li');

        list.setAttribute('id', 'li_s_' + settings.categoryId);

        var searchBox = new SearchBox(settings);
        var searchBoxHtml = searchBox.getHtml();

        list.appendChild(searchBoxHtml);

        return list;
    }

    this.buildSearchBoxResults = function(categoryId)
    {
        var listResults = document.createElement('li');

        listResults.setAttribute('id', 'li_s_r_' + categoryId);
        listResults.className = 'ieSearchFixNoResults';

        return listResults;
    }

    this.toggleExpandChar = function(categoryId)
    {
        var t = $("#li_" + categoryId);

        var className = new String(document.getElementById('li_' + categoryId).className);

        if (className.indexOf('highlighted') == 0)
            document.getElementById('li_' + categoryId).className = className.substring('highlighted '.length - 1);
        else
            document.getElementById('li_' + categoryId).className = 'highlighted ' + className;
    }

    this.buildList = function(categories, parentCategoryId, indent, grouped)
    {
        var listOuterContainer = document.createElement('li');
        var listContainer      = document.createElement('ul');

        listOuterContainer.setAttribute('id', 'ul_' + parentCategoryId);
        if (self.selectedPath)
            listContainer.className = 'customPath';

        // build search box if root level
        if (parentCategoryId == self.categoryId)
        {
            searchSettings =
            {
                searchBoxId : 'searchBox' + this.categoryId,
                searchResultId : 'li_s_r_' + this.categoryId,
                insertResultsAfterId : 'li_s_' + this.categoryId,
                onResults : function() { self.clearCategories() },
                onClear : function() { self.unsearch() },
                categoryId : self.categoryId
            }

            var searchBox = self.buildSearchBox(searchSettings);
            var searchBoxResults = self.buildSearchBoxResults(self.categoryId);

            // append search box
            listContainer.appendChild(searchBox);
            listContainer.appendChild(searchBoxResults);
        }

        if (parentCategoryId == 3233)
        {
            var list = this.buildListItem(false, 'Grocery Coupon Forum', false, false, false, indent, false, false, false, false, false, '/forums/grocery-coupons/');
            listContainer.appendChild(list);
        }

        for (var i = 0; i < categories.length; i++)
        {
            var path = categories[i].path != undefined ? categories[i].path : new Array();

            var isCbStore = (categories[i].cashback && categories[i].cashback == '1');

            if (self.selectedPath && '' != categories[i].close_url)
                var list = this.buildListItem(categories[i].category_id, categories[i].name, isCbStore, categories[i].descendants, false, indent, categories[i].url_name, path, grouped, categories[i].close_url, categories[i].selected);
            else
                var list = this.buildListItem(categories[i].category_id, categories[i].name, isCbStore, categories[i].descendants, false, indent, categories[i].url_name, path, grouped);

            if (self.selectedPath && categories[i].selected)
                list.className += ' selectedBranch';

            listContainer.appendChild(list);

        }

        listOuterContainer.appendChild(listContainer);

        return listOuterContainer;
    }

    this.buildListItem = function(categoryId, categoryName, isCbStore, isParent, isExpanded, indent, urlCategoryName, path, grouped, closeUrl, branchSelected, url)
    {
        var list = document.createElement('li');
        var linkExpand = document.createElement('a');
        var linkCategory = document.createElement('a');

        if (path.length > 0) {
            // clear the first item in the path to increace compatability with older code
            path.shift();

            // define which root level category this path is for
            for (var i = 0; i < rootCats.length; i++) {
                var rootCat = rootCats[i];
                if (path[0].name == rootCat.name) {
                    break;
                }
            }
        }

        listIdPrefix = 'li_';

        //list.setAttribute('class', 'lvl' + (indent + 1));
        list.className = 'lvl' + (indent + 1);
        if (grouped)
            list.className += ' grouped';
        if (self.selectedPath && !branchSelected)
            list.className += ' openBranch';
        list.setAttribute('id', listIdPrefix + categoryId);

        var attachExpandedLink = false;
        attachExpandedLink = (isParent);

        linkExpand.href = 'javascript:void(0)';
        linkExpand.className = 'expand';
        if (isExpanded)
            linkExpand.className += ' highlighted';
        linkExpand.onclick = function() { self.onExpand(categoryId) };
        linkExpand.innerHTML = '&nbsp;';


        if (attachExpandedLink)
        {
            list.appendChild(linkExpand);
        }
        else
        {
            var shim = document.createElement('DIV');
            $(shim).css({'width':'12px', 'height':'12px', 'float':'left', 'margin':'0 0 -2px 4px'});
            list.appendChild(shim);
        }

        var textNode = document.createTextNode('');
        list.appendChild(textNode);

        var linkSpan = document.createElement('SPAN');

         if (isCbStore) {
             var imgNode = document.createElement('IMG');
             imgNode.setAttribute('src', StaticRoot + 'i/cb_icon.gif');
             imgNode.setAttribute('border', '0');
             linkCategory.appendChild(imgNode);
         }
        var textNode = document.createTextNode(categoryName);

        var selectedUrlNames = new Array();
        if (path.length > 0) {
            for (var i = 0; i < rootCats.length; i++) {
                if (path[0].name == rootCats[i].name) {
                    selectedUrlNames.push(urlCategoryName);
                } else {
                    selectedUrlNames.push(rootCats[i].selection.urlName);
                }
            }
        }

        if (url == undefined || !url)
        {
            linkCategory.href = buildCategoryUrl(selectedUrlNames, attributes);
        }
        else
        {
            linkCategory.href = url;
        }

        if (isExpanded)
            linkCategory.className = 'highlighted';

        linkCategory.appendChild(textNode);

        if (url != undefined)
        {
            var raquo = document.createTextNode(' \u00BB');

            linkCategory.appendChild(raquo);
        }

        if (branchSelected != undefined && branchSelected) {
            linkSpan.appendChild(textNode);
        }
        else if (!grouped) {
            linkSpan.appendChild(linkCategory);
        }
        else {
            linkSpan.appendChild(textNode);
        }

        list.appendChild(linkSpan);

        if (closeUrl != undefined && (url == undefined || !url)) {
            var linkClose = document.createElement('a');
            linkClose.href = closeUrl;
            linkClose.className = 'removeCat';
            linkClose.innerHTML = '&nbsp; &nbsp; &nbsp;';
            list.appendChild(linkClose);
        }

        return list;
    }
}


/*
* SearchBox Class - Provides functionality to search categories via textbox
* Dependant on JQuery
*
* @param settings {object}     object of settings name/values
*
**/
function SearchBox(settings)
{
   var self = this;

   this.id                   = settings.searchBoxId;
   this.searchResultId       = settings.searchResultId
   this.categoryId           = settings.categoryId;
   this.insertResultsAfterId = settings.insertResultsAfterId;
   this.minimumSearchChars   = settings.minimumSearchChars != undefined ? settings.minimumSearchChars : 2;
   this.onResults            = settings.onResults != undefined ? settings.onResults : function () {};
   this.onClear              = settings.onClear != undefined ? settings.onClear : function () {};

   this.queryText            = '';
   this.expandedIds          = new Array();
   this.popularCats          = new Array();

   /***************************/
   /***** Event Listeners *****/
   /***************************/

   /*
    * Triggered when typing happens within search box
    */
   this.onChangeSearch = function()
   {
       self.queryText = document.getElementById(self.id).value;

       if (self.queryText.length >= self.minimumSearchChars)
       {
           $.getJSON("/forums/query/search_category_children.php", { cid : self.categoryId, query : URLEncode(self.queryText) }, self.onChangeSearch_Callback);
       }
       else if(self.queryText.length == 0)
       {
           this.onClear();
       }
   }

   /******************************/
   /***** Callback Functions *****/
   /******************************/

   this.collapseCategory = function(categoryId)
   {
       // expand
       var expansionKey = categoryId;

       if (!self.expansions[expansionKey])
       {
           self.toggleExpandChar(categoryId);

           if (!inArray(categoryId, self.popularCats))
           {
               $.getJSON("/forums/query/get_category_children.php", { cid : categoryId, return_type : 'search' }, self.collapseCategory_Callback);
           }
           else
           {
               $.getJSON("/forums/query/get_category_children.php", { cid : categoryId, popular : 1, return_type : 'search' }, self.collapseCategory_Callback);
           }
       }
       // collapse or respand
       else
       {
           var categoryRef = document.getElementById('s_li_' + categoryId);

           if (self.expandedIds[categoryId])
           {
               var t = document.getElementById('s_ul_' + categoryId);
               self.expansions[expansionKey] = t.cloneNode(true)

               var copyFrom = t.getElementsByTagName('A');
               var copyTo = self.expansions[expansionKey].getElementsByTagName('A');

               for (var i = 0; i < copyTo.length; i++)
               {
                   copyTo[i].onclick = copyFrom[i].onclick;
               }

               self.toggleExpandChar(categoryId);
               $('#s_ul_' + categoryId).remove();

               self.expandedIds[categoryId] = false;
           }
           // expand
           else
           {
               self.toggleExpandChar(categoryId);
               $('#s_li_' + categoryId).after(self.expansions[categoryId]);
               self.expandedIds[categoryId] = true;
           }
       }
   }

   this.collapseCategory_Callback = function(json)
   {
       var categories = json.children;
       var categoryRef = document.getElementById('s_li_' + json.request_id);

       if (categories.length)
       {
           // find indent by looking at parent nodes for parent categories
           indent = 1;
           ref = categoryRef;

           while (ref.parentNode)
           {
                if(ref.getAttribute('id'))
                {
                   var s = new String(ref.getAttribute('id'));

                   if (s.match(/^(c|s)_li_/))
                   {
                       indent++;
                   }
                }
                ref = ref.parentNode;
           }

           listContainer = self.buildList(categories, json.request_id, indent);


           var expansionKey = json.request_id;

           // add to list so we dont have to request it anymore
           if (!self.expansions[expansionKey])
               self.expansions[expansionKey] = listContainer;

           $(categoryRef).after(listContainer);
           self.expandedIds[json.request_id] = true;
       }
   }

   this.toggleExpandChar = function (categoryId)
   {
       var t = $("#s_li_" + categoryId);

       var className = new String(document.getElementById('s_li_' + categoryId).className);

       if (className.indexOf('highlighted ') == 0)
           document.getElementById('s_li_' + categoryId).className = className.substring('highlighted '.length - 1);
       else
           document.getElementById('s_li_' + categoryId).className = 'highlighted ' + className;
   }

   this.sortSearchResults = function(nodes)
   {
       for (var i = 0; i < nodes.length; i++) {
           if (nodes[i].children.length) {
               self.sortSearchResults(nodes[i].children);
           }
       }

       if (1 < nodes.length) {
           for (var x = 0; x < nodes.length; x++) {
               for (var i = 1; i < nodes.length; i++) {
                   var a = nodes[i-1];
                   var b = nodes[i];

                   if (a.name > b.name) {
                       nodes[i] = a;
                       nodes[i-1] = b;
                   }
               }
           }
       }
   }

   this.outputResultBranch = function(nodes, parentElement, level, rootName)
   {
       for (var i = 0; i < nodes.length; i++) {
           var thisNode = nodes[i];
           var catId = thisNode.category_id;
           var catName = thisNode.name;
           var catUrlName = thisNode.url_name;
           var isExpanded = thisNode.is_expanded;
           var isParent = thisNode.is_parent;

           var isCbStore = (nodes[i].cashback && nodes[i].cashback == '1');

           listItem = self.buildListItem(catId, catName, isCbStore, isParent, isExpanded, level, catUrlName, [1], rootName);
           parentElement.appendChild(listItem);

           if (thisNode.children.length) {
               newList = self.buildList({ category_id : catId, name : catName, descendants : false }, catId);
               parentElement.appendChild(newList);
               self.outputResultBranch(thisNode.children, newList.childNodes[0], level+1, rootName);
           }

           if (isExpanded) {
               self.expandedIds[catId] = true;
           }
       }
   }

   this.onChangeSearch_Callback = function(json)
   {
       var searchResults = json.results;
       if (self.onResults)
           self.onResults();
       var results = json.results;
       var resultParents = json.resultMatchIsParent;
       var refResults = document.getElementById(self.searchResultId);
       refResults.className = 'ieSearchFixResults';

       self.resetSearchValues();
       refResults.innerHTML = '';

       if (results.length)
       {
           var arrBranches = new Array;
           var resultTree = {
               category_id : results[0].root.category_id,
               parent_id : 0,
               name : results[0].root.name,
               url_name : '',
               is_expanded : '',
               children : new Array
           };

           // build result structure
           for (var i = 0; i < results.length; i++) {
               var path = results[i].path;
               for (var j = 0; j < path.length; j++) {

                   // branch meta
                   var catId = path[j].category_id;
                   var catName = path[j].name;
                   var catUrlName = path[j].url_name;
                   var isExpanded = (j+1 < path.length);

                   if (j+1 < path.length)
                       var isParent = true;
                   else if (undefined != resultParents[catId])
                       var isParent = resultParents[catId];
                   else
                       var isParent = false;

                   if (0 == j) {
                       if (undefined == arrBranches[catId]) {
                           arrBranches[catId] = { category_id : catId, parent_id : resultTree.category_id, name : catName, url_name : catUrlName, is_expanded : isExpanded, is_parent : isParent, children : new Array };
                           resultTree.children.push(arrBranches[catId]);
                       }
                   } else {
                       var parentBranch = arrBranches[path[j-1].category_id];

                       if (undefined == arrBranches[catId]) {
                           arrBranches[catId] = { category_id : catId, parent_id : parentBranch.category_id, name : catName, url_name : catUrlName, is_expanded : isExpanded, is_parent : isParent, children : new Array };
                           parentBranch.children.push(arrBranches[catId]);
                           parentBranch.is_expanded = true; // force open result branches that have child results
                       }
                   }
               }
           }

           self.sortSearchResults(resultTree.children);

           // output results
           listContainer = document.createElement('UL');
           listContainer.setAttribute('id', 'searchListContainer');
           listContainer.setAttribute('style', 'margin-left: 0;');
           self.outputResultBranch(resultTree.children, listContainer, 1, resultTree.name);
           refResults.appendChild(listContainer);

           var li = $('#searchListContainer li');

           for (var i = 0; i < li.length; i++)
           {
               var id = li[i].getAttribute('id');

               var key = id.replace('s_ul_', '');
               var children = $('#' + id + ' UL');

               if (children.length)
               {
                   self.expansions[key] = children[0].cloneNode(true);
               }
           }
       }
       else
       {
           refResults.innerHTML = 'No results';
       }
   }

   this.resetSearchValues = function()
   {
       var t = Array();
       for (var temp in this.expandedIds)
       {
           if (temp.match(/^[0-9]+_[0-9]+$/))
               continue;

           t[temp] = this.expandedIds[temp];
       }
       this.expandedIds = t;

       var t = Array();
       for (var temp in this.expansions)
       {
           if (temp.match(/^[0-9]+_[0-9]+$/))
               continue;

           t[temp] = this.expansions[temp];
       }
       this.expansions = t;
   }


   this.buildListItem = function(categoryId, categoryName, isCbStore, isParent, isExpanded, indent, urlCategoryName, path, rootName)
   {
       var list = document.createElement('li');
       var linkExpand = document.createElement('a');
       var linkCategory = document.createElement('a');

        if (path.length > 1) {
            // clear the first item in the path to increace compatability with older code
            path.shift();

            // define which root level category this path is for
            for (var i = 0; i < rootCats.length; i++) {
                var rootCat = rootCats[i];
                if (path[0].name == rootCat.name) {
                    break;
                }
            }
        }

       listIdPrefix = 's_li_';

       //list.setAttribute('class', (isExpanded ? 'highlighted ' : '') + 'lvl' + (indent + 1));
       list.className = (isExpanded ? 'highlighted ' : '') + 'lvl' + (indent + 1);
       list.setAttribute('id', listIdPrefix + categoryId);

       var attachExpandedLink = false;
       // if expanded then its a parent
       if (isExpanded)
       {
           //var textNode = document.createTextNode('-');
           attachExpandedLink = true;
       }
       else
       {
           if (isParent)
           {
               //var textNode = document.createTextNode('+');
               attachExpandedLink = true;
           }
           else
           {
               //var textNode = document.createTextNode('*');
               attachExpandedLink = false;
           }
       }

       linkExpand.href = 'javascript:void(0)';
       linkExpand.className = 'expand';
       if (isExpanded)
           linkExpand.className += ' highlighted';
       linkExpand.onclick = function() { self.collapseCategory(categoryId) };
       linkExpand.innerHTML = '&nbsp;';

       if (attachExpandedLink)
       {
           list.appendChild(linkExpand);
       }
       else
       {
            var shim = document.createElement('DIV');
            $(shim).css({'width':'12px', 'height':'12px', 'float':'left', 'margin':'0 0 -2px 4px'});
            list.appendChild(shim);
       }

       var linkSpan = document.createElement('SPAN');

        var selectedUrlNames = new Array();
        for (var i = 0; i < rootCats.length; i++) {
            if (rootName == rootCats[i].name) {
                selectedUrlNames.push(urlCategoryName);
            } else {
                selectedUrlNames.push(rootCats[i].selection.urlName);
            }
        }

        if (isCbStore) {
            var imgNode = document.createElement('IMG');
            imgNode.setAttribute('src', StaticRoot + 'i/cb_icon.gif');
            imgNode.setAttribute('border', '0');
            linkCategory.appendChild(imgNode);
        }
       var textNode = document.createTextNode(categoryName);
       linkCategory.href = buildCategoryUrl(selectedUrlNames, attributes);

       if (isExpanded)
           linkCategory.className += ' highlighted';
       linkCategory.appendChild(textNode);

       list.appendChild(linkCategory);

       linkSpan.appendChild(linkCategory);
       list.appendChild(linkSpan);

       return list;
   }

   this.buildList = function(categories, parentCategoryId, indent)
   {
       var categoriesFormatted = '';

       var listOuterContainer = document.createElement('li');
       listOuterContainer.setAttribute('id', 's_ul_' + parentCategoryId);

       var listContainer = document.createElement('ul');

       for (var i = 0; i < categories.length; i++)
       {
           var isCbStore = (categories[i].cashback && categories[i].cashback == '1');
           var rootName = categories[i].path[1].name;

           var list = self.buildListItem(categories[i].category_id, categories[i].name, isCbStore, categories[i].descendants, false, indent, categories[i].url_name, categories[i].path, rootName);
           listContainer.appendChild(list);
       }

       listOuterContainer.appendChild(listContainer);

       return listOuterContainer;
   }


   /*
    * Returns the domId of the search box
    */
   this.getId = function()
   {
       return this.id;
   }

   this.getHtml = function()
   {
       var input = document.createElement('input');

       input.setAttribute('id', this.id);
       input.setAttribute('value', 'search');
       input.onkeyup = function() { self.onChangeSearch() };
       input.onclick = function() { if (this.value == 'search') { this.value = ''; } };

       return input;
   }

   /*
    * Sets the minimum number of characters that have to exist in a search box before a search will take place
    *
    * @param minimumSearchChars {int}
    *
    */
   this.setMinimumSearchChars = function(minimumSearchChars)
   {
       this.minimumSearchChars = minimumSearchChars;
   }
}

function inArray(item, arr)
{
   for (var i = 0; i < arr.length; i++)
       if (arr[i] == item)
           return true;

   return false;
}

function buildCategoryUrl(categories, attributes, params)
{
    var url = '/forums/' + forumCategoryName + '/';

    for (var i = 0; i < categories.length; i++) {
        if (categories[i]) {
            url += categories[i] + '/';
        }
    }

    var first = true;

    if (attributes != undefined)
    {
        for (var i = 0; i < attributes.length; i++)
        {
            if (first)
            {
                url += '?';
                first = false;
            }
            else
            {
                 url += '&';
            }

            url += 'attr[]=' + attributes[i];
        }
    }

    if (params != undefined)
    {
        for (key in params)
        {
            if (first)
            {
                url += '?';
                first = false;
            }
            else
            {
                 url += '&';
            }

            url += key + '=' + params[key];
        }
    }

    return url;
}

// ====================================================================
//       URLEncode and URLDecode functions
//
// Copyright Albion Research Ltd. 2002
// http://www.albionresearch.com/
//
// You may copy these functions providing that
// (a) you leave this copyright notice intact, and
// (b) if you use these functions on a publicly accessible
//     web site you include a credit somewhere on the web site
//     with a link back to http://www.albionresearch.com/
//
// If you find or fix any bugs, please let us know at albionresearch.com
//
// SpecialThanks to Neelesh Thakur for being the first to
// report a bug in URLDecode() - now fixed 2003-02-19.
// And thanks to everyone else who has provided comments and suggestions.
// ====================================================================

function URLEncode(plaintext)
{
	// The Javascript escape and unescape functions do not correspond
	// with what browsers actually do...
	var SAFECHARS = "0123456789" +					// Numeric
					"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +	// Alphabetic
					"abcdefghijklmnopqrstuvwxyz" +
					"-_.!~*'()";					// RFC2396 Mark characters
	var HEX = "0123456789ABCDEF";
	var encoded = "";

	for (var i = 0; i < plaintext.length; i++ ) {
		var ch = plaintext.charAt(i);
	    if (ch == " ") {
		    encoded += "+";				// x-www-urlencoded, rather than %20
		} else if (SAFECHARS.indexOf(ch) != -1) {
		    encoded += ch;
		} else {
		    var charCode = ch.charCodeAt(0);
			if (charCode > 255) {
				encoded += "+";
			} else {
				encoded += "%";
				encoded += HEX.charAt((charCode >> 4) & 0xF);
				encoded += HEX.charAt(charCode & 0xF);
			}
		}
	} // for

	return encoded;
};

