/*
 * 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,
		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);