/**
 * $Id::                                                                       $
 * 
 * Tab Control creates tabs for defined contents.
 *
 * @since 1.1 n/a
 * @since 1.2 Option initialTab is fixed an can now be numeric
 * @since 1.3 Tab#selectTab() will now prevent event default if tab is an anchor
 * 
 * @copyright 2009 Phase 4
 * @version 1.3
 * @link $URL::                                                                $
 * @author $Author::                                                           $
 * @package Phase4
 * @subpackage Control 
 */

/*
 * Register the classes into subpackage Control
 */
Phase4.registerClasses('Control', new Array('Tab'));

/**
 * Tab
 * 
 * A tabcontrol
 * 
 * Options:
 * 
 * @param {String} behaviour Defines whether the panes shall be shown on 
 *                           'click' or 'mouseover'. (default: 'click')
 * @param {String,Number} initialTab The id or index of the tab that will be 
 *                                   selected on initialization.
 *                                   (default: first tab)
 * @param {Array} panes The ids of the panes in the correct order. 
 *                      If missing will use css-rule div#pane&lt;N&gt; 
 *                      within element.
 * @param {String} selectedClass The css-class that will be assigned to the 
 *                               selected tab (default: '').
 * @param {Array} tabs The ids of the tabs in the correct order. 
 *                     If missing will use css-rule div#tab&lt;N&gt; 
 *                     within element.
 * 
 * Events:
 * Unless otherwise noted, all events are bound to the current object 
 * so that it can be accessed via the keyword this.
 * 
 * @param {Funtcion} onChange Fired when ever the tab is changed. 
 *                            Passes the currently visible pane and tab 
 *                            to callback in that order.
 */
Phase4.Control.Tab = Class.create({
    /*
     * Methods
     */
    
	/**
	 * Constructor
	 * 
	 * @constructor
	 * @class
	 * 
	 * @param {Element,String} element The element that will be used as container for the tab-control
	 * @param {Object} [options] The options as described above
	 */
	initialize: function(element, options) {
	    //init properties
		this.element = $(element);
		this.options = Object.extend({
		    /* Options */
		    behaviour: 'click',
		    initialTab: '',
		    selectedClass: '',
		    
		    /* Events */
		    onChange: Prototype.emptyFunction
		}, options);
		this.panes = new Array();
		this.tabs = new Array();
		
		//bind events
		this.options.onChange = this.options.onChange.bind(this);
		
		//init tabs and panes
		this._initTabs();
		this._initPanes();
        
		//expand or default initial tab and have it displayed
		if (Object.isNumber(this.options.initialTab)) {
		    this.options.initialTab = this.tabs[this.options.initialTab];
		} else if (Object.isString(this.options.initialTab)) {
		    this.options.initialTab = $(this.options.initialTab);
		}
		this.options.initialTab = this.options.initialTab || this.tabs[0];
		this.selectTab(null, this.options.initialTab);
	},
	
	/**
	 * Destructor
	 */
	dispose: function() {},
	
	/*
	 * Private methods
	 */
	
	/**
	 * Grabs the panes
	 */
	_initPanes: function() {
		var panes = this.options.panes || 
		            this.element.select('div').findAll(function(s) {
		                return (s.id && s.id.search(/^pane(\d+)$/)==0);
		            });
		
		//iterate through panes adding them to our panes-datamember
		panes.each(function(s) {
			this.panes.push($(s));
		}, this);
	},
	
	/**
	 * Grabs the tabs and installs listeners
	 */
	_initTabs: function() {
		var tabs = this.options.tabs || 
		           this.element.select('div').findAll(function(s) {
		               return (s.id && s.id.search(/^tab(\d+)$/)==0);
	               });
		
		//iterate through tabs adding them to our tabs-datamember and setting up listeners
		tabs.each(function(s, n) {
			var elem = $(s);
			
			elem.observe(this.options.behaviour, this.selectTab.bindAsEventListener(this, elem));
			this.tabs.push(elem);
		}, this);
	},
	
	/*
	 * Public methods
	 */
	
	/**
	 * Returns the index of the selected tab.
	 * 
	 * @return {int} The index of the currently selected tab
	 */
	getSelectedIndex: function() {
	    var index;
	    
	    this.panes.each(function(pane, n) {
	        if (pane.visible()) {
	            index = n;
	            throw $break;
	        }
	    });
	    
	    return index;
	},
	
	/**
	 * Will select specified tab and show its associated pane. 
	 * 
	 * @param {Event} evt The event that fired this function (can be null if used from external script)
	 * @param {Element} tab The tab for which a pane shall be shown
	 */
	selectTab: function(evt, tab) {
	    //init vars
		var currentPane;
		var currentTab;
		
		//stop event if present
		if (evt) evt.stop();
		
		//iterate through tabs, showing/hiding the associated panes
		this.tabs.each(function(s, n) {
			if (s===tab) {
				s.addClassName(this.options.selectedClass);
				this.panes[n].show();
				currentPane = this.panes[n];
				currentTab  = s;
			} else {
				s.removeClassName(this.options.selectedClass);
				this.panes[n].hide();
			}
		}, this);
		
		//call the onChange-callback
		this.options.onChange(currentPane, currentTab);
		
		//and if tab is an anchor, we prevent default
		if (tab.tagName.toLowerCase()=='a') return false;
	},
	
	/**
	 * Selects a tab by its index.
	 * If index is lesser than 0, will default to 0.
	 * If index exceeds upper bound, will default to upper bound. 
	 * 
	 * @param {int} index The index of the tab that shall be selected.
	 * @return void
	 */
	selectTabByIndex: function(index) {
	    //make sure index is within bounds
	    index = ((0 <= index < this.tabs.length) ? index : 0);
//	    if (index < 0) {
//	        index=0;
//	    } else if (index >= this.tabs.length) {
//	        index = this.tabs.length - 1;
//	    }
	    
	    //and have it selected
	    this.selectTab(null, this.tabs[index]);
	}
});
