/*
 * Touch-Scroll
 * by scopesproject
 * 
 * How to use:
 * 	Put the content you wish to be touch scrollable inside of a div and give it
 * 	a unique id. Then call the touchScroll function on the jQuery element to
 * 	activate the touchScroll.
 * 	Note:	that the content inside the containing div will be removed and
 * 			re-inserted into the DOM. So bindings will be lost.
 * 
 * 	example:
 * 
 * 		---HTML---
 * 		<div id="touchScrollArea">
 * 			<!-- content goes here -->
 * 		</div>
 * 
 * 		---JAVASCRIPT---
 * 		$(document).ready(function(){
 * 			$('#touchScrollArea').touchScroll({
 * 				width:  '',
 * 				height: ''
 * 				[,other options]
 * 			});
 * 		});
 * 
 * 	Other options are:
 * 		- width       -- (default: 100%)
 * 		- height      -- (default: 250px)
 * 		- wait        -- how many milliseconds to wait before activating the touch scroll (default: 200)
 * 		- maxInertia  -- the maximum inertia of the content (default: 50)
 * 		- friction    -- the amount of friction (default: 1)
 * 		- showButtons -- to show scroll buttons or not (default: false)
 * 		- buttonSpeed -- The speed in which the buttons scroll (default: 10) [only valid id showButtons is true]
 * 
 * 
 * Other functions:
 * 		- touchScrollDisableSelection()
 * 			Disabled mouse down events on the DOM element it is called on.
 * 		- touchScrollReposition(mixed_var[, speed])
 * 			Scrolls the content to the specified position. The mixed var
 * 			can be:
 * 				- jQuery Selector -- e.g. '#element_id'
 * 				- jQuery Obj
 * 				- Number          -- by the specified number of pixels
 * 			Speed is optional and needs to be completed. TODO
 * 			Note:	that if the element is larger than the visible area
 * 					then the top of the element will be aligned with the
 * 					top of the viewport.
 * 		- touchScrollJumpto(mixed_var)
 * 			Jumps the position of the touch scroll area to the specified position.
 * 			The mixed_var can be:
 * 				- String	 - 'bottom'          -- jumps to the bottom of the content
 * 							 - 'top'             -- jumps to the top of the content
 * 							 - <jQuery selector> -- so the first matched element is visible
 * 				- jQuery Obj                     -- so that the [first matched] element is visible
 * 		- touchScrollSet(html[, callback])
 * 			Set the touch scroll content to the specified html. If the callback is
 * 			set it is called after the content is set.
 * 		- touchScrollLoad(url[, data][, callback])
 * 			Calls the jQuery on the specified url. Both data and callback are optional;
 * 			the data is sent with the AJAX call and the callback is called after the
 * 			AJAX call is completed (only if successful).
 * 		- touchScrollSetEnabled(boolean)
 * 			Sets the content to be enabled or not. If the set to disabled a semi-transparent
 * 			image covers the content, stopping the touch scrolling and any selection of the
 * 			elements within the content.
 */

