﻿function PublicAnimation(id)
{
	Tangora.Browser.GetType();
	this.browser = Tangora.Browser.Name.toLowerCase();
}

function AnimationItem(obj, properties, pool)
{
	this.Run = function () { };
	this.Time = properties.AnimationTime;
	this.Delay = parseInt(properties.Delay, 10);

	if (isNaN(this.Delay))
	{
		this.Delay = 0;
	}

	this.Ended = false;
	this.Pool = pool;
	var that = this;

	switch (properties.Type.toLowerCase())
	{
		case 'opacityfade':
			this.Run = function () { setTimeout(function () { Tangora.PublicAnimation.OpacityFade(obj, that, properties.Easing, properties.StartOpacity, properties.EndOpacity); }, this.Delay); };
			break;

		case 'resize':
			this.Run = function () { setTimeout(function () { Tangora.PublicAnimation.Resize(obj, that, properties.Easing, properties.Width, properties.Height, properties.RelativeToCenter, properties.HideOverflow, properties.WindowRef); }, this.Delay); };
			break;

		case 'zoom':
			this.Run = function () { setTimeout(function () { Tangora.PublicAnimation.Zoom(obj, that, properties.Easing, properties.Start, properties.End, properties.RelativeToCenter, properties.WindowRef); }, this.Delay); };
			break;

		case 'changecolor':
			this.Run = function () { setTimeout(function () { Tangora.PublicAnimation.ChangeColor(obj, that, properties.Easing, properties.ObjectProperty, properties.FromHex, properties.ToHex); }, this.Delay); };
			break;

		case 'changemargin':
			this.Run = function () { setTimeout(function () { Tangora.PublicAnimation.ChangeMargin(obj, that, properties.Easing, properties.Top, properties.Right, properties.Bottom, properties.Left); }, this.Delay); };
			break;

		case 'move':
			this.Run = function () { setTimeout(function () { Tangora.PublicAnimation.Move(obj, that, properties.Easing, properties.LeftStart, properties.LeftEnd, properties.topStart, properties.TopEnd); }, this.Delay); };
			break;

		case 'movetocenter':
			this.Run = function () { setTimeout(function () { Tangora.PublicAnimation.MoveToCenter(obj, that, properties.Easing, properties.Axis, null, properties.Width, properties.Height, properties.WindowRef); }, this.Delay); };
			break;
		default:
			this.Run = function () { };
	}
}

function AnimationPool(id)
{
	this.Animations = [];
	this.Next = function () { };
}

AnimationPool.prototype.AnimationEnded = function ()
{
	for (var i in this.Animations)
	{
		if ($ts.exists(this.Animations[i].Ended) && !this.Animations[i].Ended === true)
		{
			return;
		}
	}

	this.Next();
};

PublicAnimation.prototype.CurrentView = function (windowRef)
{
	if (!$ts.exists(windowRef))
	{
		windowRef = top.window;
	}
	var currentView = {};

	if (window.pageYOffset === undefined) // IE
	{
		if (windowRef.document.documentElement.clientWidth == 0) // Quirksmode
		{
			currentView.Left = windowRef.document.body.scrollLeft;
			currentView.Top = windowRef.document.body.scrollTop;
			currentView.Width = windowRef.document.body.clientWidth;
			currentView.Height = windowRef.document.body.clientHeight;
			currentView.HasScrollbar = windowRef.document.body.clientHeight < windowRef.document.body.offsetHeight;
		}
		else
		{
			currentView.Left = windowRef.document.documentElement.scrollLeft;
			currentView.Top = windowRef.document.documentElement.scrollTop;
			currentView.Width = windowRef.document.documentElement.clientWidth;
			currentView.Height = windowRef.document.documentElement.clientHeight;
			currentView.HasScrollbar = windowRef.document.documentElement.clientHeight < windowRef.document.body.offsetHeight;
		}
	}
	else
	{
		currentView.Left = windowRef.pageXOffset;
		currentView.Top = windowRef.pageYOffset;
		currentView.Width = windowRef.innerWidth;
		currentView.Height = windowRef.innerHeight;
		currentView.HasScrollbar = windowRef.innerHeight < windowRef.document.body.offsetHeight;
	}

	return currentView;
};

