/*
    Behaviours for the Rich Select boxes used in the site - particularly on the Browse pages.
    
    This class transforms an ordinary <select> and replaces it with the rich version preserving:
        
        * The select options (values and text)
        * The "selected" value (from the HTML)
        * The ability to call on the form VALUE using the SAME ID or and name that was applied to the select (via a hidden form element with the same id/name)

    People without JavaScript (and search engine indexers) will just see the standard select boxes instead.
    
    All that is required to set one of these up is to create the HTML for a standard select with a class of "rich-select", and then apply this constructor to that element
    (see the constructor in /js/site.pages.browse.js for examples)
    
    Requires: 
        Site.Widgets.DropDown (used for the options menu)
*/

Site.Widgets.RichSelect = Class.create
(
    Site.Base,
    {
        initialize: function($super, element, options)
        {
            $super();
            
            this.element = element;
            
            this.setOptions
            (
                options,
                {
                    width: 200,
                    optionsWidth: 350,
                    hideDuration: Site.DEFAULT_SHOW_DURATION,
                    showDuration: Site.DEFAULT_HIDE_DURATION
                }
            );
            
            this.addBlocks("hideOptions");
            this.addObservers("hideOptions", "_optionOnClick");

            
            this.className = this.element.className;
            this.name = this.element.name || this.element.id;
            this.id = this.element.id;
            
            this.width = this.options.width;
            this.optionsWidth = this.options.optionsWidth;
            
            this.selectOptions = $A(this.element.options).collect
            (
                function(option)
                {
                    return { "text" : option.text, "value": option.value || option.text, "selected": option.selected };
                }, 
                this
            );
            
            this._deriveTextValue();
            
            this.originalSelectOptions = this.selectOptions;
            this.originalValue = this.value;
            
            this._setup();
            
            // make a document click also hide the select (like regular selects)
            addEvent(document, 'click', this.observers.hideOptions);
        },
        
        
        setWidth: function(width, optionsWidth)
        {
            this.width = width;
            
            if (optionsWidth)
                this.optionsWidth = optionsWidth;
                
            this._repaint();
        },
        
        setSelectOptions: function(options)
        {
            this.selectOptions = options;
            
            this.optionsElement.update(this._getOptionsHTML());
            this._setupOptionsEvents();
            this._deriveTextValue(); 
            
            this.setValue(this.value);
        },
        
        reset: function()
        {   
            this.setSelectOptions(this.originalSelectOptions);
            this.setValue(this.originalValue);
        },
        
        hideOptions: function()
        {
            this.dropDown.hideDropDown();
        },
        
        getTextForValue: function(value)
        {
            if (option = this.selectOptions.find( function(option) { return option.value == value; } ))
            {
                return option.text;
            }
            
            return false;
        },
        
        setValue: function(value)
        {
            this.value = value;
            
            this.selectOptions.each
            (
                function(option)
                {
                    option.selected = (option.value == value);
                    
                    if (option.selected)
                        this.text = option.text;
                },
                this
            );
            
            // update the hidden element value (for the form)
            this.valueElement.value = this.value;
            
            this._updateDisplay();
        },
        
        /* 
            Pseudo-private methods 
        */
        
        _repaint: function()
        {
            this.element.setStyle( { width: (this.width) + "px" } );
            
            this.optionsElement.setStyle( { width: (this.optionsWidth - Site.Widgets.RichSelect.WIDTH_OPTIONS_CHROME) + "px" } );
            this.buttonElement.setStyle( { width: (this.width - Site.Widgets.RichSelect.WIDTH_BUTTON_CHROME) + "px" } );
            this.textElement.setStyle( { width: (this.width - Site.Widgets.RichSelect.WIDTH_TEXT_CHROME - Site.Widgets.RichSelect.WIDTH_BUTTON_CHROME) + "px" } );
            
        },
        
        _deriveTextValue: function()
        {
            this.selectOptions.each
            (
                function(option)
                {
                    if (option.selected)
                    {
                        this.text = option.text;
                        this.value = option.value;
                    }
                }, this
            );
        },
        
        
        _setup: function()
        {
            // replace the standard select with the rich HTML version
            
            this.template = new Template(Site.Widgets.RichSelect.TEMPLATE);
            this.templateOption = new Template(Site.Widgets.RichSelect.TEMPLATE_OPTION);
            //alert(this.element.id);
            
            
            this.element.replace
            (
                this.template.evaluate
                (
                    {
                        className: this.className,
                        id: this.id,
                        name: this.name,
                        value: this.value,
                        text: this.text,
                        options: this._getOptionsHTML()
                    }
                )
            );
            
            // setup pointers to elements
            
            this.element = $(this.id + '-widget');
             
            this.buttonElement = this.element.select("a.button").first();
            this.textElement = this.element.select("span.text").first();
            
            this.optionsElement = this.element.select("ul.options").first();
            this.valueElement = this.element.select("input").first();
            
            // repaint
            this._repaint();
            
            this._deriveTextValue();
            
            this.dropDown = new Site.Widgets.DropDown(this.buttonElement, this.optionsElement, this.options);

            this._setupOptionsEvents();
        },
        
        getValue: function()
        {
            return this.value;
        },
        
        _setupOptionsEvents: function()
        {
            this.optionsElement.select("li a").each
            (
                function(link)
                {
                    addEvent(link, "click", this.observers._optionOnClick);
                },
                this
            );
        },

        _getOptionsHTML: function()
        {
             return this.selectOptions.collect
             (
                 function(option, index)
                 {
                     return this.templateOption.evaluate
                            (
                                {
                                    value: option.value,
                                    text: option.text,
                                    className: option.selected ? "selected" : ""
                                }
                            ); 
                 },
                 this
             ).join('')    
        },
        
        _optionOnClick: function(event)
        {
            var element = Event.element(event);
            
            var a = element.matchUp("a");
            var li = element.matchUp("li");
            
            window.setTimeout(this.blocks.hideOptions, 100); // delay it slightly, so that the user can see the active selection being updated
        
            this.setValue(a.rel);
            
            if (this.options.onChange)
            {
                this.options.onChange(this); 
            }

            li = null;
            a = null;
            
            Event.stop(event);
        },
        
        _updateDisplay: function()
        {
            // update the display to the current text
            this.textElement.update(this.text);
            
            // update the active state
            this.optionsElement.select("li").invoke("removeClassName", "selected");
            
            if (a = this.optionsElement.select('a[rel="' + this.value + '"]').first())
            {
                if (li = a.up("li"))
                    li.addClassName("selected");
            }
        },
        
        destroy: function()
        {
            removeEvent(document, "click", this.observers.hideOptions);
            
            this.optionsElement.select("li a").each
            (
                function(link)
                {
                    removeEvent(link, "click", this.observers._optionOnClick);
                },
                this
            );
   
            this.dropDown.destroy();
        }     
    }
);

Object.extend
(
    Site.Widgets.RichSelect,
    {
        TEMPLATE:        '<div id="#{id}-widget" class="#{className}">'
                       + '<input id="#{id}" name="#{name}" type="hidden" value="#{value}" />'
				       + '<a href="#" class="button"><span class="lt"></span><span class="text">#{text}</span><span class="rt"></span></a>'
				       + '<ul class="options" style="display: none">'
				       + '#{options}'
				       + '</ul>'
				       + '</div>',

		TEMPLATE_OPTION: '<li class="#{className}"><a href="##{value}" rel="#{value}">#{text}</a></li>',
		
		WIDTH_BUTTON_CHROME: 24, /* How wide is the arrow button (on the right)? This is used to calculate correct width */
		WIDTH_TEXT_CHROME: 9, /* How wide is everything surrounding the text display (for the current value). This doesn't include the button width above */
		WIDTH_OPTIONS_CHROME: 2 /* How much padding and border is applied to the options list */
    }
);