﻿// jquery.scroller.js plug-in
/********************************************************************************
Plug-in Info:
- Name: jquery.scroller 
- Copyright (c) 2010 Department of Permitting Services, Montgomery County Maryland
- Date: 07/24/2010
- Author: Yung-Tsung Kang
- File Name: jquery.scroller.js
********************************************************************************
Version:
- 1.0 (July, 2010 - based on jQuery 1.4.2) - initial features:
	- auto-scroll (vertically|horizontally) on a list of items
	- alternate CSS background for odd/even items
	- pause on hover and show more info/description (as tooltip) if applicable
	- animate scrolling with fading effect if desirable
- 2.0 (TBD) - new features:
	- start and step implementation
	- previous/next, pause (x of y), and interval (speeding) controls 
	- slider control - start------*-------End (e.g., 1---5----10 slider)
********************************************************************************
Note, Assumption, & Limitation:
- the HTML must use a hierarchical (parent/child/grandchild) organization
- grandchild (if exists) can be used as tooltip on hover
- not limited to ul/li tags, i.e., div/div is OK
********************************************************************************
Usage: $('#id').scroller(@target, @options)
-	@target: 
	- a valid jQuery selector string, such as tag name, id, class, etc.
	- if omitted, defaults to id of the caller (this)
- @options: an array of paired settings (defaults listed as below):
	-	startIdx				: 0,				// element index to start - not used yet
	-	step						: 1, 				// number of elements to scroll - not used yet
	-	interval				: 3000, 		// milliseconds between scroll used in etInterval
	-	dir							: vertical,	// scrolling direction - [vertical|horizontal]
	-	effect					: null,			// scrolling effect - [''|fade|slide]
	-	timerID					: null,			// ID of an executed setInterval
	-	oddBackground		: {},				// odd item(0-based index) CSS background property value
	-	evenBackground	: {},				// even item(0-based index) CSS background property value
	-	label						: null,			// label as a selector to display
	- sliderControl		: null,			// ID of the slider control - see Special Notes below
	- auto						: true,			// auto scroll
	-	target					: null			// the selector string (or id of this if not provided)
********************************************************************************
Special Notes (for slider control):
- it take a 5 elements IDs in {containterID, startID, backID, pauserunID, nextID, endID, itemID)
- it must be in the above order but be null to skip an item except the containterID
********************************************************************************
Examples:
- $('selector').scroller();
- $('selector').scroller('#sc');
- $('selector').scroller('#sc', {oddBackground:{'background':'white'}, effect:'slide'});
*********************************************************************************/