PublicAnimation.prototype.WireupElements = function (evt, callback, source)
{
	var elements = [];

	var element = document.getElementById(source);

	if (element !== null)
	{
		elements.push(element);
	}

	if (elements.length === 0)
	{
		elements = document.getElementsByName(source);
	}

	if (elements.length > 0)
	{
		for (var i = 0; i < elements.length; i++)
		{
			$ts.addEvent(elements[i], evt, callback);
		}
	}
	else
	{
		switch (source.toLowerCase())
		{
			case 'window': $ts.addEvent(window, evt, callback); break;
			case 'document': $ts.addEvent(document, evt, callback); break;
			case 'body': $ts.addEvent(body, evt, callback); break;
			default:
		}
	}
};

PublicAnimation.prototype.AddAnimationCapabilities = function (obj)
{
	if ($ts.exists(obj.QueueAnimation))
	{
		return;
	}

	function QueueAnimation(animationItem)
	{
		return obj.QueueAnimations([animationItem]).Animations[0];
	}

	function QueueAnimations(animationItems)
	{
		var animationPool = new AnimationPool();
		var animation = {};
		animationPool.id = obj.id + '_AnimationPool' + new Date().getTime() + obj.AnimationPools.length;

		obj.QueueAnimationPool(animationPool);

		for (var i = 0; i < animationItems.length; i++)
		{
			animation = new AnimationItem(obj, animationItems[i], animationPool);
			animation.id = animationPool.id + '_Animation' + i;
			animationPool.Animations.push(animation);
		}

		return animationPool;
	}

	function QueueAnimationPool(animationPool)
	{
		obj.AnimationPools.push(animationPool);
	}

	function RunAnimations()
	{
		var animationPools = obj.AnimationPools;
		var animationPool;

		function AnimationEnded()
		{
			$ts.Observer.Broadcast(animationPool.id, 'AnimationPoolEnded');					
			ExecuteAnimation();
		}

		function ExecuteAnimation()
		{
			animationPool = animationPools.shift();

			if ($ts.exists(animationPool))
			{
				obj.elapsed = new Date().getDate();
				animationPool.Next = function () { AnimationEnded(); };

				for (var iAnimation = 0; iAnimation < animationPool.Animations.length; iAnimation++)
				{
					animationPool.Animations[iAnimation].Run();
				}
			}
			else
			{
				$ts.Observer.Broadcast(obj.id, 'AnimationsEnded');
			}
		}

		ExecuteAnimation();
	}

	obj.QueueAnimation = QueueAnimation;
	obj.QueueAnimations = QueueAnimations;
	obj.QueueAnimationPool = QueueAnimationPool;
	obj.RunAnimations = RunAnimations;
	obj.AnimationPools = [];
};

PublicAnimation.prototype.OpacityFade = function (obj, animation, easing, startOpacity, endOpacity)
{
	startOpacity = $ts.safeInt(startOpacity);
	endOpacity = $ts.safeInt(endOpacity);
	var fadeIn = startOpacity < endOpacity;
	var opacityDif = fadeIn ? endOpacity - startOpacity : startOpacity - endOpacity;
	var stepDuration = (animation.Time / opacityDif) >> 0;
	var opacity;

	function FadeStep(timeScale)
	{
		opacity = (timeScale * opacityDif) >> 0;
		opacity = fadeIn ? opacity + startOpacity : startOpacity - opacity;

		Tangora.PublicAnimation.SetOpacity(obj, opacity);
	}

	Tangora.PublicAnimation.StartTimer(animation, stepDuration, easing, FadeStep);
};

