﻿Ext.DomQuery.pseudos.nodeType = function(c, a){
	var r = [], ri = -1, n;
	for(var i = 0; n = c[i]; i++){
		if(n.nodeType == a){
			r[++ri] = n;
		}
	}
	return r;
};

Ext.Element.prototype.getChildren = function(){
          
        return this.select('> *:nodeType(1)');
};

Ext.Element.prototype.getSiblings = function(){
          
        return this.select('~ *:nodeType(1)');
};

/*
The MIT License

Copyright (c) 2009 Andy Cramb(andycramb@gmail.com)

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.
*/

// namespace
Ext.ns('Ext.ux')

/**
 * @class Ext.ux.CodaSlider
 * @extends Ext.util.Observable
 * @author AndyCramb
 * @version v0.1
 * @create 2009-07-07
 * @update 2009-07-07
 */
 
Ext.ux.CodaSlider = Ext.extend(Ext.util.Observable,{
    //------------------------------------------------------------
    // config options
    //------------------------------------------------------------
	
	// example usage
	//var myTabs = new Ext.ux.CodaSlider('buttons', 'panes',{startingSlide:0,animateHeight: true});
	
	/**
	 * @config {int} starting tab/pane on page load
	 * array based so first tab is 0
	 */
	startingSlide		: 0
	/**
	 * @config {string} class for the selected tab
	 */
	,activeButtonClass	: 'active' 
	/**
	 * @config {string} type of event 
	 * defaults to the click event if no event is specified in the config
	 */
	,activationEvent	: 'click' 
	/**
	 * @config {Number} duration of the animations
	 */
	,fxDuration			: 0.8
	/**
	 * @config {boolean} determines if the height should be animated
	 */
	,navSelector		: 'li'	
	/**
	 * @config {string} you can pass in a specific selector to identify your navigational items
	 */
	,animateHeight		: true
	/**
	 * @config {Number} the index of the cuurent/active tab
	 */
	,current			: 0 // zero based current pane number, read only
	/**
	 * @config {string} specifies the type of easing to aplly to the height animation
	 */
	,heightEasingEffect	: 'easeBoth'
	/**
	 * @config {string} specifies the type of easing to aplly to the scroll animation
	 */
	,scrollEasingEffect	: 'easeBoth'
	/**
	 * @config {Mixed Element} container element for the div that wraps all the div panes
	 */
	,outerSlidesBox		: null
	/**
	 * @config {Mixed Element} container element for the divs that hold the content for the tabs
	 */
	,innerSlidesBox		: null
	/**
	 * @config {CompositeElement} that holds the collection of Ext elements
	 */
	,panes				: null
	
	
	,paneScrolling		: false
	
	
	/**
	 * Constructor for this class
	 * @param {HTML element id} this is the wrapper for the navigational items
	 * @param {HTMlL element id} this is the outer wrapper for the content div that holds all the panes
	 * @param {JS lieteral Object}This contains all the configuarble options for the class
	 * @return {void}
	 */
    ,constructor 	: function(navContainer, slideContainer, config) {

        Ext.apply(this, config);      
        Ext.ux.CodaSlider.superclass.constructor.call(this);     
 
        this.addEvents( 'change','startAnimation');  
		this.initEvents(navContainer);
		
        this.init(navContainer,slideContainer);		
    }
	
	/**
	 * Will set up styles and initial config properties
	 * @param {HTML element id} this is the wrapper for the navigational items
	 * @param {HTMlL element id} this is the outer wrapper for the content div that holds all the panes
	 * @return {void}
	 */
    ,init       	: function(navContainer,slideContainer){

        if(navContainer){ 
            //this.buttons = Ext.select('#' + buttonContainer + '> li.nav');
			this.buttons = Ext.select('#' + navContainer + '> '+ this.navSelector);
        };
        
        this.outerSlidesBox = Ext.get(slideContainer);//return div#panes - correct
		this.innerSlidesBox = this.outerSlidesBox.first();//return div#content - correct
        //Ext has no getchildren method as far as I can see
		this.panes = this.innerSlidesBox.getChildren() // see condor's method

        //this.current = this.startingSlide ? this.panes.indexOf(Ext.get(this.startingSlide)) : 0;
		this.current = typeof this.startingSlide == 'number' ? this.startingSlide : 0;

        var currentEl = this.panes.item(this.current);

        this.outerSlidesBox.setStyle({'overflow':'hidden','height':currentEl.getHeight() +'px'});
		this.panes.each(function(el,index) {
		    el.setStyle({
		   'float': 'left',
		   'overflow': this.paneScrolling ? 'auto' : 'hidden'
		  });
		},this);

        this.innerSlidesBox.setStyle('float', 'left');
		
		// calculate widths so that all panes fit aligned horizontally 
		this.recalcWidths();
		
		//set initial tab if its not the default tab index 0
		if(this.current > 0){
			this.onTabChange(this.current);
		}
		else{
			this.buttons.item(this.current).addClass(this.activeButtonClass); 
		}
    }
	/**
	 * Will set up events for each child element within the navigational items container
	 * @param {HTML element id} this is the wrapper for the navigational items
	 * @return {void}
	 */
	,initEvents			: function(navContainer){
			Ext.get(navContainer).on({
				click	: this.onTabChange,
				scope	: this,
				delegate: this.navSelector
		})	
	}

	/**
	* handles the click event on the navigational items
	* switches the class to active for the selected li 
	* It can optionally be called direct passing in the index of the navigational item
	 * @param {Ext event object} 
	 * @param {Ext target object} this will represent the Ext element that was clicked on
	 * @param {number} the index of the navigational item - array based so first item will be 0
	 * @return {void}
	 */
	,onTabChange		: function(ev, t) {
	
		var el;
		
		// this  handles the event but can take a number(tab index) that specifies the tab to be selected
		//will makes sure an ext elemnt is assigned to el
		if(typeof ev == "number"){
			el = this.buttons.item(ev);
		}
		else{
			el = Ext.get(t);
		}
		
		// switch the classes on the li if the tab is not already active
        if (el.hasClass(this.activeButtonClass)){
			return;
		}
		else {
			el.radioClass(this.activeButtonClass)
		}
		
		//get the index of the  tab within the button collection		
		var buttonIndex = this.buttons.indexOf(el);

		// now this should match the elemnt within the panes collection we want to scroll to		
		this.onStartAnimation(this.panes.item(buttonIndex));
	}
	
	/**
	* Starts the animation for moving the panes
	* It may animate the scroll and or the height of the panes
	* Fires the startAnimation event
	 * @param {Ext element} represents the Ext elemnt to scroll to
	 * @return {void}
	 */
	,onStartAnimation		: function(el){	
		
		this.fireEvent('startAnimation',el);	
		
		var paneIndex = this.panes.indexOf(el)
		
		var scrollAmount = paneIndex * el.getWidth();
		
		if(this.animateHeight){
		
			this.outerSlidesBox.syncFx().animate(
				{
					scroll: {to: [scrollAmount,0]}
				},
				this.fxDuration,
				null,
				this.scrollEasingEffect,
				'scroll'
			).animate(
				{
				 height: {to:el.getHeight()}
				},
				this.fxDuration,
				null,      
				this.scrollHeightEffect, 
				'run'
			);
		
		}
		else{
			this.outerSlidesBox.animate(
				{
					scroll: {to: [scrollAmount,0]}
				},
				this.fxDuration,
				null,
				this.scrollEasingEffect,
				'scroll'
			);
			
			this.outerSlidesBox.setHeight(el.getHeight());
		}
	
		this.current = paneIndex;
	}
	/**
	* Moves to the next pane
	* If the pane is at the end it will move to the first pane
	 * @param {Ext element} represents the Ext elemnt to scroll to
	 * @return {void}
	 */
	,next					: function(){
			var next = this.current + 1;
			//if we are at the end go to the first one
			if( next == this.panes.getCount() ){
				next = 0;
			}
			
			this.onTabChange(next);
	}
	/**
	* Moves to the previous pane
	* If the pane is at the start it will move to the last pane
	 * @param {Ext element} represents the Ext elemnt to scroll to
	 * @return {void}
	 */
	,prev					: function(){
			var prev = this.current - 1;
			//if we are at the start go to the last  one
			if( prev == -1 ){
				prev = this.panes.getCount()-1;
			}
			
			this.onTabChange(prev);
	}
	/**
	*  This is called to align all the panes horizonatlly within its container
	* If the pane is at the end it will move to the first pane
	 * @param {Ext element} represents the Ext elemnt to scroll to
	 * @return {void}
	 */
	,recalcWidths		: function() {
		
		this.panes.each(function(el, index) {
		  el.setStyle('width', this.outerSlidesBox.getWidth()+ 'px');
		},this);

		this.innerSlidesBox.setStyle('width', this.outerSlidesBox.getWidth() * this.panes.getCount() + 'px');

	}
});