/***** demotive scroller object *************************
OPTIONS:
Can be sent as an object in the function call to override defaults i.e.: DEMOTIVE.scroller.init($j(element), {speed:600, scrollStyle:'group', behaviour:'fixed'});

speed :
	speed of transition in ms
scrollStyle :
	'unit' for one-click-one-unit-width,
	'group' for visible group.
	IMPORTANT - if using 'group', dummy elements are inserted to create correct multiples
behaviour :
	'loop' - infinite left/right,
	'rewind' - when reaching l/r limit scroll to opposite end,
	'fixed' - prev/next activate/deactivate appropriately
direction :
	'horizontal' or 'vertical'
timer :
	'true' or 'false'
interval (for timer):
	ms
*********************************************************/

// namespace demotive:
if (!DEMOTIVE) var DEMOTIVE = {};

// demotive scroller object
DEMOTIVE.scroller = {
	opts : {
		// default options
		speed : 300,
		scrollStyle : 'unit',
		behaviour : 'rewind',
		direction : 'horizontal',
		timer : 'false',
		interval : 3000,
		easing: 'easeOutExpo'
	},
	init : function($els, opts) {
		// loop through and set up
		$els.each(function(i) {
			var $el = jQuery(this);
			// get this scroller's "items" container
			var $itemEls = $el.find('.dem-items');
			$itemEls.prepend($('li.active'));
			// and the items
			var $unitEls = $itemEls.children();
			// set the children as data of the element
			$el.data('units', $unitEls);
			var propsObj = {};
			// options initially from init() defaults
			propsObj.speed = DEMOTIVE.scroller.opts.speed;
			propsObj.scrollStyle = DEMOTIVE.scroller.opts.scrollStyle;
			propsObj.behaviour = DEMOTIVE.scroller.opts.behaviour;
			propsObj.direction = DEMOTIVE.scroller.opts.direction;
			propsObj.timer = DEMOTIVE.scroller.opts.timer;
			propsObj.interval = DEMOTIVE.scroller.opts.interval;
			propsObj.easing = DEMOTIVE.scroller.opts.easing;
			// if options are present, overwrite the defaults in the propsObject
			if (opts) {
				if (opts.speed) {
					propsObj.speed = opts.speed;
				}
				if (opts.scrollStyle) {
					propsObj.scrollStyle = opts.scrollStyle;
				}
				if (opts.behaviour) {
					propsObj.behaviour = opts.behaviour;
				}
				if (opts.direction) {
					propsObj.direction = opts.direction;
				}
				if (opts.timer) {
					propsObj.timer = opts.timer;
				}
				if (opts.interval) {
					propsObj.interval = opts.interval;
				}
				if (opts.easing) {
					propsObj.interval = opts.easing;
				}
			}
			// store all these for function use
			$el.data('props', propsObj)
				// add a trigger class to each scroller
				.addClass('dem-scroller-active');
			// set the container to single unit height/width and set overflow etc
			$itemEls
					.css({
						'position' : 'relative',
						'overflow' : 'hidden'
					});
			if (propsObj.direction != 'vertical') {
				//Conditional statement to make sure the outer height of the scroll items are 205px to account for 2 lines of product text
				var itemHeight = ($unitEls.eq(0).outerHeight(true)< "205")?"205px":$unitEls.eq(0).outerHeight(true);
				$itemEls.css('min-height', itemHeight);
			}
			if($('.dem-items li').hasClass('TRADE')) {
				$itemEls.css('min-height', '260px');
			}
			// update measurements data
			DEMOTIVE.scroller.checkMeasurements($el);
			var visUnits = Math.floor($el.data('measurements').viewW / $el.data('measurements').unitW);
			// GROUP catching - if the multiple of units to visible width isn't right, insert "dummy" elements to make up correct numbers
			if (propsObj.scrollStyle == 'group') {
				var remainder = $unitEls.length % visUnits;
				// toCreate will give the number of dummy unit elements to create
				if (remainder > 0) {
					var toCreate = visUnits - remainder;
					// get the type of element to create:
					var elType = $unitEls[0].nodeName;
					// and the appropriate width / height:
					var dW = $unitEls.eq(0).width();
					var dH = $unitEls.eq(0).height();
					// create and append to the main set of units
					for (var i=0; i<toCreate; i++) {
						var newEl = jQuery('<' + elType + '/>')
							.css({
								 'width' : dW,
								 'height' : dH
							})
							.addClass('dem-unit-dummy');
						$itemEls.children(':last').after(newEl);
					}
					// update the elements "units" collection to take account of the dummy elements
					$unitEls = $itemEls.children();
					$el.data('units', $unitEls);
				}
			}
			// set each unit to an absolute position based on width/height
			$unitEls.each(function(j) {
				var cssObj = {};
				cssObj['position'] = 'absolute';
				if (propsObj.direction == 'vertical') {
					cssObj['top'] = jQuery(this).outerHeight(true) * j;
					cssObj['left'] = '0';
				} else {
					cssObj['top'] = '0',
					cssObj['left'] = jQuery(this).outerWidth(true) * j;
				}
				jQuery(this).css(cssObj);
			});
			// intialise tabindex properties
			DEMOTIVE.scroller.toggleUnitDisplay($el);
			// add "step" events to prev and next - if they are not <a> tags, they will be inserted for keyboard access
			// includes catcher: if the number of items is equal to or less than the number of visible units, i.e. 5 <li> tags, with 5 items visible, add helper class, don't bind
			// FUTURE AJAX FUNCTIONALITY MAY NEED TO ADDRESS THIS LOCKING FEATURE?
			var $btnEls = $el.find('.dem-prev, .dem-next');
			if ($unitEls.length > visUnits) {
				$btnEls.each(function(i) {
					if (this.tagName.toUpperCase() != 'A') {
						jQuery(this).wrapInner('<a href="#"></a>');
					}
					jQuery(this)
						.bind('click', function(e) {
							e.preventDefault();
						})
						.bind('click.demScroll', function() {
							DEMOTIVE.scroller.step(jQuery(this));
						});
					// if the behaviour is 'fixed' run a check to disable / enable prev/next
					if (jQuery(this).parents('.dem-scroller').data('props').behaviour == 'fixed') {
						DEMOTIVE.scroller.toggleControls(jQuery(this).parents('.dem-scroller'));
					}
				});
			} else {
				$btnEls.addClass('dem-single-unit');
			}
			// if timer is true, set up the Interval
			if (propsObj.timer == 'true') {
				DEMOTIVE.scroller.timerSet($el);
			}
		});
	},
	step : function($el) {
		// $el is the clicked element
		// $scrollEl is the clicked element's scroller container
		var $scrollEl = $el.parents('.dem-scroller');
		// if the scroller is automated with timer options, kill the timer
		if ($scrollEl.data('props').timer == 'true') {
			DEMOTIVE.scroller.timerClear($scrollEl);
		}
		// unbind prev / next clicker events
		var $els = $scrollEl.find('.dem-prev, .dem-next');
		$els.unbind('click.demScroll');
		// decide which direction to go based on the clicker's class name
		var dir = 'pos';
		if ($el.hasClass('dem-prev')) {
			dir = 'neg';
		}
		// if the behaviour is "loop", ensure all units are stacked ready to go
		if ($scrollEl.data('props').behaviour == 'loop') {
			DEMOTIVE.scroller.updateStack($scrollEl, dir);
		}
		// update measurements data
		DEMOTIVE.scroller.checkMeasurements($scrollEl);
		// Checking for boundaries, setting distances for each unit to scroll by
		// set the amount by which to scroll by - defaults to single unit only
		var scrollDist = $scrollEl.data('measurements').unitW;
		var sStyle = $scrollEl.data('props').scrollStyle;
		if (sStyle == 'group') {
			scrollDist = $scrollEl.data('measurements').viewW;
		}
		// checks for leftmost/rightmost units - not applicable to "loop" or "fixed" behaviour
		if ($scrollEl.data('props').behaviour == 'rewind') {
			// if the click is requesting negative and the index is at 0, catch and return (ie roll to the other end)
			if (($scrollEl.data('measurements').indexPos == 0) && (dir == 'neg')) {
				scrollDist = $scrollEl.data('measurements').overW - $scrollEl.data('measurements').viewW;
				dir = 'pos';
			}
			// what if we're at the rightmost element?
			if ((Math.round(Math.abs($scrollEl.data('measurements').indexPos) + $scrollEl.data('measurements').viewW) == $scrollEl.data('measurements').overW) && (dir == 'pos')) {
				scrollDist = Math.abs($scrollEl.data('measurements').indexPos);
				dir = 'neg';
			}
		}
		// var for the scroller's units
		var $units = $scrollEl.data('units');
		// animate units
		if ($scrollEl.data('props').direction == 'vertical') {
			$units.each(function(i) {
				var pos = parseInt(jQuery(this).css('top'));
				// generate a target _y value for this unit
				if (dir == 'neg') {
					var unitX = Math.round(pos) + scrollDist;
				} else {
					var unitX = Math.round(pos) - scrollDist;
				}
				var mySpeed = $scrollEl.data('props').speed;
				if (i < ($units.length - 1)) {
					jQuery(this)
						.stop()
							.animate({
								'top' : unitX
							}, mySpeed, DEMOTIVE.scroller.opts.easing );
				} else {
					jQuery(this)
						.stop()
							.animate({
								'top' : unitX
							},
							mySpeed,
							DEMOTIVE.scroller.opts.easing,
							function() {
								// on complete, rebind scroll click event
								$els.bind('click.demScroll', function() {
									DEMOTIVE.scroller.step(jQuery(this));
								});
								// all units that fall outside the visible area given tabindex-1 - keyboard control
								DEMOTIVE.scroller.toggleUnitDisplay($scrollEl);
								// if the behaviour is 'fixed' run a check to disable / enable prev/next
								if ($scrollEl.data('props').behaviour == 'fixed') {
									DEMOTIVE.scroller.toggleControls($scrollEl);
								}
								// if the scroller is automated with timer options, set up the timer once more
								if ($scrollEl.data('props').timer == 'true') {
									DEMOTIVE.scroller.timerSet($scrollEl);
								}
							});
				}
			});
		} else {
			$units.each(function(i) {
				var pos = parseInt(jQuery(this).css('left'));
				// generate a target _x value for this unit
				if (dir == 'neg') {
					var unitX = Math.round(pos) + scrollDist;
				} else {
					var unitX = Math.round(pos) - scrollDist;
				}
				var mySpeed = $scrollEl.data('props').speed;
				if (i < ($units.length - 1)) {
					jQuery(this)
						.stop()
							.animate({
								'left' : unitX
							}, mySpeed, DEMOTIVE.scroller.opts.easing );
				} else {
					jQuery(this)
						.stop()
							.animate({
								'left' : unitX
							},
							mySpeed,
							DEMOTIVE.scroller.opts.easing,
							function() {
								// on complete, rebind scroll click event
								$els.bind('click.demScroll', function() {
									DEMOTIVE.scroller.step(jQuery(this));
								});
								// all units that fall outside the visible area given tabindex-1 - keyboard control
								DEMOTIVE.scroller.toggleUnitDisplay($scrollEl);
								// if the behaviour is 'fixed' run a check to disable / enable prev/next
								if ($scrollEl.data('props').behaviour == 'fixed') {
									DEMOTIVE.scroller.toggleControls($scrollEl);
								}
								// if the scroller is automated with timer options, set up the timer once more
								if ($scrollEl.data('props').timer == 'true') {
									DEMOTIVE.scroller.timerSet($scrollEl);
								}
							});
				}
			});
		}
	},
	checkMeasurements : function($el) {
		// $el is the overall scroller container
		// var for the scroller's units
		var $units = $el.data('units');
		// set up a storage object
		var dataObj = {};
		// get the left value of the index unit element - all measurements done at function time to cope with resizing of elements
		if ($el.data('props').direction == 'vertical') {
			dataObj.indexPos = parseInt($units.eq(0).css('top'));
			// and its width
			dataObj.unitW = $units.eq(0).outerHeight(true);
			// get the overall width of all units
			dataObj.overW = dataObj.unitW * $units.length;
			// get the "viewport" width
			dataObj.viewW = $units.eq(0).parent().height();
		} else {
			dataObj.indexPos = parseInt($units.eq(0).css('left'));
			// and its width
			dataObj.unitW = $units.eq(0).outerWidth(true);
			// get the overall width of all units
			dataObj.overW = dataObj.unitW * $units.length;
			// get the "viewport" width
			dataObj.viewW = $units.eq(0).parent().width();
		}
		// attach it all to the elements data
		$el.data('measurements', dataObj);
	},
	toggleUnitDisplay : function($el) {
		// $el is the overall scroller container
		// update measurements data
		DEMOTIVE.scroller.checkMeasurements($el);
		// if a unit falls outside the view boundary, tabindex = -1
		$el.data('units').each(function(i) {
			// get position
			if ($el.data('props').direction == 'vertical') {
				var pos = parseInt(jQuery(this).css('top'));
			} else {
				var pos = parseInt(jQuery(this).css('left'));
			}
			if ((pos < 0) || (pos >= $el.data('measurements').viewW)) {
				jQuery(this).find('a').attr('tabindex', '-1');
			} else {
				jQuery(this).find('a').removeAttr('tabindex');
			}
		});
	},
	toggleControls : function($el) {
		// $el is the overall scroller container
		// update measurements data
		DEMOTIVE.scroller.checkMeasurements($el);
		if ($el.data('measurements').indexPos == 0) {
			// de-activate "prev"
			$el.find('.dem-prev')
				.unbind('click.demScroll')
				.addClass('dem-disabled');
		} else if (Math.round(Math.abs($el.data('measurements').indexPos) + $el.data('measurements').viewW) == $el.data('measurements').overW) {
			// de-activate "next"
			$el.find('.dem-next')
				.unbind('click.demScroll')
				.addClass('dem-disabled');
		} else {
			// ensure both are active
			$el.find('.dem-next, .dem-prev')
				.bind('click.demScroll', function() {
					DEMOTIVE.scroller.step(jQuery(this));
				})
				.removeClass('dem-disabled');
		}
	},
	updateStack : function($el, dir) {
		// $el is the overall scroller container
		// update measurements data
		DEMOTIVE.scroller.checkMeasurements($el);
		// vertical or horixontal?
		var animDir = 'left';
		if ($el.data('props').direction == 'vertical') {
			animDir = 'top';
		}
		// checks for leftmost / rightmost
		if (($el.data('measurements').indexPos == 0) && (dir == 'neg')) {
			// shift last element to first
			// var for the scroller's units
			var $units = $el.data('units');
			// "group" - need to move a whole chunk of units rather than just one
			if ($el.data('props').scrollStyle != 'group') {
				// get the last unit element
				// move it to be first in the element order and position correctly
				$units.filter(':last')
					.insertBefore($units.eq(0))
					.css(animDir, (0 - $el.data('measurements').unitW) + 'px');
			} else {
				var numEls = $el.data('measurements').viewW / $el.data('measurements').unitW;
				// get the last group of unit elements
				// move them to be first in the element order and position correctly
				var c = 0;
				for (var i=$units.length; i>=($units.length - numEls); i--) {
					var num = $el.data('measurements').unitW * c;
					num = String('-' + num + 'px');
					$units.eq(i)
						.insertBefore($el.find('.dem-items').children(':first-child'))
						.css(animDir, num);
					c++;
				}
			}
			// update the scroller's units data
			$el.data('units', $el.find('.dem-items').children());
		} else if ((Math.round(Math.abs($el.data('measurements').indexPos) + $el.data('measurements').viewW) == $el.data('measurements').overW) && (dir == 'pos')) {
			// shift first element to last
			// var for the scroller's units
			var $units = $el.data('units');
			// "group" - need to move a whole chunk of units rather than just one
			if ($el.data('props').scrollStyle != 'group') {
				// get the first unit element
				// move it to be last in the element order and position correctly
				$units.eq(0)
					.insertAfter($units.filter(':last'))
					.css(animDir, $el.data('measurements').viewW + 'px');
			} else {
				var numEls = $el.data('measurements').viewW / $el.data('measurements').unitW;
				// get the first group of unit elements
				// move them to be last in the element order and position correctly
				for (var i=0; i<numEls; i++) {
					var num = $el.data('measurements').viewW + ($el.data('measurements').unitW * i);
					num = String(num + 'px');
					$units.eq(i)
						.insertAfter($el.find('.dem-items').children(':last'))
						.css(animDir, num);
				}
			}
			// update the scroller's units data
			$el.data('units', $el.find('.dem-items').children());
		}
	},
	timerSet : function($el) {
		// $el is the overall scroller container
		// on interval, fire the "next" click event
		intervalID = window.setTimeout(function() {
			$el.find('.dem-next').click();
		}, $el.data('props').interval);
		// get a reference to the intervalID into the elements data
		$el.data('intervalID', intervalID);
	},
	timerClear : function($el) {
		// $el is the overall scroller container
		clearTimeout($el.data('intervalID'));
		$el.data('intervalID', '');
	}
}

