if (!Zapatec)
	var Zapatec = {}

/**
 * Zapatec.Effect hierarchy contains functions for working with visual effects.
 * These are called to progressively style the DOM elements as menus show
 * and hide. They do not have to set item visibility, but may want to set DOM
 * properties like clipping, opacity and position to create custom effects.
 *
 * @param ref [HTMLElement] -- target DOM element.
 * @param counter [number] -- an animation progress value, from 0 (start) to 100 (end).
 */

Zapatec.Effect = []

/**
 * \internal Sometimes is useful to execute some action for elements and all 
 * his childs.
 *
 * @param ref [HTMLElement] -- target DOM element.
 * @param funcToDo [function] -- function, that would be applied to ref.
 */

Zapatec.Effect.applyFunc = function(ref, funcToDo) {
	funcToDo(ref);

	for(var i = 0; i < ref.childNodes.length; i++) {
		Zapatec.Effect.applyFunc(ref.childNodes[i], funcToDo);
	}
}

/**
 * \internal Calculates elements offset from the left border of document
 *
 * @param ref [HTMLElement] -- target DOM element.
 */

Zapatec.Effect.elOffsetX = function(ref){
    var x = 0;

    while (ref) {
        if (typeof(ref.offsetParent) != 'undefined')
            x += ref.offsetLeft;

        ref = (typeof(ref.offsetParent) != 'undefined') ? ref.offsetParent : null;
    }

    return x;
}

/**
 * \internal Calculates elements offset from the top border of document
 *
 * @param ref [HTMLElement] -- target DOM element.
 */
Zapatec.Effect.elOffsetY = function(ref){
	var y = 0;
	
	while (ref) {
		if (typeof(ref.offsetTop) != 'undefined')
			y += ref.offsetTop;
			
		ref = typeof(ref.offsetTop) != 'undefined' ? ref.offsetParent : null;
	}
	
	return y;
}

Zapatec.Effect.fade = function(ref, counter) {
	if(ref.origfilter == null){
		ref.origfilter = document.all ? 
			ref.style.filter : ref.style.opacity != null ? 
				ref.style.opacity : ref.style.MozOpacity
		;
	}

	var f = ref.filters;
	var done = (counter >= 100);

	if (f) {
		if (!done && ref.style.filter.indexOf("alpha") == -1) {
			ref.style.filter += ' alpha(opacity=' + counter + ')';
		} else if (f.length && f.alpha) {
			if (done) {
				f.alpha.enabled = false;
			} else {
				f.alpha.opacity = counter; 
				f.alpha.enabled = true;
			}
		}
	} else {
		if(counter < 100 && counter > 0)
			ref.style.opacity = ref.style.MozOpacity = counter / 100;
	}

	if(counter <= 0){
		ref.style.display = 'none';
	}

	if(counter >= 100 && ref.origfilter != null) {
		if(ref.origfilter != "") {
			ref.style.filter = ref.style.opacity = ref.style.MozOpacity = ref.origfilter;
		}
			
		ref.origfilter = null;
	}
};

Zapatec.Effect.slide = function(ref, counter) {
	var cP = Math.pow(Math.sin(Math.PI*counter/200),0.75);
	
	var noClip = ((window.opera || navigator.userAgent.indexOf('KHTML') > -1) ?
		'' : 'rect(auto, auto, auto, auto)');
	
	if (typeof ref.__zp_origmargintop == 'undefined') {
		ref.__zp_origmargintop = ref.style.marginTop;
	}
	
	ref.style.marginTop = (counter==100) ?
		ref.__zp_origmargintop : '-' + (ref.offsetHeight*(1-cP)) + 'px';
	
	ref.style.clip = (counter==100) ? noClip :
		'rect(' + (ref.offsetHeight*(1-cP)) + ', ' + ref.offsetWidth +
		'px, ' + ref.offsetHeight + 'px, 0)';

	if(counter <= 0){
		ref.style.display = 'none';
	}
};

