


function LogoCategoryNode(title, parent)
{
    var _children = [];
    var _this = this;



    this.enumerateNodesInPath = function (path, includeThisNode)
    {
        if (!isObjectString(path))
        {
            throw "The specified path is not a string.";
        }

        if (isObjectNullOrUndefined(includeThisNode))
        {
            includeThisNode = true;
        }

        else if (!isObjectBoolean(includeThisNode))
        {
            throw "The value specified for includeThisNode is not a boolean.";
        }

        var nodeArray = [];

        if (includeThisNode)
        {
            nodeArray.push(this);
        }

        var context = this;

        var pathNodes = path.split(">");

        for (index in pathNodes)
        {
            var pathNode = pathNodes[index].trim();

            if (pathNode.length > 0)
            {
                context = context.getChild(pathNode);

                if (isObjectNullOrUndefined(context))
                {
                    throw "The node \"" + pathNode + "\" could not be found in \"" + path + "\".";
                }

                nodeArray.push(context);
            }
        }

        return nodeArray;
    }



    this.getChild = function (title)
    {
        return getOrCreateChild(title, false);
    }



    this.getChildCount = function ()
    {
        return (_children.length);
    }



    this.getChildren = function ()
    {
        var children = [];

        for (index in _children)
        {
            children.push(_children[index]);
        }

        return children;
    }



    function getOrCreateChild(title, createIfNotFound)
    {
        if (!isObjectString(title))
        {
            throw "The specified title is not a string.";
        }

        if (title.length == 0)
        {
            throw "The specified title is empty.";
        }

        if (isObjectNullOrUndefined(createIfNotFound))
        {
            createIfNotFound = false;
        }

        else if (!isObjectBoolean(createIfNotFound))
        {
            throw "The value specified for createIfNotFound is not a boolean.";
        }

        for (var index in _children)
        {
            var logoCategoryNode = _children[index];

            if (logoCategoryNode.getTitle() === title)
            {
                return logoCategoryNode;
            }
        }

        if (createIfNotFound)
        {
            var logoCategoryNode = new LogoCategoryNode(title, _this);

            _children.push(logoCategoryNode);

            return logoCategoryNode;
        }

        return null;
    }



    this.getTitle = function ()
    {
        return title;
    }




    this.getParent = function ()
    {
        return parent;
    }



    this.importNodesFromPath = function (path)
    {
        if (isObjectArray(path))
        {
            if (path.length > 0)
            {
                if (!isObjectString(path[0]))
                {
                    throw "An node in the specified path is not a String.";
                }

                var context = this;

                var pathNode = path[0].trim();

                if (pathNode.length > 0)
                {
                    context = getOrCreateChild(pathNode, true);
                }

                context.importNodesFromPath(path.slice(1));
            }
        }

        else
        {
            if (!isObjectString(path))
            {
                throw "The specified path is not a String.";
            }

            this.importNodesFromPath(path.split(">"));
        }       
    }



    this.isLeaf = function ()
    {
        return (_children.length === 0);
    }



    this.isRoot = function ()
    {
        return isObjectNull(parent);
    }



    this.toString = function (concatPath)
    {
        var separatorChar = ">";
        var separator = " " + separatorChar + " ";
        var path = "";
        var context = this;

        if (isObjectNullOrUndefined(concatPath))
        {
            concatPath = "";
        }

        else if (!isObjectString(concatPath))
        {
            throw "The specified path to concat is not a string";
        }

        while (true)
        {
            if (isObjectNullOrUndefined(context) || context.isRoot())
            {
                break;
            }

            if (path.length == 0)
            {
                path = context.getTitle();
            }

            else
            {
                path = context.getTitle() + separator + path;
            }

            context = context.getParent();
        }

        if (concatPath.length > 0)
        {
            concatPath = concatPath.trim();

            while (concatPath.charAt(0) === separatorChar)
            {
                concatPath = concatPath.substr(1).trimStart();
            }

            if (concatPath.length > 0)
            {
                path += ((path.length > 0) ? separator : "") + concatPath;
            }
        }

        return path;
    }



    if (isObjectUndefined(parent))
    {
        parent = null;
    }

    if (!isObjectNull(parent))
    {
        if (!(parent instanceof LogoCategoryNode))
        {
            throw "The parent is not a LogoCategoryNode.";
        }

        if (!isObjectString(title))
        {
            throw "The specified title is not a string.";
        }

        if (title.length == 0)
        {
            throw "The specified title is empty.";
        }
    }

    else
    {
        if (isObjectUndefined(title))
        {
            title = null;
        }

        else if (!isObjectNull(title) && !isObjectString(title))
        {
            throw "The specified title is not a string.";
        }
    }
}



