/*
jQuery Plugin: iosSlider v0.4 beta
*/

;(function($) {
	
	/* global variables */
	var scrollbarNumber = 0;
	var sliderMin = 0;
	var scrollDistance = 0;
	var scrollIntervalTime = 10;
	var scrollbarDistance = 0;
	
	/* private functions */
	function slowScrollHorizontalInterval(node, newOffset, sliderMax, scrollbarClass, scrollbarWidth, stageWidth, scrollMargin, scrollBorder, activeChildOffset, childrenOffsets, settings) {
		
		newOffset = Math.floor(newOffset);
		
		$(node).css({
			webkitTransform: 'translateX(' + newOffset + 'px)'
		});
		
		if(settings.scrollbar) {
			
			scrollbarDistance = Math.floor((newOffset * -1) / (sliderMax) * (stageWidth - scrollMargin - scrollbarWidth));
			var width = scrollbarWidth - scrollBorder;
			
			if(newOffset >= sliderMin) {
				
				width = scrollbarWidth - scrollBorder - (scrollbarDistance * -1);
				
				$('.' + scrollbarClass).css({
					webkitTransform: 'translateX(0)',
					width: width + 'px'
				});
			
			} else if(newOffset <= ((sliderMax * -1) + 1)) {
				
				width = stageWidth - scrollMargin - scrollBorder - scrollbarDistance;
				
				$('.' + scrollbarClass).css({
					webkitTransform: 'translateX(' + scrollbarDistance + 'px)',
					width: width + 'px'
				});
				
			} else {
			
				$('.' + scrollbarClass).css({
					webkitTransform: 'translateX(' + scrollbarDistance + 'px)',
					width: width + 'px'
				});
			
			}
			
			$('body').append('<div></div>');
			
		}
		
		if(settings.onSlideChange != 'function () {}') {
			checkSlideChange(node, newOffset, childrenOffsets, settings, activeChildOffset);
		}
		
	}
	
	function hideScrollbar(newOffset, opacity, sliderMax, scrollbarClass, scrollbarWidth, stageWidth, scrollMargin, scrollBorder, settings) {
		
		scrollbarDistance = (newOffset * -1) / (sliderMax) * (stageWidth - scrollMargin - scrollBorder - scrollbarWidth);

		$('.' + scrollbarClass).css({
			webkitTransform: 'translateX(' + scrollbarDistance + 'px)',
			opacity: settings.scrollbarOpacity * opacity
		});
		
	}
	
	function checkSlideChange(node, currentOffset, childrenOffsets, settings, activeChildOffset) {
		
		var newChildOffset = 0;
		for(var j = 0; j < childrenOffsets.length-1; j++) {
			newChildOffset = j;
			
			var middle = (childrenOffsets[newChildOffset] + childrenOffsets[newChildOffset+1]) * 0.5;
			if(middle < currentOffset) break;
		}
		
		//if(activeChildOffset != newChildOffset) {
			settings.onSlideChange(settings, node, newChildOffset);
		//}
		
	}
	
	function slowScrollHorizontal(node, scrollTimeouts, sliderMax, scrollbarClass, scrollDistance, scrollbarWidth, stageWidth, scrollMargin, scrollBorder, activeChildOffset, childrenOffsets, settings) {
		
		var distanceOffsetArray = new Array();
		webkitTransformArray = $(node).css('-webkit-transform').split(','); 
		var nodeOffset = parseInt(webkitTransformArray[4], 10);
		frictionCoefficient = settings.frictionCoefficient;
		elasticFrictionCoefficient = settings.elasticFrictionCoefficient;
		snapFrictionCoefficient = settings.snapFrictionCoefficient;
		snapToChildren = settings.snapToChildren;
		
		if(scrollDistance < -100) {
			scrollDistance = -100;
		} else if(scrollDistance > 100) {
			scrollDistance = 100;
		}

		i = 0;
		while((scrollDistance > 1) || (scrollDistance < -1)) {
			scrollDistance = scrollDistance * frictionCoefficient;
			nodeOffset = nodeOffset + scrollDistance;
			
			if((nodeOffset > sliderMin) || (nodeOffset < (sliderMax * -1))) {
				scrollDistance = scrollDistance * elasticFrictionCoefficient;
				nodeOffset = nodeOffset + scrollDistance;
			}
			
			distanceOffsetArray[i] = nodeOffset;
			
			i++;
	
		}
		
		while(nodeOffset > (sliderMin + 0.5)) {
			
			nodeOffset = nodeOffset * frictionCoefficient;
			distanceOffsetArray[i] = nodeOffset;
			i++;
			
		}
		
		while(nodeOffset < ((sliderMax * -1) - 0.5)) {
			
			nodeOffset = ((nodeOffset - (sliderMax * -1)) * frictionCoefficient) + (sliderMax * -1);
			distanceOffsetArray[i] = nodeOffset;
			i++;
			
		}
					
		for(var j = 0; j < childrenOffsets.length-1; j++) {
			activeChildOffset = j;
			
			var middle = (childrenOffsets[activeChildOffset] + childrenOffsets[activeChildOffset+1]) * 0.5;
			if(middle < nodeOffset) break;
		}
		
		if(snapToChildren) {
						
			while(nodeOffset < (childrenOffsets[activeChildOffset] - 0.5)) {
		
				nodeOffset = ((nodeOffset - (childrenOffsets[activeChildOffset])) * snapFrictionCoefficient) + (childrenOffsets[activeChildOffset]);
				distanceOffsetArray[i] = nodeOffset;					
				i++;
				
			}
			
			while(nodeOffset > (childrenOffsets[activeChildOffset] + 0.5)) {
		
				nodeOffset = ((nodeOffset - (childrenOffsets[activeChildOffset])) * snapFrictionCoefficient) + (childrenOffsets[activeChildOffset]);
				distanceOffsetArray[i] = nodeOffset;
				
				i++;
				
			}
			
			distanceOffsetArray[i] = childrenOffsets[activeChildOffset];
				
		}
		
		var jStart = 1;
		if((distanceOffsetArray.length%2) != 0) {
			jStart = 0;
		}
		
		var lastTimeoutRegistered = 0;
		var count = 0;
		for(var j = jStart; j < distanceOffsetArray.length; j = j + 2) {
				
			scrollTimeouts[j] = setTimeout(slowScrollHorizontalInterval, scrollIntervalTime * j, node, distanceOffsetArray[j], sliderMax, scrollbarClass, scrollbarWidth, stageWidth, scrollMargin, scrollBorder, activeChildOffset, childrenOffsets, settings);	
			
		}

		scrollTimeouts[j+1] = setTimeout(function() {
			settings.onSlideComplete(settings, this, activeChildOffset);
		}, scrollIntervalTime * (j+1));	
		
		if(settings.scrollbar && settings.scrollbarHide) {
		
			for(var k = j; k < j+25; k++) {
				scrollTimeouts[k] = setTimeout(hideScrollbar, scrollIntervalTime * k, distanceOffsetArray[j], ((j + 24) - k) / 24, sliderMax, scrollbarClass, scrollbarWidth, stageWidth, scrollMargin, scrollBorder, settings);	
			}
		
		}
			
	}
	
	/* public functions */
	$.fn.iosSlider = function(options) {
		
		var settings = jQuery.extend({}, jQuery.fn.iosSlider.defaults, options);

	    return $(this).each(function(i) {

			var scrollTimeouts = new Array();
			var sliderMax = 0;
			var currentScrollRate = new Array(0, 0, 0);
			scrollbarNumber++;
			var scrollbarBlockClass = 'scrollbarBlock' + scrollbarNumber;
			var scrollbarClass = 'scrollbar' + scrollbarNumber;
			var scrollbarWidth;
			var windowWidth = $(window).width();;
			var stageWidth = $(this).width();
			var scrollMargin;
			var scrollBorder;
			var lastTouch;
			var activeChildOffset = 0;
			var webkitTransformArray = new Array();
			var childrenOffsets = new Array();
			var scrollbarStartOpacity = 0;
			var scrollStartPosition = 0;
			var currentTouches = 0;
			var scrollerNode = $(this).children(':first-child');
			
			$(this).css({
				position: 'relative',
				top: '0',
				left: '0',
				overflow: 'hidden'
			});
			
			$(scrollerNode).children().each(function(j) {
			
				$(this).css({
					float: 'left'
				});
				
				childrenOffsets[j] = sliderMax * -1;
				
				sliderMax = sliderMax + $(this).width() + parseInt($(this).css('paddingLeft'), 10) + parseInt($(this).css('paddingRight'), 10) + parseInt($(this).css('marginLeft'), 10) + parseInt($(this).css('marginRight'), 10) + parseInt($(this).css('borderLeftWidth'), 10) + parseInt($(this).css('borderRightWidth'), 10);
			});
			
			$(scrollerNode).css({
				width: sliderMax + 'px'
			});
			
			childrenOffsets[childrenOffsets.length] = sliderMax * -1;
			sliderMax = sliderMax - stageWidth;
			
			$(scrollerNode).css({
				'webkitPerspective': 1000,
				'webkitBackfaceVisibility': 'hidden',
				'webkitTransform': 'translateX(0)',
				width: sliderMax + stageWidth + 'px'
			});
			
			if(settings.scrollbar) {

				$(scrollerNode).parent().append("<div class = '" + scrollbarBlockClass + "'><div class = '" + scrollbarClass + "'></div></div>");
				
				$('.' + scrollbarBlockClass).css({ 
					margin: settings.scrollbarMargin,
					overflow: 'hidden',
					display: 'none'
				});
				
				$('.' + scrollbarBlockClass + ' .' + scrollbarClass).css({ 
					border: settings.scrollbarBorder
				});
				
				scrollMargin = parseInt($('.' + scrollbarBlockClass).css('marginLeft')) + parseInt($('.' + scrollbarBlockClass).css('marginRight'));
				scrollBorder = parseInt($('.' + scrollbarBlockClass + ' .' + scrollbarClass).css('borderLeftWidth'), 10) + parseInt($('.' + scrollbarBlockClass + ' .' + scrollbarClass).css('borderRightWidth'), 10);
				scrollbarWidth = Math.floor((stageWidth) / (sliderMax + stageWidth) * (stageWidth - scrollMargin));

				if(!settings.scrollbarHide) {
					scrollbarStartOpacity = settings.scrollbarOpacity;
				}
				
				var lastSheet = document.styleSheets[0];
				lastSheet.insertRule("." + scrollbarBlockClass + " { position: absolute; left: 0; " + settings.scrollbarLocation + ": 0; width: " + (stageWidth - scrollMargin) + "px; margin: " + settings.scrollbarMargin + "; }", lastSheet.cssRules.length);
				lastSheet.insertRule("." + scrollbarBlockClass + " ." + scrollbarClass + " { border-radius: " + settings.scrollbarBorderRadius + "; background: " + settings.scrollbarBackground + "; height: " + settings.scrollbarHeight + "; width: " + (scrollbarWidth - scrollBorder) + "px; min-width: " + settings.scrollbarHeight + "; border: " + settings.scrollbarBorder + "; -webkit-perspective: 1000; -webkit-backface-visibility: hidden; opacity: " + scrollbarStartOpacity + "; box-shadow: " + settings.scrollbarShadow + "; }", lastSheet.cssRules.length);

				$('.' + scrollbarBlockClass).css({
					display: 'block'
				});
				
			}	
			
			$(scrollerNode).bind('touchstart', function() {
				
				settings.onSlideStart(settings, this, activeChildOffset);
				currentScrollRate = new Array(0, 0, 0);
				scrollDistance = 0;
				
				for(var j = 0; j < scrollTimeouts.length; j++) {
					clearTimeout(scrollTimeouts[j]);
				}
				
				webkitTransformArray = $(this).css('-webkit-transform').split(','); 
				var scrollPosition = parseInt(webkitTransformArray[4], 10);
				
				if(scrollPosition > sliderMin) {
					scrollPosition = sliderMin;
					
					$(this).css({
						webkitTransform: 'translateX(' + scrollPosition + 'px)'
					});
					
					$('.' + scrollbarClass).css({
						width: (scrollbarWidth - scrollBorder) + 'px'
					});
				}
				
				if(scrollPosition < (sliderMax * -1)) {
					scrollPosition = sliderMax * -1;
					
					$(this).css({
						webkitTransform: 'translateX(' + scrollPosition + 'px)'
					});
					
					$('.' + scrollbarClass).css({
						webkitTransform: 'translateX(' + (stageWidth - scrollMargin - scrollbarWidth) + 'px)',
						width: (scrollbarWidth - scrollBorder) + 'px'
					});
				}
				
				$('body').append('<div></div>');
				
				webkitTransformArray = $(this).css('-webkit-transform').split(',');
				scrollStartPosition = (parseInt(webkitTransformArray[4], 10) - event.touches[0].pageX) * -1;
				
				event.preventDefault();
			});
			
			$(scrollerNode).bind('touchmove', function() {
				
				webkitTransformArray = $(this).css('-webkit-transform').split(',');
				
				if(currentTouches != event.touches.length) {
					scrollStartPosition = (parseInt(webkitTransformArray[4], 10) * -1) + event.touches[0].pageX;	
				}
				
				currentTouches = event.touches.length;
				
				if(settings.onSlideChange != 'function () {}') {
					checkSlideChange(this, (scrollStartPosition - event.touches[0].pageX) * -1, childrenOffsets, settings, 0); 
				}
						
				var edgeDegradation = 0;
				elasticPullResistance = settings.elasticPullResistance;
				
				if(parseInt(webkitTransformArray[4], 10) > sliderMin) {
				
					edgeDegradation = (scrollStartPosition - event.touches[0].pageX) * elasticPullResistance;
					
				}
				
				if(parseInt(webkitTransformArray[4], 10) < (sliderMax * -1)) {
					
					edgeDegradation = (sliderMax + ((scrollStartPosition - event.touches[0].pageX) * -1)) * elasticPullResistance * -1;
								
				}
				
				$(this).css({
					webkitTransform: 'translateX(' + (scrollStartPosition - event.touches[0].pageX - edgeDegradation) * -1 + 'px)'
				});
				
				if(settings.scrollbar) {
					
					if(settings.scrollbarHide) {
						$('.' + scrollbarClass).css({
							opacity: settings.scrollbarOpacity
						});
					}

					scrollbarDistance = Math.floor((scrollStartPosition - event.touches[0].pageX - edgeDegradation) / (sliderMax) * (stageWidth - scrollMargin - scrollbarWidth));
					var width = scrollbarWidth;
					
					if(parseInt(webkitTransformArray[4], 10) >= sliderMin) {
						
						width = scrollbarWidth - scrollBorder - (scrollbarDistance * -1);
						
						$('.' + scrollbarClass).css({
							webkitTransform: 'translateX(0)',
							width: width + 'px'
						});
						
					} else if(parseInt(webkitTransformArray[4], 10) <= ((sliderMax * -1) + 1)) {
						
						width = stageWidth - scrollMargin - scrollBorder - scrollbarDistance;
						
						$('.' + scrollbarClass).css({
							webkitTransform: 'translateX(' + scrollbarDistance + 'px)',
							width: width + 'px'
						});	
						
					} else {
					
						$('.' + scrollbarClass).css({
							webkitTransform: 'translateX(' + scrollbarDistance + 'px)'
						});
						
					}
					
					$('body').append('<div></div>');
					
				}
				
				webkitTransformArray = $(this).css('-webkit-transform').split(','); 
				currentScrollRate[0] = currentScrollRate[1];
				currentScrollRate[1] = currentScrollRate[2];
				currentScrollRate[2] = parseInt(webkitTransformArray[4], 10);
				scrollDistance = ((currentScrollRate[2] - currentScrollRate[1]) + (currentScrollRate[1] - currentScrollRate[0])) / 2;
				event.preventDefault();
				
				lastTouch = event.touches[0].pageX;
				
			});
			
			$(scrollerNode).bind('touchend', function() {
					
				if(event.touches.length != 0) {
					
					for(var j = 0; j < sizeof(event.touches.length); j++) {
						
						if(event.touches[j].pageX == lastTouch) {
							slowScrollHorizontal(this, scrollTimeouts, sliderMax, scrollbarClass, scrollDistance, scrollbarWidth, stageWidth, scrollMargin, scrollBorder, activeChildOffset, childrenOffsets, settings);
						}
						
					}
					
				} else {
					slowScrollHorizontal(this, scrollTimeouts, sliderMax, scrollbarClass, scrollDistance, scrollbarWidth, stageWidth, scrollMargin, scrollBorder, activeChildOffset, childrenOffsets, settings);
				}	
				
			});
			
		});
		
		return this;
	
    };
    
    //elasticPullResistance - decimal between 0 and 1: the elastic resistance when pulling on the slider edge.
	//frictionCoefficient - decimal between 0 and 1: the friction coefficient applied to the momentum on touch release affecting the rate of deceleration.
	//elasticFrictionCoefficient - decimal between 0 and 1: additonal friction coefficient applied when momentum slides outside the bounds of the slider.
	//snapFrictionCoefficient - decimal between 0 and 1: friction applied to the slider when snapping to an element.
	//snapToChildren - boolean: slider will slide to the closest child element on touch release.
	//scrollbar - boolean: show or hide the scrollbar.
	//scrollbarHide - boolean: show or hide the scrollbar when it is idle.
	//scrollbarLocation - string: can be set to 'top' or 'bottom'.
	//scrollbarOpacity - decimal between 0 and 1: css opacity of the scrollbar when visible.
	//scrollbarHeight - the css height in px of the scrollbar.
	//scrollbarBackground - the css background properties of the scrollbar.
	//scrollbarBorderRadius - the css border radius of the scrollbar.
	//onSlideStart - function: executed when the initial touch event is triggered.
	//onSlideChange - function: executed when sliding over a different child node.
	//onSlideComplete - function: executed when the slide animation has completed.
    jQuery.fn.iosSlider.defaults = {
    	'elasticPullResistance': 0.6, 		
		'frictionCoefficient': 0.92,
		'elasticFrictionCoefficient': 0.8,
		'snapFrictionCoefficient': 0.92,
		'snapToChildren': false,
		'scrollbar': false,
		'scrollbarHide': true,
		'scrollbarLocation': 'top',
		'scrollbarOpacity': 0.4,
		'scrollbarHeight': '4px',
		'scrollbarBorder': '0',
		'scrollbarMargin': '5px',
		'scrollbarBackground': '#000',
		'scrollbarBorderRadius': '100px',
		'scrollbarShadow': '0 0 0 #000',
		'onSlideStart': function() {},
		'onSlideChange': function() {},
		'onSlideComplete': function() {}		
	};

}) (jQuery);