Zapatec.Effect.glide = function(ref, counter) {
	var cP = Math.pow(Math.sin(Math.PI*counter/200),0.75);

	var noClip = ((window.opera || navigator.userAgent.indexOf('KHTML') > -1) ?
		'' : 'rect(auto, auto, auto, auto)');
	
	ref.style.clip = (counter==100) ? noClip :
		'rect(0, ' + ref.offsetWidth + 'px, ' + (ref.offsetHeight*cP) + 'px, 0)';

	if(counter <= 0){
		ref.style.display = 'none';
	}
};

Zapatec.Effect.wipe = function(ref, counter) {
	var noClip = ((window.opera || navigator.userAgent.indexOf('KHTML') > -1) ?
		'' : 'rect(auto, auto, auto, auto)');
	
	ref.style.clip = (counter==100) ? noClip :
		'rect(0, ' + (ref.offsetWidth*(counter/100)) + 'px, ' +
		(ref.offsetHeight*(counter/100)) + 'px, 0)';

	if(counter <= 0){
		ref.style.display = 'none';
	}
};

Zapatec.Effect.unfurl = function(ref, counter) {
	var noClip = ((window.opera || navigator.userAgent.indexOf('KHTML') > -1) ?
		'' : 'rect(auto, auto, auto, auto)');
	
	if (counter <= 50) {
		ref.style.clip = 'rect(0, ' + (ref.offsetWidth*(counter/50)) +
			'px, 10px, 0)';
	}
	else if (counter < 100) {
		ref.style.clip =  'rect(0, ' + ref.offsetWidth + 'px, ' +
			(ref.offsetHeight*((counter-50)/50)) + 'px, 0)';

	}
	else {
		ref.style.clip = noClip;
	}

	if(counter <= 0){
		ref.style.display = 'none';
	}
};

Zapatec.Effect.shrink = function(ref, counter) {
	var noClip = ((window.opera || navigator.userAgent.indexOf('KHTML') > -1) ?
		'' : 'rect(auto, auto, auto, auto)');

	var paddingWidth = Math.floor(ref.offsetWidth * counter / 200);
	var paddingHeight = Math.floor(ref.offsetHeight * counter / 200);

	ref.style.clip = (counter >= 100) ? 
		noClip : "rect(" + (ref.offsetHeight / 2 - paddingHeight) + "px, " + (ref.offsetWidth/2 + paddingWidth) + "px, "
			+ (ref.offsetHeight / 2 + paddingHeight) + "px, " + (ref.offsetWidth/2 - paddingWidth) + "px)";

	if(counter <= 0){
		ref.style.display = 'none';
	}
}

Zapatec.Effect.grow = function(ref, counter) {
	Zapatec.Effect.shrink(ref, 100 - counter);
}

Zapatec.Effect.highlight = function(ref, counter) {
	if(ref.origbackground == null) {
		Zapatec.Effect.applyFunc(ref, function(){ 
			var el = arguments[0];

			if(el.nodeType == 1) {
				el.origbackground = el.style.backgroundColor;
			}
		});
	}

	Zapatec.Effect.applyFunc(ref, function(){ 
		var el = arguments[0];

		if(el.nodeType == 1) {
			el.style.backgroundColor = "#FFFF" + (255 - Math.floor(counter*1.5)).toString(16);
		}
	});

	if(counter <= 0 || counter >= 100) {
		Zapatec.Effect.applyFunc(ref, function(){ 
			var el = arguments[0];

			if(el.nodeType == 1) {
				el.style.backgroundColor = el.origbackground;
				el.origbackground = null;
			}
		});
	}
}

