/**
 * Copyright (c) 2006-2015, JGraph Holdings Ltd
 * Copyright (c) 2006-2015, draw.io AG
 */
var mxEffects =
{

	/**
	 * Class: mxEffects
	 * 
	 * Provides animation effects.
	 */

	/**
	 * Function: animateChanges
	 * 
	 * Asynchronous animated move operation. See also: <mxMorphing>.
	 * 
	 * Example:
	 * 
	 * (code)
	 * graph.model.addListener(mxEvent.CHANGE, function(sender, evt)
	 * {
	 *   var changes = evt.getProperty('edit').changes;
	 * 
	 *   if (changes.length < 10)
	 *   {
	 *     mxEffects.animateChanges(graph, changes);
	 *   }
	 * });
	 * (end)
	 * 
	 * Parameters:
	 * 
	 * graph - <mxGraph> that received the changes.
	 * changes - Array of changes to be animated.
	 * done - Optional function argument that is invoked after the
	 * last step of the animation.
	 */
	animateChanges: function(graph, changes, done)
	{
		var maxStep = 10;
		var step = 0;

		var animate = function() 
		{
			var isRequired = false;
			
			for (var i = 0; i < changes.length; i++)
			{
				var change = changes[i];
				
				if (change instanceof mxGeometryChange ||
					change instanceof mxTerminalChange ||
					change instanceof mxValueChange ||
					change instanceof mxChildChange ||
					change instanceof mxStyleChange)
				{
					var state = graph.getView().getState(change.cell || change.child, false);
					
					if (state != null)
					{
						isRequired = true;
					
						if (change.constructor != mxGeometryChange || graph.model.isEdge(change.cell))
						{
							mxUtils.setOpacity(state.shape.node, 100 * step / maxStep);
						}
						else
						{
							var scale = graph.getView().scale;					

							var dx = (change.geometry.x - change.previous.x) * scale;
							var dy = (change.geometry.y - change.previous.y) * scale;
							
							var sx = (change.geometry.width - change.previous.width) * scale;
							var sy = (change.geometry.height - change.previous.height) * scale;
							
							if (step == 0)
							{
								state.x -= dx;
								state.y -= dy;
								state.width -= sx;
								state.height -= sy;
							}
							else
							{
								state.x += dx / maxStep;
								state.y += dy / maxStep;
								state.width += sx / maxStep;
								state.height += sy / maxStep;
							}
							
							graph.cellRenderer.redraw(state);
							
							// Fades all connected edges and children
							mxEffects.cascadeOpacity(graph, change.cell, 100 * step / maxStep);
						}
					}
				}
			}

			if (step < maxStep && isRequired)
			{
				step++;
				window.setTimeout(animate, delay);
			}
			else if (done != null)
			{
				done();
			}
		};
		
		var delay = 30;
		animate();
	},
    
	/**
	 * Function: cascadeOpacity
	 * 
	 * Sets the opacity on the given cell and its descendants.
	 * 
	 * Parameters:
	 * 
	 * graph - <mxGraph> that contains the cells.
	 * cell - <mxCell> to set the opacity for.
	 * opacity - New value for the opacity in %.
	 */
    cascadeOpacity: function(graph, cell, opacity)
	{
		// Fades all children
		var childCount = graph.model.getChildCount(cell);
		
		for (var i=0; i<childCount; i++)
		{
			var child = graph.model.getChildAt(cell, i);
			var childState = graph.getView().getState(child);
			
			if (childState != null)
			{
				mxUtils.setOpacity(childState.shape.node, opacity);
				mxEffects.cascadeOpacity(graph, child, opacity);
			}
		}
		
		// Fades all connected edges
		var edges = graph.model.getEdges(cell);
		
		if (edges != null)
		{
			for (var i=0; i<edges.length; i++)
			{
				var edgeState = graph.getView().getState(edges[i]);
				
				if (edgeState != null)
				{
					mxUtils.setOpacity(edgeState.shape.node, opacity);
				}
			}
		}
	},

	/**
	 * Function: fadeOut
	 * 
	 * Asynchronous fade-out operation.
	 */
	fadeOut: function(node, from, remove, step, delay, isEnabled)
	{
		step = step || 40;
		delay = delay || 30;
		
		var opacity = from || 100;
		
		mxUtils.setOpacity(node, opacity);
		
		if (isEnabled || isEnabled == null)
		{
			var f = function()
			{
			    opacity = Math.max(opacity-step, 0);
				mxUtils.setOpacity(node, opacity);
				
				if (opacity > 0)
				{
					window.setTimeout(f, delay);
				}
				else
				{
					node.style.visibility = 'hidden';
					
					if (remove && node.parentNode)
					{
						node.parentNode.removeChild(node);
					}
				}
			};
			window.setTimeout(f, delay);
		}
		else
		{
			node.style.visibility = 'hidden';
			
			if (remove && node.parentNode)
			{
				node.parentNode.removeChild(node);
			}
		}
	}

};
