﻿//
// YUI AT communication component hover animation
// 9-4-2008 Michiel Rijkers
//
// Animates message elements in the communication component
// when moused over.
//
// REQUIRES: yahoo-dom-event.js, animation.js
//

Array.prototype.each = function(func) {
	for (var i = 0, len = this.length; i < len; ++i) {
		func(this[i], i);
	}
};

YAHOO.namespace('Wisdom');
YAHOO.Wisdom.Animator = (function() {
	// Aliases ("using").
	var D = YAHOO.util.Dom;
	var E = YAHOO.util.Event;
	var Motion = YAHOO.util.Motion;
	var Easing = YAHOO.util.Easing;
  
	// This type is a wrapper to make an array of animations
	// look and behave somewhat like a single animation.
	var AnimationBatch = function(animationArray) {
		this.push = function(stuff) {
			return animationArray.push(stuff);
		};
		this.animate = function() {
			animationArray.each(function(animation) {
				animation.animate();
			});
		}
	};

	// This type coordinates animations with mouse events.
	var AnimationState = function(itemid, onActivate, onActivateAffectOthers, 
		onDeactivate, onDeactivateAffectOthers, onDeactivateWhenSwitching, states) {
		var self = this;
		var active = false;          
		var autoDeactivateTimer = null;
		var activateTimer = null;

		var cancelAutoDeactivate = function () {
			if (autoDeactivateTimer != null) {
				clearTimeout(autoDeactivateTimer);
				autoDeactivateTimer = null;
			}
		};
		var cancelActivate = function () {
			if (activateTimer != null) {
				clearTimeout(activateTimer);
				activateTimer = null;
			}
		};
		this.over = function() {
			cancelAutoDeactivate();
			if (activateTimer == null) {
				activateTimer = setTimeout(self.activate, self.mouseoverDelay * 1000);
			}
		};
		this.out = function() {
			cancelActivate();
			if (autoDeactivateTimer == null) {
				autoDeactivateTimer = setTimeout(function() {self.deactivate(false);}, self.mouseoutDelay * 1000);
			}
		};
		this.activate = function() {
			cancelAutoDeactivate();
			if (!active) {
				var oneWasActive = false;
				states.each(function(state) { 
					if (state.deactivate(true)) { 
						oneWasActive = true; 
					}
				});
				active = true;
				onActivate();
				if (!oneWasActive) {
					onActivateAffectOthers();
				}
			}
		};
		this.deactivate = function(switching) {
			cancelAutoDeactivate();
			if (active) {
				active = false;
				if (switching) {
					onDeactivateWhenSwitching();
				} else {
					onDeactivate();
					onDeactivateAffectOthers();
				}
				return true;
			}
			return false;
		};
	};

	// Copies properties from the second object to the first,
	// if they are undefined on the first.
	var copyDefaults = function(to, from) {
		for (var prop in from) {
			if (typeof to[prop] == 'undefined') {
				to[prop] = from[prop];
			}
		}
		return to;
	};
  
	// Default values for runtime options. 
	// See below for an explanation of what each does.
	var defaultOptions = {
		fontSizeWhenSmall: 100,
		fontSizeWhenBig: 200,
		duration: 2,
		mouseoverDelay: .3,
		mouseoutDelay: 2,
		easing: Easing.backOut,
		centerPoint: {bias: [1, 1]},
		attraction: [.1, .1],
		offset: [-.1, -.1],
		parentId: 'communicationComponent',
		childClass: 'HomepageMessage'
	};

	var self = {
		options: defaultOptions,

		// Attaches onload and resize handlers to (re-) initialize the hover animations.
		init: function() {
			E.onContentReady(self.options.parentId, self.hoverize);
			E.on(window, 'resize', self.hoverize);
		},

		// Attaches the hover animations.
		hoverize: function() {
			var options = copyDefaults(self.options, self.defaultOptions);

			var items = D.getElementsByClassName(options.childClass);
			if (!items || items.length == 0) {
				return;
			}

			// Clean off any mouseover/out handlers already on the elements.
			E.removeListener(items, 'mouseover');
			E.removeListener(items, 'mouseout');

			var states = [];     
			AnimationState.prototype.mouseoverDelay = options.mouseoverDelay;
			AnimationState.prototype.mouseoutDelay = options.mouseoutDelay;
      
			// Set up the center 'vanishing point' of the animations.
			var centerPoint = options.centerPoint['XY'];
			if (centerPoint) {
				if (options.centerPoint['relative']) {
					centerPoint = [centerPoint[0] * D.getViewportWidth(), centerPoint[1] * D.getViewportHeight()];
				}
			} else {
				// Calculate the center-of-gravity of all items.
				centerPoint = (function () {
					var commonRegion = null;
					items.each(function(item) {
						var itemRegion = D.getRegion(item);
						commonRegion = commonRegion ? commonRegion.union(itemRegion) : itemRegion;
					});
					var bias = options.centerPoint['bias'] || [1, 1];
					return [
						(commonRegion.right + bias[0] * commonRegion.left) / (1 + bias[0]),
						(commonRegion.bottom + bias[1] * commonRegion.top) / (1 + bias[1])
					];
				})();
			}

			// Create animations for items flying away from, or back to, the center of gravity.
			var flyAway = [];
			var flyBack = [];
			items.each(function(item) {
				var itemPos = D.getXY(item);
				var itemPosAway = [
					itemPos[0] + options.attraction[0] * (itemPos[0] - centerPoint[0]),
					itemPos[1] + options.attraction[1] * (itemPos[1] - centerPoint[1])
				];

				flyAway.push(new Motion(item, {points: {to: itemPosAway}}, options.duration, Easing.easeOutStrong));
				flyBack.push(new Motion(item, {points: {to: itemPos}}, options.duration, Easing.easeOutStrong));
			});

			// Set up mouse over/out handlers.
			items.each(function(item, index) {
				var growth = options.fontSizeWhenBig / options.fontSizeWhenSmall;

				var boundingBox = D.getRegion(item);
				var itemWidth = boundingBox.right - boundingBox.left;
				var itemHeight = boundingBox.bottom - boundingBox.top;
				var itemPos = [boundingBox.left, boundingBox.top];
				var itemPosWhenBig = [
					itemPos[0] + options.offset[0] * (itemPos[0] - centerPoint[0]) - itemWidth / 2, 
					itemPos[1] + options.offset[1] * (itemPos[1] - centerPoint[1]) - itemHeight / 2
				];
				var itemPosAway = [
					itemPos[0] + options.attraction[0] * (itemPos[0] - centerPoint[0]),
					itemPos[1] + options.attraction[1] * (itemPos[1] - centerPoint[1])
				];

				var growAnimation = new AnimationBatch([
					new Motion(item, {
						fontSize: {to: options.fontSizeWhenBig, from: options.fontSizeWhenSmall, unit: '%'},
						width: {to: itemWidth * growth, from: itemWidth},
//						height: {to: itemHeight * growth, from: itemHeight},
						points: {to: itemPosWhenBig}
					}, options.duration, options.easing)
				]);

				var shrinkAnimation = new AnimationBatch([
					new Motion(item, {
						fontSize: {to: options.fontSizeWhenSmall, from: options.fontSizeWhenBig, unit: '%'},
						width: {to: itemWidth, from: itemWidth * growth},
//						height: {to: itemHeight, from: itemHeight * growth},
						points: {to: itemPos}
					}, options.duration, options.easing)
				]);

				var shrinkToAwayPosAnimation = new AnimationBatch([
					new Motion(item, {
						fontSize: {to: options.fontSizeWhenSmall, from: options.fontSizeWhenBig, unit: '%'},
						width: {to: itemWidth, from: itemWidth * growth},
//						height: {to: itemHeight, from: itemHeight * growth},
						points: {to: itemPosAway}
					}, options.duration, options.easing)
				]);


				var flyAwayAnimation = new AnimationBatch([]);
				flyAway.each(function(animation, animationIndex) { 
					if (index != animationIndex) {
						flyAwayAnimation.push(animation);
					}          
				});

				var flyBackAnimation = new AnimationBatch([]);
				flyBack.each(function(animation, animationIndex) { 
					if (index != animationIndex) {
						flyBackAnimation.push(animation);
					}          
				});

				// Get and animate the flash header, if it's there.
				var headerObject = D.getElementsByClassName('sIFR-flash', 'object', item);
				if (headerObject) {
					var headerHeight = parseInt(D.getStyle(headerObject, 'height'));
					var headerWidth = parseInt(D.getStyle(headerObject, 'width'));

					growAnimation.push(new Motion(headerObject, {
							width: {to: headerWidth * growth, from: headerWidth},
							height: {to: headerHeight * growth, from: headerHeight}
						}, options.duration, options.easing)
					);
						
					shrinkAnimation.push(new Motion(headerObject, {
							width: {to: headerWidth, from: headerWidth * growth},
							height: {to: headerHeight, from: headerHeight * growth}
						}, options.duration, options.easing)
					);
					shrinkToAwayPosAnimation.push(new Motion(headerObject, {
							width: {to: headerWidth, from: headerWidth * growth},
							height: {to: headerHeight, from: headerHeight * growth}
						}, options.duration, options.easing)
					);
				}

				var state = new AnimationState(item.id, growAnimation.animate, flyAwayAnimation.animate,
					shrinkAnimation.animate, flyBackAnimation.animate, shrinkToAwayPosAnimation.animate, states);
				states.push(state);

				// Finally, hook up the animation to mouse events.
				E.on(item, 'mouseover', state.over);
				E.on(item, 'mouseout', state.out);
			});
		}
	}
	return self;
})();

//YAHOO.Wisdom.Animator.init();