PublicAnimation.prototype.Resize = function (obj, animation, easing, width, height, relativeToCenter, hideOverflow, windowRef)
{
	var manipulateWidth = $ts.exists(width),
	manipulateHeight = $ts.exists(height),
	originalOverflowSetting = obj.style.overflow,
	widthDiff = 0, heightDiff = 0, startWidth, startHeight, growWidth, growHeight, stepDuration, widthStep, heightStep, setWidth, setHeight;

	if (manipulateWidth)
	{
		startWidth = obj.clientWidth;
		growWidth = width > startWidth;
		widthDiff = growWidth ? width - startWidth : startWidth - width;
	}

	if (manipulateHeight)
	{
		startHeight = obj.clientHeight;
		growHeight = height > startHeight;
		heightDiff = growHeight ? height - startHeight : startHeight - height;
	}

	stepDuration = (animation.Time / Math.max(widthDiff, heightDiff)) >> 0;

	function ResizeStep(timeScale)
	{
		if (manipulateWidth)
		{
			widthStep = (timeScale * widthDiff) >> 0;
			setWidth = growWidth ? widthStep + startWidth : startWidth - widthStep;
			obj.style.width = setWidth + 'px';
		}

		if (manipulateHeight)
		{
			heightStep = (timeScale * heightDiff) >> 0;
			setHeight = growHeight ? heightStep + startHeight : startHeight - heightStep;
			obj.style.height = setHeight + 'px';
		}
		
		if ($ts.exists(hideOverflow) && hideOverflow === true)
		{
			if (timeScale < 1 && obj.style.overflow !== 'hidden')
			{
				obj.style.overflow = 'hidden';
			}
			else if (timeScale >= 1)
			{
				obj.style.overflow = originalOverflowSetting;
			}
		}
		
		if ($ts.exists(relativeToCenter) && relativeToCenter === true && obj.style.position === 'absolute')
		{
			Tangora.PublicAnimation.MoveToCenter(obj, { "Time": 0 }, null, null, null, null, null, null, windowRef);
		}

		$ts.Observer.Broadcast(obj.id, 'ResizeStep');
	}

	Tangora.PublicAnimation.StartTimer(animation, stepDuration, easing, ResizeStep);
};

PublicAnimation.prototype.Zoom = function (obj, animation, easing, startLevel, endLevel, relativeToCenter, windowRef)
{
	var grow = endLevel > startLevel;
	var diff = grow ? endLevel - startLevel : startLevel - endLevel;
	var stepDuration = 30;
	var step, setLevel, firefox = this.browser === 'firefox';
	var currentView = Tangora.PublicAnimation.CurrentView(windowRef);
	var browser = this.browser;

	function ZoomStep(timeScale)
	{
		step = (timeScale * diff);
		setLevel = grow ? step + startLevel : startLevel - step;

		if (firefox)
		{
			if (setLevel === 1)
			{
				obj.style.MozTransform = null;
				obj.style.MozTransformOrigin = null;
			}
			else
			{
				obj.style.MozTransform = 'scale(' + setLevel + ')';
				obj.style.MozTransformOrigin = '0 0';
			}
		}
		else
		{
			obj.style.zoom = setLevel;
		}

		if ($ts.exists(relativeToCenter) && relativeToCenter === true)
		{
			var width = (obj.clientWidth * setLevel),
			height = (obj.clientHeight * setLevel);

			if (obj.style.position === 'absolute')
			{
				var leftOffset = (currentView.Width / 2) + currentView.Left;
				var left = Math.max(Tangora.PublicAnimation.ZoomPosCorrection(obj, Math.round(leftOffset - (width / 2))), 0);
				var topOffset = (currentView.Height / 2) + currentView.Top;
				var top = Math.max(Tangora.PublicAnimation.ZoomPosCorrection(obj, Math.round(topOffset - (height / 2))), 0);
				obj.style.left = left + 'px';
				obj.style.top = top + 'px';
			}
		}

		$ts.Observer.Broadcast(obj.id, 'ZoomStep', setLevel);
	}

	Tangora.PublicAnimation.StartTimer(animation, stepDuration, easing, ZoomStep);
};

