// colorflow - fade the background color of a given (link) element
// Tim Reeves for Seminarraum, 2009-06-20

// It would be cleaner to clear any outstanding timeouts, but in recursive
// calls they are difficult to track. So quick and dirty (but effective):
loading = true;

// Well, at least sort of object oriented :)
function myrgb (R, G, B) {
	this.R = R;
	this.G = G;
	this.B = B;
}

// BEGIN CONFIG AREA ----------------------------

// The base and peak colors for an available link
var lnbase = new myrgb(255,239,239);	/* 0 / 16 / 16 - 8 Zyklen */
var lnpeak = new myrgb(255,255,255);	/* White */
// How much to change the color channels per flow increment
// Down is always simply the negative opposite of up
var lnup = new myrgb(0,2,2);
var lndn = new myrgb(0,-2,-2);

// The base and peak colors for an open link (here unused)
var oplnbase = new myrgb(255,246,230);	/* Originalfarbe */
var oplnpeak = new myrgb(255,246,230);	/* wie lnpeak */
var oplnup = new myrgb(0,0,0);			/* 6 Zyklen */
var oplndn = new myrgb(0,0,0);

// Time in milliseconds between each flow increment
// 12 x 8 = 0.096 seconds may seem short, but is about optimal when
// the mouse is moved quickly over a whole menu full of flowing links
var period = 12;

// Set this true for unidirectional flow, otherwise the converse flow is automatic!
var uniflow = false;

// END CONFIG AREA ----------------------------

// Declare an own object type for use in an array
// The array tracks the state of each active flow element
function q_elem (key, linktype, mouseIsOver, isNowFlowing) {
	this.key = key;
	this.linktype = linktype;
	this.over = mouseIsOver;
	this.flow = isNowFlowing;
}

my_q = new Array();

// mytrace = new Array();
// function showtrace() {
// 	var stmt = '', l = mytrace.length;
// 	var b = l > 50 ? l - 50 : 0;
// 	for (var i=b; i<l; i++) stmt = stmt + mytrace[i];
// 	alert(stmt);
// }

function do_up(i) {
	// mytrace[mytrace.length] = "do_up(" + i + ")  ";
	my_q[i].flow = true;
	if (my_q[i].linktype == 'offen')
		flow( i, 1, 'oplnbase', 'oplnpeak', 'oplnup');
	else
		flow( i, 1, 'lnbase', 'lnpeak', 'lnup');
}

function do_dn(i) {
	// mytrace[mytrace.length] = "do_dn(" + i + ")  ";
	my_q[i].flow = true;
	if (my_q[i].linktype == 'offen')
		flow( i, 0, 'oplnpeak', 'oplnbase', 'oplndn');
	else
		flow( i, 0, 'lnpeak', 'lnbase', 'lndn');
}

function release(i) {
	// Release this array slot (page is [un]loading)
	// mytrace[mytrace.length] = "release(" + i + ") => " + my_q[i].key + "\n";
	my_q[i].key = '';
	my_q[i].linktype = '';
	my_q[i].over = true;
	my_q[i].flow = false;
}

