/* ################################# *\
|                                     |
|               F A C E               |
|               -------               |
|  Faruk's Automated CSS Enhancement  |
|                                     |
+-------------------------------------+
|  Read the documentation if you      |
|  want to know how to use this:      |
|                                     |
|  http://kurafire.net/projects/face  |
|       http://www.phest.net/         |
|                                     |
\* ################################# */

var face = {

	defSpeed   : 10,    // * the default speed for FACE changes in milliseconds
	defDelay   : 100,   // * the default delay between FACE events on child elements
	when       : 'L',   // * FACE can be triggered on onload (L) or mouseover (MO)
	running    : false, // Is FACE active
	heartbeat  : 0,     // get incremented with pace every cycle (speed determined by face.speed) 
	pace       : 0,     // the greatest common denominator from speed and delay 
	level      : 0,     // amount of running animations
	active     : {},    // List of elements that are active at this very moment
	events     : {},    // List of events. One timestamp can contain multiple events
	delaylist  : [],    // array with [0] => $id_for_elem, where 0 = delayCounter
	folder     : '/',   // Folder which contains the face.css file
	sheet      : 'face.css', // name of the stylesheet that contains the animation css
	rerun      : false,

	//* NOTE: can be changed with the Class Configuration Method

	pre : function(id)
	{
		if (!document.getElementById || !document.getElementsByTagName)
		{
			return false;
		}
		if (!document.getElementById(id))
		{
			return false;
		}

		face.startid = id;

		face.css(face.sheet);

		face.init(id);
	},

	init : function(id)
	{
		var constructs = document.getElementById(id);
		var nodes = constructs.className.split(':');
		face.id = id;

		if (nodes.length < 3) return false;

		if (face.startid != id)
		{
			face.changeconstructs(constructs, nodes);
		}

		for (var i = 0; i < nodes.length; i++)
		{
			switch (i)
			{
				case 0: face.type  = (nodes[i].toUpperCase() == 'C') ? 'C' : 'S'; break;
				case 1: face.classname = (nodes[i].match(/[a-z0-9-]+/i)) ? nodes[i] : false; break;
				case 2: face.steps = (typeof parseInt(nodes[i]) == 'number') ? nodes[i] : false; break;
				case 3: face.when  = (nodes[i].toUpperCase() != 'B') ? ((nodes[i].toUpperCase() == 'MO') ? 'MO' : 'L') : 'L'; break;
				case 4: face.speed = (typeof parseInt(nodes[i]) == 'number') ? parseInt(nodes[i]) : faceDefSpeed; break;
				case 5: face.delay = (typeof parseInt(nodes[i]) == 'number') ? parseInt(nodes[i]) : faceDefDelay; break;
				case 6: face.after = nodes[i].split(","); break;
			}
		}

		if (face.startid == id)
		{
			if (nodes[3] == 'B')
			{
				face.rerun = true;
			}

			face.changeconstructs(constructs, nodes);
		}

		if (face.classname == false || face.steps == false) 
		{
			face.css(face.sheet, 'delete');
			return false;	
		}

		face.gather(constructs);

		face.calcpace();

		if (face.when == 'L') face.populate(face.delaylist);
	},

	gather : function(elem)
	{
		var counter = 0, i;
		if (face.type == 'S') 
		{
			elem.setAttribute('id', face.id + '-' + counter);
			face.delaylist[counter] = face.id + '-' + counter;
		}
		else if (face.type == 'C' && elem.hasChildNodes())
		{				
			for (i = 0; i < elem.childNodes.length; i++)
			{				
				if (elem.childNodes[i].nodeType == 3 || elem.childNodes[i].nodeType == 8) 
				{ 
					continue;
				} 

				elem.childNodes[i].setAttribute('id', face.id + '-' + counter);

				if (face.when == 'MO')
				{
					elem.childNodes[i].onmouseover = function() { face.isactive(this) };
				}

				face.delaylist[counter] = face.id + '-' + counter;
				counter++;
			}
		}
	},

	populate : function(element)
	{
		var current, i, j, pacekey, times;

		if (face.when == 'MO')
		{
			face.active[element.id] = element.id;
		}

		times = element.constructor == Array ? element.length : 1;
		for (i = 0; i < times; i++)
		{
			current = element.constructor == Array ? element[i] : element.id;
			pacekey = face.when == 'L' ? face.heartbeat + face.delay * (i + 1) : face.heartbeat + face.pace;
			
			for (j = 0; j < face.steps; j++)
			{		
				pacekey = pacekey + face.pace;
				if (face.events[pacekey] == undefined || face.events[pacekey] == '')
				{
					face.events[pacekey] = new Array(current + ':' + j);
				}
				else
				{	
					face.events[pacekey].push(current + ':' + j);
				}
			}
			
			if (face.events[pacekey + face.pace] == undefined || face.events[pacekey + face.pace] == '')
			{
				face.events[pacekey + face.pace] = new Array(current + ':end');
			}
			else
			{
				face.events[pacekey + face.pace].push(current + ':end');
			}

			face.level++;

		}
		if (face.running == false)
		{
			face.start();
		}
	},

	start : function()
	{
		var result, target;

		if (face.running == true && face.level == 0) 
		{
			face.stop();
			return;
		}
		
		face.running = true;
		if (face.events[face.heartbeat] != undefined) 
		{
			for (i = 0; i < face.events[face.heartbeat].length; i++)
			{
				result = face.events[face.heartbeat][i].split(':');
				if (result[1] == 'end')
				{
					face.level--;
					delete face.active[result[0]];
					continue;
				}

				target = document.getElementById(result[0]);
				target.className = face.classname + result[1];

			}
			delete face.events[face.heartbeat];			
		}
		
		face.heartbeat = face.heartbeat + face.pace;
		setTimeout(function() { face.start(); }, face.speed);
	},	

	stop : function()
	{
		face.running = false;
		if (face.rerun == true && face.startid == face.id)
		{
			face.afterlife();
		}
	},

	afterlife : function()
	{
		var i;
		
		face.rerun = false;

		if (face.after == undefined)
		{
			face.init(face.id);
		}
		else
		{
			for(i = 0; face.after.length > i; i++)
			{
				face.init(face.after[i]);
			}
		}
	},

	changeconstructs : function(element, nodes)
	{
		var i = 1;
		var elclass;
		if (nodes[3].toUpperCase() == 'B')
		{
			nodes[3] = 'MO';
			elclass = nodes[0];
			while (i < nodes.length)
			{
				elclass += ':' + nodes[i];
				i++;
			}

			element.className = elclass;
		}

	},

	isactive : function(element)
	{
		if (face.active[element.id] == undefined || face.active[element.id] == '')
		{
			face.populate(element);
		}
	},
				
	calcpace : function()
	{
		if (face.when == 'MO') 
		{
			face.pace = face.speed;
			return;
		} 

		var x, gcd; 
		var a = face.speed;
		var b = face.delay
		while (b > 0)
		{
			gcd = a % b;
			a = b;
			b = gcd; 
		}

		face.pace = a;
	},

	css : function(sheet, action)
	{
		var head, link;

		if (action == 'delete')
		{
			link = document.getElementById('face');
			link.parentNode.removeChild(link);
			return;
		}

		head = document.getElementsByTagName('head');	
		link = document.createElement('link');

		link.setAttribute('media', 'screen');
		link.setAttribute('href', face.folder + sheet);
		link.setAttribute('rel', 'stylesheet');
		link.setAttribute('id', 'face');

		head[0].appendChild(link);
	}
};

window.onload = function () { face.pre("enhance"); }