/* 
*
* 2011 www.g-u-i.net
*
*/

(function($){ 

	jQuery.extend({
		mouse: {
			x: 0,
			y: 0
		},
		clickNScroll: {
			mousedown:false,
			emaX: 0,
			emaY: 0,
			started : false
		}  
	});

	var methods = {
		init : function(options){
			
			var settings = {
				scroller:'',
				allowHiliting:false,
				acceleration:.65,
				deceleration:.85,
				decelRate:64,
				reverse:false,
				rightMouse:false,
				allowThrowing:true,
				throwOnOut:true,
				stepableparent:false,
				changeLimit:3,
				stepInSpeed:500,
				axis:false,
				distance:0,
				easing:'linear',
				disabled : false,
				started:false,
			};
			


			return this.each(function(){  
				var $this = $(this),
						ops = $.extend({}, settings, options || {});				

				ops.scroller = ops.scroller != '' ? $(ops.scroller, $this) : $this;
				ops.id = $this.attr('id');
				
				$this.data("clicknscroll", ops);                                  
				
				if(!ops.allowHiliting) {
					if (jQuery.browser.msie) {
						$this.get(0).onselectstart = function () { return false; };
					} else {
						$this.get(0).onmousedown = function(e){e.preventDefault()}
					}   
				}
				
				
				this.addEventListener('mouseup', function(e) {
					if($(this).data('clicknscroll').disabled)
						return;
				
					if($(this).hasClass('cns-scrolling')){
						e.stopPropagation(); // stopPropagation to prevent mouseUp on dom children when just scrolling
						$(document).trigger('mouseup');
						
						if(ops.allowThrowing) 
							sling($(this));  
						
					}

					$.clickNScroll.mousedown = false;						
					
				}, true); // add this event listener with addEventListener because of useCapture = true, that is impossible yet with jQuery bind() …
				
				
				$this
					.mousedown(function(e) {
					// $this.bind('mousedown click name', function(e) {
						// $.log('mousedown | $(this)',$(this));
						if($(this).data('clicknscroll').disabled)
							return false;
					
						$this.stop().clearQueue();
						$.clickNScroll.startX = e.pageX;
						$.clickNScroll.startY = e.pageY;
						$.clickNScroll.mousedown = $this;
					})
					.mouseout(function(e) {
						if($(this).data('clicknscroll').disabled)
							return;
					
						var from = e.relatedTarget || e.toElement;
						if (!from || from.nodeName == "HTML") {
							if($.clickNScroll.mousedown && ops.allowThrowing && ops.throwOnOut)  sling($(this));   
							$.clickNScroll.mousedown = false;
						}
					});
			
			});			
		},
		enable : function(){
			// $.log('clickNScroll | enable | this',this);
			return this.each(function() {
				$(this).data('clicknscroll').disabled = false;
			});
		},
		disable : function(){
			// $.log('clickNScroll | disable | this',this);
			return this.each(function() {
				$(this).data('clicknscroll').disabled = true;
			});
		},
		isStarted : function(){
			return $(this).data('clicknscroll').started;
		},
		stop : function(){
			return this.each(function() {
				if(!$(this).data('clicknscroll').started){
						$.clickNScroll.mousedown = false;
						$(this).data('clicknscroll').started = false;
				}
			});
		},
		scrollTo : function(delta){
			// $.log('scrollTo | delta = '+delta);
			return this.each(function() {
				$(this).data('clicknscroll').scroller.scrollLeft(delta);
			});
		}
	};

	jQuery.fn.extend({
		clickNScroll: function(options) {
			// Method calling logic
	    if ( methods[options] ) {
	      return methods[ options ].apply( this, Array.prototype.slice.call( arguments, 1 ));
	    } else if ( typeof options === 'object' || ! options ) {				
				return methods.init.apply( this, arguments );
			} else {
	      $.error( 'Method ' +  options + ' does not exist on jQuery.clickNScroll' );
	    }
		}
	});
	
	/**
	* sling($this)
	*/
	function sling($this) {
		var data = $this.data("clicknscroll"),
				$scroller = data.scroller,
				
				changeX = ($.clickNScroll.emaX)*data.deceleration,   
				changeY = ($.clickNScroll.emaY)*data.deceleration;

		if( Math.abs(changeX) > 0 && Math.abs(changeX) <= data.changeLimit && data.stepableparent) {
			// $.log('ClicknScroll :: end sling X');
		
			var scrollPos = $scroller.scrollLeft()+$scroller.width()*.5;
		
			$(data.stepableparent, $scroller).children(':visible').each(function(i) {
				var $child = $(this),
						childPos = $child.position();
			
				if(scrollPos >= childPos.left && scrollPos <= childPos.left+$child.width() ){
				
					var stepLeft = childPos.left + $child.width()*.5 - $scroller.width()*.5;
				
					var e = jQuery.Event('stepInLeft');
					e.options = data;
					e.stepIndex = i;
					e.stepTarget = $child;
					e.direction = changeX < 0 ? -1 : 1;
					$this.trigger(e);
				
					$scroller.animate({scrollLeft: stepLeft}, {easing:data.easing, duration:data.stepInSpeed, queue:false , complete:function() {
							$this.trigger('stepedInLeft',data);
						}
					});
				
					$.clickNScroll.emaX = 0;
					changeX = 0;
					return;
				}
			
			});
				
		}  

		if( Math.abs(changeY) > 0 && Math.abs(changeY) <= data.changeLimit && data.stepableparent) {
			// $.log('ClicknScroll :: end sling Y');
		
			var scrollPos = $scroller.scrollTop()+$scroller.height()*.5;

			$(data.stepableparent, $scroller).children(':visible').each(function(i) {
				var $child = $(this),
						childPos = $child.position();

				if(scrollPos >= childPos.top && scrollPos <= childPos.top+$child.height() ){

					var stepTop = childPos.top + $child.height()*.5 - $scroller.height()*.5;

					var e = jQuery.Event('stepInTop');
					e.options = data;
					e.stepIndex = i;
					e.stepTarget = $child;
					e.direction = changeY < 0 ? -1 : 1;
					$this.trigger(e);

					$scroller.animate({scrollTop: stepTop}, {easing:data.easing, duration:data.stepInSpeed, queue:false , complete:function() {
							$this.trigger('stepedInTop',data);
						}
					});

					$.clickNScroll.emaY = 0;
					changeY = 0;
					return;
				}
			});	
		}			

		if(Math.abs(changeX) < .01 && Math.abs(changeY) < .01 && $this.hasClass('cns-scrolling')){
			$this.removeClass('cns-scrolling').trigger('clickNScrollStop');
			return;	
		}
		// return;			

		move($this, changeX, changeY);            

		setTimeout(function() {
			sling($this);
			}, 1000/data.decelRate);

	};

	/**
	* move($scroller, changeX, changeY)
	*/
	function move($this, changeX, changeY) {
		$.log('clicknscroll | move');
		// if(!$this.data('clicknscroll').started){
		// 	$this.trigger('clickNScrollStart').data('clicknscroll').started = true;
		// }
		if(!$this.hasClass('cns-scrolling')){
			$this.addClass('cns-scrolling').trigger('clickNScrollStart');
		}
		$this.trigger('clickNScrollMove');
		if(($.clickNScroll.emaX < 0 && changeX > 0) || ($.clickNScroll.emaX > 0 && changeX < 0)) 
			$.clickNScroll.emaX = 0;

		if(($.clickNScroll.emaY < 0 && changeY > 0) || ($.clickNScroll.emaY > 0 && changeY < 0))
			$.clickNScroll.emaY = 0;

		var ops = $this.data("clicknscroll"),
				$scroller = ops.scroller,
				
				amntX = ops.acceleration * changeX + (1 - ops.acceleration) * $.clickNScroll.emaX,
				amntY = ops.acceleration * changeY + (1 - ops.acceleration) * $.clickNScroll.emaY,

				scrollRight = $scroller[0].scrollWidth ? $scroller[0].scrollWidth - $scroller[0].clientWidth : $scroller[0].body.scrollWidth - $scroller[0].body.clientWidth,
				scrollBottom = $scroller[0].scrollHeight ? $scroller[0].scrollHeight - $scroller[0].clientHeight : $scroller[0].body.scrollHeight - $scroller[0].body.clientHeight; 


		if(($scroller.scrollLeft() >= 0 && changeX >= 0) || ($scroller.scrollLeft() <= scrollRight && changeX <= 0))
			$scroller.scrollLeft($scroller.scrollLeft() + (amntX));

		if(($scroller.scrollTop() >= 0 && changeY >= 0) || ($scroller.scrollTop() <= scrollBottom && changeY <= 0))
			$scroller.scrollTop($scroller.scrollTop() + (amntY));                                                       

		$.clickNScroll.emaX = amntX;
		$.clickNScroll.emaY = amntY;
	};

	/**
	* mousemove event
	*/
	$(document).mousemove(function(e) {
		if($.clickNScroll.mousedown) {      
			var $this  = $.clickNScroll.mousedown,
					ops    = $this.data("clicknscroll");
		
			if(ops.rightMouse && e.button != 2) return;
			else if(!ops.rightMouse && e.button == 2) return;  
		  
			var changeX = ops.axis != 'y' ? e.pageX - $.mouse.x : 0,   
					changeY = ops.axis != 'x' ? e.pageY - $.mouse.y : 0; 
				
			if(!ops.reverse) {
				changeX = 0-changeX;
				changeY = 0-changeY;
			}
			if((Math.abs($.clickNScroll.startX - e.pageX) > ops.distance && ops.axis != 'y')
			|| (Math.abs($.clickNScroll.startY - e.pageY) > ops.distance && ops.axis != 'x'))
				move($this, changeX, changeY);
		}  
		$.mouse = {
			x: e.pageX,
			y: e.pageY
		};                          
	}); 

})(jQuery);