function LogoSelectBoxes(
    selectBoxWidth,
    selectBoxContainer)
{
    var _dontKnowTitle = "(Don't Know or Not Listed)";
    var _selectChain = [];
    var _selectOnChangeEvents = new EventList();



    this.addOnChange = function (func)
    {
        return _selectOnChangeEvents.add(func);
    }



    function createSelectBoxForLogoCategoryNode(logoCategoryNode)
    {
        if (!(logoCategoryNode instanceof LogoCategoryNode))
        {
            throw "The logoCategoryNode must be a LogoCategoryNode.";
        }

        // Create the select box
        var select = document.createElement("select");

        select.style.width = selectBoxWidth;
        select.logoCategoryNode = logoCategoryNode;
        select.onchange = function () { onSelectBoxChanged(this); };

        // Add the default option
        var option = document.createElement("option");
        option.value = logoCategoryNode.toString(_dontKnowTitle);
        option.logicalPath = logoCategoryNode.toString();

        option.appendChild(document.createTextNode(_dontKnowTitle));

        select.appendChild(option);

        // Now enumerate the children of logo category node
        var children = logoCategoryNode.getChildren();

        for (var index in children)
        {
            var child = children[index];

            // Add an option for the child logo category node
            option = document.createElement("option");
            option.value = child.toString();
            option.logicalPath = option.value;

            option.appendChild(document.createTextNode(child.getTitle()));

            select.appendChild(option);
        }

        // Insert the select box into the DOM
        selectBoxContainer.appendChild(select);

        // And reference the select box in our internal list
        _selectChain.push(select);

        // Call this event to indicate the default option was selected
        onSelectBoxChanged(select);
    }



    this.getDontKnowTitle = function ()
    {
        return _dontKnowTitle;
    }



    this.getSelectedDisplayPath = function ()
    {
        var logoCategoryNode = this.getSelectedLogoCategoryNode();

        if (logoCategoryNode.getChildCount() === 0)
        {
            return logoCategoryNode.toString();
        }

        return logoCategoryNode.toString(_dontKnowTitle);
    }



    this.getSelectedLogoCategoryNode = function ()
    {
        if (_selectChain.length == 0)
        {
            throw "The select boxes have not yet been initialized.";
        }

        var select = _selectChain[_selectChain.length - 1];

        if (select.selectedIndex === 0)
        {
            return select.logoCategoryNode;
        }

        var option = select.options[select.selectedIndex];

        var childLogoCategoryNode = select.logoCategoryNode.getChild(option.text);

        if (isObjectNullOrUndefined(childLogoCategoryNode))
        {
            throw "A logo category node could not be located.";
        }

        return childLogoCategoryNode;
    }



    this.initializeSelectBoxes = function ()
    {
        if (_selectChain.length == 0)
        {
            createSelectBoxForLogoCategoryNode(
                LogoCategoryNode.createHierarchy());
        }

        else
        {
            var option = _selectChain[0].options[0];

            if (!option.selected)
            {
                option.selected = true;

                if (isObjectFunction(select.onchange))
                {
                    select.onchange();
                }
            }
        }
    }



    function onSelectBoxChanged(select)
    {
        if (isObjectNullOrUndefined(select))
        {
            throw "The select box is null or undefined.";
        }

        // An option has been selected in a select box.  Since this is now
        // the active select box, remove all trailing select boxes which are
        // now out of the context/scope.
        removeTrailingSelectBoxes(select);

        // Get the logo category node for the selected child
        if (select.selectedIndex > 0)
        {
            var option = select.options[select.selectedIndex];

            var childLogoCategoryNode = select.logoCategoryNode.getChild(option.text);

            if (isObjectNullOrUndefined(childLogoCategoryNode))
            {
                throw "A logo category node could not be located.";
            }

            // If necessary, create a select box for the child option
            if (childLogoCategoryNode.getChildCount() > 0)
            {
                createSelectBoxForLogoCategoryNode(childLogoCategoryNode);

                return;
            }
        }

        _selectOnChangeEvents.invoke(this);
    }



    this.removeOnChange = function (func)
    {
        return _selectOnChangeEvents.remove(func);
    }



    function removeTrailingSelectBoxes(select)
    {
        if (isObjectNullOrUndefined(select))
        {
            throw "The select box is null or undefined.";
        }

        // Enumerate all select boxes in the DOM
        var newLength = 0;

        for (var index = 0; index < _selectChain.length; ++index)
        {
            var enumSelect = _selectChain[index];

            // If this is after the specified select box, remove it
            if (newLength !== 0)
            {
                selectBoxContainer.removeChild(enumSelect);
            }

            // If this is the specified select box, mark it so that all
            // remaining select boxes are removed.
            else if (enumSelect === select)
            {
                newLength = index + 1;
            }
        }

        if (newLength === 0)
        {
            throw "The specified select box was not found in the select box chain.";
        }

        // Remove the references to the removed select boxes.
        _selectChain.splice(newLength, _selectChain.length - newLength);
    }



    function selectOptionByText(select, text, startIndex)
    {
        if (isObjectNullOrUndefined(select))
        {
            throw "A valid select object is required.";
        }

        if (!isObjectString(text))
        {
            throw "The text must be a valid string.";
        }

        if (isObjectNullOrUndefined(startIndex))
        {
            startIndex = 0;
        }

        else if (!isObjectNumber(startIndex))
        {
            throw "The startIndex must be a valid number.";
        }

        else if (startIndex < 0)
        {
            startIndex = 0;
        }

        var options = select.options;

        while (startIndex < options.length)
        {
            var option = options[startIndex++];

            if (option.text === text)
            {
                option.selected = true;

                if (isObjectFunction(select.onchange))
                {
                    select.onchange();
                }

                return true;
            }
        }

        return false;
    }



    this.selectOptionByLogicalPath = function (pathLogical)
    {
        if (!isObjectString(pathLogical))
        {
            throw "A valid path to a logo category node is required.";
        }

        if (_selectChain.length == 0)
        {
            throw "The select boxes have not yet been initialized.";
        }

        var rootNode = _selectChain[0].logoCategoryNode;

        var nodesInPath = rootNode.enumerateNodesInPath(pathLogical, false);

        for (var index = 0;; index++)
        {
            if (index >= nodesInPath.length ||
                index >= _selectChain.length)
            {
                break;
            }

            var node = nodesInPath[index];
            var select = _selectChain[index];

            // Use '1' to skip the display "Don't Know" option
            if (!selectOptionByText(select, node.getTitle(), 1))
            {
                break;
            }
        }
    }



    // Validate the input parameters and construct the object.
    if (!isObjectNumber(selectBoxWidth) || selectBoxWidth < 0)
    {
        throw "The specified selectBoxWidth is not a valid width.";
    }

    if (!isObjectString(selectBoxContainer) || selectBoxContainer.length === 0)
    {
        throw "The specified selectBoxContainer is not a valid string id.";
    }

    selectBoxContainer = document.getElementById(selectBoxContainer);

    if (isObjectNullOrUndefined(selectBoxContainer))
    {
        throw "Could not locate the selectBoxContainer element.";
    }
}



