/* ************************************************************************************* *\
 * The MIT License
 * Copyright (c) 2009 Christian Zenker (http://www.christianbloggt.de)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this
 * software and associated documentation files (the "Software"), to deal in the Software
 * without restriction, including without limitation the rights to use, copy, modify,
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
\* ************************************************************************************* */

/**
 * CzTabs - Unobtrusive Tabs for webpage headers with Ajax
 * 
 * Simple and clean Tab plugin for MooTools 1.2
 * including support for Ajax content and various
 * custom Events to customise the appearance.
 * 
 * This is a derivative work of SimpleTabs 1.0rc0 by
 * Harald Kirschner <mail [at] digitarald.de>
 *
 * @version		0.1
 * 
 * @see			Events, Options
 * 
 * @license		MIT License
 * @author		Harald Kirschner <mail [at] digitarald.de>
 *				Christian Zenker <http://www.christianbloggt.de>
 * @copyright	2009 Author
 *
 */
var CzTabs = new Class({

	/**
	 * Options
	 * 
	 * show: Number, default 0: index for the initial selected tab
	 * delay: Number. time in milliseconds to switch to next tab. value 0 - disabled
	 * delayOnMouseOver: Number. time in milliseconds to switch to next tab when user selected a tab. value 0 - stop
	 * delayCrossFade: Number. time in milliseconds. Delay between fading in and fading out. value 500 - complete fade out before fading in
	 * idMainBox: String, default "tx-cztabs". Selector for the element containing everything
	 * idMainItems: String, default "tx-cztabs-items". Selector for the item list. This is usually the displayed navigation list.
	 * meatItem: String, default "li". Selector to find the tab elements under the given parent element
	 * metaItemLink: String, default "a.tx-cztabs-dynamic-link". Selector for the items containing meta information on the items
	 * idMainContent: String, default "tx-cztabs-content". Selector for the box containing all content elementsnts
	 * classContainer:  String, default ".tx-cztabs-container". class name for the single container elements
	 * classNoFollowLink: String, default "cztabs-item-link-jsblock". a link on a tab title will not be followed if this class is set.
	 * classAjaxLoader: String, default "cztabs-ajax-load". The class to set if Ajax is loading.
	 * classToggleActive: String, default "cztabs-selected". A class to add if tab is active.
	 * ajaxUrls: Array of Strings, urls for ajax. First url is for first toggle without content element and so on.
	 * preloadContent: Boolean, default true. The content of the tabs is preloaded on domready
	 * maxTries: Integer, default 5. Number of tries to load tabs from the server.
	 * onTabCreate: Event. Fires when a tab was created. Add your own events or whatever you like here
	 * onShow: Event. Fires when a container is shown, arguments: (tabElement, containerElement, tabIndex, tabElementOld, containerElementOld, tabIndexOld)
	 * onHide: Event. Fires when a container is hidden, same arguments
	 * onRequest: Event. Fires when Ajax request starts, same arguments
	 * onComplete: Event. Fires when Ajax request is completed successfully, same arguments
	 * onFailure: Event. Fires when a Ajax request fails, same arguments
	 * 
	 */
	options: {
		show:             0,
		delay:            10000,
		delayOnMouseOver: 0,
		delayCrossFade:   200,
  		idMainBox:        'cztabs',
		idMainItems:      'cztabs-items',
  		metaItem:         'li',
		metaItemLink:     'a.cztabs-item-meta',
		idMainContent:    'cztabs-content',
		classContainer:   'cztabs-container',
		classNoFollowLink:'cztabs-item-link-jsblock',
		classAjaxLoader:  'cztabs-ajax-loading',
		classToggleActive:'cztabs-selected',
		classLoadContent: 'cztabs-load',
		classStart:       'cztabs-start',
		classStop:        'cztabs-stop',
		ajaxUrls:         [],
		preloadContent:   true,
		maxTries:         5,
		onTabCreate: function(toggle, container, index) {
			container.setStyles({opacity: 0, visibility: 'hidden'});
		},
		onShow: function(toggle, container, index, oldindex) {
			if(this.slowBrowser) {
				container.setStyles({visibility:'visible', opacity:1});
			}else{
				container.fade('in');
			}
		},
		onHide: function(toggle, container, index, newindex) {
			if(this.slowBrowser) {
				container.setStyles({visibility:'hidden', opacity:0});
			}else{
				container.fade('out');
			}
		},
		onRequest: function(toggle, container, index) {
			container.addClass(this.options.classAjaxLoader);
		},
		onComplete: function(toggle, container, index, mydelay) {
			container.removeClass(this.options.classAjaxLoader);
		},
		onFailure: function(toggle, container, index, mydelay) {
			container.removeClass(this.options.classAjaxLoader);
		}
	},

	/**
	 * Constructor
	 * 
	 * @param {Element} The parent Element that holds the entry elements
	 * @param {Object} Options
	 */
	initialize: function(el, options) {
		this.setOptions(options);
		this.element = $(el);
		this.current = 0;
		this.selected = null;
		this.delay = this.options.delay;
		this.delayOnMouseOver = this.options.delayOnMouseOver;
		this.build();
		if(this.options.preloadContent) {
			this.ajaxPreloader();
		}
		
		// variable to enable different effects for known slow browsers
		if((Browser.Engine.trident && Browser.Engine.version<5)/*IE6*/ ||
		   (Browser.Engine.webkit && Browser.Engine.version<500)/*Konqueror*/) {
			this.slowBrowser = true;
		}else{
			this.slowBrowser = false
		}
		
	},

	/**
	 * called by Constructor
	 */
	build: function() {
		this.entries = [];
		var count  = 0;
		var acount = 0; // ajax array counter
		var ccount = 0; // content boxes counter
		
		//add a start/stop toggle
		
		this.stop = 0;
		this.startstop = new Element('a', {'href': '#', 'class': this.options.classStop})
			.set('text', 'stopp')
			.inject($(this.options.idMainContent))
			.addEvent('click', this.onToggleSwitch.bindWithEvent(this));
		
		
		$(this.options.idMainItems).getElements(this.options.metaItem).each(function(el) {
			var content = (el.hasClass(this.options.classLoadContent) ? this.options.ajaxUrls[acount++] : $$('.'+this.options.classContainer)[ccount++]);
			this.addTab(content, el, count);
			count++;
		}, this);
		
		this.count = count;
		
		if (this.entries.length) {
			this.select(this.options.show, this.delay);
			this.current = this.options.show;
    	}
	},

	/**
	 * Add a new tab at the end of the tab menu
	 *
	 * @param {String} inner text of link
	 * @param {String} title in the a-tag
	 * @param {Element|String} Content Element or URL for Ajax
	 * @param {Element} Navigation Element
 	 *
	 * @return this 
	 */
	addTab: function(content, toggle, index) {
		if ($type(content) == 'string' && !$(content))
			var url = content;
		var container = $(content) || new Element('div');
		this.entries.push({
			container: container.addClass(this.options.classContainer).inject($(this.options.idMainContent)).addEvents({
				'mouseenter': this.onContainerMouseEnter.bindWithEvent(this, [this.entries.length]),
				'mouseleave': this.onContainerMouseLeave.bindWithEvent(this, [this.entries.length])
				}),
			toggle: $(toggle).addEvents({
				'click': this.killEvents.bindWithEvent(this, [this.entries.length]),
				'mouseenter': this.onMouseEnter.bindWithEvent(this, [this.entries.length]),
				'mouseleave': this.onMouseLeave.bindWithEvent(this, [this.entries.length])
				}),
			url: url || null,
			idLoading: false,
			numTry: 0
		});
		
		this.fireEvent('onTabCreate', [toggle, container, index]);
		
		toggle.getElements('a.'+this.options.classNoFollowLink).each(function(el) {
			el.addEvent('click', function(event){
				event.preventDefault();
			});
		});
		
		
		return this;
	},
	
	/**
	 * Do something when mouse enters...
	 *
	 * ...tab menu
	 **/
	onMouseEnter: function(evt, index) {
		if(this.stop)
			return;
		if(this.selected == index) {
			this.prepareTimer(index+1, this.delayOnMouseOver);
		}else {
			this.select(index, this.delayOnMouseOver);
		}
	},
 
	onMouseLeave: function(evt, index) {
		if(this.stop)
			return;
		if(this.selected == index) {
			this.prepareTimer(index+1, this.delay);
		}else {
			this.select(index, this.options.delay);
		}
	},

	/**
	 * ...container
	 **/
 	onContainerMouseEnter: function(evt, index) {
		if(this.stop)
			return;
		this.prepareTimer(index+1, this.delayOnMouseOver);
	},
 
	onContainerMouseLeave: function(evt, index) {
		if(this.stop)
			return;
		this.prepareTimer(index+1, this.delay);
	},
	
	/**
	 * ...toggle
	 **/
	onToggleSwitch: function() {
		if(this.stop) {
			//if: was stopped
			this.prepareTimer(this.selected+1, this.delay);
			this.startstop.set('text', 'stopp').removeClass(this.options.classStart).addClass(this.options.classStop)
				;
			
		}else{
			this.prepareTimer(this.selected+1, 0);
			this.startstop.set('text', 'start').removeClass(this.options.classStop).addClass(this.options.classStart)
				;
		}
		this.stop = !this.stop;
	},
	
	/**
	 * Stop all sliding effects
	 *
	 */
	killEvents: function() {
		this.delay = 0;
		this.delayOnMouseOver = 0;
	},

	ajaxPreloader: function () {
		var index = 0;
		this.entries.each((function(entry){
			if(entry.url) {
				var params = [entry.toggle, entry.container, index];
				this.ajaxLoad(index, params, -1);
			}
			index++;
		}).bind(this));
	},
 
	ajaxLoad: function(index, params, mydelay) {
		if(this.entries[index].numTry > this.options.maxTries || this.entries[index].isLoading)
			return false;
		
		this.entries[index].isLoading = true;
		this.entries[index].numTry++;
		new Request({
			method: 'get',
			url: this.entries[index].url,
			onRequest: this.fireEvent.pass(['onRequest', params], this),
			onFailure: function (resp) {
				this.entries[index].isLoading = false;
				this.fireEvent.pass(['onFailure', params.extend([mydelay])], this);
				if(mydelay >= 0) {
					this.select(index+1, mydelay);
				}
			}.bind(this),
			onComplete: function(resp) {
				if(resp != undefined) {
					this.entries[index].loaded = true;
					this.entries[index].isLoading = false;
					this.entries[index].container.empty().set('html',resp);
					this.fireEvent('onComplete', params.extend([mydelay]));
					if(mydelay >= 0) {
						this.select(index, mydelay);
					}
				}
			}.bind(this)
		}).send();
	},

	/**
	 * Select the tab via tab-index
	 *
	 * @param {Number} Tab-index
	 * @param {Number} Time (in ms) to show next tab
	 */
	select: function(index, mydelay) {
		if (index >=this.count) index = index%this.count;
		if (this.selected == index || !this.entries[index]) return this;
		var entry = this.entries[index];
		var params = [entry.toggle, entry.container, index];
		if (this.selected !== null) {
			var current = this.entries[this.selected];
			if (this.ajax && this.ajax.running) this.ajax.cancel();
			params.concat([current.toggle, current.container, this.selected]);
		}
		//try to load it via ajax
		if (entry.url && !entry.loaded && !entry.isLoading) {
			this.ajaxLoad(index, params, mydelay);
			
		}else{
			if(this.selected !== null) {
				this.fireEvent('onHide', [current.toggle, current.container, this.selected, index]);
			}
			
			this.fireEvent('onShow', params.extend([this.selected]));
			this.selected = index;
	
			this.prepareTimer(this.selected+1, mydelay);
		}
		
		return this;
	},
	
	/**
	 * set the timer for next tab
	 * @param {Number} Tab-index
	 * @param {Number} Time (in ms) to show next tab
	 */

	prepareTimer: function(index, mydelay) {
		//console.log('prepareTimer' + index + ' in '+ mydelay + ' ms');
		$clear(this.timer);
		if (mydelay>0) {
			if (index >=this.count) index = index%this.count;
			this.timer = this.select.delay(mydelay, this, [index, this.options.delay]);
		}
	}

}).implement(new Events).implement(new Options);
