﻿/**

* jQuery.smoothDivScroll - Smooth div scrolling using jQuery.

* This plugin is for turning a set of HTML elements's into a smooth scrolling area.

*

* Copyright (c) 2009 Thomas Kahn - thomas.kahn(at)karnhuset(dot)net

*

* This plugin is free software: you can redistribute it and/or modify

* it under the terms of the GNU General Public License as published by

* the Free Software Foundation, either version 3 of the License, or

* any later version.

*

* This plugin is distributed in the hope that it will be useful,

* but WITHOUT ANY WARRANTY; without even the implied warranty of

* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

* GNU General Public License for more details. <http://www.gnu.org/licenses/>.

*

* Date: 2009-04-20

* @author Thomas Kahn

* @version 0.8

*

* Changelog

* ---------------------------------------------

* 0.8   - Major update. New parameter setup. Lots of new autoscrolling capabilities and 

*		  new parameters for controlling the scrolling speed. Made it possible to start 

*		  the scroller at a specific element.

* 

* 0.7   - Added support for autoscrolling after the page has loaded. 

*         Added support for making the hot spots visible at start for X number of seconds

*         or visible all the time.

*

* 0.6   - First version.

*/



(function($) { 

	jQuery.fn.smoothDivScroll = function(options){



		var defaults = {

		scrollingHotSpotLeft: "div.scrollingHotSpotLeft", // The hot spot that triggers scrolling left

		scrollingHotSpotRight: "div.scrollingHotSpotRight", // The hot spot that triggers scrolling right

		scrollWrapper: "div.scrollWrapper", // The wrapper element that surrounds the scrollable area

		scrollableArea: "div.scrollableArea", // The actual element that is scrolled left or right

		hiddenOnStart: false, // True or false. Determines whether the element should be visible or hidden on start

		ajaxContentURL: "", // Optional. If supplied, content is fetched through AJAX using the supplied URL

		countOnlyClass: "", // Optional. If supplied, the function that calculates the width of the scrollable area will only count elements of this class

		scrollingSpeed: 25, // A way of controlling the scrolling speed. 1=slowest and 100= fastest.

		mouseDownSpeedBooster: 3, // 1 is normal speed (no speed boost), 2 is twice as fast, 3 is three times as fast, and so on

		autoScroll: "", // Optional. Leave it blank if you don't want any autoscroll. 

						// Otherwise use the values "onstart" or "always". 

						// onstart - the scrolling will start automatically after 

						// the page has loaded and scroll according to the method you've selected 

						// using the autoScrollDirection parameter. When the user moves the mouse 

						// over the left or right hot spot the autoscroll will stop. After that 

						// the scrolling will only be triggered by the host spots.

						// always - the hot spots are disabled alltogether and the scrollable area 

						// will only scroll automatically.

		autoScrollDirection: "right", 	// This parameter controls the direction and behavior of the autoscrolling.	

										// Optional. The values are:

										// right - autoscrolls right and stops when it reaches the end

										// left - autoscrolls left and stops when it reaches the end 

										// (only relevant if you have set the parameter startAtElementId).

										// backandforth - starts autoscrolling right and when it reaches 

										// the end, switches to autoscrolling left and so on. Ping-pong style.

										// endlessloop - continuous scrolling right. An endless loop of elements.

		autoScrollSpeed: 1,	//  1-2 = slow, 3-4 = medium, 5-13 = fast -- anything higher = superfast

		pauseAutoScroll: "", // Optional. Values mousedown and mouseover. Leave blank for no pausing abilities.

		visibleHotSpots: "", 	// Optional. Leave it blank for invisible hot spots. 

								// Otherwise use the values  "onstart" or "always". 

								// onstart - makes the hot spots visible for X-number of seconds 

								// after tha page has loaded and then they become invisible. 

								// always - hot spots are visible all the time.

		hotSpotsVisibleTime: 5, // If you have selected "onstart" as the value for visibleHotSpots, 

								// you set the number of seconds that you want the hot spots to be 

								// visible after the page has loaded. After this time they will fade 

								// away and become invisible again.

		startAtElementId: ""	// Optional. Use this parameter if you want the offset of the 

								// scrollable area to be positioned at a specific element directly 

								// after the page has loaded. First give your element an ID in the 

								// HTML code and then provide this ID as a parameter.

		};



		options = $.extend(defaults, options);



		/* Identify global variables so JSLint won't raise errors when verifying the code */

		/*global autoScrollInterval, autoScroll, clearInterval, doScrollLeft, doScrollRight, hideHotSpotBackgrounds, hideHotSpotBackgroundsInterval, hideLeftHotSpot, hideRightHotSpot, jQuery, makeHotSpotBackgroundsVisible, setHotSpotHeightForIE, setInterval, showHideHotSpots, window, windowIsResized */





		// Iterate and make each matched element a SmoothDivScroll

		return this.each(function() {

		

			// Create a variable for the current "mother element"

			var $mom = $(this);

			

			// Load the content of the scrollable area using the optional URL.

			// If no ajaxContentURL is supplied, we assume that the content of

			// the scrolling area is already in place.

			if(options.ajaxContentURL.length !== 0){

				$mom.scrollableAreaWidth = 0;

				$mom.find(options.scrollableArea).load((options.ajaxContentURL), function(){	

					$mom.find(options.scrollableArea).children((options.countOnlyClass)).each(function() {

						$mom.scrollableAreaWidth = $mom.scrollableAreaWidth + $(this).outerWidth(true);

					});



					// Set the width of the scrollable area

					$mom.find(options.scrollableArea).css("width", ($mom.scrollableAreaWidth + "px"));

					

					// Hide the mother element if it shouldn't be visible on start

					if(options.hiddenOnStart) {

						$mom.hide();

					}

					

					windowIsResized();

					

					setHotSpotHeightForIE();

				});		

			}

			

			// Some variables used for working with the scrolling

			var scrollXpos;

			var booster;

			

			// The left offset of the container on which you place 

			// the scrolling behavior.

			// This offset is used when calculating the mouse x-position 

			// in relation to scroll hot spots

			var motherElementOffset = $mom.offset().left;

			

			// A variable used for storing the current hot spot width.

			// It is used when calculating the scroll speed

			var hotSpotWidth = 0;

			

			// Set the booster value to normal (doesn't change until the user

			// holds down the mouse button over one of the hot spots)

			booster = 1;

			

			var hasExtended = false;

			

			// Stuff to do once on load

			$(window).one("load",function(){

				// If the content of the scrolling area is not loaded through ajax,

				// we assume it's already there and can run the code to calculate

				// the width of the scrolling area, resize it to that width

				if(options.ajaxContentURL.length === 0) {

					$mom.scrollableAreaWidth = 0;

					$mom.tempStartingPosition = 0;

					

					$mom.find(options.scrollableArea).children((options.countOnlyClass)).each(function() {

						

						// Check to see if the current element in the loop is the one where the scrolling should start

						if( (options.startAtElementId.length !== 0) && (($(this).attr("id")) == options.startAtElementId) ) {

						$mom.tempStartingPosition = $mom.scrollableAreaWidth;

						}



						// Add the width of the current element in the loop to the total width

						$mom.scrollableAreaWidth = $mom.scrollableAreaWidth + $(this).outerWidth(true);

						

					});

					

					// Set the width of the scrollableArea to the accumulated width

					$mom.find(options.scrollableArea).css("width", $mom.scrollableAreaWidth + "px");

					

					// Check to see if the whole thing should be hidden at start

					if(options.hiddenOnStart) {

						$mom.hide();

					}

				}

				

				// Set the starting position of the scrollable area. If no startAtElementId is set, the starting position

				// will be the default value (zero)

				$mom.find(options.scrollWrapper).scrollLeft($mom.tempStartingPosition);

				

				// If the user has set the option autoScroll, the scollable area will

				// start scrolling automatically

				if(options.autoScroll !== "") {

					autoScrollInterval = setInterval(autoScroll, 6);

				}



				// If autoScroll is set to always, the hot spots should be disabled

				if(options.autoScroll == "always")

				{

					hideLeftHotSpot();

					hideRightHotSpot();

				}

	

				// If the user wants to have visible hot spots, here is where it's taken care of

				switch(options.visibleHotSpots)

				{

					case "always":

						makeHotSpotBackgroundsVisible();

						break;

					case "onstart":

						makeHotSpotBackgroundsVisible();

						hideHotSpotBackgroundsInterval = setInterval(hideHotSpotBackgrounds, (options.hotSpotsVisibleTime * 1000));

						break;

					default:

						break;	

				}

				

			});

			

			// If autoScroll is running, here's where it's stopped when the user positions the mouse over one of the hot spots

			$mom.find(options.scrollingHotSpotRight, options.scrollingHotSpotLeft).one('mouseover',function(){

				if(options.autoScroll == "onstart") {

					clearInterval(autoScrollInterval);

				}

			});	



			

			// EVENT - window resize

			$(window).bind("resize",function(){

				windowIsResized();

			});



			// A function for doing the stuff that needs to be

			// done when the browser window is resized

			function windowIsResized() {

			

				// If the scrollable area is not hidden on start, reset and recalculate the

				// width of the scrollable area

				if(!(options.hiddenOnStart))

				{

					$mom.scrollableAreaWidth = 0;

					$mom.find(options.scrollableArea).children((options.countOnlyClass)).each(function() {

						$mom.scrollableAreaWidth = $mom.scrollableAreaWidth + $(this).outerWidth(true);

					});

					

					$mom.find(options.scrollableArea).css("width", $mom.scrollableAreaWidth + 'px');

				}



				// Reset the left offset of the scroll wrapper

				$mom.find(options.scrollWrapper).scrollLeft("0");

				

				// Get the width of the page (body)

				var bodyWidth = $("body").innerWidth();

				

				// If the scrollable area is shorter than the current

				// window width, both scroll hot spots should be hidden.

				// Otherwise, check which hot spots should be shown.

				if(options.autoScroll !== "always")

				{

					if($mom.scrollableAreaWidth < bodyWidth)

					{	

						hideLeftHotSpot();

						hideRightHotSpot();

					}

					else

					{

						showHideHotSpots();

					}

				}

			}

			

			// HELPER FUNCTIONS FOR SHOWING AND HIDING HOT SPOTS

			function hideLeftHotSpot(){

				$mom.find(options.scrollingHotSpotLeft).hide();

			}

			

			function hideRightHotSpot(){

				$mom.find(options.scrollingHotSpotRight).hide();

			}

			

			function showLeftHotSpot(){

				$mom.find(options.scrollingHotSpotLeft).show();

				// Recalculate the hot spot width. Do it here because you can

				// be sure that the hot spot is visible and has a width

				if(hotSpotWidth <= 0) {

					hotSpotWidth = $mom.find(options.scrollingHotSpotLeft).width();

				}

			}

			

			function showRightHotSpot(){

				$mom.find(options.scrollingHotSpotRight).show();

				// Recalculate the hot spot width. Do it here because you can

				// be sure that the hot spot is visible and has a width

				if(hotSpotWidth <= 0) {

					hotSpotWidth = $mom.find(options.scrollingHotSpotRight).width();

				}

			}

			

			function setHotSpotHeightForIE()

			{

				// Some bugfixing for IE 6

				jQuery.each(jQuery.browser, function(i, val) {

					if(i=="msie" && jQuery.browser.version.substr(0,1)=="6")

					{

						$mom.find(options.scrollingHotSpotLeft).css("height", ($mom.find(options.scrollableArea).innerHeight()));

						$mom.find(options.scrollingHotSpotRight).css("height", ($mom.find(options.scrollableArea).innerHeight()));				

					}

				});

			}

			// **************************************************

			// EVENTS - scroll right

			// **************************************************

			

			// Check the mouse X position and calculate the relative X position inside the right hot spot

			$mom.find(options.scrollingHotSpotRight).bind('mousemove',function(e){

				var x = e.pageX - (this.offsetLeft + motherElementOffset);

				scrollXpos = Math.round((x/hotSpotWidth) * options.scrollingSpeed);

				if(scrollXpos === Infinity) {

					scrollXpos = 0;

				}



			});



			// mouseover right hot spot

			$mom.find(options.scrollingHotSpotRight).bind('mouseover',function(){

				if(options.autoScroll == "onstart") {

					clearInterval(autoScrollInterval);

				}

				rightScrollInterval = setInterval(doScrollRight, 6);

			});	

			

			// mouseout right hot spot

			$mom.find(options.scrollingHotSpotRight).bind('mouseout',function(){

				clearInterval(rightScrollInterval);

				scrollXpos = 0;

			});

			

			// scrolling speed booster right

			$mom.find(options.scrollingHotSpotRight).bind('mousedown',function(){

				booster = options.mouseDownSpeedBooster;

			});

			

			// stop boosting the scrolling speed

			$("*").bind('mouseup',function(){

				booster = 1;

			});

	

			

			// The function that does the actual scrolling right

			var doScrollRight = function()

			{	

				if(scrollXpos > 0) {

					$mom.find(options.scrollWrapper).scrollLeft($mom.find(options.scrollWrapper).scrollLeft() + (scrollXpos*booster));

				}

				showHideHotSpots();

			};

			

			// **************************************************

			// Autoscrolling

			// **************************************************



			if(options.pauseAutoScroll == "mousedown" && options.autoScroll == "always")

			{

				$mom.find(options.scrollWrapper).bind('mousedown',function(){

					clearInterval(autoScrollInterval);

				});

				

				$mom.find(options.scrollWrapper).bind('mouseup',function(){

					autoScrollInterval = setInterval(autoScroll, 6);

				});

			}

			else if(options.pauseAutoScroll == "mouseover" && options.autoScroll == "always")

			{

				$mom.find(options.scrollWrapper).bind('mouseover',function(){

					clearInterval(autoScrollInterval);

				});

				

				$mom.find(options.scrollWrapper).bind('mouseout',function(){

					autoScrollInterval = setInterval(autoScroll, 6);

				});

			}

			

			var previousScrollLeft = 0;

			var pingPongDirection = "right";

			var hasChanged = false;

			var swapAt;

			// The autoScroll function

			var autoScroll = function()

			{	

				if (options.autoScroll == "onstart") {

					showHideHotSpots();

				}

				

				switch(options.autoScrollDirection)

				{

					case "right":

						$mom.find(options.scrollWrapper).scrollLeft($mom.find(options.scrollWrapper).scrollLeft() + options.autoScrollSpeed);

						break;

						

					case "left":

						$mom.find(options.scrollWrapper).scrollLeft($mom.find(options.scrollWrapper).scrollLeft() - options.autoScrollSpeed);

						break;

						

					case "backandforth":

						// Calculate an indicator variable to see if the scrolling has reached the end

						previousScrollLeft = $mom.find(options.scrollWrapper).scrollLeft();

						

						if(pingPongDirection == "right") {

							$mom.find(options.scrollWrapper).scrollLeft($mom.find(options.scrollWrapper).scrollLeft() + options.autoScrollSpeed);

						}

						else {

							$mom.find(options.scrollWrapper).scrollLeft($mom.find(options.scrollWrapper).scrollLeft() - options.autoScrollSpeed);

						}

						

						if(previousScrollLeft === $mom.find(options.scrollWrapper).scrollLeft())

						{

							if(pingPongDirection == "right") {

								pingPongDirection = "left";

							}

							else {

								pingPongDirection = "right";

							}

						}

						break;

		

					case "endlessloop":

						// Calculate an indicator variable to see if the scrolling has reached the end

						previousScrollLeft = $mom.find(options.scrollWrapper).scrollLeft();

						

						if(!(hasChanged))

						{

							if(options.startAtElementId !== "") {

								swapAt = $mom.find(options.scrollWrapper).scrollLeft() + $("#" + options.startAtElementId).outerWidth();

							}

							else {

								swapAt = $mom.find(options.scrollWrapper).scrollLeft() + $mom.find(options.scrollableArea).children(":first-child").outerWidth();

							}

							hasChanged = true;						

						}

						

						// Do the autoscrolling

						$mom.find(options.scrollWrapper).scrollLeft($mom.find(options.scrollWrapper).scrollLeft() + options.autoScrollSpeed);

						

						// Check to see if the first element in the scrollableArea needs to be moved to become the last or if the scrolling has reached the end

						if((swapAt <= $mom.find(options.scrollWrapper).scrollLeft()) || ((options.startAtElementId !== "") && (previousScrollLeft === $mom.find(options.scrollWrapper).scrollLeft())))

						{

							// Clone the first element and append it last in the scrollableArea

							$mom.find(options.scrollableArea).append($mom.find(options.scrollableArea).children(":first-child").clone());



							// Compensate for the removal of the first element by

							$mom.find(options.scrollWrapper).scrollLeft(($mom.find(options.scrollWrapper).scrollLeft() - $mom.find(options.scrollableArea).children(":first-child").outerWidth()));

							

							// Remove it from its original position as the first element

							$mom.find(options.scrollableArea).children(":first-child").remove();

							hasChanged = false;

						}

						

						break;

					default:

						break;

						

				}



			};

			

			

			// **************************************************

			// EVENTS - scroll left

			// **************************************************

		

			// Check the mouse X position and calculate the relative X position inside the left hot spot

			$mom.find(options.scrollingHotSpotLeft).bind('mousemove',function(e){

				var x = $mom.find(options.scrollingHotSpotLeft).innerWidth() - (e.pageX - motherElementOffset);

				scrollXpos = Math.round((x/hotSpotWidth) * options.scrollingSpeed);

				if(scrollXpos === Infinity)

				{

					scrollXpos = 0;

				}

			});

			

			// mouseover left hot spot

			$mom.find(options.scrollingHotSpotLeft).bind('mouseover',function(){

				if(options.autoScroll == "onstart") {

					clearInterval(autoScrollInterval);

				}

				

				leftScrollInterval = setInterval(doScrollLeft, 6);

			});	

			

			// mouseout left hot spot

			$mom.find(options.scrollingHotSpotLeft).bind('mouseout',function(){

				clearInterval(leftScrollInterval);

				scrollXpos = 0;

			});

			

			// scrolling speed booster left

			$mom.find(options.scrollingHotSpotLeft).bind('mousedown',function(){

				booster = options.mouseDownSpeedBooster;

			});

			

			// The function that does the actual scrolling left

			var doScrollLeft = function()

			{	

				if(scrollXpos > 0) {

					$mom.find(options.scrollWrapper).scrollLeft($mom.find(options.scrollWrapper).scrollLeft() - (scrollXpos*booster));

				}

				showHideHotSpots();

			};

			

			// **************************************************

			// Hot spot functions

			// **************************************************

			

			// Function for showing and hiding hot spots depending on the

			// offset of the scrolling

			function showHideHotSpots()

			{

				// When you can't scroll further left

				// the left scroll hot spot should be hidden

				// and the right hot spot visible

				if($mom.find(options.scrollWrapper).scrollLeft() === 0)

				{

					hideLeftHotSpot();

					showRightHotSpot();

				}

				// When you can't scroll further right

				// the right scroll hot spot should be hidden

				// and the left hot spot visible

				else if(($mom.scrollableAreaWidth) <= ($mom.find(options.scrollWrapper).innerWidth() + $mom.find(options.scrollWrapper).scrollLeft()))

				{

					hideRightHotSpot();

					showLeftHotSpot();

				}

				// If you are somewhere in the middle of your

				// scrolling, both hot spots should be visible

				else

				{

					showRightHotSpot();

					showLeftHotSpot();

				}



			}

			

			// Function for making the hot spot background visible

			function makeHotSpotBackgroundsVisible()

			{

				// Alter the CSS (SmoothDivScroll.css) if you want to customize

				// the look'n'feel of the visible hot spots

				

				// The left hot spot

				$mom.find(options.scrollingHotSpotLeft).addClass("scrollingHotSpotLeftVisible");



				// The right hot spot

				$mom.find(options.scrollingHotSpotRight).addClass("scrollingHotSpotRightVisible");

			}

			

			// Hide the hot spot backgrounds.

			function hideHotSpotBackgrounds()

			{

				clearInterval(hideHotSpotBackgroundsInterval);

				

				// Fade out the left hot spot

				$mom.find(options.scrollingHotSpotLeft).fadeTo("slow", 0.0, function(){

					$mom.find(options.scrollingHotSpotLeft).removeClass("scrollingHotSpotLeftVisible");

				});



				// Fade out the right hot spot

				$mom.find(options.scrollingHotSpotRight).fadeTo("slow", 0.0, function(){

					$mom.find(options.scrollingHotSpotRight).removeClass("scrollingHotSpotRightVisible");

				});

			}

			

	});

};



})(jQuery);



