﻿//---------------------------------------- ANIMATIONS --------------------------------------------------//
//Controls to benefit are - tooltip, window, dock, colorpicker
//Animations combine two animations [open/close] using the same set of data.
//Properties that are needed to provide information show and hide animations
//startBounds, endBounds, Sunny.Web.UI.ToolTipPosition position!-> will probably need to be made common for all controls.

//Window can be moved from original location, their opener can be moved, so calculating exact values to use can requier more actions to get the correct values
//Close animations assume the animated element's size and position
Type.registerNamespace("Sunny.Web.UI.Animations");

Sunny.Web.UI.Animations.playJQueryAnimation = function(animatedElement, animationType, startBounds, endBounds, position, onAnimationStart, onAnimationEnd)
{
	//If no animated element, do nothing
	if (!animatedElement) return;

	//Set animation type
	if (!animationType) animationType = 2; //Fade
	//Resize: 1,
	//Fade : 2,
	//Slide : 4,
	//FlyIn : 8

	//If no startBounds, provide default
	if (!startBounds) startBounds = new Sys.UI.Bounds(1, 1, 1, 1);
	if (!endBounds) endBounds = new Sys.UI.Bounds(1, 1, 1, 1);

	//If no position, provide default!
	if (!position) position = 32; //Sunny.Web.UI.ToolTipPosition.BottomCenter;
	//Make sure it is a string
	position += "";

	//Determine horizontal and vertical positions
	var verPosition = parseInt(position.substr(0, 1));
	var horPosition = parseInt(position.substr(1, 1));

	//Execute the animation start
	if (onAnimationStart) onAnimationStart();

	//stop(clearQueue, gotoEnd)
	//clearQueue (Boolean): A Boolean (true/false) that when set to true clears the animation queue, effectively stopping all queued animations
	//gotoEnd (Boolean): A Boolean (true/false) that when set to true causes the currently playing animation to immediately complete,
	//including resetting original styles on show and hide and calling the callback function
	$Sunny.$(animatedElement).stop(false, true);

	if (animationType == 2) //Fade animation
	{
		$Sunny.$(animatedElement).css({
			"left": endBounds.x,
			"top": endBounds.y
		}).fadeIn(500, onAnimationEnd);

		//return to prevent the FlyIn, Slide and Resize from executing
		return;
	}

	//FlyIn, Slide and Resize animations here
	if (animationType == 8) //FlyIn animation
	{
		var browserBounds = $Sunny.getClientBounds();
		var clientBounds = $Sunny.getClientBounds();
		startBounds.x = clientBounds.width / 2;
		startBounds.y = clientBounds.height;

		switch (horPosition)
		{
			case 2: //Center
				startBounds.x = endBounds.x;
				break;
			case 3: //Right
				startBounds.x = browserBounds.width;
				break;
			case 1: //Left
				startBounds.x = browserBounds.x;
		}

		switch (verPosition)
		{
			case 2: //Middle
				startBounds.y = endBounds.y;
				break;
			case 1: //Top
				startBounds.y = browserBounds.y - endBounds.height;
				break;
			case 3: //Bottom
				startBounds.y = browserBounds.height;
		}
	}
	else if (animationType == 4) //Slide animation
	{
		startBounds.x = endBounds.x;
		startBounds.y = endBounds.y;
		startBounds.width = endBounds.width;
		startBounds.height = 1;

		switch (horPosition)
		{
			case 2: //Center
				startBounds.x = endBounds.x;
				break;
			case 3: //Right
				startBounds.x = endBounds.x;
				break;
			case 1: //Left
				var startX1 = endBounds.x;

				if (2 == verPosition) //Only if vertical horPosition is middle!
					startX1 += endBounds.width;
				startBounds.x = startX1;
		}

		switch (verPosition)
		{
			case 2: //Middle
				startBounds.y = endBounds.y;
				startBounds.height = endBounds.height;
				startBounds.width = 1;
				break;
			case 1: //Top
				startBounds.y = endBounds.y + endBounds.height;
				break;
			case 3: //Bottom
				startBounds.y = endBounds.y;
		}

	}
	else if (animationType == 1) //Resize animation
	{
		//Do nothing, just use the start and end bounds
	}

	$Sunny.$(animatedElement).css({
		"width": startBounds.width,
		"height": startBounds.height,
		"left": startBounds.x,
		"top": startBounds.y,
		"opacity": 0.1,
		"filter": "alpha(opacity=10)"
	}).show().animate({
		width: endBounds.width,
		height: endBounds.height,
		left: endBounds.x,
		top: endBounds.y,
		opacity: 1
	}, 500, null, onAnimationEnd);
}

// Overwrite the step method, used internally by the animate method, in order to implement pause and resume functionality.
// The jMove animation, used by the RadRotator control, takes advantage of this modification.
// The change to the method applies only to elements, that have the "paused" attribute and that animate the "left" or "top" property.
$Sunny.$.fx.prototype.oldstep = $Sunny.$.fx.prototype.step;
$Sunny.$.fx.prototype.step = function(gotoEnd)
{
	if (this.prop == "left" || this.prop == "top")
	{
		if (this.elem.getAttribute("paused"))
		{
			if (!this.elem.getAttribute("elapsedTime"))
			{
				var elapsedTime = (+new Date) - this.startTime;
				this.elem.setAttribute("elapsedTime", elapsedTime);
			}
			return true;
		}

		if (this.elem.getAttribute("elapsedTime"))
		{
			this.startTime = (+new Date) - this.elem.getAttribute("elapsedTime");
			this.elem.removeAttribute("elapsedTime");
		}
	}

	return this.oldstep(gotoEnd);
};