(function($) {
	//********** $.fn.scroller **********//
	$.fn.scroller = function(target, options) {
		//********** defaults and settings **********	//
		var defaults = {
			startIdx: 0,
			step: 1,
			interval: 3000,
			dir: 'vertical',
			timerID: null,
			effect: null,
			effectIn: 1500,
			effectOut: 500,
			oddBackground: { 'background': 'white' },
			evenBackground: { 'background': 'white' },
			label: null,
			sliderControl: null,
			auto: true,
			target: null
		};

		// extend default settings based on user input
		settings = $.extend(defaults, options);
		// set the target - default = source id
		settings.target = isNaN(target) ? '#' + $(this).attr('id') : target;

		// set initial background
		$(target + ':first > *:even').css(settings.evenBackground);
		$(target + ':first > *:odd').css(settings.oddBackground);

		// set item of items
		if (settings.sliderControl != null) {
			$('#' + settings.sliderControl.item).html("1 of " + $(settings.target).children().size());
		}
		//********** action for each element **********//
		return this.each(function() {
			// set local variables for multiple instances
			var 
				selector = settings.target,
				startIdx = settings.startIdx,
				effect = settings.effect,
				effectIn = settings.effectIn,
				effectOut = settings.effectOut,
				evenBg = settings.evenBackground,
				oddBg = settings.oddBackground,
				timerID = settings.timerID,
				target = settings.target,
				interval = settings.interval,
				label = settings.label,
				sc = settings.sliderControl,
				auto = settings.auto,
				activeIdx = 0, 														// local variable - index of the active item
				totalItems = $(target).children().size()	// local variable - total number of items
			;
			// slider control setup - bind click event
			if (sc != null) {
				if (sc.start != null) {			// Start
					$("#" + sc.start).bind('click', function() {
						if (activeIdx > 0) moveFirst();  //moveX(totalItems - activeIdx, -(totalItems - activeIdx));
					});
				}
				if (sc.previous != null) {	// Previous
					$("#" + sc.previous).bind('click', function() {
						moveBack();  //moveX(-1, 0);
					});
				}
				if (sc.next != null) {			// Next
					$("#" + sc.next).bind('click', function() {
						move();
					});
				}
				if (sc.end != null) {				// End
					$("#" + sc.end).bind('click', function() {
						if (activeIdx < totalItems) moveLast(); //moveX(totalItems - activeIdx - 1, activeIdx);
					});
				}
				if (sc.pause != null) {
					// hide pause - not auto scroll
					if (!auto) { $("#" + sc.pause).hide(); }
					$("#" + sc.pause).bind('click', function() {
						auto = false; run(true);
						// hide self and show scroll
						$(this).hide(); $("#" + sc.scroll).show();
						// unbind pause on hover - not auto scroll anymore
					});
				}
				if (sc.scroll != null) {
					// hide scroll - auto acroll
					if (auto) { $("#" + sc.scroll).hide(); }

					$("#" + sc.scroll).bind('click', function() {
						auto = true; run(false);
						// hide self and show pause
						$(this).hide(); $("#" + sc.pause).show();
						// bind pause on hover - start auto scroll
					});
				}

			}

			// start item
			if (startIdx > 0) {
				// move to startIdx
				moveX(startIdx, 0);
				// have to reset startIdx = 0
				startIdx = 0;
			} else {
				// show the label of the first one
				if (label != null) {
					$(target).children(":eq(0)").find(label).css("visibility", "visible");
				}
			}

			// pause on hover and scroll when left?

			// auto run
			if (auto) {
				$(target).hover(function() { run(true); }, function() { run(false); });
				run(false);
			}

			//********** private functions **********//
			//********** set CSS background for even/odd items
			function setBackground() {
				$(target + ':first > *:even').css(evenBg);
				$(target + ':first > *:odd').css(oddBg);
			}
			//********** set label visibility - hide current and show next
			function labelHideShow(o) {
				if (label != null) {
					$(o).find(label).css("visibility", "hidden");
					$(o).next().find(label).css("visibility", "visible");
				}
			}
			//********** set Item of Items
			function setItemOfItems() {
				if (sc != null) $('#' + sc.item).html((activeIdx + 1) + " of " + totalItems);
			}
			//********** go (pause = false) or pause (pause = true)
			function run(pause) {
				clearInterval(timerID);
				timerID = pause ? null : setInterval(move, interval);
			};
			//********** set activeIdx
			function setActiveIdx(a) {
				if (a == "start") {						// start
					activeIdx = 0;
				} else if (a == "previous") {	// previous
					activeIdx = (activeIdx == 0) ? totalItems - 1 : activeIdx - 1;
				} else if (a == "next") {			// next
					activeIdx = (activeIdx == (totalItems - 1)) ? 0 : activeIdx + 1;
				} else if (a == "end") {			// last
					activeIdx = totalItems - 1;
				}
			}

			//**********
			// move to the end
			function moveLast() {
				x = totalItems - activeIdx - 2;
				$(target).children(":lt(" + x + ")").each(function() {
					$(this).insertAfter($(this).parent().children().last()).show();
					setActiveIdx("next"); // always to "next"
					setItemOfItems();
				});
				move();
			}
			// move to the start
			function moveFirst() {
				x = totalItems - activeIdx - 1;
				$(target).children(":lt(" + x + ")").each(function() {
					$(this).insertAfter($(this).parent().children().last()).show();
					setActiveIdx("next"); // always to "next"
					setItemOfItems();
				});
				move();
			}
			// move one forward
			function move() {
				$first = $(target).children(":eq(0)");
				//$last = $(target).children().last();
				$next = $first.next();
				$next.hide();

				switch (effect) {
					case 'fade':
						$first.fadeOut(effectOut, function() {
							//labelHideShow($(this));
							$(this).insertAfter($(this).parent().children().last()).show();
							$next.fadeIn(effectIn);
							//setBackground();
						});
						break;
					case 'slide':
						$first.slideUp(effectOut, function() {
							//labelHideShow($(this));
							$(this).insertAfter($(this).parent().children().last()).show();
							$next.slideDown(effectIn);
							//setBackground();
						});
						break;
					default:
						$first.hide(100, function() {
							//labelHideShow($(this));
							$(this).insertAfter($(this).parent().children().last()).show();
							$next.show(100);
							//setBackground();
						});
				}

				// set activeIdx and Item of Items
				setBackground();
				setActiveIdx("next"); // always to "next"
				setItemOfItems();
			};
			// move one backward
			function moveBack() {
				// get the last and insert before the first
				$first = $(target).children(":eq(0)");
				$last = $(target).children().last();
				$last.hide();

				// hide and show
				if (effect == 'fade') {
					$first.fadeOut(effectOut, function() {
						$last.insertBefore($first).fadeIn(effectIn);
						$first.show();
						//$first.show();
						//$last = $(this).parent().children().last();
						//if (label != null) $(this).find(label).css("visibility", "hidden");
						// $last.insertBefore($(this)).fadeIn(1000);
						//if (label != null) $last.find(label).css("visibility", "visible");
						//setBackground();
					});
				} else if (effect = 'slide') {
					$first.slideUp(effectOut, function() {
						$last.insertBefore($first).slideDown(effectIn);
						$first.show();
						//$last = $(this).parent().children().last();
						//if (label != null) $(this).find(label).css("visibility", "hidden");
						//$last.insertBefore($(this)).slideDown(1000);
						//if (label != null) $last.find(label).css("visibility", "visible");
						//setBackground();
					});
				} else {
					$first.hide(100, function() {
						$last.insertBefore($first).show(100);
						$first.show();
						//$last = $(this).parent().children().last();
						//if (label != null) $(this).find(label).css("visibility", "hidden");
						//$last.insertBefore($(this)).show(1000);
						//if (label != null) $last.find(label).css("visibility", "visible");
						//setBackground();
					});
				}

				setBackground();
				setActiveIdx("previous");
				setItemOfItems();
			}
		}); // end of element action
	}; // end of $.fn.scroller
})(jQuery);


