/*
 * simplyScroll 2 - a scroll-tastic jQuery plugin
 *
 * http://logicbox.net/jquery/simplyscroll/
 *
 * Copyright (c) 2009-2012 Will Kelly - http://logicbox.net
 *
 * Dual licensed under the MIT and GPL licenses.
 *
 * Version: 2.0.5 Last revised: 10/05/2012
 *
 */

(function($,window,undefined) {

$.fn.simplyScroll = function(options) {
	return this.each(function() {
		new $.simplyScroll(this,options);
	});
};

var defaults = {
	customClass: 'simply-scroll',
	frameRate: 24, //No of movements per second
	speed: 1, //No of pixels per frame
	orientation: 'horizontal', //'horizontal or 'vertical' - not to be confused with device orientation
	auto: true,
	allowPageScroll: true,
	autoMode: 'loop', //auto = true, 'loop' or 'bounce',
	manualMode: 'end', //auto = false, 'loop' or 'end'
	direction: 'forwards', //'forwards' or 'backwards'.
	pauseOnHover: true, //autoMode = loop|bounce only
	pauseOnTouch: true, //" touch device only
	pauseButton: false, //" generates an extra element to allow manual pausing 
	startOnLoad: false //use this to delay starting of plugin until all page assets have loaded
};
	
$.simplyScroll = function(el,options) {
	
	var self = this;
	
	this.o = $.extend({}, defaults, options || {});
	this.isAuto = this.o.auto!==false && this.o.autoMode.match(/^loop|bounce$/)!==null;
	this.isHorizontal = this.o.orientation.match(/^horizontal|vertical$/)!==null && this.o.orientation==defaults.orientation; 
	this.isRTL = this.isHorizontal && $("html").attr('dir') == 'rtl';
	this.isForwards = !this.isAuto  || (this.isAuto && this.o.direction.match(/^forwards|backwards$/)!==null && this.o.direction==defaults.direction) && !this.isRTL;
	this.isLoop = this.isAuto && this.o.autoMode == 'loop' || !this.isAuto && this.o.manualMode == 'loop';
	
	this.supportsTouch = ('createTouch' in document);
	
	this.events = this.supportsTouch ? 
		{start:'touchstart MozTouchDown',move:'touchmove MozTouchMove',end:'touchend touchcancel MozTouchRelease'} : 
		{start:'mouseenter',end:'mouseleave'};
	
	this.$list = $(el); //called on ul/ol/div etc
	var $items = this.$list.children();
	
	//generate extra markup
	this.$list.addClass('simply-scroll-list')
		.wrap('<div class="simply-scroll-clip"></div>')
		.parent().wrap('<div class="' + this.o.customClass + ' simply-scroll-container"></div>');
	
	if (!this.isAuto) { //button placeholders
		this.$list.parent().parent()
		.prepend('<div class="simply-scroll-forward"></div>')
		.prepend('<div class="simply-scroll-back"></div>');
	} else {
		if (this.o.pauseButton) {
			this.$list.parent().parent()
			.prepend('<div class="simply-scroll-btn simply-scroll-btn-pause"></div>');
			this.o.pauseOnHover = false;
		}
	}
	
	//wrap an extra div around the whole lot if elements scrolled aren't equal
	if ($items.length > 1) {
		
		var extra_wrap = false,
			total = 0;
			
		if (this.isHorizontal) {
			$items.each(function() { total+=$(this).outerWidth(true); });
			extra_wrap = $items.eq(0).outerWidth(true) * $items.length !== total;
		} else {
			$items.each(function() { total+=$(this).outerHeight(true); });
			extra_wrap = $items.eq(0).outerHeight(true) * $items.length !== total;
		}
		
		if (extra_wrap) {
			this.$list = this.$list.wrap('<div></div>').parent().addClass('simply-scroll-list');
			if (this.isHorizontal) {
				this.$list.children().css({"float":'left',width: total + 'px'});	
			} else {
				this.$list.children().css({height: total + 'px'});
			}
		}
	}
	
	if (!this.o.startOnLoad) {
		this.init();
	} else {
		//wait for load before completing setup
		$(window).load(function() { self.init();  });
	}
		
};
	
$.simplyScroll.fn = $.simplyScroll.prototype = {};

$.simplyScroll.fn.extend = $.simplyScroll.extend = $.extend;

$.simplyScroll.fn.extend({
	init: function() {

		this.$items = this.$list.children();
		this.$clip = this.$list.parent(); //this is the element that scrolls
		this.$container = this.$clip.parent();
		this.$btnBack = $('.simply-scroll-back',this.$container);
		this.$btnForward = $('.simply-scroll-forward',this.$container);

		if (!this.isHorizontal) {
			this.itemMax = this.$items.eq(0).outerHeight(true); 
			this.clipMax = this.$clip.height();
			this.dimension = 'height';			
			this.moveBackClass = 'simply-scroll-btn-up';
			this.moveForwardClass = 'simply-scroll-btn-down';
			this.scrollPos = 'Top';
		} else {
			this.itemMax = this.$items.eq(0).outerWidth(true);
			this.clipMax = this.$clip.width();			
			this.dimension = 'width';
			this.moveBackClass = 'simply-scroll-btn-left';
			this.moveForwardClass = 'simply-scroll-btn-right';
			this.scrollPos = 'Left';
		}
		
		this.posMin = 0;
		
		this.posMax = this.$items.length * this.itemMax;
		
		var addItems = Math.ceil(this.clipMax / this.itemMax);
		
		//auto scroll loop & manual scroll bounce or end(to-end)
		if (this.isAuto && this.o.autoMode=='loop') {
			
			this.$list.css(this.dimension,this.posMax+(this.itemMax*addItems) +'px');
			
			this.posMax += (this.clipMax - this.o.speed);
			
			if (this.isForwards) {
				this.$items.slice(0,addItems).clone(true).appendTo(this.$list);
				this.resetPosition = 0;
				
			} else {
				this.$items.slice(-addItems).clone(true).prependTo(this.$list);
				this.resetPosition = this.$items.length * this.itemMax;
				//due to inconsistent RTL implementation force back to LTR then fake
				if (this.isRTL) {
					this.$clip[0].dir = 'ltr';
					//based on feedback seems a good idea to force float right
					this.$items.css('float','right');
				}
			}
		
		//manual and loop
		} else if (!this.isAuto && this.o.manualMode=='loop') {
			
			this.posMax += this.itemMax * addItems;
			
			this.$list.css(this.dimension,this.posMax+(this.itemMax*addItems) +'px');
			
			this.posMax += (this.clipMax - this.o.speed);
			
			var items_append  = this.$items.slice(0,addItems).clone(true).appendTo(this.$list);
			var items_prepend = this.$items.slice(-addItems).clone(true).prependTo(this.$list);
			
			this.resetPositionForwards = this.resetPosition = addItems * this.itemMax;
			this.resetPositionBackwards = this.$items.length * this.itemMax;
			
			//extra events to force scroll direction change
			var self = this;
			
			this.$btnBack.bind(this.events.start,function() {
				self.isForwards = false;
				self.resetPosition = self.resetPositionBackwards;
			});
			
			this.$btnForward.bind(this.events.start,function() {
				self.isForwards = true;
				self.resetPosition = self.resetPositionForwards;
			});
			
		} else { //(!this.isAuto && this.o.manualMode=='end') 
			
			this.$list.css(this.dimension,this.posMax +'px');
			
			if (this.isForwards) {
				this.resetPosition = 0;
				
			} else {
				this.resetPosition = this.$items.length * this.itemMax;
				//due to inconsistent RTL implementation force back to LTR then fake
				if (this.isRTL) {
					this.$clip[0].dir = 'ltr';
					//based on feedback seems a good idea to force float right
					this.$items.css('float','right');
				}
			}
		}
		
		this.resetPos() //ensure scroll position is reset
		
		this.interval = null;	
		this.intervalDelay = Math.floor(1000 / this.o.frameRate);
		
		if (!(!this.isAuto && this.o.manualMode=='end')) { //loop mode
			//ensure that speed is divisible by item width. Helps to always make images even not odd widths!
			while (this.itemMax % this.o.speed !== 0) {
				this.o.speed--;
				if (this.o.speed===0) {
					this.o.speed=1; break;	
				}
			}
		}
		
		var self = this;
		this.trigger = null;
		this.funcMoveBack = function(e) { 
			if (e !== undefined) {
				e.preventDefault();
			}
			self.trigger = !self.isAuto && self.o.manualMode=='end' ? this : null;
			if (self.isAuto) {
				self.isForwards ? self.moveBack() : self.moveForward(); 
			} else {
				self.moveBack();	
			}
		};
		this.funcMoveForward = function(e) { 
			if (e !== undefined) {
				e.preventDefault();
			}
			self.trigger = !self.isAuto && self.o.manualMode=='end' ? this : null;
			if (self.isAuto) {
				self.isForwards ? self.moveForward() : self.moveBack(); 
			} else {
				self.moveForward();	
			}
		};
		this.funcMovePause = function() { self.movePause(); };
		this.funcMoveStop = function() { self.moveStop(); };
		this.funcMoveResume = function() { self.moveResume(); };
		
		
		
		if (this.isAuto) {
			
			this.paused = false;
			
			function togglePause() {
				if (self.paused===false) {
					self.paused=true;
					self.funcMovePause();
				} else {
					self.paused=false;
					self.funcMoveResume();
				}
				return self.paused;
			};
			
			//disable pauseTouch when links are present
			if (this.supportsTouch && this.$items.find('a').length) {
				this.supportsTouch=false;
			}
			
			if (this.isAuto && this.o.pauseOnHover && !this.supportsTouch) {
				this.$clip.bind(this.events.start,this.funcMovePause).bind(this.events.end,this.funcMoveResume);
			} else if (this.isAuto && this.o.pauseOnTouch && !this.o.pauseButton && this.supportsTouch) {
				
				var touchStartPos, scrollStartPos;
				
				this.$clip.bind(this.events.start,function(e) {
					togglePause();
					var touch = e.originalEvent.touches[0];
					touchStartPos = self.isHorizontal ? touch.pageX : touch.pageY;
					scrollStartPos = self.$clip[0]['scroll' + self.scrollPos];
					e.stopPropagation();
					e.preventDefault();
					
				}).bind(this.events.move,function(e) {
					
					e.stopPropagation();
					e.preventDefault();
					
					var touch = e.originalEvent.touches[0],
						endTouchPos = self.isHorizontal ? touch.pageX : touch.pageY,
						pos = (touchStartPos - endTouchPos) + scrollStartPos;
					
					if (pos < 0) pos = 0;
					else if (pos > self.posMax) pos = self.posMax;
					
					self.$clip[0]['scroll' + self.scrollPos] = pos;
					
					//force pause
					self.funcMovePause();
					self.paused = true;
				});	
			} else {
				if (this.o.pauseButton) {
					
					this.$btnPause = $(".simply-scroll-btn-pause",this.$container)
						.bind('click',function(e) {
							e.preventDefault();
							togglePause() ? $(this).addClass('active') : $(this).removeClass('active');
					});
				}
			}
			this.funcMoveForward();
		} else {

			this.$btnBack 
				.addClass('simply-scroll-btn' + ' ' + this.moveBackClass)
				.bind(this.events.start,this.funcMoveBack).bind(this.events.end,this.funcMoveStop);
			this.$btnForward
				.addClass('simply-scroll-btn' + ' ' + this.moveForwardClass)
				.bind(this.events.start,this.funcMoveForward).bind(this.events.end,this.funcMoveStop);
				
			if (this.o.manualMode == 'end') {
				!this.isRTL ? this.$btnBack.addClass('disabled') : this.$btnForward.addClass('disabled');	
			}
		}
	},
	moveForward: function() {
		var self = this;
		this.movement = 'forward';
		if (this.trigger !== null) {
			this.$btnBack.removeClass('disabled');
		}
		self.interval = setInterval(function() {
			if (self.$clip[0]['scroll' + self.scrollPos] < (self.posMax-self.clipMax)) {
				self.$clip[0]['scroll' + self.scrollPos] += self.o.speed;
			} else if (self.isLoop) {
				self.resetPos();
			} else {
				self.moveStop(self.movement);
			}
		},self.intervalDelay);
	},
	moveBack: function() {
		var self = this;
		this.movement = 'back';
		if (this.trigger !== null) {
			this.$btnForward.removeClass('disabled');
		}
		self.interval = setInterval(function() {
			if (self.$clip[0]['scroll' + self.scrollPos] > self.posMin) {
				self.$clip[0]['scroll' + self.scrollPos] -= self.o.speed;
			} else if (self.isLoop) {
				self.resetPos();
			} else {
				self.moveStop(self.movement);
			}
		},self.intervalDelay);
	},
	movePause: function() {
		clearInterval(this.interval);	
	},
	moveStop: function(moveDir) {
		this.movePause();
		if (this.trigger!==null) {
			if (typeof moveDir !== 'undefined') {
				$(this.trigger).addClass('disabled');
			}
			this.trigger = null;
		}
		if (this.isAuto) {
			if (this.o.autoMode=='bounce') {
				moveDir == 'forward' ? this.moveBack() : this.moveForward();
			}
		}
	},
	moveResume: function() {
		this.movement=='forward' ? this.moveForward() : this.moveBack();
	},
	resetPos: function() {
		this.$clip[0]['scroll' + this.scrollPos] = this.resetPosition;
	}
});
		  
})(jQuery,window);