Sunny.Web.UI.Animations.jMove = function(owner, element, duration, horizontal, vertical)
{
	this._owner = owner;
	this._element = element;
	this._duration = duration;
	this._horizontal = (typeof (horizontal) == "undefined" || horizontal == null) ? 0 : horizontal;
	this._vertical = (typeof (vertical) == "undefined" || vertical == null) ? 0 : vertical;
	this._events = null;
	this._animationEndedDelegate = null;

	this._isPlaying = false;
	this._isPaused = false;
	this._isCyclic = false;
}

Sunny.Web.UI.Animations.jMove.prototype =
{
	initialize: function()
	{
		this._animationEndedDelegate = Function.createDelegate(this, this._animationEnded);
	},

	dispose: function()
	{
		this._getAnimationQuery().stop(true, false);

		this._owner = null;
		this._element = null;
		this._events = null;
		this._animationEndedDelegate = null;
	},

	// Properties
	get_vertical: function()
	{
		return this._vertical;
	},

	set_vertical: function(value)
	{
		this._vertical = value;
	},

	get_horizontal: function()
	{
		return this._horizontal;
	},

	set_horizontal: function(value)
	{
		this._horizontal = value;
	},

	get_isPlaying: function()
	{
		return this._isPlaying;
	},

	get_isCyclic: function()
	{
		return this._isCyclic;
	},

	set_isCyclic: function(value)
	{
		this._isCyclic = value;
	},

	// TODO: implement or remove this property if not needed.
	get_isActive: function()
	{
		return true;
	},

	get_events: function()
	{
		if (!this._events)
		{
			this._events = new Sys.EventHandlerList();
		}

		return this._events;
	},

	// Public methods

	// TODO: Rework the play method not to use the _setAnimationTimeout internal method of the RadRotator. Currently, we use it
	// so that the animation does not start immediately when the mouse leaves the RadRotator in the case AutomaticAdvance.
	play: function(timeoutPassed)
	{
		var elem = this._element;
		var pausedAttribute = elem.getAttribute("paused");
		elem.removeAttribute("paused");

		// In case the element has both the 'paused' and 'elapsedTime' attributes, the animation is paused while it was playing
		// and we only have to remove the 'paused' attribute in order for it to continue.
		if (!(pausedAttribute && elem.getAttribute("elapsedTime")))
		{
			var owner = this._owner;
			var timeout = owner.get_frameDuration();

			// In case a cyclic animation is paused between two calls of the "play" method, we have to call the "play" method again.
			if (this._isPaused && this._isCyclic && (timeout > 0 && !timeoutPassed) && owner._setAnimationTimeout)
			{
				// Call the "play" method with timeout.
				owner._setAnimationTimeout(timeout);
			}
			// Call the "play" method without timeout.
			else
			{
				var shouldContinue = this._animationStarted();
				if (shouldContinue != false)
				{
					var finalPosition = (isNaN(parseInt(this._vertical))) ? this._horizontal : this._vertical;
					this._playAnimation(finalPosition);
				}
			}
		}

		this._isPlaying = true;
		this._isPaused = false;
	},

	stop: function()
	{
		//Stop the current Animation and force the animated elements to their final positions.
		this._getAnimationQuery().stop(false, true);
		this._isPlaying = false;
	},

	pause: function()
	{
		if (this._isPlaying)
			this._element.setAttribute("paused", true);

		this._isPlaying = false;
		this._isPaused = true;
	},

	// Events
	add_started: function(handler)
	{
		this.get_events().addHandler("started", handler);
	},

	remove_started: function(handler)
	{
		this.get_events().removeHandler("started", handler);
	},

	add_ended: function(handler)
	{
		this.get_events().addHandler("ended", handler);
	},

	remove_ended: function(handler)
	{
		this.get_events().removeHandler("ended", handler);
	},

	// Private methods
	_getAnimationQuery: function()
	{
		return $Sunny.$(this._element);
	},

	_playAnimation: function(finalPosition)
	{
		var query = this._getAnimationQuery();
		var animatedProperty = this._getAnimatedStyleProperty();

		var params = { queue: true };
		params[animatedProperty] = finalPosition;

		// In case the animation calls its play method as soon as it ends (in its ended event handler), the second parameter
		// of the stop method must be false. Otherwise, calling stop would raise the ended event.
		query.stop(true, !this._isCyclic).animate(params,
			this._duration,
			null,
			this._animationEndedDelegate
		);
	},

	_getAnimatedStyleProperty: function()
	{
		return (isNaN(parseInt(this._vertical))) ? "left" : "top";
	},

	_getPosition: function()
	{
		var element = this._element;
		var animatedProperty = this._getAnimatedStyleProperty();

		return element.style[animatedProperty];
	},

	_animationStarted: function()
	{
		var args = new Sys.CancelEventArgs();
		this._raiseEvent("started", args);
		return !args.get_cancel();
	},

	_animationEnded: function()
	{
		// In case we use this animation, together with Pulse (FadeIn, FadeOut), at this point, the opacity may still be 0.
		this._getAnimationQuery().css("opacity", "1");

		this._isPlaying = false;
		this._raiseEvent("ended", Sys.EventArgs.Empty);
	},

	_raiseEvent: function(eventName, eventArgs)
	{
		var handler = this.get_events().getHandler(eventName);

		if (handler)
		{
			if (!eventArgs)
			{
				eventArgs = Sys.EventArgs.Empty;
			}

			handler(this, eventArgs);
		}
	}
}

Sunny.Web.UI.Animations.jMove.registerClass("Sunny.Web.UI.Animations.jMove", null, Sys.IDisposable);
if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();