/**
 * clipTrans library for WebDDM: aids in wiping/clipping transition
 * effects for menus.
 *
 * @version: 0.3
 * @started: 12/11/2004
 * @copyright: Copyright (c) 2004, 2005 JPortal, All Rights Reserved
 * @website: www.jportalhome.com
 * @license: GPL vs2.0
 * @subversion: $Id: clipTrans.WebDDM.js 126 2006-01-22 07:09:05Z josh $
 */
 
// Register clipTrans transition plugin
WebDDM_registerPlugin_transition('clipTrans');

// Hash of clipAPI object for the clipTrans. Each item container
// that slides has its own clipAPI object.
WebDDM.prototype.clipperObjects = new Hash();

/**
 * The clip transition system. Uses clip:rect(...,...,...,...) to create
 * the effect of sliding submenus.
 */
WebDDM.prototype.clipTrans = function (itemsHash, itemsId, visibility)
{
	// Get transition data - WebDDM automatically checks if it's available before
	// calling transition functions, so no need to check here
	var transitionData = itemsHash.get('clipTrans');

	// Initialize the clipper if needed
	var clipper;
	if (!this.clipperObjects.has(itemsId))
	{
		// Create new clipper
		clipper = new clipAPI(itemsId, this);

		// Save clipper
		this.clipperObjects.set(itemsId, clipper);
	}
	else
	{
		// Get clipper
		clipper = this.clipperObjects.get(itemsId);
	}

	// Set clipper configuration
	clipper.duration = transitionData.get('duration');
	clipper.frames = transitionData.get('frames');

	// Set start alpha and stop alpha
	if (visibility)
	{
		// Get new transition
		var transition;
		if (transitionData.has('transitionIn'))
		{
			transition = transitionData.get('transitionIn');
		}
		else if (transitionData.has('transition'))
		{
			transition = transitionData.get('transition');
		}
		else
		{
			transition = 'none';
		}
		
		// Set new transition
		clipper.transition = transition;
		
		// Set direction
		clipper.setDirection('in');
	}
	else
	{
		// Get new transition
		var transition;
		if (transitionData.has('transitionOut'))
		{
			transition = transitionData.get('transitionOut');
		}
		else if (transitionData.has('transition'))
		{
			transition = transitionData.get('transition');
		}
		else
		{
			transition = 'none';
		}
		
		// Set new transition
		clipper.transition = transition;
		
		// Set direction
		clipper.setDirection('out');
	}

	// Start fading
	clipper.start();
};


/**
 * clipAPI - Made for WebDDM for sliding/clipping submenus
 */
function clipAPI (elId, parent)
{
	// Get the element that the element ID corresponds to
	this.element = WebDDM_getElement(elId);
	this.elementId = elId;
	// Save "parent" WebDDM object
	this.parent = parent;

	// Set default properties
	// Duration (in milliseconds) of effect. Must be set before start() is called.
	this.duration = 500;
	// Number of frames total; must be set before start() is called
	this.frames = 10;
	// Current frame #
	this.currentFrame = false;
	// Transition name.
	this.transition = 'none';
	// Direction of transition, in or out. Must be set before start() is called.
	this.direction = '';
	// Variable that holds the domLib_setTimeout result
	this.clipTimer = false;
	// Starting clip object
	this.startClip = false;
	// Object of what the clip WILL be after the current transition is done
	//this.targetClip = false;
	this.targetClip = false;
	// Object of current clip coordinates
	this.currentClip = false;
	// Starting position object
	this.startPos = false;
	// Object of what the position WILL be after the current transition is done
	this.targetPos = false;
	// Object of the current position coordinates
	this.currentPos = false;
	// Last transition (transitionName+direction) completed
	this.lastTransCompleted = '';

	// Original x/y position of the element (used for sliding)
	this.originalPos =
	{
		'left': parseInt(this.element.offsetLeft),
		'top': parseInt(this.element.offsetTop)
	};	
	
	return this;
}


/**
 * Starts the clip sliding effect.
 */
