/**
 * 2002-06-13 Fixed Mozilla support and improved build performance
 *
 * @class ComboBox
 * @contructor
 * @author Mike de Boer (mdeboer AT ebuddy.com)
 */
var ComboBox = Class.create();
ComboBox.prototype = {
    /**
     * Initialize the ComboBox class.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @param {String} id Inherited from the 'old' HTML checkbox input.
	 * @param {HTMLDOMElement} container The container element of this Checkbox instance.
	 * @param {Object} options Optional.
     * @type ComboBox
	 */
    initialize :
    function(id, container, options) {
        this.id                = id;
        this.container         = container || document;
        this.optionsList       = new Array();
        this.matchingOptions   = new Array();
        this.value             = "";
        this.activeOption      = null;
        this.opened            = false;
        this.builtOnce         = false;

        this.setOptions(options);
        this._draw();
        this._attachBehaviors();
    },
    /**
	 * Set the optional global settings for the Tabcontrol tabs. If no options are provided,
	 * the defaults are used.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @param {Object} options Generic object, containing all the custom options.
	 * @type void
	 */
    setOptions :
    function(options) {
        this.options = {
            zIndex              : 30000,
            autoComplete        : false,
            classContainer      : 'combo_container',
            classContainerHover : 'combo_container_hilite',
            classButton         : 'combo_button',
            classOption         : 'combo_item',
            classOptionHover    : 'combo_hilite',
            classInput          : 'combo_input',
            classList           : 'combo_list',
            pathToContent       : '',
            theme               : 'default',
            classListCSS        : 'combobox.css',
            fixedListWidth      : 0,
            fixedListHeight     : 0,
            group               : null,
            listOpacity         : 1.0,
            defaultValue        : '',
            top                 : 25,
            left                : 60,
            buttonImage         : 'images/ui/groups_unfolded.gif',
            actionData          : null,
            onSelect            : null,
            useLocale           : false,
            onLocaleChange      : null
        }
        Object.extend(this.options, options || {});
    },
    /**
	 * Return the Identifier of this Button instance
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @type String
	 */
    getId :
    function() {
        return this.id;
    },
    /**
     * Draw all the HTML elements at startup time.
     * @author Erik Arvidsson {@link http://webfx.eae.net/contact.html#erik Erik's Homepage} and Mike de Boer (mdeboer AT ebuddy.com)
	 * @type void
	 */
    _draw :
    function() {
        var input_id, button_img;
        this.domNode = document.createElement("div");
        this.domNode.setAttribute('id', this.id);
        Element.addClassName(this.domNode, this.options.classContainer);
        this.domNode.style.top = this.options.top + "px";
        this.domNode.style.left = this.options.left + "px";
        this.domNode.style.zIndex = this.options.zIndex;
        this.container.appendChild(this.domNode);

        input_id = this.id + "_txt";

        this.inputbox = document.createElement("input");
        this.inputbox.setAttribute('type', 'text');
        this.inputbox.setAttribute('name', input_id);
        this.inputbox.setAttribute('id', input_id);
        Element.addClassName(this.inputbox, this.options.classInput);
        this.domNode.appendChild(this.inputbox);
        if (this.options.defaultValue) this.inputbox.value = this.value = this.options.defaultValue;

        this.hiddenValue = document.createElement("input");
        this.hiddenValue.setAttribute('type', 'hidden');
        this.container.appendChild(this.hiddenValue);

        this.button = document.createElement("div");
        this.button.setAttribute('id', this.id + '_btn');
		if(this.options.buttonImage) {
			button_img = new Image;
			button_img.src = this.options.pathToContent + this.options.theme + "/" + this.options.buttonImage;
			this.button.appendChild(button_img);
		}
        Element.addClassName(this.button, this.options.classButton);
        this.domNode.appendChild(this.button);

		if (is_ie) {
			this._Popup = window.createPopup();
			this._Document = this._Popup.document;
		} else {
			var oIframe = this._Popup = window.document.createElement('iframe');
			oIframe.frameBorder = "0";
			oIframe.scrolling = "auto";
			oIframe.style.position = "absolute";
			oIframe.width = oIframe.height = 0;
			oIframe.style.zIndex = this.options.zIndex;

			window.document.body.appendChild(oIframe);

			this._Document = oIframe.contentWindow.document;

			this._Document.open();
			this._Document.write('<html><head></head><body><\/body><\/html>');
			this._Document.close();
			this._Document.body.style.margin = this._Document.body.style.padding = "0px";
		}
		appendStyleSheet(this._Document, this.options.pathToContent + this.options.theme + "/" + this.options.classListCSS);
		this.Content = this._Document.body.appendChild(this._Document.createElement('div'));
		this.opslist = this.Content.appendChild(this._Document.createElement('div'));
		Element.addClassName(this.opslist, this.options.classList);
		this.opslist.id = this.id + '_list';
		Element.setOpacity(this.opslist, parseFloat(this.options.listOpacity));
    },
    /**
	 * Remove the Combobox DOM node from the document tree.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @type void
	 */
    remove :
    function() {
        container = this.container;
        node = this.domNode;
        this.container = this.domNode = this.domNode.combobox = null;
        this.inputbox = this.hiddenValue = this.button = this._Document = this.Content = this.opslist = null;
        container.removeChild(node);
    },
    /**
     * Build the list of options that appears below the inputbox.
     * @author Jared Nuzzolillo, Erik Arvidsson {@link http://webfx.eae.net/contact.html#erik Erik's Homepage} and Mike de Boer (mdeboer AT ebuddy.com)
     * @param {Array} options The options, consisting of ONLY ComboBoxItem objects
	 * @type void
	 */
    buildOptions :
    function(options, width) {
        var parts, html, options_len, j, i, opts_hidden;
        if (typeof options == "undefined")
            options = this.optionsList;
        if (typeof width == "undefined")
        	width = null;
	    this.opslist.innerHTML = "";
        for (i = 0; i < options.length; i++) {
        	options[i].redraw(width);
	        this.opslist.appendChild(options[i].domNode);
        }
        
        this.builtOnce = true;
    },
    /**
     * Helper function of ComboBox.handleKey.
     * @author Jared Nuzzolillo and Erik Arvidsson {@link http://webfx.eae.net/contact.html#erik Erik's Homepage}
	 * @type void
	 */
    update :
    function() {
        var options_len, option_part, crt_value, crt_value_len, i, found;
        found = false;
        options_len = this.optionsList.length;
        crt_value = this.inputbox.value.toLowerCase();
        crt_value_len = crt_value.length;
        this.matchingOptions = new Array();
        if (crt_value_len == 0) {
            for (i = 0; i < options_len; i++) {
                this.matchingOptions.push(this.optionsList[i]);
                found = true;
            }
        } else {
            for (i = 0; i < options_len; i++) {
                option_part = this.optionsList[i].text.toLowerCase().substring(0, crt_value_len);
                if (crt_value == option_part) {
                    this.matchingOptions.push(this.optionsList[i]);
                    found = true;
                }
            }
        }
        if (!found) {this.matchingOptions[0] = new ComboBoxItem('', '(No matches)', null, this);}
    },
    /**
     * Auto-completes the entered text, if possible.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @param {DOMElement} srcElement The input box element of this instance
	 * @type void
	 */
    handleKey :
    function(e) {
        if (!this.options.autoComplete) return false;
        var srcElement = Event.element(e);
        this.update();
        this.buildOptions(this.matchingOptions);
        if (this.matchingOptions.length == 1 && this.matchingOptions[0].text == "(No matches)"){}//empty
        else {this.showOptions(false);}
        this.value = srcElement.value;
        this.hiddenValue.value = srcElement.value;
    },
    /**
     * Change the current (old) caption of this instance to the new one.
     * @author Jared Nuzzolillo and Erik Arvidsson {@link http://webfx.eae.net/contact.html#erik Erik's Homepage}
     * @param {string} value Value that appears in the inputbox
	 * @type void
	 */
    choose :
    function(value, noaction) {
        if (typeof noaction == "undefined") noaction = false;
        if (this.hasOptions()) {
            this.hideOptions(false);
            var option = this.getItemByValue(value);
            if (option) {
                this.activeOption = option;
                this.value = option.value;
                this.inputbox.value = (this.options.useLocale) ? _(option.text) : option.text;
                if (option.image != null) {
                    option.setImage(option.image);
                } else {
                	this.inputbox.style.background = "";
                    //this.inputbox.style.paddingLeft = "0px";
                }
                this.hiddenValue.value = value;
                if (this.options.onSelect != null && !noaction && value != "") {
                    this.options.onSelect(value, this);
                }
            }
        }
    },
    /**
     * Retrieves the ComboBoxItem object that corresponds with the given value.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @param {string} val Value that has to be matched
	 * @type ComboBoxItem
	 */
    getItemByValue :
    function(val) {
        for (var i = 0; i < this.optionsList.length; i++) {
            if (this.optionsList[i].value == val) {
                return this.optionsList[i];
            }
        }
    },
    /**
     * Removes a ComBoxItem from this instance.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @param {int} index Key of the item that needs to be removed
	 * @type void
	 */
    removeItem :
    function(val) {
        this.optionsList = this.optionsList.reject(function(item){return item.value == val;});
    },
    /**
     * Clear the combobox of ALL items (start over from scratch). 
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @author Menno van Slooten (mvanslooten AT ebuddy.com)
	 * @type void
	 */
    removeAll :
    function() {
        if (this.hasOptions()) {
            for (var i = 0; i < this.optionsList.length; i++)
                this.optionsList[i].onRemove();
            this.optionsList = new Array();
            this.matchingOptions = new Array();
        }
        this.opslist.innerHTML = "";
        this.value = "";
        this.inputbox.value = "";
    },
    /**
     * Add ComboBoxItem(s) to this instance.
     * @author Jared Nuzzolillo and Erik Arvidsson {@link http://webfx.eae.net/contact.html#erik Erik's Homepage}
	 * @type void
	 */
    add :
    function(value, text, image) {
        if (typeof value != "undefined" && typeof text != "undefined") {
            if (typeof image == "undefined")
                image = null;
            this.optionsList.push(new ComboBoxItem(value, text, image, this));
        }
    },
    /**
     * Tells you whether this instance contains any items, or not.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @type boolean
	 */
    hasOptions :
    function() {
        return (this.optionsList.length > 0);
    },
    /**
     * Toggle the display state of the options list to hidden or visible.
     * @author Jared Nuzzolillo and Erik Arvidsson {@link http://webfx.eae.net/contact.html#erik Erik's Homepage}
	 * @type void
	 */
    toggle :
    function() {
        if (this.opslist) {
            if (!this.opened) {
                this.showOptions();
            } else {
                this.hideOptions();
            }
        } else {
            this.showOptions();
        }
    },
    /**
	 * Change the visual appearance of this Combobox as the mouse hovers over it.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @type void
	 */
    onEnter :
    function() {
		if (!Element.hasClassName(this.domNode, this.options.classContainerHover))
            Element.addClassName(this.domNode, this.options.classContainerHover);
    },
    /**
	 * Change the visual appearance of this Combobox after the mouse left.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @type void
	 */
    onLeave :
    function() {
		if (Element.hasClassName(this.domNode, this.options.classContainerHover))
            Element.removeClassName(this.domNode, this.options.classContainerHover);
    },
    /**
	 * Change the locale.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @param {string} newKey
	 * @type void
	 */
    onChangeLocale :
    function(e) {
       if (this.options.useLocale) {
           for (var i = 0; i < this.optionsList.length; i++)
               this.optionsList[i].onChangeLocale();
               if (this.activeOption)
                   this.inputbox.value = _(this.activeOption.text);
           if (this.options.onChangeLocale != null)
               this.options.onChangeLocale(this, e);
       }
    },
    /**
	 * Change the color theme.
	 * @author Elvin Priyadi (epriyadi AT ebuddy.com)
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @param 
	 * @type void
	 */
    onChangeTheme :
	function(theme, isDefault) {
	    if (this.options.theme != theme) {
	        this.options.theme = theme;
	        if (!isDefault) {
	            appendStyleSheet(this._Document, this.options.pathToContent + theme + "/" + this.options.classListCSS);
	        } else {
	            var oHead = this._Document.getElementsByTagName('head').item(0);
	            for (var i = 0; i < oHead.childNodes.length; i++)
	                if (oHead.childNodes[i].nodeName.toLowerCase() == "link")
	                    oHead.childNodes[i].href = this.options.pathToContent + theme + "/" + this.options.classListCSS;
	        }
	        $(this.id + '_id').src = this.options.pathToContent + theme + "/" + this.options.buttonImage;
	    }
	},
    /**
     * Set the display state of the options list to hidden.
     * @author Jared Nuzzolillo and Erik Arvidsson {@link http://webfx.eae.net/contact.html#erik Erik's Homepage}
     * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @type void
	 */
    hideOptions :
    function() {
    	if (!this.opened)
    		return;
    	if (is_ie) {
    		this._Popup.hide();
    	} else {
			this._Popup.width = this._Popup.height = 0;
    	}
    	this.opened = false;
    },
    /**
     * Set the display state of the options list to visible.
     * @author Jared Nuzzolillo and Erik Arvidsson {@link http://webfx.eae.net/contact.html#erik Erik's Homepage}
     * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @type void
	 */
    showOptions :
    function() {
        var doBuild = (arguments.length == 1) ? arguments[0] : true;
        /*
        if (!this.options.autoComplete && this.builtOnce)
            doBuild = false;
        */
		var width = this.domNode.offsetWidth;
		if (this.options.fixedListWidth)
		    width = this.options.fixedListWidth;

		if (doBuild) {
            this.update();
            this.buildOptions(this.optionsList, width);
            if (is_ie)
            	this.opslist.style.width = width + "px";
            else
	            this.opslist.style.width = (width - 8) + "px";
        }

        var x = 0;
        var y = this.domNode.offsetHeight;

        if (is_ie)
        	this._Popup.show(x, y, 0, 0, this.domNode);

        var height = this.opslist.offsetHeight;
        if (this.options.fixedListHeight > 0 && height > this.options.fixedListHeight)
            height = this.options.fixedListHeight;

        if (is_ie) {
        	this._Popup.show(x, y, width, height + 1, this.domNode);
        } else {
			var pos = getElementPosition(this.domNode, window);
			x += pos.X + 3;
			y += pos.Y + 2;
			if (x < 0) x = 0;
			this._Popup.style.left = x + "px";
			this._Popup.style.top = y + "px";
			this._Popup.width = width;
			this._Popup.height = height;
        }
        this.inputbox.focus();
        this.opened = true;
    },
    /**
     * Put focus (cursor) on the inputbox element.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type void
     */
    focusOnInput :
    function() {
        this.inputbox.focus();
    },
    /**
     * Mimic an Event, to avoid errors under exceptional circumstances.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type Object
     */
   mimicEvent :
   function() {
   	   var type = (arguments.length == 1) ? arguments[0] : "click";
   	   return {type: type, _bogus: true};
   },
   /**
     * Fire a custom event (API functionality).
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @param {String} type
     * @type Object
     */
   fireEvent :
   function(type) {
       var fullType = new String("on-" + type).camelize();
       arguments[0] = this.mimicEvent();
       if (typeof this[fullType] == "function")
           this[fullType].apply(this, arguments);
       else if (typeof this.options[fullType] != "undefined" && this.options[fullType] != null)
           this.options[fullType](this, this.mimicEvent());
    },
    /**
     * Add various event handlers to a <i>ComboBox</i> object.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type void
     */
    _attachBehaviors :
    function() {
        /*this.domNode.setAttribute('rico:widget', 'combobox');
        if (this.options.group != null)
           this.domNode.setAttribute('rico:group', this.options.group);
        this.domNode.combobox = this;*/
        ComboBox.cache[this.id] = this;
        if (this.options.group != null) {
            if (!ComboBox.groupCache[this.options.group])
                ComboBox.groupCache[this.options.group] = new Array();
            ComboBox.groupCache[this.options.group].push(this);
        }
        
	    this.domNode.onclick     = this.toggle.bindAsEventListener(this);
	    this.button.onfocus      = this.focusOnInput.bindAsEventListener(this);
	    this.inputbox.onblur     = this.hideOptions.bindAsEventListener(this);
        this.domNode.onmouseover = this.onEnter.bindAsEventListener(this);
	    this.domNode.onmouseout  = this.onLeave.bindAsEventListener(this);
        this.domNode.onkeyup     = this.handleKey.bindAsEventListener(this);
        
        if (!this.options.autoComplete) {
	        this.inputbox.onkeydown     = function(){return false;};
	        this.inputbox.onselectstart = function(){return false;};
        }
    }
};
/**
 * @class ComboBoxItem - Represents a single item/ option of a ComboBox.
 * @constructor
 * @extends ComboBox
 * @author Mike de Boer (mdeboer AT ebuddy.com)
 * @return ComboBoxItem
 */