PublicAnimation.prototype.ChangeColor = function (obj, animation, easing, property, fromHex, toHex)
{
	function ExtractColor(color, hex)
	{
		var startIndex, endIndex;
		switch (color)
		{
			case 'r': startIndex = 0; break;
			case 'g': startIndex = shortHand ? 1 : 2; break;
			case 'b': startIndex = shortHand ? 2 : 4; break;
		}
		endIndex = shortHand ? startIndex + 1 : startIndex + 2;
		return hex.substring(startIndex, endIndex);
	}

	function DecimalToHex(value)
	{
		var hex = value.toString(16);

		if (hex.length == 1)
		{
			hex = '0' + hex;
		}

		return hex;
	}

	function HexToDecimal(value)
	{
		return parseInt(value, 16);
	}

	fromHex = fromHex.replace('#', '');
	toHex = toHex.replace('#', '');
	var shortHand = fromHex.length === 3;
	var startR = HexToDecimal(ExtractColor('r', fromHex));
	var startG = HexToDecimal(ExtractColor('g', fromHex));
	var startB = HexToDecimal(ExtractColor('b', fromHex));
	shortHand = toHex.length === 3;
	var endR = HexToDecimal(ExtractColor('r', toHex));
	var endG = HexToDecimal(ExtractColor('g', toHex));
	var endB = HexToDecimal(ExtractColor('b', toHex));
	var brightenR = endR > startR;
	var brightenG = endG > startG;
	var brightenB = endB > startB;
	var rDiff = brightenR ? endR - startR : startR - endR;
	var gDiff = brightenG ? endG - startG : startG - endG;
	var bDiff = brightenB ? endB - startB : startB - endB;
	var stepDuration = (animation.Time / Math.max(rDiff, gDiff, bDiff)) >> 0;
	var rStep, gStep, bStep, setR, setG, setB, color;

	function ColorStep(timeScale)
	{
		rStep = (timeScale * rDiff) >> 0;
		gStep = (timeScale * gDiff) >> 0;
		bStep = (timeScale * bDiff) >> 0;
		setR = (brightenR ? rStep + startR : startR - rStep);
		setG = (brightenG ? gStep + startG : startG - gStep);
		setB = (brightenB ? bStep + startB : startB - bStep);
		setR = DecimalToHex(setR);
		setG = DecimalToHex(setG);
		setB = DecimalToHex(setB);

		color = '#' + setR + setG + setB;

		switch (property)
		{
			case 'foreground': obj.style.color = color; break;
			case 'border': obj.style.borderColor = color; break;
			default: obj.style.backgroundColor = color;
		}
	}

	Tangora.PublicAnimation.StartTimer(animation, stepDuration, easing, ColorStep);
};

