/**
 * jQuery Roundabout - v1.1 http://fredhq.com/projects/roundabout/
 * 
 * Moves list-items of enabled ordered and unordered lists long a chosen path.
 * Includes the default "lazySusan" path, that moves items long a spinning
 * turntable.
 * 
 * Terms of Use // jQuery Roundabout
 * 
 * Open source under the BSD license
 * 
 * Copyright (c) 2010, Fred LeBlanc All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *  - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer. - Redistributions in
 * binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other
 * materials provided with the distribution. - Neither the name of the author
 * nor the names of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

jQuery.extend({
	roundabout_shape : {
		def : 'lazySusan',
		lazySusan : function(r, a, t) {
			return {
				x : Math.sin(r + a),
				y : (Math.sin(r + 3 * Math.PI / 2 + a) / 8) * t,
				z : (Math.cos(r + a) + 1) / 2,
				scale : (Math.sin(r + Math.PI / 2 + a) / 2) + 0.5
			}
		}
	}
});
jQuery.fn.roundabout = function() {
	var options = (typeof arguments[0] != 'object') ? {} : arguments[0];
	options = {
		bearing : (typeof options.bearing == 'undefined') ? 0.0 : jQuery
				.roundabout_toFloat(options.bearing % 360.0),
		tilt : (typeof options.tilt == 'undefined') ? 0.0 : jQuery
				.roundabout_toFloat(options.tilt),
		minZ : (typeof options.minZ == 'undefined') ? 100 : parseInt(
				options.minZ, 10),
		maxZ : (typeof options.maxZ == 'undefined') ? 400 : parseInt(
				options.maxZ, 10),
		minOpacity : (typeof options.minOpacity == 'undefined') ? 0.40 : jQuery
				.roundabout_toFloat(options.minOpacity),
		maxOpacity : (typeof options.maxOpacity == 'undefined') ? 1.00 : jQuery
				.roundabout_toFloat(options.maxOpacity),
		minScale : (typeof options.minScale == 'undefined') ? 0.40 : jQuery
				.roundabout_toFloat(options.minScale),
		maxScale : (typeof options.maxScale == 'undefined') ? 1.00 : jQuery
				.roundabout_toFloat(options.maxScale),
		duration : (typeof options.duration == 'undefined') ? 600 : parseInt(
				options.duration, 10),
		btnNext : options.btnNext || null,
		btnPrev : options.btnPrev || null,
		easing : options.easing || 'swing',
		clickToFocus : (options.clickToFocus !== false),
		focusBearing : (typeof options.focusBearing == 'undefined') ? 0.0
				: jQuery.roundabout_toFloat(options.focusBearing % 360.0),
		shape : options.shape || 'lazySusan',
		debug : options.debug || false,
		childSelector : options.childSelector || 'li',
		startingChild : (typeof options.startingChild == 'undefined') ? null
				: parseInt(options.startingChild, 10),
		reflect : (typeof options.reflect == 'undefined' || options.reflect === false) ? false
				: true
	};
	this
			.each(function(i) {
				var ref = jQuery(this);
				var period = jQuery.roundabout_toFloat(360.0 / ref
						.children(options.childSelector).length);
				var startingBearing = (options.startingChild === null) ? options.bearing
						: options.startingChild * period;
				ref.addClass('roundabout-holder').css('padding', 0).css(
						'position', 'relative').css('z-index', options.minZ);
				ref.data('roundabout', {
					'bearing' : startingBearing,
					'tilt' : options.tilt,
					'minZ' : options.minZ,
					'maxZ' : options.maxZ,
					'minOpacity' : options.minOpacity,
					'maxOpacity' : options.maxOpacity,
					'minScale' : options.minScale,
					'maxScale' : options.maxScale,
					'duration' : options.duration,
					'easing' : options.easing,
					'clickToFocus' : options.clickToFocus,
					'focusBearing' : options.focusBearing,
					'animating' : 0,
					'childInFocus' : -1,
					'shape' : options.shape,
					'period' : period,
					'debug' : options.debug,
					'childSelector' : options.childSelector,
					'reflect' : options.reflect
				});
				if (options.clickToFocus === true) {
					ref
							.children(options.childSelector)
							.each(
									function(i) {
										jQuery(this)
												.click(
														function(e) {
															var degrees = (options.reflect === true) ? 360.0 - (period * i)
																	: period
																			* i;
															degrees = jQuery
																	.roundabout_toFloat(degrees);
															if (!jQuery
																	.roundabout_isInFocus(
																			ref,
																			degrees)) {
																e
																		.preventDefault();
																if (ref
																		.data('roundabout').animating === 0) {
																	ref
																			.roundabout_animateAngleToFocus(degrees)
																}
																return false
															}
														})
									})
				}
				if (options.btnNext) {
					jQuery(options.btnNext).bind('click.roundabout',
							function(e) {
								e.preventDefault();
								if (ref.data('roundabout').animating === 0) {
									ref.roundabout_animateToNextChild()
								}
								return false
							})
				}
				if (options.btnPrev) {
					jQuery(options.btnPrev).bind('click.roundabout',
							function(e) {
								e.preventDefault();
								if (ref.data('roundabout').animating === 0) {
									ref.roundabout_animateToPreviousChild()
								}
								return false
							})
				}
			});
	this.roundabout_startChildren();
	if (typeof arguments[1] === 'function') {
		var callback = arguments[1], ref = this;
		setTimeout(function() {
			callback(ref)
		}, 0)
	}
	return this
};
jQuery.fn.roundabout_startChildren = function() {
	this.each(function(i) {
		var ref = jQuery(this);
		var data = ref.data('roundabout');
		var children = ref.children(data.childSelector);
		children.each(function(i) {
			var degrees = (data.reflect === true) ? 360.0 - (data.period * i)
					: data.period * i;
			jQuery(this).addClass('roundabout-moveable-item').css('position',
					'absolute');
			jQuery(this).data('roundabout', {
				'startWidth' : jQuery(this).width(),
				'startHeight' : jQuery(this).height(),
				'startFontSize' : parseInt(jQuery(this).css('font-size'), 7),
				'degrees' : degrees
			})
		});
		ref.roundabout_updateChildPositions()
	});
	return this
};
jQuery.fn.roundabout_setTilt = function(newTilt) {
	this.each(function(i) {
		jQuery(this).data('roundabout').tilt = newTilt;
		jQuery(this).roundabout_updateChildPositions()
	});
	if (typeof arguments[1] === 'function') {
		var callback = arguments[1], ref = this;
		setTimeout(function() {
			callback(ref)
		}, 0)
	}
	return this
};
jQuery.fn.roundabout_setBearing = function(newBearing) {
	this.each(function(i) {
		jQuery(this).data('roundabout').bearing = jQuery.roundabout_toFloat(
				newBearing % 360, 2);
		jQuery(this).roundabout_updateChildPositions()
	});
	if (typeof arguments[1] === 'function') {
		var callback = arguments[1], ref = this;
		setTimeout(function() {
			callback(ref)
		}, 0)
	}
	return this
};
jQuery.fn.roundabout_adjustBearing = function(delta) {
	delta = jQuery.roundabout_toFloat(delta);
	if (delta !== 0) {
		this.each(function(i) {
			jQuery(this).data('roundabout').bearing = jQuery
					.roundabout_getBearing(jQuery(this))
					+ delta;
			jQuery(this).roundabout_updateChildPositions()
		})
	}
	if (typeof arguments[1] === 'function') {
		var callback = arguments[1], ref = this;
		setTimeout(function() {
			callback(ref)
		}, 0)
	}
	return this
};
jQuery.fn.roundabout_adjustTilt = function(delta) {
	delta = jQuery.roundabout_toFloat(delta);
	if (delta !== 0) {
		this.each(function(i) {
			jQuery(this).data('roundabout').tilt = jQuery
					.roundabout_toFloat(jQuery(this).roundabout_get('tilt')
							+ delta);
			jQuery(this).roundabout_updateChildPositions()
		})
	}
	if (typeof arguments[1] === 'function') {
		var callback = arguments[1], ref = this;
		setTimeout(function() {
			callback(ref)
		}, 0)
	}
	return this
};
jQuery.fn.roundabout_animateToBearing = function(bearing) {
	bearing = jQuery.roundabout_toFloat(bearing);
	var currentTime = new Date();
	var duration = (typeof arguments[1] == 'undefined') ? null : arguments[1];
	var easingType = (typeof arguments[2] == 'undefined') ? null : arguments[2];
	var passedData = (typeof arguments[3] !== 'object') ? null : arguments[3];
	this
			.each(function(i) {
				var ref = jQuery(this), data = ref.data('roundabout'), timer, easingFn, newBearing;
				var thisDuration = (duration === null) ? data.duration
						: duration;
				var thisEasingType = (easingType !== null) ? easingType
						: data.easing || 'swing';
				if (passedData === null) {
					passedData = {
						timerStart : currentTime,
						start : jQuery.roundabout_getBearing(ref),
						totalTime : thisDuration
					}
				}
				timer = currentTime - passedData.timerStart;
				if (timer < thisDuration) {
					data.animating = 1;
					if (typeof jQuery.easing.def == 'string') {
						easingFn = jQuery.easing[thisEasingType]
								|| jQuery.easing[jQuery.easing.def];
						newBearing = easingFn(null, timer, passedData.start,
								bearing - passedData.start,
								passedData.totalTime)
					} else {
						newBearing = jQuery.easing[thisEasingType](
								(timer / passedData.totalTime), timer,
								passedData.start, bearing - passedData.start,
								passedData.totalTime)
					}
					ref.roundabout_setBearing(newBearing, function() {
						ref.roundabout_animateToBearing(bearing, thisDuration,
								thisEasingType, passedData)
					})
				} else {
					bearing = (bearing < 0) ? bearing + 360 : bearing % 360;
					data.animating = 0;
					ref.roundabout_setBearing(bearing)
				}
			});
	return this
};
jQuery.fn.roundabout_animateToDelta = function(delta) {
	var duration = arguments[1], easing = arguments[2];
	this.each(function(i) {
		delta = jQuery.roundabout_getBearing(jQuery(this))
				+ jQuery.roundabout_toFloat(delta);
		jQuery(this).roundabout_animateToBearing(delta, duration, easing)
	});
	return this
};
jQuery.fn.roundabout_animateToChild = function(childPos) {
	var duration = arguments[1], easing = arguments[2];
	this.each(function(i) {
		var ref = jQuery(this), data = ref.data('roundabout');
		if (data.childInFocus !== childPos && data.animating === 0) {
			var child = jQuery(ref.children(data.childSelector)[childPos]);
			ref.roundabout_animateAngleToFocus(
					child.data('roundabout').degrees, duration, easing)
		}
	});
	return this
};
jQuery.fn.roundabout_animateToNearbyChild = function(passedArgs, which) {
	var duration = passedArgs[0], easing = passedArgs[1];
	this.each(function(i) {
		var data = jQuery(this).data('roundabout');
		var bearing = jQuery.roundabout_toFloat(360.0 - jQuery
				.roundabout_getBearing(jQuery(this)));
		var period = data.period, j = 0, range;
		var reflect = data.reflect;
		var length = jQuery(this).children(data.childSelector).length;
		bearing = (reflect === true) ? bearing % 360.0 : bearing;
		if (data.animating === 0) {
			if ((reflect === false && which === 'next')
					|| (reflect === true && which !== 'next')) {
				bearing = (bearing === 0) ? 360 : bearing;
				while (true && j < length) {
					range = {
						lower : jQuery.roundabout_toFloat(period * j),
						upper : jQuery.roundabout_toFloat(period * (j + 1))
					};
					range.upper = (j == length - 1) ? 360.0 : range.upper;
					if (bearing <= range.upper && bearing > range.lower) {
						jQuery(this).roundabout_animateToDelta(
								bearing - range.lower, duration, easing);
						break
					}
					j++
				}
			} else {
				while (true) {
					range = {
						lower : jQuery.roundabout_toFloat(period * j),
						upper : jQuery.roundabout_toFloat(period * (j + 1))
					};
					range.upper = (j == length - 1) ? 360.0 : range.upper;
					if (bearing >= range.lower && bearing < range.upper) {
						jQuery(this).roundabout_animateToDelta(
								bearing - range.upper, duration, easing);
						break
					}
					j++
				}
			}
		}
	});
	return this
};
jQuery.fn.roundabout_animateToNextChild = function() {
	return this.roundabout_animateToNearbyChild(arguments, 'next')
};
jQuery.fn.roundabout_animateToPreviousChild = function() {
	return this.roundabout_animateToNearbyChild(arguments, 'previous')
};
jQuery.fn.roundabout_animateAngleToFocus = function(target) {
	var duration = arguments[1], easing = arguments[2];
	this
			.each(function(i) {
				var delta = jQuery.roundabout_getBearing(jQuery(this)) - target;
				delta = (Math.abs(360.0 - delta) < Math.abs(0.0 - delta)) ? 360.0 - delta
						: 0.0 - delta;
				delta = (delta > 180) ? -(360.0 - delta) : delta;
				if (delta !== 0) {
					jQuery(this).roundabout_animateToDelta(delta, duration,
							easing)
				}
			});
	return this
};
jQuery.fn.roundabout_updateChildPositions = function() {
	this.each(function(i) {
		var ref = jQuery(this), data = ref.data('roundabout');
		var inFocus = -1;
		var info = {
			bearing : jQuery.roundabout_getBearing(ref),
			tilt : data.tilt,
			stage : {
				width : Math.floor(ref.width() * 0.9),
				height : Math.floor(ref.height() * 0.9)
			},
			animating : data.animating,
			inFocus : data.childInFocus,
			focusBearingRad : jQuery.roundabout_degToRad(data.focusBearing),
			shape : jQuery.roundabout_shape[data.shape]
					|| jQuery.roundabout_shape[jQuery.roundabout_shape.def]
		};
		info.midStage = {
			width : info.stage.width / 4,
			height : info.stage.height / 2
		};
		info.nudge = {
			width : info.midStage.width + info.stage.width * 0.3,
			height : info.midStage.height + info.stage.height * 0.09
		};
		info.zValues = {
			min : data.minZ,
			max : data.maxZ,
			diff : data.maxZ - data.minZ
		};
		info.opacity = {
			min : data.minOpacity,
			max : data.maxOpacity,
			diff : data.maxOpacity - data.minOpacity
		};
		info.scale = {
			min : data.minScale,
			max : data.maxScale,
			diff : data.maxScale - data.minScale
		};
		ref.children(data.childSelector).each(
				function(i) {
					if (jQuery.roundabout_updateChildPosition(jQuery(this),
							ref, info, i)
							&& info.animating === 0) {
						inFocus = i;
						jQuery(this).addClass('roundabout-in-focus')
					} else {
						jQuery(this).removeClass('roundabout-in-focus')
					}
				});
		if (inFocus !== info.inFocus) {
			jQuery.roundabout_triggerEvent(ref, info.inFocus, 'blur');
			if (inFocus !== -1) {
				jQuery.roundabout_triggerEvent(ref, inFocus, 'focus')
			}
			data.childInFocus = inFocus
		}
	});
	return this
};
jQuery.roundabout_getBearing = function(el) {
	return jQuery.roundabout_toFloat(el.data('roundabout').bearing) % 360
};
jQuery.roundabout_degToRad = function(degrees) {
	return (degrees % 360.0) * Math.PI / 180.0
};
jQuery.roundabout_isInFocus = function(el, target) {
	return (jQuery.roundabout_getBearing(el) % 360 === (target % 360))
};
jQuery.roundabout_triggerEvent = function(el, child, eventType) {
	return (child < 0) ? this : jQuery(
			el.children(el.data('roundabout').childSelector)[child]).trigger(
			eventType)
};
jQuery.roundabout_toFloat = function(number) {
	number = Math.round(parseFloat(number) * 1000) / 1000;
	return parseFloat(number.toFixed(2))
};
jQuery.roundabout_updateChildPosition = function(child, container, info,
		childPos) {
	var ref = jQuery(child), data = ref.data('roundabout'), out = [];
	var rad = jQuery
			.roundabout_degToRad((360.0 - ref.data('roundabout').degrees)
					+ info.bearing);
	while (rad < 0) {
		rad = rad + Math.PI * 2
	}
	while (rad > Math.PI * 2) {
		rad = rad - Math.PI * 2
	}
	var factors = info.shape(rad, info.focusBearingRad, info.tilt);
	factors.scale = (factors.scale > 1) ? 1 : factors.scale;
	factors.adjustedScale = (info.scale.min + (info.scale.diff * factors.scale))
			.toFixed(4);
	factors.width = (factors.adjustedScale * data.startWidth).toFixed(4);
	factors.height = (factors.adjustedScale * data.startHeight).toFixed(4);
	ref
			.css(
					'left',
					((factors.x * info.midStage.width + info.nudge.width) - factors.width / 2.0)
							.toFixed(1)
							+ 'px')
			.css(
					'top',
					((factors.y * info.midStage.height + info.nudge.height) - factors.height / 2.0)
							.toFixed(1)
							+ 'px').css('width', factors.width + 'px').css(
					'height', factors.height + 'px').css(
					'opacity',
					(info.opacity.min + (info.opacity.diff * factors.scale))
							.toFixed(2)).css(
					'z-index',
					Math.round(info.zValues.min
							+ (info.zValues.diff * factors.z))).css(
					'font-size',
					(factors.adjustedScale * data.startFontSize).toFixed(0.1)
							+ 'px')
			.attr('current-scale', factors.adjustedScale);
	if (container.data('roundabout').debug === true) {
		out
				.push('<div style="font-weight: normal; font-size: 10px; padding: 2px; width: '
						+ ref.css('width') + '; background-color: #ffc;">');
		out.push('<strong style="font-size: 12px; white-space: nowrap;">Child '
				+ childPos + '</strong><br />');
		out.push('<strong>left:</strong> ' + ref.css('left')
				+ '<br /><strong>top:</strong> ' + ref.css('top') + '<br />');
		out.push('<strong>width:</strong> ' + ref.css('width')
				+ '<br /><strong>opacity:</strong> ' + ref.css('opacity')
				+ '<br />');
		out.push('<strong>z-index:</strong> ' + ref.css('z-index')
				+ '<br /><strong>font-size:</strong> ' + ref.css('font-size')
				+ '<br />');
		out.push('<strong>scale:</strong> ' + ref.attr('current-scale'));
		out.push('</div>');
		ref.html(out.join(''))
	}
	return jQuery.roundabout_isInFocus(container,
			ref.data('roundabout').degrees)
};