Zapatec.Effect.roundCorners = function(ref, outerColor, innerColor){
	if(!document.getElementById || !document.createElement){
	    return;
	}

	var ua = navigator.userAgent.toLowerCase();

	if(ua.indexOf("msie 5") != -1 && ua.indexOf("opera") == -1){
	    return;
	}

	var top = document.createElement("div");
	top.className = "rtop";
	top.style.backgroundColor = outerColor;
		
	for(var i = 1; i <= 4; i++){
		var child = document.createElement("span");
		child.className = "r" + i;
		child.style.backgroundColor = innerColor;
		top.appendChild(child);
	}

	ref.firstChild == null ? 
		ref.appendChild(top) : ref.insertBefore(top, ref.firstChild);

	var bottom = document.createElement("div");
	bottom.className = 'rbottom';
	bottom.style.backgroundColor = outerColor;

	for(var i = 4; i >= 1; i--){
		var child = document.createElement("span");
		child.className = 'r' + i;
		child.style.backgroundColor = innerColor;
		bottom.appendChild(child);
	}

	ref.appendChild(bottom);
	ref.__zp_roundCorners = true;
	ref.__zp_outerColor = outerColor;

	// if element has shadow - 
	if(ref.__zp_dropshadow != null){
		document.body.removeChild(ref.__zp_dropshadow);
		ref.__zp_dropshadow = null;
		Zapatec.Effect.dropShadow(ref, ref.__zp_deep);
	}
}

Zapatec.Effect.dropShadow = function(ref, deep) {
	// if element already have shadow - do nothing
	if(ref.__zp_dropshadow != null){
		return;
	}

	// parse deep parameter.
	if(deep == null || isNaN(parseInt(deep))) {
		deep = 5;
	}

	ref.__zp_deep = deep;

	var shadow = document.createElement("div");
	
	shadow.style.position = "absolute";
	shadow.style.backgroundColor = "#666666";
	shadow.style.MozOpacity = 0.50;
	shadow.style.filter = "Alpha(Opacity=50)";
	shadow.style.left = (Zapatec.Effect.elOffsetX(ref) + deep) + "px";
	shadow.style.top = (Zapatec.Effect.elOffsetY(ref) + deep) + "px";
	shadow.style.width = ref.offsetWidth + "px";
	shadow.style.height = ref.offsetHeight + "px";

	var innerElement = document.createElement('div');
	innerElement.style.height = (parseInt(shadow.style.height) - 10) + "px";
	innerElement.appendChild(document.createTextNode(''))
	shadow.appendChild(innerElement);

	ref.__zp_dropshadow = shadow;
		
	document.body.insertBefore(shadow, document.body.firstChild);

	if(ref.__zp_roundCorners){
		Zapatec.Effects.apply(shadow, 'roundCorners', {outerColor: ref.__zp_outerColor, innerColor: "#666666"});
	}
}

Zapatec.Effects = []

/**
 * This method is used to show HTML element with some visual effects.
 *
 * @param ref [HTMLElement] -- the DOM element that contains the menu items.
 * @param animSpeed [number] -- animation speed. From 1(low speed) to 100(high speed)
 * @param effects [String or array] -- what effects apply to element. May be a 
 * string(when only one effect would be applied) or array of strings
 * @param onFinish[function] -- function to call when effect ends
 */

Zapatec.Effects.show = function(ref, animSpeed, effects, onFinish) {
	Zapatec.Effects.init(ref, true, animSpeed, effects, onFinish);
}

/**
 * This method is used to hide HTML element with some visual effects.
 *
 * @param ref [HTMLElement] -- the DOM element that contains the menu items.
 * @param animSpeed [number] -- animation speed. From 1(low speed) to 100(high speed)
 * @param effects [String or array] -- what effects apply to element. May be a 
 * string(when only one effect would be applied) or array of strings
 * @param onFinish[function] -- function to call when effect ends
 */

Zapatec.Effects.hide = function(ref, animSpeed, effects, onFinish) {
	Zapatec.Effects.init(ref, false, animSpeed, effects, onFinish);
}