function reflow( i, up, sr, sg, sb, str_finl, str_incr ) {
	// up: not a boolean - passing problems - 1=up 0=dn
	// mytrace[mytrace.length] = "reflow(" + i + ",up=" + up + ") key => " + my_q[i].key + "  ";
	if (loading) { release(i); return; }
	// Firefox 1.0.7 stolpert beim Variablen-Namen "final", daher "finl"
	var finl = eval(str_finl);
	var incr = eval(str_incr);
	// Calculate new value from and in "start" using "incr"
	if (incr.R > 0 && sr < finl.R) sr += incr.R;
	if (incr.R < 0 && sr > finl.R) sr += incr.R;
	if (incr.G > 0 && sg < finl.G) sg += incr.G;
	if (incr.G < 0 && sg > finl.G) sg += incr.G;
	if (incr.B > 0 && sb < finl.B) sb += incr.B;
	if (incr.B < 0 && sb > finl.B) sb += incr.B;
	// Note if we are done (have reached final) and correct any overshoot
	// This way assures that inhomogeneous parameters never exceed "final"
	var doner=false, doneg=false, doneb=false;
	if (incr.R == 0 || (incr.R > 0 && sr >= finl.R) || (incr.R < 0 && sr <= finl.R)) { sr = finl.R; doner=true; }
	if (incr.G == 0 || (incr.G > 0 && sg >= finl.G) || (incr.G < 0 && sg <= finl.G)) { sg = finl.G; doneg=true; }
	if (incr.B == 0 || (incr.B > 0 && sb >= finl.B) || (incr.B < 0 && sb <= finl.B)) { sb = finl.B; doneb=true; }
	// Assign the new value, checking if element is still defined
	// It may not be - if the page is reloaded or another page loaded
	var newcolor = "rgb(" + sr + "," + sg + "," + sb + ")";
	if (document.getElementById(my_q[i].key))
		document.getElementById(my_q[i].key).style.backgroundColor = newcolor;
	else {
		// The page appears to have been unloaded
		// mytrace[mytrace.length] = "reflow: Page unloaded  ";
		release(i); return;
		}
	if (doner && doneg && doneb) {
		// This cycle is done - is any action outstanding?
		// mytrace[mytrace.length] = "reflow done for " + i + ",up=" + up + ",over=" + my_q[i].over + "\n";
		if (uniflow) {		// global config - unidirectional flow
			release(i);
			// showtrace();
			return;
			}
		if (up == 1) {
			// Finished flowing up - down again or wait for down event
			if (my_q[i].over == false) do_dn(i); else my_q[i].flow = false;
			}
		else {
			// Finished flowing down - up again or release key
			if (my_q[i].over) do_up(i); else release(i);
			}
//		showtrace();
		return;
		}
	// This flow is not yet finished - call ourselves again
	// Für den Aufruf stehen lokale Variablen nicht mehr zur Verfügung!
	var aufruf = "reflow(" + i + "," + up + "," + sr + "," + sg + "," + sb + ",'" + str_finl + "','" + str_incr + "')";
	// mytrace[mytrace.length] = "reflow: in " + period + "ms  ";
	window.setTimeout(aufruf,period);
}

function flow( i, up, str_start, str_final, str_incr ) {
	// mytrace[mytrace.length] = "flow(index=" + i + ", up=" + up + ", start=" + str_start + ")\n";
	// str_start, str_final and str_incr are (string) names of objects of type myrgb
	// Note that calling is normally by value, but for objects by reference,
	// so jump into the recursive timed function with individual values
	var start = eval(str_start);
	var sr = start.R, sg = start.G, sb = start.B;
	reflow( i, up, sr, sg, sb, str_final, str_incr );
}

function flowupcommon(id,linktype,go_up) {
	// id = unique numeric identifier for id's
	// mytrace[mytrace.length] = "flowupcommon(" + id + "," + linktype + (go_up ? ',true' : ',false') + ")  ";
	var key = 'flow' + id;
	if (! document.getElementById(key)) {
		// The page appears to have been unloaded
		return;
		}
	// Find or assign the/a queue element for this key
	var i, l = my_q.length, freeslot = -1;
	for (i=0; i<l; i++) {
		if (my_q[i].key == key) {
			// Use the existing element
			break;
			}
		// Note first free slot (if any)
		if (freeslot == -1 && my_q[i].key == '') freeslot = i;
		}
	if (i < l)
		// The given key is already in use
		my_q[i].over = true;
	else
	if (freeslot > -1) {
		// Re-use a free element
		i = freeslot;
		my_q[i].key = key;
		my_q[i].linktype = linktype;
		// over & flow are released to "true / false"
		}
	else
		// Assign a new element
		my_q[i] = new q_elem(key,linktype,true,false);
	// Initiate the flow up if we are not already flowing
	if (my_q[i].flow == false) {
		if (go_up) do_up(i); else do_dn(i);
		}
}

function flowup(id,linktype) {
	flowupcommon(id,linktype,true);
}

function flowuprev(id,linktype) {	// flow up with color reversal
	flowupcommon(id,linktype,false);
}

function flowdn(id) {
	// mytrace[mytrace.length] = "flowdn(" + id + ")  ";
	var key = 'flow' + id;
	if (! document.getElementById(key)) {
		// The page appears to have been unloaded
		return;
		}
	// Find the queue element for this key
	var i, l = my_q.length;
	for (i=0; i<l; i++) {
		if (my_q[i].key == key) break;
		}
	// In case the mouse started over the button...
	if (i == l) return;
	// The mouse has moved out of this area
	my_q[i].over = false;
	// Start the flow down if the last flow up is finished
	if (my_q[i].flow == false) do_dn(i);
	// Otherwise it will be started deferred, in reflow()
}

loading = false;