clipAPI.prototype.start = function ()
{
	// Kill any running timers
	domLib_clearTimeout(this.clipTimer);

	// If transition is set to none or the last transition
	// completed is the same transition we're doing now,
	// just make the element visible/hidden
	if (this.transition == 'none' ||
		this.lastTransCompleted == (this.direction + this.transition))
	{
		this.element.style.visibility = (this.direction == 'in'
			? 'visible'
			: 'hidden');
		return;
	}
	
	// Get element height/width
	var elHeight = parseInt(this.element.offsetHeight);
	var elWidth = parseInt(this.element.offsetWidth);
	
	// Get direction
	// Are we transitioning in (showing)?
	if (this.direction == 'in')
	{
		// Get start clip offsets and positions if this is the first transition or
		// a completely new transition.
		if (!this.startClip || !this.clipTimer || (this.startClip == this.targetClip))
		{
			// Get initial clip offsets and positions
			this.startClip = {};
			this.startPos = {'top': this.originalPos.top, 'left': this.originalPos.left};
			this.initStartProperties('vertical', 'horizontal');
			this.initStartProperties('horizontal', 'vertical');
		}

		// Initialize current clip offset properties
		this.currentClip = WebDDM_mergeArrays({}, this.startClip);

		// Set the target clip to full
		this.targetClip =
		{
			'top': 0,
			'right': elWidth,
			'bottom': elHeight,
			'left': 0
		};
		
		// Set the target position to the original position
		this.targetPos = this.originalPos;
		
		// Make sure element is visible
		this.element.style.visibility = 'visible';

		// Set initial positions
		this.currentPos = WebDDM_mergeArrays({}, this.startPos);
	}
	// We are transitioning out (hiding)
	else
	{
		// Are we sliding?
		var sliding = this.transition.match('slide') ? true : false;
		
		// Get target clip positions and positions
		var topClipPos, bottomClipPos, leftClipPos, rightClipPos;
		var topPos = this.startPos.top;
		var leftPos = this.startPos.left;

		// Get initial vertical clip offsets and positions
		if (this.transition.match(/north(.*?)\-to\-south(.*?)/))
		{
			topClipPos = this.startClip.bottom;
			bottomClipPos = this.startClip.bottom;
			if (sliding)
			{
				topPos = this.currentPos.top - (this.currentClip.bottom - this.currentClip.top);
			}
		}
		else if (this.transition.match(/south(.*?)\-to\-north(.*?)/))
		{
			topClipPos = this.startClip.top;
			bottomClipPos = this.startClip.top;
			if (sliding)
			{
				topPos = this.currentPos.top + (this.currentClip.bottom - this.currentClip.top);
			}
		}
		else if (this.transition.match('center-to-edges'))
		{
			topClipPos = elHeight / 2;
			bottomClipPos = elHeight / 2;
		}
		else if (this.transition.match('center-to-horizontal-edges'))
		{
			topClipPos = elHeight / 2;
			bottomClipPos = elHeight / 2;
		}
		else
		{
			topClipPos = this.startClip.top;
			bottomClipPos = this.startClip.bottom;
		}
		
		// Get initial horizontal clip offsets and positions
		if (this.transition.match(/(.*?)west\-to\-(.*?)east/))
		{
			leftClipPos = this.startClip.right;
			rightClipPos = this.startClip.right;
			if (sliding)
			{
				leftPos = this.currentPos.left - (this.currentClip.right - this.currentClip.left);
			}
		}
		else if (this.transition.match(/(.*?)east\-to\-(.*?)west/))
		{
			leftClipPos = this.startClip.left;
			rightClipPos = this.startClip.left;
			if (sliding)
			{
				leftPos = this.currentPos.left + (this.currentClip.right - this.currentClip.left);
			}
		}
		else if (this.transition.match('center-to-edges'))
		{
			leftClipPos = elWidth / 2;
			rightClipPos = elWidth / 2;
		}
		else if (this.transition.match('center-to-vertical-edges'))
		{
			leftClipPos = elWidth / 2;
			rightClipPos = elWidth / 2;
		}
		else
		{
			leftClipPos = this.startClip.left;
			rightClipPos = this.startClip.right;
		}

		// Save the starting clip offsets
		this.startClip = WebDDM_mergeArrays({}, this.currentClip);

		// Set the target clip offsets
		this.targetClip =
		{
			'top': topClipPos,
			'right': rightClipPos,
			'bottom': bottomClipPos,
			'left': leftClipPos
		};

		// Save the starting position
		this.startPos =
		{
			'top': this.currentPos.top,
			'left': this.currentPos.left
		};
		
		// Set the target positions
		this.targetPos =
		{
			'top': topPos,
			'left': leftPos
		};
	}

	// Slide
	this.slide();
};

/**
 * Initialize starting clip offsets and positions. The orientation argument
 * should be a string, either "horizontal" or "vertical".
 */
clipAPI.prototype.initStartProperties = function (orientation, oppositeOrientation)
{
	// Get the two opposite cardinal directions
	var directions = (orientation == 'horizontal'
		? ['(.*?)east', '(.*?)west']
		: ['north(.*?)', 'south(.*?)']);

	// The two positions we need to set, one "high" and the other low.
	var highPos = (orientation == 'horizontal' ? 'left' : 'top');
	var lowPos = (orientation == 'horizontal' ? 'right' : 'bottom');

	// Get element side size: either height or width
	var sideSize = parseInt(orientation == 'horizontal' ? this.element.offsetWidth : this.element.offsetHeight);

	// Get initial offsets and initial start position if we're sliding
	// Top-to-bottom transition (example: north-to-south)?
	if (this.transition.match(new RegExp(directions[0] + '\\-to\\-' + directions[1])))
	{
		this.startClip[highPos] = this.startClip[lowPos] = 0;
		if (this.transition.match('slide'))
		{
			this.startPos[highPos] = this.originalPos[highPos] + sideSize;
		}
	}
	// Bottom-to-top transition (example: south-to-north)?
	else if (this.transition.match(new RegExp(directions[1] + '\\-to\\-' + directions[0])))
	{
		this.startClip[highPos] = this.startClip[lowPos] = sideSize;
		if (this.transition.match('slide'))
		{
			this.startPos[highPos] = this.originalPos[highPos] - sideSize;
		}
	}
	// Slide from the middle of the element to the sides
	else if (this.transition.match(new RegExp('center\\-to\\-('+oppositeOrientation+'\\-)?edges')))
	{
		this.startClip[highPos] = this.startClip[lowPos] = sideSize / 2;
	}
	// No transition for this orientation
	else
	{
		this.startClip[highPos] = 0;
		this.startClip[lowPos] = sideSize;
	}
};