ComboBoxItem = Class.create();
ComboBoxItem.prototype = {
    /**
     * Initialize the Checkbox class.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @param {String} value
	 * @param {String} text
	 * @param {String} image URL to the image.
	 * @param {ComboBox} combobox Parent ComboBox object
	 * @type ComboBoxItem
	 */
    initialize :
    function(value, text, image, combobox) {
        this.value    = value;
        this.text     = text;
        this.image    = image;
        this.combobox = combobox;
        this._draw(null);
        this._attachBehaviors();
    },
    /**
	 * Draw this ComboBoxItem instance to the HTML grid.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @type void
	 */
    _draw :
    function(width) {
        this.domNode = this.combobox._Document.createElement('div');
		/* MvS: This ID is assigned to all options...should be className? */
        this.domNode.setAttribute('id', this.combobox.id + '_option');
        this.domNode.className = this.combobox.options.classOption;
        if (this.image != null) {
            this.imageNode = this.combobox._Document.createElement('img');
            this.imageNode.setAttribute('alt', '');
            this.imageNode.src = this.image;
            this.domNode.appendChild(this.imageNode);
        }
        var caption = this.combobox._Document.createElement('span');
        var text = (this.combobox.options.useLocale) ? _(this.text) : this.text;
        this.textNode = caption.appendChild(this.combobox._Document.createTextNode(text));
        this.domNode.appendChild(caption);
        if (width != null)
        	this.domNode.style.width = (width - 10) + "px";
    },
     /**
	 * Set the image of this item instance to a different location.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @param {String} image
	 * @type void
	 */
    setImage :
    function(image) {
    	if (typeof image != "string" | empty(image)) return;
    	this.image = image;
    	this.imageNode.src = this.image;
    	if (this.combobox.value == this.value) {
            this.combobox.inputbox.style.backgroundImage = "url(" + this.image + ")";
            this.combobox.inputbox.style.backgroundRepeat = "no-repeat";
            this.combobox.inputbox.style.paddingLeft = "18px";
    	}
    },
    /**
	 * Re-draw this ComboBoxItem instance to the HTML grid, upon a public call.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @type void
	 */
    redraw :
    function(width) {
    	this._draw(width);
    	this._attachBehaviors();
    },
    /**
	 * Remove this ComboBoxItem instance from the HTML grid.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @type void
	 */
	onRemove :
	function() {
	    this.combobox = null;
	    this.domNode.onmouseover = this.domNode.onmouseout = this.domNode.onclick = null;
	    var node = this.domNode;
	    this.domNode = this.image = this.imageNode = this.textNode = null;
	    if (node && node.parentNode) node.parentNode.removeChild(node);
	},
    /**
	 * Change the visual appearance of this Item as the mouse hovers over it.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @param {Event} e
	 * @type void
	 */
    onEnter :
    function(e) {
        if (!Element.hasClassName(this.domNode, this.combobox.options.classOptionHover))
            Element.addClassName(this.domNode, this.combobox.options.classOptionHover);
        if (e) Event.stop(e);
    },
    /**
	 * Change the visual appearance of this Item after the mouse left.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @param {Event} e
	 * @type void
	 */
    onLeave :
    function(e) {
        if (Element.hasClassName(this.domNode, this.combobox.options.classOptionHover))
            Element.removeClassName(this.domNode, this.combobox.options.classOptionHover);
        if (e) Event.stop(e);
    },
    /**
	 * Change the value of the parent ComboBox after it has been clicked.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @param {Event} e
	 * @type void
	 */
    onClick :
    function(e) {
        this.combobox.choose(this.value);
        if (e) Event.stop(e);
    },
    /**
	 * Change the locale.
	 * @author Mike de Boer (mdeboer AT ebuddy.com)
	 * @type void
	 */
    onChangeLocale:
    function() {
        if (this.textNode)
            this.textNode.nodeValue = _(this.text);
    },
    /**
     * Add various event handlers to a <i>ComboBoxItem</i> object.
     * @author Mike de Boer (mdeboer AT ebuddy.com)
     * @type void
     */
    _attachBehaviors :
    function() {
        this.domNode.onmouseover = this.onEnter.bindAsEventListener(this);
        this.domNode.onmouseout  = this.onLeave.bindAsEventListener(this);
        this.domNode.onclick     = this.onClick.bindAsEventListener(this);
    }
};

ComboBox.cache = [];
ComboBox.groupCache = [];