LogoCategoryNode.createHierarchy = function ()
{
    var logoCategoryNode = new LogoCategoryNode();

    logoCategoryNode.importNodesFromPath("Audio > Audio Device");
    logoCategoryNode.importNodesFromPath("Audio > System Effect Audio Processing Objects");
    logoCategoryNode.importNodesFromPath("Bus Controllers and Ports > 1394 Controller");
    logoCategoryNode.importNodesFromPath("Bus Controllers and Ports > Bluetooth Controller");
    logoCategoryNode.importNodesFromPath("Bus Controllers and Ports > CardBus-PCMCIA Controller");
    logoCategoryNode.importNodesFromPath("Bus Controllers and Ports > Secure Digital Host Controller");
    logoCategoryNode.importNodesFromPath("Bus Controllers and Ports > Serial Port Adapters");
    logoCategoryNode.importNodesFromPath("Bus Controllers and Ports > USB Controller");
    logoCategoryNode.importNodesFromPath("Bus Controllers and Ports > USB Hub");
    logoCategoryNode.importNodesFromPath("Communications Device");
    logoCategoryNode.importNodesFromPath("Display > Display Adapters/Chipsets");
    logoCategoryNode.importNodesFromPath("Display > Hybrid Graphics");
    logoCategoryNode.importNodesFromPath("Display > Monitors > CRT");
    logoCategoryNode.importNodesFromPath("Display > Monitors > LCD");
    logoCategoryNode.importNodesFromPath("Display > Monitors > Plasma");
    logoCategoryNode.importNodesFromPath("Display > Monitors > Projector");
    logoCategoryNode.importNodesFromPath("Display > Windows SideShow");
    logoCategoryNode.importNodesFromPath("Game Devices > Common Controller");
    logoCategoryNode.importNodesFromPath("Game Devices > Generic");
    logoCategoryNode.importNodesFromPath("Input > Digitizer Pen/Singletouch");
    logoCategoryNode.importNodesFromPath("Input > Digitizer Windows Touch");
    logoCategoryNode.importNodesFromPath("Input > Keyboard");
    logoCategoryNode.importNodesFromPath("Input > Keyboard Video Mouse Switch");
    logoCategoryNode.importNodesFromPath("Input > MCE IR Receiver");
    logoCategoryNode.importNodesFromPath("Input > MCE Remote Control");
    logoCategoryNode.importNodesFromPath("Input > Pointing Drawing");
    logoCategoryNode.importNodesFromPath("Input > Sensor");
    logoCategoryNode.importNodesFromPath("Input > Smart Card Minidriver");
    logoCategoryNode.importNodesFromPath("Input > Smart Card Reader");
    logoCategoryNode.importNodesFromPath("Network > ECP Peer Method");
    logoCategoryNode.importNodesFromPath("Network > ECP Peer Tunnel Method");
    logoCategoryNode.importNodesFromPath("Network > ECP Supplicant");
    logoCategoryNode.importNodesFromPath("Network > Filter LAN (Ethernet)");
    logoCategoryNode.importNodesFromPath("Network > ISDN (CoNDIS\NDIS)");
    logoCategoryNode.importNodesFromPath("Network > LAN (Ethernet)");
    logoCategoryNode.importNodesFromPath("Network > Mobile Broadband Network");
    logoCategoryNode.importNodesFromPath("Network > Modem");
    logoCategoryNode.importNodesFromPath("Network > Router");
    logoCategoryNode.importNodesFromPath("Network > Security Software");
    logoCategoryNode.importNodesFromPath("Network > Wireless LAN (802.11)");
    logoCategoryNode.importNodesFromPath("Network > Wireless Router");
    logoCategoryNode.importNodesFromPath("Network Media Device > Digital Media Controller");
    logoCategoryNode.importNodesFromPath("Network Media Device > Digital Media Controller Wireless");
    logoCategoryNode.importNodesFromPath("Network Media Device > Digital Media Renderer");
    logoCategoryNode.importNodesFromPath("Network Media Device > Digital Media Renderer Wireless");
    logoCategoryNode.importNodesFromPath("Network Media Device > Digital Media Server");
    logoCategoryNode.importNodesFromPath("Network Media Device > Digital Media Server Wireless");
    logoCategoryNode.importNodesFromPath("Network Media Device > Media Center Extender");
    logoCategoryNode.importNodesFromPath("Portable Devices > Cellular Handsets");
    logoCategoryNode.importNodesFromPath("Portable Devices > Digital Still Camera");
    logoCategoryNode.importNodesFromPath("Portable Devices > Media Player");
    logoCategoryNode.importNodesFromPath("Printer - Vertical Pairing (Wireless)");
    logoCategoryNode.importNodesFromPath("Printing");
    logoCategoryNode.importNodesFromPath("Scanner");
    logoCategoryNode.importNodesFromPath("Storage > Adapter or Controller");
    logoCategoryNode.importNodesFromPath("Storage > Bridge");
    logoCategoryNode.importNodesFromPath("Storage > Hard Disk Drive (HDD)");
    logoCategoryNode.importNodesFromPath("Storage > Hardware based RAID (Storage Array)");
    logoCategoryNode.importNodesFromPath("Storage > iSCSI Boot Component");
    logoCategoryNode.importNodesFromPath("Storage > Medium Changer");
    logoCategoryNode.importNodesFromPath("Storage > Network Attached Storage");
    logoCategoryNode.importNodesFromPath("Storage > Optical Disk Drive (ODD)");
    logoCategoryNode.importNodesFromPath("Storage > Removable Storage");
    logoCategoryNode.importNodesFromPath("Storage > Tape Drive");
    logoCategoryNode.importNodesFromPath("Storage > Wireless Network Attached Storage");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast > Video Capture");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast Devices > Broadcast Receiver ATSC");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast Devices > Broadcast Receiver DVB-C");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast Devices > Broadcast Receiver DVB-S");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast Devices > Broadcast Receiver DVB-T");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast Devices > Broadcast Receiver ISDB-S");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast Devices > Broadcast Receiver ISDB-T");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast Devices > Broadcast Receiver NTSC");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast Devices > Broadcast Receiver NTSC_M_J");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast Devices > Broadcast Receiver PAL");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast Devices > Broadcast Receiver Proprietary");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast Devices > Broadcast Receiver QAM");
    logoCategoryNode.importNodesFromPath("Streaming Media and Broadcast Devices > Broadcast Receiver SECAM");
    logoCategoryNode.importNodesFromPath("Transfer Cable");
    logoCategoryNode.importNodesFromPath("Unclassified");
    logoCategoryNode.importNodesFromPath("Windows Hardware Error Architecture");

    return logoCategoryNode;
}




