/**
 * $().colorFlash();
 * jQuery Extension
 * 
 * - Flashes a color for a specified parameter, repeating a specified number of times.
 * - Can be a smooth transition or rigid.
 * - Currently, color values should be provided in hex format
 * 
 * Syntax: jQuery(element).colorFlash( [{ param: value, param: value, ...}] );
 * 
 * Defaults: see User-definable parameters
 * 
 * Examples:
 * 		$("#nameField").colorFlash();
 * 		$("#nameField").colorFlash({ smooth: true, speed: "fast", property: "borderColor", color: "#efb3cb" });
 * 
 * To Do:
 * - Clean up/optimize code a bit.
 * - Comment code better.
 * 
 * History:
 * *0.9.1:
 * 		- Improved cross-browser compatibility. (works in IE now)
 * *0.9:
 * 		- Fully functional in most browsers.
 * 
 * @author Nate
 * @version 0.9.1
 * 	(I'm calling it 0.9 because it does what it's supposed to but needs more extensive testing)
 * Last update: Feb 19, 2008 
 */

jQuery.fn.extend({
	
	colorFlash: function(params) {
		var jQ = jQuery;
		
		var params = jQ.extend({
			// User-definable parameters (defaults)
			smooth: true,				// use smooth color transition (true or false) *not working
			speed: "normal",			// milliseconds, or "normal" (250), "fast" (100), "slow" (500)
			repeat: 3,					// how many times to flash
			color: "#ffa0a0",			// color to change [property] to
			property: "backgroundColor",// CSS property to change
			//defColor: "#ffffff",
			
			// Private parameters. PRIVATE I SAY.
			iteration: 0,
			lastIteration: -1
		},params);
		
		// Stuff that only needs to be done on the first iteration.
		
		// Give the element a unique identifier (for FF it seems)
		params.tag = this[0].nodeName;
		params.uid = "cF"+(Math.ceil(Math.random()*100000)).toString();
		this.addClass(params.uid);
		
		// Speed parameter - string alias values
		switch(params.speed) {
			case "slow": params.speed = 500; break;
			case "normal": params.speed = 250; break;
			case "fast": params.speed = 100; break;
			default:
				if(typeof(params.speed) == "string" && isNaN(parseInt(params.speed))) { // speed has been set to an invalid string
					params.speed = 250;
				}
				else if(!isNaN(parseInt(params.speed))) {
					params.speed = parseInt(params.speed);
				}
		}
		
		// Find the initial (default) color
		params.defColor = this.css(params.property);
		if(params.defColor == "") {
			params.defColor = "rgb(255,255,255)";
		}
		
		// Convert rgb(x,x,x) to a hex value
		if(params.defColor.search(/(rgb|\(|\))/g) > -1) { // convert rgb() to #hex
			//params.defColor = '#'+(eval(params.defColor.replace(/(rgb|\(|\))/g,'').replace(',','*'))).toString(16);
			var defClr = params.defColor.replace(/(rgb|\(|\))/g,'').split(",");
			var defClrHex = "#";
			//params.defRGB = { r: defClr[0], g: defClr[1], b: defClr[2] };
			
			for(i=0; i < defClr.length; i++) {
				defClrHex += parseInt(defClr[i]).toString(16);
			}
			params.defColor = defClrHex;
		}
		
		// Okay, let's try this with RGB values instead...
		var chex = params.color.replace('#','');
		params.colorRGB = { r: parseInt(chex.substr(0,2),16), g: parseInt(chex.substr(2,2),16), b: parseInt(chex.substr(4,2),16) };
		chex = params.defColor.replace('#','');
		params.defRGB = { r: parseInt(chex.substr(0,2),16), g: parseInt(chex.substr(2,2),16), b: parseInt(chex.substr(4,2),16) };
		
		params.diffRGB = {
			r: Math.abs(params.colorRGB['r'] - params.defRGB['r']),
			g: Math.abs(params.colorRGB['g'] - params.defRGB['g']),
			b: Math.abs(params.colorRGB['b'] - params.defRGB['b'])
		};
		
		// Numeric distance between the two colors
		params.diff = (Math.abs(parseInt(params.defColor.replace("#",""),16) - parseInt(params.color.replace("#",""),16)));
		
		// Set step size
		if(params.smooth) {
			params.sleepTime = 50; // for setTimeout, which is in the $().eFcycle() function, below
			params.step = Math.floor(params.diff / (Math.round(params.speed / params.sleepTime))); // how big of a step per cycle
			var cycleSize = Math.round(params.speed / params.sleepTime);
			params.stepRGB = {
				r: Math.floor(params.diffRGB['r'] / cycleSize),
				g: Math.floor(params.diffRGB['g'] / cycleSize),
				b: Math.floor(params.diffRGB['b'] / cycleSize)
			};
		}
		else {
			params.step = params.diff;
			params.sleepTime = params.speed;
		}
		params.count =  0; // when count == diff, reset count and iterate.
		params.countRGB = { r: 0, g: 0, b: 0 };
		params.repeat = params.repeat * 2; // one iteration is 2 cycles
		params.dc = parseInt(params.defColor.replace("#",""),16); // integer representation of defColor
		params.c = parseInt(params.color.replace("#",""),16); // integer representation of color
		
		if(params.smooth) {
			this.cFcycleSmooth(params);
		}
		else {
			this.cFcycle(params);
		}
	},
	
	// These next two functions are intended for 'internal' use, and generally require colorFlash()
	// to have been called first.
	
	// Normal cycle - not smooth
	cFcycle: function(par) {
		var obj = this;
		obj.params = par;
		if($.browser.msie) {
			obj.params.interval = window.setInterval(function() {
				obj.params = obj.cFc();
			},par.sleepTime);
		}
		else {
			obj.cFc();
		}
	},
	
	cFc: function() {
		// perform on each iteration, but not each cycle
		var obj = this;
		var params = obj.params;
		if(params.iteration % 2 == 0 && params.lastIteration != params.iteration) {
			params.target = params.c;
			params.start = params.dc;
			params.direction = ( (params.c - params.dc) < 0) ? -1 : 1;
			params.lastIteration = params.iteration;
		}
		else if(params.iteration != params.lastIteration) {
			params.target = params.dc;
			params.start = params.c;
			params.direction = ( (params.dc - params.c) < 0) ? -1 : 1;
			params.lastIteration = params.iteration;
		}
		
		if(params.iteration < params.repeat) {
			//var clrRGB = {};
			var clr = '';
			
			params.count += params.step;
			clr = '#' + (params.start + (params.count * params.direction)).toString(16);
			
			obj.css(params.property, clr);
			
			if(params.count >= params.diff) {
				params.count = 0;
				params.iteration++;
				obj.css(params.property, '#'+params.target.toString(16));
			}
			
			// I'M CHARGIN MAH LASER
			if(!$.browser.msie) {
				var timeout = window.setTimeout('$("'+params.tag+'.'+params.uid+'").cFcycle('+params.toSource()+')',params.sleepTime);
			}
		}
		else {
			params.iteration = 0;
			obj.css(params.property, params.defColor);
			obj.removeClass(params.uid);
			if($.browser.msie) {
				window.clearInterval(obj.params.interval);
			}
			//delete cFWrap[params.uid];
		}
		return params;
	},
	
	// Smooth cycle
	cFcycleSmooth: function(par) {
		var obj = this;
		obj.params = par;
		obj.params.interval = window.setInterval(function() {
			// perform on each iteration, but not each cycle
			var params = obj.params;
			if(params.iteration % 2 == 0 && params.lastIteration != params.iteration) {
				params.target = params.colorRGB;
				params.start = params.defRGB;
				params.direction = ( (params.c - params.dc) < 0) ? -1 : 1;
				params.lastIteration = params.iteration;
			}
			else if(params.iteration != params.lastIteration) {
				params.target = params.defRGB;
				params.start = params.colorRGB;
				params.direction = ( (params.dc - params.c) < 0) ? -1 : 1;
				params.lastIteration = params.iteration;
			}
			
			if(params.iteration < params.repeat) {
				var clrRGB = {};
				var clr = '';
				params.count += params.step;
				
				for(a in params.countRGB) {
					params.countRGB[a] += params.stepRGB[a] * params.direction;
					clrRGB[a] = params.start[a] + params.countRGB[a];
				}
				clr = 'rgb('+clrRGB['r']+','+clrRGB['g']+','+clrRGB['b']+')';
					
				obj.css(params.property, clr);
				
				if(params.count >= params.diff) {
					params.count = 0;
					params.countRGB = { r: 0, g: 0, b: 0 };
					params.iteration++;
					obj.css(params.property, 'rgb('+params.target['r']+','+params.target['g']+','+params.target['b']+')');
				}
				
				// I'M CHARGIN MAH LASER
				//if(!$.browser.msie) {
				//	var timeout = window.setTimeout('$("'+params.tag+'.'+params.uid+'").cFcycleSmooth('+params.toSource()+')',params.sleepTime);
				//}
			}
			else {
				params.iteration = 0;
				obj.css(params.property, params.defColor);
				obj.removeClass(params.uid);
				window.clearInterval(obj.params.interval);
			}
			obj.params = params;
		},par.sleepTime);
	}
});