PublicAnimation.prototype.ChangeMargin = function (obj, animation, easing, top, right, bottom, left)
{
	function Exists(val)
	{
		return $ts.exists(val) && !isNaN(parseInt(val, 10));
	};

	function GetCurrent(margin)
	{
		var margin = parseInt(margin, 10);

		if (isNaN(margin))
		{
			margin = 0;
		}

		return margin;
	};

	var adjustTop = Exists(top), topOrig, topDif,
	adjustRight = Exists(right), rightOrig, rightDif,
	adjustBottom = Exists(bottom), bottomOrig, bottomDif,
	adjustLeft = Exists(left), leftOrig, leftDif;

	if (adjustTop)
	{
		topOrig = GetCurrent(obj.style.marginTop),
		topDif = top - topOrig;
	}

	if (adjustRight)
	{
		rightOrig = GetCurrent(obj.style.marginRight),
		rightDif = right - rightOrig;
	}

	if (adjustBottom)
	{
		bottomOrig = GetCurrent(obj.style.marginBottom),
		bottomDif = bottom - bottomOrig;
	}

	if (adjustLeft)
	{
		leftOrig = GetCurrent(obj.style.marginLeft),
		leftDif = left - leftOrig;
	}

	function SetMargin(timeScale)
	{
		if (adjustTop === true)
		{
			obj.style.marginTop = Math.round(topOrig + (timeScale * topDif)) + 'px';
		}

		if (adjustRight === true)
		{
			obj.style.marginRight = Math.round(rightOrig + (timeScale * rightDif)) + 'px';
		}

		if (adjustBottom === true)
		{
			obj.style.marginBottom = Math.round(bottomOrig + (timeScale * bottomDif)) + 'px';
		}

		if (adjustLeft === true)
		{
			obj.style.marginLeft = Math.round(leftOrig + (timeScale * leftDif)) + 'px';
		}

		if (timeScale >= 1)
		{
			$ts.Observer.Broadcast(obj.id, 'MarginChangeEnded');
		}
	}

	if (animation.Time > 0)
	{
		Tangora.PublicAnimation.StartTimer(animation, (animation.Time / Math.max(topDif, rightDif, bottomDif, leftDif) >> 0), easing, SetMargin);
	}
	else
	{
		SetMargin(1);
	}
};

PublicAnimation.prototype.MoveToCenter = function (obj, animation, easing, axis, body, overrideWidth, overrideHeight, preventLeftOverflow, windowRef)
{
	obj.style.position = 'absolute';

	// Ensure required variables are set.
	if (!$ts.exists(axis)) { axis = ''; }
	if (!$ts.exists(body)) { body = top.window.document.body; }

	var x = false;
	var y = false;

	var leftStart = null;
	var leftEnd = null;
	var topStart = null;
	var topEnd = null;
	var currentView = Tangora.PublicAnimation.CurrentView(windowRef);

	switch (axis.toLowerCase())
	{
		case 'x': x = true; break;
		case 'y': y = true; break;
		default: x = true; y = true;
	}

	if (x)
	{
		var width = obj.clientWidth,
		ie8 = (typeof window.getSelection == 'undefined'),
		xOffset = 0;

		if ($ts.exists(overrideWidth) && overrideWidth > 0)
		{
			width = overrideWidth;
		}

		// Detect scrollbar offset
		xOffset = (currentView.HasScrollbar && !ie8 ? 17 : 0)

		leftStart = obj.offsetLeft;
		leftEnd = ((((currentView.Width - xOffset) / 2)) + currentView.Left) - (width / 2);

		if ($ts.exists(preventLeftOverflow) && preventLeftOverflow === true && leftEnd < 0)
		{
			leftEnd = 0;
		}
	}

	if (y)
	{
		var height = obj.clientHeight;

		if ($ts.exists(overrideHeight) && overrideHeight > 0)
		{
			height = overrideHeight;
		}

		topStart = obj.offsetTop;
		topEnd = ((currentView.Height / 2) + currentView.Top) - (height / 2);

		//Ensures that the close button wont be unreachable
		if (topEnd < currentView.Top + 18)
		{
			topEnd = currentView.Top + 18;
		}
	}

	Tangora.PublicAnimation.Move(obj, animation, easing, leftStart, leftEnd, topStart, topEnd);
};