/**
 * Slide method - slides element a little and then recurses.
 */
clipAPI.prototype.slide = function ()
{
	// Reset the timeout ID
	this.clipTimer = false;

	// Get timeout time
	var timeoutTime = this.duration / this.frames;

	// Update clip offset properties
	//this.currentClip = {};
	var positions = ['top', 'right', 'bottom', 'left'];
	for (var i = 0; i < positions.length; i++)
	{
		var pos = positions[i];

		// Decrement position
		if (this.currentClip[pos] > this.targetClip[pos])
		{
			var delta = (this.startClip[pos] - this.targetClip[pos]) / this.frames;
			this.currentClip[pos] = this.startClip[pos] - (delta * this.currentFrame);
			
			// Correct clip pos (don't go too far)
			if (this.currentClip[pos] < this.targetClip[pos])
			{
				this.currentClip[pos] = this.targetClip[pos];
			}
		}
		// Increment position
		else if (this.currentClip[pos] < this.targetClip[pos])
		{
			var delta = (this.targetClip[pos] - this.startClip[pos]) / this.frames;
			this.currentClip[pos] = this.startClip[pos] + (delta * this.currentFrame);
			
			// Correct clip pos (don't go too far)
			if (this.currentClip[pos] > this.targetClip[pos])
			{
				this.currentClip[pos] = this.targetClip[pos];
			}
		}
	}
	
	// Update position properties
	var positions = ['top', 'left'];
	for (var i in positions)
	{
		// Get position name
		var pos = positions[i];

		// Is the current position above the target position?
		if (this.currentPos[pos] > this.targetPos[pos])
		{
			// Slide more
			var delta = (this.startPos[pos] - this.targetPos[pos]) / this.frames;
			this.currentPos[pos] = this.startPos[pos] - parseInt(delta * this.currentFrame);

			// Should we jump to target?
			if (this.currentPos[pos] < this.targetPos[pos])
			{
				// Jump to target
				this.currentPos[pos] = this.targetPos[pos];
			}
		}
		// Is the target position above the current position?
		else if (this.currentPos[pos] < this.targetPos[pos])
		{
			// Slide more
			var delta = (this.targetPos[pos] - this.startPos[pos]) / this.frames;
			this.currentPos[pos] = this.startPos[pos] + parseInt(delta * this.currentFrame);
			
			// Should we jump to target?
			if (this.currentPos[pos] > this.targetPos[pos])
			{
				// Jump to target
				this.currentPos[pos] = this.targetPos[pos];
			}
		}

		// Set element's style position
		this.element.style[pos] = this.currentPos[pos] + 'px';
	}

	// Set clip offsets in CSS
	this.setClip();

	// Scroll again
	if (this.currentFrame < this.frames)
	{
		// Do a timeout
		var instance = this;
		this.clipTimer = domLib_setTimeout(function ()
		{
			instance.slide();
		}, timeoutTime);
		
		// Increment frame counter
		this.currentFrame++;

		// Reset last transition completed identifier (since nothing is complete now!)
		this.lastTransCompleted = '';
	}
	else
	{
		// Set string identifier of last transition completed
		this.lastTransCompleted = this.direction + this.transition;
		this.element.style.visibility = (this.direction == 'in'
			? 'visible'
			: 'hidden');
		// Send transition done message to parent WebDDM object
		this.parent.transitionCompleted(this.element, (this.direction == 'out' ? false : true), this.elementId);
	}
};


/**
 * Set clip property of element.
 */
clipAPI.prototype.setClip = function ()
{
	this.element.style.clip = 'rect('+this.currentClip.top+'px,'+this.currentClip.right+'px,'+this.currentClip.bottom+'px,'+this.currentClip.left+'px)';
};


/**
 * Sets direction of sliding. It's good to use this so sliding is
 * smooth and frames don't jump.
 */
clipAPI.prototype.setDirection = function (dir)
{
	// Kill any running timers
	domLib_clearTimeout(this.clipTimer);

	// Reset properties to continue the transition
	this.startPos = this.targetPos = this.currentPos;
	this.startClip = this.currentClip;
	this.currentFrame = 0;
	this.direction = dir;
};
