﻿/**
 * the Pushbox class
 * puts DOM elements in a pushbox with optional navigation
 *
 * @author Klaas Dieleman
 * @since nov 2010
 * @copyright eFocus
 *
 * @require jQuery 1.4.2, <http://www.jquery.com>
 *
 * @param Array - jQuery array of DOM elements
 * @param Object options (optional)
 *
 */
function Pushbox(arrSlides, options) {
	
	if(arrSlides.length > 1) {
	
		this.arrSlides = arrSlides;
		
		var opt = options || {};
		
		this.options = {
			'navigation'		: true,				// set to true for automatic creation or provide jQuery array with DOM elements
			'navigationclass'	: 'pushbox_nav',	// classname to be given to created navigation
			'interval'			: 5,				// interval in seconds, set to 0 to disable automatic rotation
			'transition'		: 'slide',			// 'fade', 'slide' or anything else for instant change without transition
			'freezeonhover'		: true				// enables pausing of automatic rotation on mouseover
		};
		
		// implement options
		for(i in this.options) {
			if(opt[i] != undefined) {
				this.options[i] = opt[i];
			}
		}
		
		this.initialize();
		
	} else {
		
		return false;
		
	}

};

Pushbox.prototype = {
	
	/**
	 * intializes Pushbox
	 */
	initialize: function() {
	
		// set global class vars
		this.elViewport = jQuery(this.arrSlides).parent();
		this.intCurrent = 0;
		this.intSlideWidth = this.elViewport.innerWidth();
		this.blnPaused = false;
		
		if(this.options.transition == 'fade') {
			this.duration = 800;
		} else if(this.options.transition == 'slide') {
			this.duration = 500;
		} else {
			this.duration = 0;
		}
		
		// init functions
		if(this.options.navigation) {
			// if navigation option is set to true, create navigation
			// otherwise, use given jQuery array
			if(!this.options.navigation.jquery) this.options.navigation = this.createNav();
			
			this.arrNav = jQuery(this.options.navigation);
			this.initNav();
		}
		
		this.initViewport();
		
		if(this.options.freezeonhover) this.setHoverEvents();
		
		if(this.options.interval > 0) this.queueSlide();
    },
    
    /**
     * creates navigation and injects it after the parent element of the slides (viewport)
     * html structure:	<div class='#navigationclass#'>
     *						<ul class='#navigationclass#'>
     *							<li><span>#slidenumber#</span><li>
     *							...
     *						</ul>
     *					</div>
     *
     * @return Array - jQuery array with navigation elements (<li>)
     */
    createNav: function() {
	
		var elNavContainer = jQuery('<ul class=' + this.options.navigationclass + '/>').insertAfter(this.elViewport);
		elNavContainer.wrap('<div class=' + this.options.navigationclass + '/>');
		
		this.arrSlides.each(function(intIndex, elSlide) {
			jQuery('<li><span>' + String(intIndex + 1) + '</span></li>').appendTo(elNavContainer);
		});
		
		return jQuery(elNavContainer).children('li');

	},
	
	/**
     * sets required styles on viewport element and slide elements
     */
	initViewport: function() {
	
		this.elViewport.css({
			'position'	: 'relative',
			'overflow'	: 'hidden'
		});
		
		this.arrSlides.css({
			'position'	: 'absolute',
			'left'		: 0,
			'top'		: 0,
			'z-index'	: 10
		});
		
		jQuery(this.arrSlides[this.intCurrent]).css({
			'z-index'	: 20
		});
	
	},
	
	/**
     * checks if navigation matches number of slides and if so, adds behaviour to navigation elements
     * otherwise, remove navigation
     */
	initNav: function() {
	
		if(this.arrSlides.length != this.arrNav.length) {
			this.arrNav.remove();
			return false;
		}
		
		this.arrNav.each(jQuery.proxy(function(index, item){
			
			jQuery(item).click(jQuery.proxy(function(){
				
				if(index != this.intCurrent) this.showSlide(index);
				
			}, this));
			
		}, this));
		
		this.updateNav(this.intCurrent);
	
	},
	
	/**
     * adds freeze on mouse-hover functionality
     */
	setHoverEvents: function() {
	
		this.elViewport.bind({
			'mouseenter' : jQuery.proxy(function(event) {
				jQuery(this.arrSlides).clearQueue();
				if(this.timer) clearTimeout(this.timer);
				this.blnPaused = true;
			}, this),
			
			'mouseleave' : jQuery.proxy(function() {
				this.blnPaused = false;
				this.queueSlide();
			}, this)
		});
	
	},
	
	/**
     * queues next slide with given interval
     */
	queueSlide: function() {
		
		this.timer = setTimeout(jQuery.proxy(function(){
				this.showSlide('next');
			}, this), this.options.interval * 1000);
	
	},
	
	/**
     * show given slide with optional transition
     *
     * @param mixed - 'prev', 'next' or index number
     */
	showSlide: function(slide) {
		
		if(this.timer) clearTimeout(this.timer);
		
		var intAnimSource = this.intSlideWidth;
		var intTargetSlide;

		if(typeof(slide) == 'number') {
			
			intTargetSlide = parseInt(slide);
			
		} else {
			
			if (slide == 'next') {
				intTargetSlide = this.getNext();
			} else if (slide == 'prev') {
				intAnimSource = -intAnimSource;
				intTargetSlide = this.getPrev();
			} else {
				return false;
			}
			
		}
		
		jQuery(this.arrSlides[intTargetSlide]).stop();
		jQuery(this.arrSlides[intTargetSlide]).clearQueue();
			
		if(this.options.transition == 'slide') {

			jQuery(this.arrSlides[intTargetSlide]).css('left', intAnimSource);
			jQuery(this.arrSlides[intTargetSlide]).css('z-index', 20);
			jQuery(this.arrSlides[this.intCurrent]).css('z-index', 15);
			
			jQuery(this.arrSlides[intTargetSlide]).animate({'left' : 0},	{
				'duration'	: this.duration,
				'complete'	: jQuery.proxy(function(){
					jQuery(this.arrSlides[this.intCurrent]).css({
						'z-index'	: 10
					});
					
					this.updateNav(intTargetSlide);
					if(!this.blnPaused) this.queueSlide();
				}, this)
			});
		
		} else {
			
			jQuery(this.arrSlides[intTargetSlide]).css('opacity', 0);
			jQuery(this.arrSlides[intTargetSlide]).css('z-index', 20);
			jQuery(this.arrSlides[this.intCurrent]).css('z-index', 15);
			
			jQuery(this.arrSlides[intTargetSlide]).animate({'opacity' : 1},	{
				'duration'	: this.duration,
				'complete'	: jQuery.proxy(function(){
					jQuery(this.arrSlides[this.intCurrent]).css({
						'z-index'	: 10
					});
					
					this.updateNav(intTargetSlide);
					if(!this.blnPaused) this.queueSlide();
				}, this)
			});
		
		}
	
	},
		
	/**
     * gets next slide number
     *
     * @return integer - number of next slide
     */
	getNext: function() {
	
		var intNext;
		if(this.intCurrent < this.arrSlides.length - 1) {
			intNext = this.intCurrent + 1;
		} else {
			intNext = 0;
		}
		return intNext;
	
	},
	
	/**
     * gets previous slide number
     *
     * @return integer - number of previous slide
     */
	getPrev: function() {
	
		var intPrev;
		if(this.intCurrent > 0) {
			intPrev = this.intCurrent - 1;
		} else {
			intPrev = this.arrSlides.length - 1;
		}
		return intPrev;
	
	},
	
	/**
     * updates internal slide counter and adds 'active' class to corresponding navigation item
     *
     * @param integer - index number of newly shown slide
     */
	updateNav: function(intNewSlide) {
		
		this.intCurrent = intNewSlide;
					
		if(this.arrNav) {
			this.arrNav.removeClass('active');
			jQuery(this.arrNav[this.intCurrent]).addClass('active');
		}
	}

};