PublicAnimation.prototype.Move = function (obj, animation, easing, leftStart, leftEnd, topStart, topEnd)
{
	obj.style.position = 'absolute';

	var moveRight, leftDiff, moveDown, topDiff;

	function SetPositionX(newLeftDiff)
	{
		var x = (moveRight ? newLeftDiff + leftStart : leftStart - newLeftDiff);
		obj.style.left = Tangora.PublicAnimation.ZoomPosCorrection(obj, x) + 'px';
	}

	function PositionStepX(timeScale)
	{
		SetPositionX(Math.round(timeScale * leftDiff));
	}

	function SetPositionY(newTopDiff)
	{
		var y = (moveDown ? newTopDiff + topStart : topStart - newTopDiff);
		obj.style.top = Tangora.PublicAnimation.ZoomPosCorrection(obj, y) + 'px';
	}

	function PositionStepY(timeScale)
	{
		SetPositionY(Math.round(timeScale * topDiff));
	}

	var broadcastY = true;

	if ($ts.exists(leftStart) && $ts.exists(leftEnd))
	{
		moveRight = leftEnd > leftStart;
		leftDiff = moveRight ? leftEnd - leftStart : leftStart - leftEnd;

		if (animation.Time > 0)
		{
			var xStepDuration = (animation.Time / Math.max(leftDiff, topDiff)) >> 0;

			Tangora.PublicAnimation.StartTimer(animation, xStepDuration, easing, PositionStepX);

			broadcastY = false;
		}
		else
		{
			SetPositionX(leftDiff);
		}
	}

	if ($ts.exists(topStart) && $ts.exists(topEnd))
	{
		moveDown = topEnd > topStart;
		topDiff = moveDown ? topEnd - topStart : topStart - topEnd;

		if (animation.Time > 0)
		{
			var yStepDuration = (animation.Time / Math.max(leftDiff, topDiff)) >> 0;

			Tangora.PublicAnimation.StartTimer(animation, yStepDuration, easing, PositionStepY, broadcastY);
		}
		else
		{
			SetPositionY(topDiff);
		}
	}

	if (animation.Time === 0)
	{
		animation.Ended = true;
		if ($ts.exists(animation.id))
		{
			$ts.Observer.Broadcast(animation.id, 'AnimationEnded');
		}
		if ($ts.exists(animation.Pool))
		{
			animation.Pool.AnimationEnded();
		}
	}
};

PublicAnimation.prototype.StartTimer = function (animation, stepDuration, easing, intervalCallback, broadcastEnd)
{
	var startTime = (new Date()).getTime();
	var elapsed;
	var relativeTimeFactor;

	if (stepDuration < 50)
	{
		stepDuration = 50;
	}

	function Tick()
	{
		elapsed = (new Date()).getTime() - startTime;

		if (elapsed > animation.Time)
		{
			clearInterval(runInterval);
			intervalCallback(1);

			if (!$ts.exists(broadcastEnd) || broadcastEnd === true)
			{
				animation.Ended = true;
				if ($ts.exists(animation.id))
				{
					$ts.Observer.Broadcast(animation.id, 'AnimationEnded');
				}

				if ($ts.exists(animation.Pool))
				{
					animation.Pool.AnimationEnded();
				}
			}
		}
		else
		{
			relativeTimeFactor = elapsed / animation.Time;

			if ($ts.exists(easing) && easing === true)
			{
				relativeTimeFactor = Tangora.PublicAnimation.Ease(easing.toString().toLowerCase(), relativeTimeFactor * 100) / 100;
			}

			intervalCallback(relativeTimeFactor);
		}
	}

	var runInterval = setInterval(function () { Tick(); }, stepDuration);
};

PublicAnimation.prototype.Ease = function (direction, val)
{
	if (direction === 'in')
	{
		val = 100 - val;
	}

	var log = 50;
	var retval = (Math.log(((val * log) / 100) + 1) / Math.log(log)) * 100;

	if (direction === 'in')
	{
		retval = 100 - retval;
	}

	return Math.max(Math.min(retval, 100), 0);
};

PublicAnimation.prototype.SetOpacity = function (obj, opacity)
{
	obj.style.opacity = opacity * 0.01;
	obj.style.filter = 'alpha(opacity=' + opacity + ')';
};

PublicAnimation.prototype.ZoomPosCorrection = function (obj, val, offset)
{
	if (this.browser === 'safari' && obj.style.zoom)
	{
		val = (val * 100) / (obj.style.zoom * 100);
	}

	return Math.round(val);
};

Tangora.PublicAnimation = new PublicAnimation('PublicAnimation');

