/**
 * jQuery scrollToIfNeeded plugin
 *
 * Unlike jquery.scrollTo this plugin only scrolls if the element 
 * is not already visible.
 *
 * Use jquery.scrollTo if you need to control animation speed
**/

(function($){

	// This function scrolls the window to a particular element if needed
	function scrollWindowToElement (element) {
	
		// cross browser support for window height
		function getWindowInnerHeight() {
			var windowInnerHeight = 0;
			
			if (typeof(window.innerWidth) == 'number') {
				// Non-IE
				windowInnerHeight = window.innerHeight;
			} else if (document.documentElement && document.documentElement.clientHeight) {
				// IE 6+ in 'standards compliant mode'
				windowInnerHeight = document.documentElement.clientHeight;
			}
			
			return windowInnerHeight;
		}
		
		function getWindowScrollHeight() {
			var windowScrollHeight = 0;
			
			if (document.body && typeof(document.body.scrollHeight) == 'number') {
				// DOM compliant
				windowScrollHeight = document.body.scrollHeight;
			} else if (document.documentElement && document.documentElement.scrollHeight) {
				// IE6 standards compliant mode
				windowScrollHeight = document.documentElement.scrollHeight;
			}
			return windowScrollHeight;
		}
		
		// cross browser support for window scrollTop
		function getWindowScrollTop() {
			var windowScrollTop = 0;
		
			if (document.body && typeof(document.body.scrollTop) == 'number') {
				// DOM compliant
				windowScrollTop = document.body.scrollTop;
			} else if (document.documentElement && document.documentElement.scrollTop) {
				// IE6 standards compliant mode
				windowScrollTop = document.documentElement.scrollTop;
			}
			return windowScrollTop;
		}
		
		// cross browser support to scroll window
		function scrollWindowTo(y) {
			if (document.body && (typeof(document.body.scrollTop) == 'number')) {
				// DOM compliant
				document.body.scrollTop = y;
			} else if (document.documentElement && document.documentElement.scrollTop) {
				// IE6 standards compliant mode
				document.documentElement.scrollTop = y;
			}
		}
		
		var windowInnerHeight = getWindowInnerHeight();
		var windowScrollHeight = getWindowScrollHeight();
		var windowScrollTop = getWindowScrollTop();
		var windowScrollBottom = windowScrollTop + windowInnerHeight;
		
		// Getting the elementTop is a little trickier because we have to walk up the DOM
		var elementTop = 0;
		var temp = element[0];
		if (temp.offsetParent) {
			do {
				elementTop += temp.offsetTop;
			} while (temp = temp.offsetParent);
		}
		var elementBottom = elementTop + element[0].offsetHeight;
		
		if (elementTop < windowScrollTop) {
			// scroll up to element including some padding for focus rings, etc
			scrollWindowTo(Math.max(elementTop - 2, 0));
		} else if (elementBottom > windowScrollBottom) {
			// scroll down to element including some padding for focus rings, etc
			scrollWindowTo(Math.min(elementBottom+2, windowScrollHeight) - windowInnerHeight);
		}
	}
	
	// This function scrolls a scrollable element to a particular child if needed
	function scrollElementToElement (scrollMe, toMe) {
		if (!scrollMe.length || !toMe.length) { return; } // bad inputs
		
		// Walk up the DOM to scrollMe
		var toMeOffsetTop = toMe[0].offsetTop;
		var offsetParent = toMe[0].offsetParent;
		while (offsetParent && scrollMe.index(offsetParent) < 0) {
			toMeOffsetTop += offsetParent.offsetTop;
			offsetParent = offsetParent.offsetParent;
		}
	
		var toMeOffsetBottom = toMeOffsetTop + toMe[0].offsetHeight;
		var scrollMeOffsetTop = scrollMe.scrollTop();
		var scrollMeOffsetBottom = scrollMeOffsetTop + scrollMe[0].offsetHeight;
		
		// Check if the element is scrolled out of view
		if (toMeOffsetTop < scrollMeOffsetTop) {
			scrollMe.scrollTop(toMeOffsetTop); // scroll up
			
		} else if (toMeOffsetBottom > scrollMeOffsetBottom && 
				toMe[0].offsetHeight <= scrollMe[0].offsetHeight) {
			// scroll down if we can fit the entire content in the viewable area
			scrollMe.scrollTop(toMeOffsetBottom - scrollMe[0].offsetHeight); 
		}
	}

	$.fn.extend({
		scrollToIfNeeded: function (target) {
			var scrollMe = this.eq(0);
			var toMe = null;
			
			if (typeof target == 'string') {
				toMe = $(target).eq(0);
				
			} else if (typeof target == 'object') {
				if (target.jquery) {
					toMe = target.eq(0);
				} else {
					toMe = $(target).eq(0);
				}
			}
			
			if (!scrollMe || !scrollMe.length || !toMe || !toMe.length) {
				return;
			}
			
			// Are we scrolling the entire document or a scrollable element?
			var docNodeNames = ['iframe', '#document', 'html', 'body'];
			var nodeName = scrollMe[0].nodeName;
			
			if (!nodeName || $.inArray(nodeName.toLowerCase(), docNodeNames)) {
				return scrollWindowToElement(toMe);
			} else {
				return scrollElementToElement(scrollMe, toMe);
			}
		}
	});
})(jQuery);

