/*
jquery.combobox
version 0.1.2.3 alpha

ahura mazda
copyright 2007
jquery.sanchezsalvador.com
*/
jQuery.fn.combobox = function(options)
{
        // Setting class
        var settings =
        {               
                comboboxContainerClass: null,
                comboboxValueContainerClass: null,
                comboboxValueContentClass: null,
                comboboxDropDownButtonClass: null,
                comboboxDropDownClass: null,
                comboboxDropDownItemClass: null,
                comboboxDropDownItemHoverClass: null,
                comboboxDropDownGroupItemHeaderClass: null,
                comboboxDropDownGroupItemContainerClass: null,
                animationType: "slide",
                width: "120px"
        };
        
        if (options)
        {
                jQuery.extend(settings, options);
        }
        
        //#region public events
        
        this.onChange =
                function()
                {
                        //Intentionally left empty
                };
        
        //#endregion public events
        
        return this.each(
                function()
                {
                
                        //#region 'private' variables
                        
                        // This class can operate of N elements depending on how ComboBox is called
                        // for example jQuery('select').combobox() could return multiple Selects.
                        // This variable should always be a Select JQuery element.
                        // TODO: Check if select control is used
                        var _originalElementJQuery = jQuery(this);
                        var _containerJQuery = null;
                        var _containerDefaultStyle = "border-left: solid 2px #777;border-top: solid 2px #777;border-right: solid 1px #ccc;border-bottom: solid 1px #ccc;";
                        var _containerEnforcedStyle = "padding:0;";
                        var _dropDownListJQuery = null;
                        var _dropDownListEnforcedStyle = "list-style-type:none;min-height:15px;padding-top:0;margin:0;";
                        var _dropDownListDefaultStyle = "cursor:default;padding:2px;background:#fff;border-right:solid 1px #000;border-bottom:solid 1px #000;border-left:solid 1px #aaa;border-top:solid 1px #aaa;overflow:auto";
                        var _dropDownListItemEnforcedStyle = "display:block;";
                        var _dropDownListItemDefaultStyle = "cursor:default;padding-left:2px;font-weight:normal;font-style:normal;";
                        var _dropDownListGroupItemContainerEnforcedStyle = "list-style-type:none;";
                        var _dropDownListGroupItemContainerDefaultStyle = "padding-left:10px;margin-left:0;";
                        var _dropDownListGroupItemHeaderEnforcedStyle = "";
                        var _dropDownListGroupItemHeaderDefaultStyle = "font-style:italic;font-weight:bold;";                   
                        var _valueDisplayContainerJQuery = null;
                        var _valueDisplayContainerEnforcedStyle = "position:relative;overflow:hidden;";
                        var _valueDisplayJQuery = null;
                        var _valueDisplayEnforcedStyle = "float:left;position:absolute;cursor:default;overflow:hidden;";
                        var _dropDownButtonJQuery = null;
                        var _dropDownButtonDefaultStyle = "overflow:hidden;width: 16px;height: 18px;color:#000;background: #D6D3CE;,font-family: verdana;font-size: 10px;cursor: default;text-align: center;vertical-align:middle;";
                        var _dropDownButtonEnforcedStyle = "background-repeat:no-repeat;float:right;";
                        var _dropDownButtonDefaultUnselectedStyle = "padding-left:0px;padding-top:1px;width:12px;height:13px;border-right:solid 2px #404040;border-bottom:solid 2px #404040;border-left:solid 2px #f0f0f0;border-top:solid 2px #f0f0f0";
                        var _dropDownButtonDefaultSelectedStyle = "padding-left:1px;padding-top:3px;width:12px;height:13px;border:solid 1px #808080";
                        var _dropDownButtonDefaultCharacter = "&#9660;";
                        var _lastItemSelectedJQuery = null;
                        var _lastValue = null;
                        var _downdownListPositionIsInverted = false;
                        var _maximumItemLength = 0;
                        var _dropDownListOffset = null;
                        
                        //#endregion 'private' variables
                        
                        //#region 'private' methods
                        
                        ///<summary>
                        /// Function extension to String.
                        ///     Replaces the placeholder tags '{0}...{n}' with the parameters based on ordinal position of the parameters
                        ///     Example: String.format("The quick {0} fox {2} over the lazy {1}.", "brown", "Dog", "jumps");
                        ///     Output: The quick brown fox jumps over the lazy Dog.
                        ///</summary>
                        String.format =
                                function()
                                {
                                        var currentString = null;
                                        if (arguments.length != 0)
                                        {
                                                currentString = arguments[0];
                                                for (var argumentIndex = 1; argumentIndex < arguments.length; argumentIndex++)
                                                {
                                                        var modifiedString = new RegExp('\\{' + (argumentIndex - 1) + '\\}','gm');
                                                        currentString = currentString.replace(modifiedString, arguments[argumentIndex]);
                                                }
                                        }
                                        
                                        return currentString;
                                };

                        ///<summary>
                        ///     Sets the width of an element taking into consideration any borders and padding.
                        ///     Works exactly like Internet Explorers Box Model, where the padding and border is considered
                        //      part of the width. For the purposes of this control, it is require in certain circumstances.
                        ///     Example:
                        ///      <div id="container" style="width: 150px; border:solid 2px #000"></div>
                        ///             jQuery('#container').width(); // 150px
                        ///             jQuery('#container').outerWidth(); // 154px (2px border on the left and right)
                        ///             setInnerWidth(jQuery('#container'), 120);
                        ///             jQuery('#container').width(); // 116px
                        ///             jQuery('#container').outerWidth(); // 120px (2px border on the left and right)
                        ///</summary>                           
                        function setInnerWidth(elementJQuery, width)
                        {
                                var differenceWidth = (elementJQuery.outerWidth() - elementJQuery.width());
                                
                                elementJQuery.width(width - differenceWidth);
                        }
                        
                        ///<summary>
                        ///     Sets the height of an element taking into consideration any borders and padding.
                        ///     Works exactly like Internet Explorers Box Model, where the padding and border is considered
                        //      part of the height. For the purposes of this control, it is require in certain circumstances.                   
                        ///</summary>                           
                        function setInnerHeight(elementJQuery, height)
                        {
                                var differenceheight = (elementJQuery.outerHeight() - elementJQuery.height());
                                
                                elementJQuery.height(height - differenceheight);
                        }
                        
                        ///<summary>
                        /// Builds the elements that make up the always visible portion of the control.
                        ///     The equivalent of a Textbox-type element.
                        /// Also creates the DropDownButton
                        ///</summary>
                        function buildValueDisplay()
                        {
                                // A container for the Display Value and DropDownButton. A container is required as the child elements
                                // are floated
                                var valueDisplayContainerHTML = "";
                                if (settings.comboboxValueContainerClass)
                                {
                                        valueDisplayContainerHTML = String.format("<div class='{0}' style='{1}'></div>", settings.comboboxValueContainerClass, _valueDisplayContainerEnforcedStyle);
                                }
                                else
                                {
                                        valueDisplayContainerHTML = String.format("<div style='{0}'></div>", _valueDisplayContainerEnforcedStyle);
                                }
                                
                                // Create the equivalent of the select 'textbox' where the current selected value is shown
                                var valueDisplayHTML = "";
                                if (settings.comboboxValueContentClass)
                                {
                                        valueDisplayHTML = String.format("<div class='{0}' style='{1}'></div>", settings.comboboxValueContentClass, _valueDisplayEnforcedStyle);
                                }
                                else
                                {
                                        valueDisplayHTML = String.format("<div style='{0}'></div>", _valueDisplayEnforcedStyle);
                                }
                                
                                var dropdownButtonHTML = "";
                                if (settings.comboboxDropDownButtonClass)
                                {
                                        dropdownButtonHTML = String.format("<div class='{1}' style='{0}'></div>",_dropDownButtonEnforcedStyle, settings.comboboxDropDownButtonClass);
                                }
                                else
                                {
                                        dropdownButtonHTML = String.format("<div style='{0}'>{1}</div>", (_dropDownButtonEnforcedStyle + _dropDownButtonDefaultStyle), _dropDownButtonDefaultCharacter);
                                }
                                
                                _valueDisplayJQuery = jQuery(valueDisplayHTML);
                                _dropDownButtonJQuery = jQuery(dropdownButtonHTML);
                                _valueDisplayContainerJQuery = jQuery(valueDisplayContainerHTML);
                                
                                _valueDisplayContainerJQuery.appendTo(_containerJQuery);
                                _valueDisplayJQuery.appendTo(_valueDisplayContainerJQuery);
                                _dropDownButtonJQuery.appendTo(_valueDisplayContainerJQuery);
                        
                                setDropDownButtonState(0);
                        }
                        
                        ///<summary>
                        ///     Build a drop down list element populating it will values, text, styles and class
                        ///     depending on the source value type. The source value (childJQuery) can be an option or
                        ///     and optgroup element.
                        ///</summary>
                        function buildDropDownItem(childJQuery)
                        {
                                var dataItemHTML = "";
                                var dataItemClass = null;
                                var dataItemText = "";
                                var dataItemValue = null;
                                var dataItemStyle = "";
                                var dataItemType = "option";
                                
                                if (childJQuery.is('option'))
                                {
                                        dataItemText = childJQuery.text();
                                        dataItemValue = childJQuery.val();
                                        
                                        if (settings.comboboxDropDownItemClass)
                                        {
                                                dataItemClass = settings.comboboxDropDownItemClass;
                                                dataItemStyle = _dropDownListItemEnforcedStyle;
                                        }
                                        else
                                        {
                                                dataItemStyle = (_dropDownListItemEnforcedStyle + _dropDownListItemDefaultStyle);
                                        }
                                        
                                        if (dataItemClass)
                                        {                                               
                                                dataItemHTML = String.format("<li style='{0}' class='{1}'>{2}</li>", dataItemStyle, dataItemClass, dataItemText);
                                        }
                                        else
                                        {
                                                dataItemHTML = String.format("<li style='{0}'>{1}</li>", dataItemStyle, dataItemText);
                                        }
                                        
                                }
                                else
                                {
                                        dataItemText = childJQuery.attr('label');
                                        dataItemValue = childJQuery.attr('class');
                                        dataItemType = "optgroup";
                                        
                                        if (settings.comboboxDropDownGroupItemHeaderClass)
                                        {
                                                dataItemClass = settings.comboboxDropDownGroupItemHeaderClass;
                                                dataItemStyle = _dropDownListGroupItemHeaderEnforcedStyle;
                                        }
                                        else
                                        {
                                                dataItemStyle = (_dropDownListGroupItemHeaderEnforcedStyle + _dropDownListGroupItemHeaderDefaultStyle);
                                        }
                                        
                                        if (dataItemClass)
                                        {                                               
                                                dataItemHTML = String.format("<li><span style='{0}' class='{1}'>{2}</span></li>", dataItemStyle, dataItemClass, dataItemText);
                                        }
                                        else
                                        {
                                                dataItemHTML = String.format("<li><span style='{0}'>{1}</span></li>", dataItemStyle, dataItemText);
                                        }
                                }
                                
                                var dataItemJQuery = jQuery(dataItemHTML);
                                
                                // The element's style is set to inline for the calculation of the true width
                                // The element is then reset to its enforced style (display:block) later
                                dataItemJQuery.css("display", "inline");
                                // Store the value with the DOMElement which is persisted with the Browser
                                dataItemJQuery[0].dataValue = dataItemValue;
                                dataItemJQuery[0].dataType = dataItemType;
                                dataItemJQuery[0].title = dataItemText;
                                
                                return dataItemJQuery;
                        }
                        
                        ///<summary>
                        ///     Recusively build the drop down list elements based on the options and optgroups contained
                        ///     with the original Select element
                        ///</summary>
                        function recursivelyBuildList(parentJQuery, childrenOptionsJQuery)
                        {
                                childrenOptionsJQuery.each(
                                        function()
                                        {
                                                var childJQuery = jQuery(this);
                                                var builtDropDownItemJQuery = buildDropDownItem(childJQuery);
                                                parentJQuery.append(builtDropDownItemJQuery);
                                                
                                                // Calculate the true width of the item taking into consideration the offset from the left-edge
                                                // of the drop-down list.
                                                // Get the left offset of the DropDown list container and subtract that from the builtDropDownItemJQuery left offset
                                                //      to get the distance the builtDropDownItemJQuery is from its container
                                                var offsetLeft = builtDropDownItemJQuery.offset().left;
                                                
                                                offsetLeft -= _dropDownListOffset.left;
                                                
                                                if (offsetLeft < 0)
                                                {
                                                        offsetLeft = 0;
                                                }
                                                
                                                var width = (offsetLeft + builtDropDownItemJQuery.outerWidth());
                                                if (width > _maximumItemLength)
                                                {
                                                        _maximumItemLength = width;
                                                }
                                                
                                                // Set the enforced style of display:block after the width has been calculated.
                                                applyMultipleStyles(builtDropDownItemJQuery, _dropDownListItemEnforcedStyle);
                                                
                                                if (childJQuery.is('optgroup'))
                                                {
                                                        var dataGroupItemHTML = "";
                                                        if (settings.comboboxDropDownGroupItemContainerClass)
                                                        {
                                                                dataGroupItemHTML = String.format("<ul style='{0}' class='{1}'></ul>", _dropDownListGroupItemContainerEnforcedStyle, settings.comboboxDropDownGroupItemContainerClass);
                                                        }
                                                        else
                                                        {
                                                                dataGroupItemHTML = String.format("<ul style='{0}'></ul>", (_dropDownListGroupItemContainerEnforcedStyle + _dropDownListGroupItemContainerDefaultStyle));
                                                        }
                                                        
                                                        var dataGroupItemJQuery = jQuery(dataGroupItemHTML);
                                                        builtDropDownItemJQuery.append(dataGroupItemJQuery);
                                                        
                                                        // If not an option, then the child of a Select must be an optgroup element
                                                        recursivelyBuildList(dataGroupItemJQuery, childJQuery.children());
                                                }
                                        });
                        }
                        
                        ///<summary>
                        /// Creates an unordered list of values from the original Select control
                        ///</summary>
                        function buildDropDownList()
                        {
                                var originalElementChildrenJQuery = _originalElementJQuery.children();
                                _lastItemSelectedJQuery = null;
                                _lastValue = null;

                                // If the Drop Down List container already exists, recreate only the items,
                                // else create the container and the items as well.
                                if (_dropDownListJQuery)
                                {
                                        // Clear out any existing children elements
                                        _dropDownListJQuery.empty();
                                }
                                else
                                {
                                        var dropDownHTML = "";
                                        if (settings.comboboxDropDownClass)
                                        {
                                                dropDownHTML = String.format("<ul class='{0}' style='{1}'></ul>", settings.comboboxDropDownClass, _dropDownListEnforcedStyle);
                                        }
                                        else
                                        {
                                                dropDownHTML = String.format("<ul style='{0}'></ul>", (_dropDownListEnforcedStyle + _dropDownListDefaultStyle));
                                        }
                                        
                                        _dropDownListJQuery = jQuery(dropDownHTML);
                                        // Create the equivalent of the drop down list where the all the values are shown
                                        _dropDownListJQuery.appendTo(_containerJQuery);
                                        
                                        // Enable the Drop Down List to be able to receive focus and key events
                                        _dropDownListJQuery.attr("tabIndex", 0);
                                }
                                
                                // Create the internal list of values if they exist
                                if (originalElementChildrenJQuery.length > 0)
                                {
                                        _maximumItemLength = 0;
                                        _dropDownListOffset = _dropDownListJQuery.offset();
                                                
                                        recursivelyBuildList(_dropDownListJQuery, originalElementChildrenJQuery);
                                }
                        }
                        
                        ///<summary>
                        /// Applies CSS styling from a string that contains multiple style settings
                        ///     Example: "background-color:#fff;color:#0f0;border:solid 1px #00f;"
                        ///</summary>                   
                        function applyMultipleStyles(elementJQuery, multipleCSSStyles)
                        {
                                var stylePairArray = multipleCSSStyles.split(";");
                                if (stylePairArray.length > 0)
                                {
                                        for (var stylePairArrayIndex = 0; stylePairArrayIndex < stylePairArray.length; stylePairArrayIndex++)
                                        {
                                                var stylePair = stylePairArray[stylePairArrayIndex];
                                                var splitStylePair = stylePair.split(":");
                                                
                                                elementJQuery.css(splitStylePair[0], splitStylePair[1]);
                                        }
                                }
                        }
                        
                        ///<summary>
                        ///     Changes the image of the drop down button based on the state
                        ///     Normal = 0
                        ///     Pressed = 1
                        ///</summary>
                        function setDropDownButtonState(state)
                        {
                                if (settings.comboboxDropDownButtonClass)
                                {
                                        var width = _dropDownButtonJQuery.width();
                                        var offset = state * width;
                                        var background_positionCSS = String.format("-{0}px 0px", offset);
                                        _dropDownButtonJQuery.css("background-position", background_positionCSS);
                                }
                                else
                                {
                                        var style = _dropDownButtonDefaultUnselectedStyle;
                                        
                                        if (state == 1)
                                        {
                                                style = _dropDownButtonDefaultSelectedStyle;
                                        }
                                        
                                        applyMultipleStyles(_dropDownButtonJQuery, style);
                                }
                        }
                        
                        ///<summary>
                        ///     Adjust the width of the DropDown list based on the widest item or the set width (options), whichever
                        ///     is larger.
                        ///</summary>
                        function updateDropDownListWidth()
                        {
                                //Drop down list element
                                var dropdownListWidth = _containerJQuery.outerWidth();
                                if (dropdownListWidth < _maximumItemLength)
                                {
                                        dropdownListWidth = _maximumItemLength;
                                }
                                
                                _dropDownListJQuery.width(dropdownListWidth);
                        }
                        
                        ///<summary>
                        /// Repositions the display value based on height of the element.

                        ///     Note: the height will only have meaning if the display value element has text
                        ///</summary>
                        function positionDisplayValue()
                        {
                                var displayValueHeight = _valueDisplayJQuery.outerHeight();
                                var displayContainerHeight = _valueDisplayContainerJQuery.height();
                                var difference = ((displayContainerHeight - displayValueHeight) / 2);
                                
                                if (difference < 0)
                                {
                                        difference = 0;
                                }
                                
                                //TODO: add other alignments for the user, such as left, top, middle, bottom, etc
                                _valueDisplayJQuery.css("top", difference);
                        }
                        
                        ///<summary>
                        ///     Applies custom layout position and sizing to the controls
                        ///</summary>
                        function applyLayout()
                        {
                                _containerJQuery.width(settings.width);
                                
                                // Removes any units and retrieves only the value of width
                                var controlWidth = _containerJQuery.width();
                                setInnerWidth(_valueDisplayContainerJQuery, controlWidth);
                                
                                var displayValueWidth = (_valueDisplayContainerJQuery.width() - _dropDownButtonJQuery.outerWidth());
                                setInnerWidth(_valueDisplayJQuery, displayValueWidth);
                                var dropDownButtonHeight = _dropDownButtonJQuery.outerHeight();
                                setInnerHeight(_valueDisplayContainerJQuery, dropDownButtonHeight);
                                
                                _dropDownListJQuery.css("position", "absolute");
                                _dropDownListJQuery.css("z-index", "20000");
                                
                                updateDropDownListWidth();
                                
                                // Position the drop down list correctly, taking the main display control border into consideration
                                var currentLeftPosition = _dropDownListJQuery.offset().left;
                                var leftPosition = (currentLeftPosition - (_containerJQuery.outerWidth() - _containerJQuery.width()));
                                _dropDownListJQuery.css("left", leftPosition + 1);
                                _dropDownListJQuery.hide();
                        }

                        ///<summary>
                        /// Bind all items to mouse events except for UL elements
                        /// and LI elements that are option group labels
                        ///</summary>                   
                        function bindItemEvents()
                        {
                                jQuery("*", _dropDownListJQuery).not("ul").not("span").not("[@dataType='optgroup']").each(
                                        function()
                                        {
                                                var itemJQuery = jQuery(this);
                                                itemJQuery.click(
                                                        function(clickEvent)
                                                        {
                                                                // Stops the click event propagating to the Container and the Container.onClick firing
                                                                clickEvent.stopPropagation();
                                                                

                                                                container_onItemClick(itemJQuery);
                                                        });
                                                
                                                itemJQuery.mouseover(
                                                        function()
                                                        {
                                                                container_onItemMouseOver(itemJQuery);
                                                        });
                                                        
                                                itemJQuery.mouseout(
                                                        function()
                                                        {
                                                                container_onItemMouseOut(itemJQuery);
                                                        });
                                        });                     
                        }

                        ///<summary>
                        ///             Bind the dropdown list control blur event to a function
                        ///</summary>
                        function bindBlurEvent()
                        {
                                _dropDownListJQuery.blur(
                                        function(blurEvent)
                                        {
                                                blurEvent.stopPropagation();
                                                
                                                dropDownListJQuery_onBlur();
                                        });
                        }
                        
                        ///<summary>
                        ///     Bind the click event of the container to a function
                        ///</summary>
                        function bindContainerClickEvent()
                        {
                                _containerJQuery.click(
                                        function(clickEvent)
                                        {
                                                container_onClick();
                                        });
                        }

                        ///<summary>
                        ///     Remove the binding of a custom function from the container's click event
                        ///</summary>
                        function unbindContainerClickEvent()
                        {
                                _containerJQuery.unbind("click");
                        }
                                                
                        ///<summary>
                        ///             Bind this control to the events to custom functions
                        ///</summary>
                        function bindEvents()
                        {
                                _containerJQuery.keydown(
                                        function(keyEvent)
                                        {
                                                keyEvent.preventDefault();container_onKeyDown(keyEvent)
                                        });
                                        
                                bindContainerClickEvent();
                                        
                                bindBlurEvent();
                                        
                                bindItemEvents();
                        }
                                                
                        ///<summary>
                        ///             Sets the value both internally and visually to the user
                        ///</summary>
                        function setDisplayValue()
                        {
                                var valueHasChanged = false;
                                var originalElement = _originalElementJQuery[0];
                                
                                if (originalElement.length > 0)
                                {
                                        var selectedText = originalElement[originalElement.selectedIndex].text;
                                        _valueDisplayJQuery.text(selectedText);
                                        _valueDisplayJQuery.attr("title", selectedText);
                                        
                                        // Reposition the display value based on height of the element after the text has changed
                                        positionDisplayValue();
                                        
                                        if (_lastValue)
                                        {
                                                if (_lastValue != _originalElementJQuery.val())
                                                {
                                                        valueHasChanged = true;
                                                }
                                        }
                                        
                                        _lastValue = _originalElementJQuery.val();
                                        
                                        //  If the selected value has changed since the last click, fire the onChange event
                                        if (valueHasChanged)
                                        {
                                                // Check if the onChange event is being consumed, otherwise it will be undefined
                                                if (_originalElementJQuery.combobox.onChange)
                                                {
                                                        _originalElementJQuery.combobox.onChange();
                                                }
                                        }
                                        
                                        // If _lastItemSelectedJQuery has been set, remove the highlight from it, before setting it to the current
                                        // value
                                        if (_lastItemSelectedJQuery)
                                        {
                                                toggleItemHighlight(_lastItemSelectedJQuery, false);
                                        }
                                        
                                        // Find the DropDown Item Element that corresponds to the current value in the Select element
                                        _lastItemSelectedJQuery = jQuery("li[@dataValue='" + _lastValue + "']", _dropDownListJQuery);
                                        
                                        toggleItemHighlight(_lastItemSelectedJQuery, true);
                                }
                        }
                        
                        ///<summary>
                        ///     Highlights/Unhighlights a specific option.
                        ///     If a class is not set, then the background and foreground colours are inverted
                        ///</summary>
                        function toggleItemHighlight(elementJQuery, isHighlighted)
                        {
                                if (elementJQuery)
                                {
                                        if (settings.comboboxDropDownItemHoverClass)
                                        {
                                                if (isHighlighted)
                                                {
                                                        elementJQuery.addClass(settings.comboboxDropDownItemHoverClass);
                                                }
                                                else
                                                {
                                                        elementJQuery.removeClass(settings.comboboxDropDownItemHoverClass);
                                                }
                                        }
                                        else
                                        {
                                                if (isHighlighted)
                                                {
                                                        elementJQuery.css("background", "#000");
                                                        elementJQuery.css("color", "#fff");
                                                }
                                                else
                                                {
                                                        elementJQuery.css("background", "");
                                                        elementJQuery.css("color", "");
                                                }
                                        }
                                }
                        }

                        ///<summary>
                        ///     Builds the Outermost control and swaps out the original Select element.
                        ///     The Select element then becomes an hidden control within.
                        ///</summary>
                        function buildContainer()
                        {
                                var containerHTML = "";
                                if (settings.comboboxContainerClass)
                                {
                                        containerHTML = String.format("<div class='{0}' style='{1}'></div>", settings.comboboxContainerClass, _containerEnforcedStyle);
                                }
                                else
                                {
                                        containerHTML = String.format("<div style='{0}' style='{1}'></div>", _containerDefaultStyle, _containerEnforcedStyle);
                                }
                                _containerJQuery = jQuery(containerHTML);
                                _originalElementJQuery.before(_containerJQuery);
                                _containerJQuery.append(_originalElementJQuery);
                                _originalElementJQuery.hide();
                                
                                // Allow the custom jquery.combobox be able to receive focus and key events
                                _containerJQuery.attr("tabIndex", 0);
                        }
                        
                        ///<summary>
                        ///     Converts an existing Select element to a JQuery.combobox.
                        ///     The Select element is kept and updated accordingly, but visually is represented
                        ///     by other richer HTML elements
                        ///</summary>
                        function initialiseControl()
                        {
                                buildContainer();
                                
                                buildValueDisplay();
                                
                                buildDropDownList();
                                
                                applyLayout();
                                
                                bindEvents();
                                
                                setDisplayValue();
                        }
                        
                        ///<summary>
                        ///     Focus must be set to the DropDown list element only after it has shown.
                        ///     This is due to IE executing the Blur event before the list has immediately shown
                        ///</summary>
                        function setDropDownListFocus()
                        {
                                _dropDownListJQuery.focus();
                        }

                        ///<summary>
                        ///     Focus set to the Combobox Container
                        ///</summary>
                        function setAndBindContainerFocus()
                        {
                                _containerJQuery.focus();
                                bindContainerClickEvent();
                        }
                        
                        ///<summary>
                        ///     Slides up the DropDownlist when it is to be placed above the CB
                        ///</summary>
                        function slideUp(newTop)
                        {
                                _dropDownListJQuery.animate(
                                        {
                                                height: "toggle",
                                                top: newTop
                                        },
                                        "fast",
                                        setDropDownListFocus);
                        }
                        
                        ///<summary>
                        ///     Slides closed the DropDownlist when it is placed above the CB.
                        ///     Binds the CB Container click event after the DDL is hidden to avoid a bug in IE
                        ///     where the click event fires re-opening the DDL.
                        ///</summary>
                        function slideDown(newTop)
                        {
                                _dropDownListJQuery.animate(
                                        {
                                                height: "toggle",
                                                top: newTop
                                        },
                                        "fast",
                                        setAndBindContainerFocus);
                        }
                        
                        ///<summary>
                        ///     Get the proposed top position of the drop down list container.
                        ///     Also sets whether the drop down list is inverted. Inverted means that the
                        ///     list is shown above the container as opposed to the normal position of below the combobox 
                        ///     container
                        ///</summary>
                        function getDropDownListTop()
                        {
                                var comboboxTop = _containerJQuery.position().top;
                                var dropdownListHeight = _dropDownListJQuery.outerHeight();
                                var comboboxBottom = (comboboxTop + _containerJQuery.outerHeight());
                                var windowScrollTop = jQuery(window).scrollTop();
                                var windowHeight = jQuery(window).height();     
                                var availableSpaceBelow = (windowHeight - (comboboxBottom - windowScrollTop));
                                var dropdownListTop;

                                // Set values to display dropdown list below combobox as default                                
                                dropdownListTop = comboboxBottom;
                                _downdownListPositionIsInverted = false;

                                // Check if there is enough space below to display the full height of the drop down list
                                if (availableSpaceBelow < dropdownListHeight)
                                {
                                        // There is no available space below the combobox to display the dropdown list
                                        // Check if there is available space above. If not, then display below as default
                                        if ((comboboxTop - windowScrollTop)> dropdownListHeight)
                                        {
                                                // There is space above
                                                dropdownListTop = (comboboxTop - dropdownListHeight);
                                                _downdownListPositionIsInverted = true;
                                        }
                                }
                                
                                return dropdownListTop;
                        }
                        
                        ///<summary>
                        ///     Hides/Shows the list of values.
                        ///     The method of display or hiding is specified as settings.animationType.
                        ///     This method also changes the button state
                        ///</summary>                                   
                        function toggleDropDownList(isShown)
                        {
                                if (isShown)
                                {
                                        if (_dropDownListJQuery.is(":hidden"))
                                        {
                                                // Remove the click event from the container because when the dropdown list is shown
                                                // and the container is clicked, the dropdownlist blur event is fired which hides the control
                                                // and the container click is fired after which will show the list again (error);
                                                unbindContainerClickEvent();
                                                
                                                // When the DropDown list is shown, highlist the current value in the list
                                                toggleItemHighlight(_lastItemSelectedJQuery, true);
                
                                                setDropDownButtonState(1);
                                                
                                                var dropdownListTop = getDropDownListTop();
                                                _dropDownListJQuery.css("top", dropdownListTop);
                                                _dropDownListJQuery.css("left", _containerJQuery.offset().left);
                                                
                                                switch (settings.animationType)
                                                {
                                                        case "slide":
                                                                if (_downdownListPositionIsInverted)
                                                                {
                                                                        var comboboxTop = _containerJQuery.position().top;
                                                                        var containerHeight = _containerJQuery.outerHeight();

                                                                        _dropDownListJQuery.css("top", (comboboxTop - containerHeight));

                                                                        slideUp(dropdownListTop);
                                                                }
                                                                else
                                                                {
                                                                        _dropDownListJQuery.slideDown("fast", setDropDownListFocus);
                                                                }
                                                                break;
                                                                
                                                        case "fade":
                                                                _dropDownListJQuery.fadeIn("fast", setDropDownListFocus);
                                                                break;
                                                                
                                                        default:
                                                                _dropDownListJQuery.show();
                                                                setDropDownListFocus();
                                                }
                                        }
                                }
                                else
                                {
                                        if (_dropDownListJQuery.is(":visible"))
                                        {
                                                setDropDownButtonState(0);
                                                
                                                switch (settings.animationType)
                                                {
                                                        case "slide":
                                                                if (_downdownListPositionIsInverted)
                                                                {
                                                                        comboboxTop = _containerJQuery.position().top;
                                                                        dropdownListHeight = _dropDownListJQuery.height();

                                                                        slideDown(comboboxTop - _containerJQuery.outerHeight());
                                                                }
                                                                else
                                                                {
                                                                        _dropDownListJQuery.slideUp("fast", setAndBindContainerFocus)
                                                                }
                                                                break;
                                                                
                                                        case "fade":
                                                                _dropDownListJQuery.fadeOut("fast", setAndBindContainerFocus);
                                                                break;
                                                                
                                                        default:
                                                                _dropDownListJQuery.hide();
                                                                setAndBindContainerFocus();
                                                }
                                        }
                                }
                        }

                        ///<summary>
                        ///     Selects a value from the list of options from the original Select options.
                        ///     Does not use JQuery Selectors ':last' and ':first' because they take optgroup elements into
                        ///     account.
                        ///</summary>                                   
                        function selectValue(subSelector)
                        {
                                var originalElement = _originalElementJQuery[0];
                                var currentIndex = originalElement.selectedIndex;
                                var newIndex = -1;
                                // {select}.length returns the array size of the options. Does not count optgroup elements
                                var optionCountZeroBased = originalElement.length - 1;
                                
                                switch (subSelector)
                                {
                                        case ":next":
                                                newIndex = currentIndex + 1;
                                                if (newIndex > optionCountZeroBased)
                                                {
                                                        newIndex = optionCountZeroBased;
                                                }
                                                break;
                                        
                                        case ":previous":
                                                newIndex = currentIndex - 1;
                                                if (newIndex < 0)
                                                {
                                                        newIndex = 0;
                                                }

                                                break;
                                                
                                        case ":first":
                                                newIndex = 0;
                                                
                                                break;
                                                
                                        case ":last":
                                                newIndex = optionCountZeroBased;
                                                
                                                break;
                                }

                                originalElement.selectedIndex = newIndex;
                                setDisplayValue();
                        }
                        
                        ///<summary>
                        ///     Returns true if the DropDownList visible on screen, else false
                        ///</summary>
                        function isDropDownVisible()
                        {
                                return _dropDownListJQuery.is(":visible");
                        }
                        
                        //#endregion 'private' functions
                        
                        //#region public methods
                        
                        ///<summary>
                        ///     Updates the combobox with the current selected item.
                        ///     This function is called if the original Select element selection has been changed
                        ///</summary>
                        _originalElementJQuery.combobox.updateSelection = 
                                function()
                                {
                                        setDisplayValue();
                                };
                                
                        ///<summary>
                        ///     The Drop Down List Container will already have been created.
                        ///     This function recreates the items and binds the events to them.
                        ///     Thereafter, the current selection is set.
                        ///</summary>
                        _originalElementJQuery.combobox.update =
                                function()
                                {
                                        buildDropDownList();
                                        updateDropDownListWidth();
                                        bindItemEvents();
                                        setDisplayValue();
                                };
                        
                        //#endregion public methods
                        
                        //#region private events
                        
                        function container_onClick()
                        {
                                if (_dropDownListJQuery.is(":hidden"))
                                {
                                        toggleDropDownList(true);
                                }
                                else
                                {
                                        toggleDropDownList(false);
                                }
                        }
                        
                        function dropDownListJQuery_onBlur()
                        {
                                if (_dropDownListJQuery.is(":visible"))
                                {
                                        toggleDropDownList(false);
                                }
                        }
                        
                        function container_onItemClick(itemJQuery)
                        {
                                _originalElementJQuery.val(itemJQuery[0].dataValue);
                                
                                setDisplayValue();
                                
                                toggleDropDownList(false);
                        }
                        
                        function container_onItemMouseOver(itemJQuery)
                        {
                                // An item may be selected from the previous selection and will require
                                // to be set to normal.
                                // TODO: find a better method of matching _lastItemSelectedJQuery to itemJQuery and optimising the removal
                                // of the class, instead of removing it consistently
                                toggleItemHighlight(_lastItemSelectedJQuery, false);
                                
                                toggleItemHighlight(itemJQuery, true);
                        }
                        
                        function container_onItemMouseOut(itemJQuery)
                        {
                                toggleItemHighlight(itemJQuery, false);
                        }
                        
                        function container_onKeyDown(keyEvent)
                        {
                                switch (keyEvent.which)
                                {
                                        case 33:
                                                //Page Up
                                        case 36:
                                                //Home
                                                selectValue(":first");
                                                break;
                                        
                                        case 34:
                                                //Page Down
                                        case 35:
                                                //End
                                                selectValue(":last");
                                                break;

                                        case 37:
                                                //Left
                                                selectValue(":previous");
                                                break;
                                                
                                        case 38:
                                                //Up
                                                if (keyEvent.altKey)
                                                {
                                                        // alt-up
                                                        // If DDL is hidden, then it is shown and vice-versa
                                                        toggleDropDownList(!(isDropDownVisible()));
                                                }
                                                else
                                                {
                                                        selectValue(":previous");
                                                }
                                                break;

                                        case 39:
                                                //Right
                                                selectValue(":next");
                                                break;
                                                
                                        case 40:
                                                //Down
                                                if (keyEvent.altKey)
                                                {
                                                        // alt-down
                                                        // If DDL is hidden, then it is shown and vice-versa
                                                        toggleDropDownList(!(isDropDownVisible()));
                                                }
                                                else
                                                {
                                                        selectValue(":next");
                                                }
                                                break;
                                                
                                        case 27:
                                        case 13:
                                                // Escape
                                                toggleDropDownList(false);
                                                break;

                                        case 9:
                                                // Tab
                                                //TODO: Support alt-tab
                                                //TODO: Does not truly leave the Combobox if the DropDown is visible
                                                _dropDownListJQuery.blur();
                                                
                                                // This is required in Internet Explorer as the blur() order is different
                                                $(window)[0].focus();
                                                
                                                break;
                                }
                                
                                keyEvent.cancelBubble = true;
                        }
                        
                        //#endregion private events
                        
                        initialiseControl();
                });
}