/**
 * This method is used to show/hide HTML element with some visual effects.
 *
 * @param ref [HTMLElement] -- the DOM element that contains the menu items.
 * @param show [boolean] -- if true - show element, false - hide element.
 * @param animSpeed [number] -- animation speed. From 1(low speed) to 100(high speed)
 * @param effects [String or array] -- what effects apply to element. May be a 
 * string(when only one effect would be applied) or array of strings
 * @param onFinish[function] -- function to call when effect ends
 */

Zapatec.Effects.init = function(ref, show, animSpeed, effects, onFinish){
	// checking input parameters
	if(ref == null || effects == null || effects.length == 0){
		return null;
	}

	if(typeof ref == "string"){
		ref = document.getElementById(ref);
	}

	if(ref == null){
		return null;
	}

	ref.animations = [];

	// if effects is given as string - replace it with array with one value
	if(typeof effects == "string")
		effects = [effects];

	for(var i = 0; i < effects.length; i++){
		var effect = null;
	    
		// analyzing given effects names
		switch(effects[i]){
			case 'fade':
				effect = Zapatec.Effect.fade;
				break;
			case 'slide':
				effect = Zapatec.Effect.slide;
				break;
			case 'glide':
				effect = Zapatec.Effect.glide;
				break;
			case 'wipe':
				effect = Zapatec.Effect.wipe;
				break; 
			case 'unfurl':
				effect = Zapatec.Effect.unfurl;
				break;
			case 'grow':
				effect = Zapatec.Effect.grow;
				break;
			case 'shrink':
				effect = Zapatec.Effect.shrink;
				break;
			case 'highlight':
				effect = Zapatec.Effect.highlight;
				break;
		}

		if(effect != null)
			ref.animations[ref.animations.length] = effect;
	}

	if(ref.animations.length != 0 && ref.running == null) {
		ref.running = true;
		Zapatec.Effects.run(ref, animSpeed, show, null, onFinish);
	}
}

/**
 * \internal is called from Zapatec.Effects.init. Runs periodically
 * updating element properties.
 *
 * @param ref [HTMLElement] -- the DOM element that contains the menu items.
 * @param animSpeed [number] -- animation speed. From 1(low speed) to 100(high speed)
 * @param show [boolean] -- if true - show element, false - hide element.
 * @param currVal [number] -- current progress - from 0 to 100.
 * @param onFinish[function] -- function to call when effect ends
 */

Zapatec.Effects.run = function(ref, animSpeed, show, currVal, onFinish) {
	if(animSpeed == null)
		animSpeed = 10;

	if(currVal < 0){
		currVal = 0;
	}

	if(currVal > 100){
		currVal = 100;
	}

	if(currVal == null) {
		if(show){
			currVal = 0

			if(ref.style.display == "none"){
				ref.style.display = '';

				if(ref.__zp_dropshadow != null) {
					ref.__zp_dropshadow.style.display = '';
				}
			}
		}
		else {
			currVal = 100;
		}
	}

	currVal += (show ? 1 : -1) * animSpeed;
	
	// run attached effects
	for (var i = 0; i < ref.animations.length; i++) {
		ref.animations[i](ref, currVal);
		
		if(ref.__zp_dropshadow != null) {
			ref.animations[i](ref.__zp_dropshadow, currVal);
		}
	}

	if (currVal <= 0 || currVal >= 100) {
		ref.running = null;

		if(onFinish != null){
			onFinish();
		}
		
		return;
	}
	else {
		setTimeout(function() {
			Zapatec.Effects.run(ref, animSpeed, show, currVal, onFinish);
		}, 50);
	}
}

Zapatec.Effects.apply = function(ref, effect, params){
	if(ref == null || effect == null) {
		return;
	}

	if(typeof ref == "string") {
		ref = document.getElementById(ref);
	}

	if(ref == null) {
		return;
	}

	switch(effect) {
		case 'roundCorners':
			Zapatec.Effect.roundCorners(ref, params['outerColor'], params['innerColor']);
			break;
		case 'dropShadow':
			Zapatec.Effect.dropShadow(ref, params['deep']);
			break;
	}
}