(function($) {
	
	var repositioning = null;
	
	// initialise the touch-scroll on the specified element
	$.fn.touchScroll = function(o) {
		
		defaults = {
			height: '250px',
			width: '100%',
			
			wait: 150,
			maxInertia: 50,
			friction: 1,
			showButtons: true,
			buttonSpeed: 10
		};
		o = $.extend(defaults, o || {});
		o = $.extend(o, {
			touch: false,
			oldDif: 0,
			posY: 0,
			inertia: 0
		});
		
		// add the containers into the element
		this.html('\r\n' +
'<div class="touchScrollContainer" style="width:'+ o.width +'; height:'+ o.height +';">\r\n'+
'	<div class="touchScrollDisabled"></div>'+
'	<div class="touchScrollCover"></div>\r\n'+
'	<div class="touchScrollContent">\r\n'+
'		'+ this.html() +'\r\n'+
'	</div>\r\n'+
'</div>\r\n'
		);
		
		// disable mousedown events inside the element
		this.find('.touchScrollContent').touchScrollDisableSelection();

		
		var touchScrollArea = this;
		var container       = this.find('.touchScrollContainer');
		var cover           = this.find('.touchScrollCover');
		var content         = this.find('.touchScrollContent');
		
		// if the scroll buttons should be shown //TODO complete
		if( o.showButtons ) {
			container.append('\r\n'+
'<input type="button" value="UP" class="clickable red up"\r\n'+
'	onclick="$(\'#'+ this.attr('id') +'\').touchScrollReposition('+  o.buttonSpeed +');return false;"\r\n'+
'	style="position:absolute; top:0px; right:0px; z-index:2; padding:2px; width:35px;height:35px;" />\r\n'+
'<input type="button" value="DN" class="clickable green down"\r\n'+
'	onclick="$(\'#'+ this.attr('id') +'\').touchScrollReposition(-'+ o.buttonSpeed +');return false;"\r\n'+
'	style="position:absolute; bottom:0px; right:0px; z-index:2; padding:2px; width:35px;height:35px;" />\r\n'+
'\r\n'
			);
			
			var scrolling;
			container.find('.up').mousedown(function(){
				scrolling = setInterval(function(){
					touchScrollArea.touchScrollReposition(o.buttonSpeed);
				}, 5);
			});
			container.find('.up').mouseup(function(){
				clearInterval(scrolling);
			});
			container.find('.up').mouseout(function(){
				clearInterval(scrolling);
			});
			container.find('.down').mousedown(function(){
				scrolling = setInterval(function(){
					touchScrollArea.touchScrollReposition(-o.buttonSpeed);
				}, 5);
			});
			container.find('.down').mouseup(function(){
				clearInterval(scrolling);
			});
			container.find('.down').mouseout(function(){
				clearInterval(scrolling);
			});
		}
		
		
		// add the mouse events
		container.mousedown(function(){
			o.touch   = true;
			o.inertia = 0;
			setTimeout(function(){
				if( o.touch ) {
					cover.show();
					o.oldDif = 0;
					o.posY   = Number.NaN;
					cover.focus();
				}
			}, o.wait);
			return false;
		});
		container.mouseup(function(){
			o.touch = false;
			cover.hide();
		});
		
		cover.mouseout(function(e){
			o.touch = false;
			cover.hide();
		});
		cover.mousemove(function(e){
			// get the new mouse position
			var y = parseInt(e.clientY);
			
			// if not the first time around
			if( !isNaN(o.posY) ) {
				// calculate the difference
				var dif = y - o.posY;
				// get the new y position
				var newY = parseInt(content.css('top')) + dif;
				// if within the bounds apply the new y position
				if( newY <= 0 && newY >= (container.height() - content.height()) ) {
					content.css('top', newY);
				}
				
				// get the change in dif
				var change = dif - o.oldDif;
				if( change == 0 )  {
					change = dif;
				}
				// if not reached the maximum inertia increase the inertia
				if( o.inertia < 0 ) {
					if( o.inertia + change <= -o.maxInertia ) {
						o.inertia = -o.maxInertia;
					}
					else {
						o.inertia += change;
					}
				}
				else {
					if( o.inertia + change >= o.maxInertia ) {
						o.inertia = o.maxInertia;
					}
					else {
						o.inertia += change;
					}
				}
				o.oldDif  = dif;
			}
			
			// update the y position
			o.posY = y;
		});
		
		// start the touch scroll inertia
		setInterval(function(){
			// if not being touched and still has inertia
			if( !o.touch && o.inertia != 0 ) {	
				var newY = parseInt(content.css('top')) + o.inertia;
				if( newY <=0 && newY >= (container.height() - content.height()) ) {
					content.css('top', newY);
				}
			}
			// apply friction
			if( o.inertia > 0 ) {
				o.inertia -= o.friction;
				if( o.inertia < 0 )
					o.inertia = 0;
			}
			else {
				o.inertia += o.friction;
				if( o.inertia > 0 )
					o.inertia = 0;
			}

		}, 50);
	};
	
	
	/*********************************************** 
	* Disable Text Selection script- © Dynamic Drive DHTML code library (www.dynamicdrive.com) 
	* This notice MUST stay intact for legal use 
	* Visit Dynamic Drive at http://www.dynamicdrive.com/ for full source code 
	***********************************************/ 
	$.fn.touchScrollDisableSelection = function() {
		if( this.length ) {
			var elem = this[0];
			// disable the container
			if( typeof elem.onselectstart != "undefined" ) {              //IE route
				elem.onselectstart = function(){return false;};
			} else if( typeof elem.style.MozUserSelect != "undefined" ) { //Firefox route 
				elem.style.MozUserSelect = "none";
			} else {                                                      //All other route (ie: Opera) 
				elem.onmousedown = function(){return false;};             //How to disable
			}
			
			elem.style.cursor = "default";
		}
	};
	
	$.fn.touchScrollReposition = function(mixed_var) {
		// if is a number
		if( !isNaN(parseInt(mixed_var)) ) {
			var touchScrollArea = this;
			// get the container, content and the top of the content
			var container   = touchScrollArea.find('.touchScrollContainer');
			var content     = touchScrollArea.find('.touchScrollContent');
			var contentTop  = parseInt(content.css('top'));
			var viewportBtm = parseInt(container.height());
			// get the new position
			var newY = parseInt(mixed_var) + contentTop;
			// if the content is larger than the container
			
			if( content.height() > container.height() ) {
				// If the top above the top of the container view port
				if( newY <= 0 && newY >= (container.height() - content.height()) ) {
					// move the content
					content.css('top', newY);
				}
				else if( newY > 0 ) {
					// move the content
					content.css('top', 0);
				}
				else if( newY < (container.height() - content.height()) ) {
					// move the content
					content.css('top', container.height() - content.height());
				}
			}
		}
		else {
			var elem;
			if( typeof mixed_var == 'string' ) {
				elem = $(this).find(mixed_var);
			} else {
				elem = mixed_var;
			}
			var touchScrollArea = this;
			// the the element exists
			if( elem.length ) {
				// get the top and bottom of the element
				var elemTop = parseInt(elem.position().top);
				var elemBtm = parseInt(elem.outerHeight(true) + elemTop);
				// get the container, content and the top of the content
				var container   = touchScrollArea.find('.touchScrollContainer');
				var content     = touchScrollArea.find('.touchScrollContent');
				var contentTop  = parseInt(content.css('top'));
				var viewportBtm = parseInt(container.height());

				// stop conflictions
				clearTimeout(repositioning);
				// If the top above the top of the container view port
				if( (elemTop + contentTop) < 0 ) {
					// move the content down
					if( (elemTop + contentTop) > -4 ) {
						content.css('top', -elemTop);
					} else {
						content.css('top', contentTop + 4);
					}
					// set to repeat
					repositioning = setTimeout(function(){
						touchScrollArea.touchScrollReposition(elem);
					}, 5);
				}
				// If the top is below the bottom of the container view port
				else if( (elemTop + contentTop) > viewportBtm ) {
					// move the content up
					content.css('top', contentTop - 4);
					// set to repeat
					repositioning = setTimeout(function(){
						touchScrollArea.touchScrollReposition(elem);
					}, 5);
				}
				// the top is visible,
				// if the buttom is not visible
				else if( (elemBtm + contentTop) > viewportBtm ) {
					// if the elem is not already positioned at the top
					if( (elemTop + contentTop) != 0 ) {
						// move the content up
						if( (elemTop + contentTop) < 4 ) {
							content.css('top', contentTop - (elemTop + contentTop));
						} else {
							content.css('top', contentTop - 4);
						}
						// set to repeat
						repositioning = setTimeout(function(){
							touchScrollArea.touchScrollReposition(elem);
						}, 5);
					}
				}
			}
		}
	};
	$.fn.touchScrollJumpto = function(mixed_var) {
		var touchScrollArea = this;
		// if a selector string
		if( typeof mixed_var == 'string' ) {
			if( mixed_var == 'bottom' ) {
				// get the container, content and the top of the content
				var container   = touchScrollArea.find('.touchScrollContainer');
				var content     = touchScrollArea.find('.touchScrollContent');
				var contentTop  = parseInt(content.css('top'));
				var viewportBtm = parseInt(container.height());
				
				if( content.height() < viewportBtm ) {
					touchScrollArea.find('.touchScrollContent').css('top', 0);
				} else {
					content.css('top', -(content.height()-viewportBtm));
				}
			}
			else if( mixed_var == 'top' ) {
				touchScrollArea.find('.touchScrollContent').css('top', 0);
			}
			else { //TODO jumpto not working going "up"
				var element = $(this).find(mixed_var);
				// if it exists
				if( element.length ) {
					// get the top and bottom of the element
					var elemTop = parseInt(element.position().top);
					var elemBtm = parseInt(element.outerHeight(true) + elemTop);
					// get the container, content and the top of the content
					var container   = touchScrollArea.find('.touchScrollContainer');
					var content     = touchScrollArea.find('.touchScrollContent');
					var contentTop  = parseInt(content.css('top'));
					var viewportBtm = parseInt(container.height());
					
					if( content.height() < viewportBtm ) {
						touchScrollArea.find('.touchScrollContent').css('top', 0);
					}
					else if( element.outerHeight(true) < content.height() &&
						(contentTop-elemTop)+content.height() < viewportBtm ) {
						content.css('top', -(content.height()-viewportBtm));
					} else {
						content.css('top', -elemTop);
					}
				}
			}
		}
		else {
			var element = mixed_var;
			// if it exists
			if( element.length ) {
				// get the top and bottom of the element
				var elemTop = parseInt(element.position().top);
				var elemBtm = parseInt(element.outerHeight(true) + elemTop);
				// get the container, content and the top of the content
				var container   = touchScrollArea.find('.touchScrollContainer');
				var content     = touchScrollArea.find('.touchScrollContent');
				var contentTop  = parseInt(content.css('top'));
				var viewportBtm = parseInt(container.height());
				
				if( content.height() < viewportBtm ) {
					touchScrollArea.find('.touchScrollContent').css('top', 0);
				}
				else if( element.outerHeight(true) < content.height() &&
					(contentTop-elemTop)+content.height() < viewportBtm ) {
					content.css('top', -(content.height()-viewportBtm));
				} else {
					content.css('top', -elemTop);
				}
			}
		}
	};
	
	$.fn.touchScrollSet = function(html, callback) {
		this.touchScrollReposition();
		this.find('.touchScrollContent').html(html);
		
		callback = callback || null;
		if( callback != null && typeof callback == 'function' ) {
			callback.call(this);
		}
	};
	$.fn.touchScrollLoad = function(url, data, callback) {
		data     = data || null;
		callback = callback || null;
		
		this.touchScrollJumpto('top');
		if( data != null ) {
			if( callback != null ) {
				this.find('.touchScrollContent').load(url, data, callback);
			}
			else {
				this.find('.touchScrollContent').load(url, data);
			}
		}
		else {
			this.find('.touchScrollContent').load(url);
		}
		
	};
	
	$.fn.touchScrollSetEnabled = function(enabled) {
		if( enabled ) {
			$(this).find('.touchScrollDisabled').css('display', 'none');
		} else {
			$(this).find('.touchScrollDisabled').css('display', 'block');
		}
	};
	
})(jQuery);



