Source for "Facebook Auto-Colorizer"

By Justin Ormont
Has 4 other scripts.


Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)

// ==UserScript==
// @name          Facebook Auto-Colorizer
// @version       v1.2.7
// @author	  Justin Ormont
// @namespace     http://wisc.facebook.com/message.php?id=8638838
// @description	  Colorizes facebook based on the user's photo.
// @include       http://*.facebook.com/*
// ==/UserScript==

//
// (c) 2007 Justin Ormont.  All rights reserved.
// Please don't steal my code. But if you do anyway, please credit me.
// Btw, stealing code includes reuse/adaption for another plug-in/script.
//
// Have ideas for new features?  Or want to create a script/plugin based
// on Facebook Auto-Colorizer?  Send me a message with your ideas. 
//
//
// About me: I'm a PhD candidate at the University of Wisconsin - Madison.
//
//
// To Do: 
//
//  On the help page, application icons are being chosen when they should not.
//  Colors will sometimes be cached with the wrong image when loading many color schemes at a time. May be server side issue.  May be a caching issue.
//  Make sure in profile, the profile pic is picked when an event image is also displayed. (done)
//  Check that the image is not already precached in function PreCacheChangedProfilePics. (done)
//  Precache all newly changed friends pictures. (done)
//  Add a separate cache for different types of pictures (profile pics, group picts, etc) to make searching cache faster. (done) 
//	Add a function to check contrast.  If too low, use standard or artificially increasing the contrast.
//	Color the category bars in groups. (done)
//	Make sure on the 'home' and groups pages that the correct image is analyzed. (done) 
//      Quantify 'bad' color schemes.  Likely a voting system fed into a bayesian probability graph.
//      Allow user to choose between the standard color picker and choices of bold, natural, etc.  Store choice on profile or server side.
//	Precache the next photo. (done)
//	Automatically set current page colors to the last viewed page's colors when there is no picture on the page.  (done)
//
//

var thisScriptVersion = new Array(1,2,6);
var startingColorArray = new Array(10);
var finalColorArray = new Array(10);
var fadedColorArray = new Array(10);

var timers = new Array();
var timerRecord = "";
timersOff = true;

(function()
 {  
 
	var now = new Date();
	try { 
		if (typeof GM_getValue('lastCheckedForVersion','') != 'undefined') var lastCheckedForVersion = GM_getValue('lastCheckedForVersion','');
		else lastCheckedForVersion = 0;   
	}
	catch(err) { lastCheckedForVersion = 0; }
	
	// alert("Time diff: " + now.getTime() + " " + lastCheckedForVersion + " " + (now.getTime() - lastCheckedForVersion))
	
	if ((now.getTime() - lastCheckedForVersion) > 86400000) CheckForUpdate(); //check once per day
	
	
	CheckForTemporaryStoredVariables();
	AddToApplicationsList();
	
	StartTimer("Looking for image");
	myImages = document.getElementsByTagName('img');
	
	//look specifically for a group image
	if (document.URL.indexOf('/group.php?')>-1) {
		for (var i=0; i<myImages.length; i++) {
			url = myImages[i].src;
			if (((url.indexOf('/object/')>-1)) && (url.indexOf('t_default.jpg')==-1)) {
				if (typeof photoUrl == 'undefined') {
					if (!IsInChatBar(myImages[i])) photoUrl = url.split('/n').join('/t').split('/s').join('/t').split('/q').join('/t');
				}
			}
		}
	}
	
	//look specifically for a user image
	if (document.URL.indexOf('/profile.php?')>-1) {
		for (var i=0; i<myImages.length; i++) {
			url = myImages[i].src;
			if (((url.indexOf('http://profile.')>-1)) && (url.indexOf('t_default.jpg')==-1)) {
				if (typeof photoUrl == 'undefined') {
					if (!IsInChatBar(myImages[i])) photoUrl = url.split('/n').join('/t').split('/s').join('/t').split('/q').join('/t');
				}
			}
		}
	}
	
	//look for a group or event image
	if (typeof photoUrl == 'undefined') {
		for (var i=0; i<myImages.length; i++) {
			url = myImages[i].src;
				  
			if (((url.indexOf('/gpic/')>-1) || (url.indexOf('/ppic/')>-1) || (url.indexOf('/object')>-1)) && (url.indexOf('t_default.jpg')==-1) && (url.indexOf('/object/')==-1)) {
				if (typeof photoUrl == 'undefined') {
					if (!IsInChatBar(myImages[i])) photoUrl = url.split('/n').join('/t').split('/s').join('/t').split('/q').join('/t');
				}
			}
		}
	}
	
	//look for a user image
	if (typeof photoUrl == 'undefined') {
		for (var i=0; i<myImages.length; i++) {
			url = myImages[i].src;
			if (((url.indexOf('http://photos-')>-1) || (url.indexOf('http://profile.')>-1)  || (url.indexOf('http://photos.')>-1) || (url.indexOf('http://pe-')>-1) || (url.indexOf('http://vthumb.')>-1)) && (url.indexOf('/app_')==-1)) {
				if (typeof photoUrl == 'undefined') {
					if (!IsInChatBar(myImages[i])) photoUrl = url.split('/n').join('/t').split('/s').join('/t').split('/q').join('/t');
				}
			}
		}
	}
	RecordTimer("Looking for image");
	
	if (typeof photoUrl != 'undefined') cacheType = UrlToCacheType(photoUrl);
	else cacheType = "";
	
	//if (typeof photoUrl == 'undefined') alert("photoUrl = " + undefined);
	//else alert("photoUrl = " + photoUrl);
	
	/*
	if (typeof photoUrl == 'undefined') {  //no images found on page 
		try{ApplyColorSchemeFromLastPage()}
		catch(err){}
		return; 
	}
	*/
	
	var splitUrl = photoUrl.split("/");
	var fileName = splitUrl[splitUrl.length - 1];
	
	StartTimer("Retrieve From Cache");
	colorArray = RetrieveFromCache(fileName, cacheType);
	RecordTimer("Retrieve From Cache");
	
	//alert("colorArray = " + colorArray);
	
	if (colorArray != null) {
		StartTimer("InsertCSS");
		InsertCSS(colorArray);
		RecordTimer("InsertCSS");
		
		StartTimer("preCache");
		var preCached = AttemptToPreCacheForNextPage();  // note the return next
		RecordTimer("preCache");
		
		if (document.URL.indexOf('/home.php?')>-1) PreCacheChangedProfilePics();
		if ((document.URL.indexOf('/album.php?')>-1) || (document.URL.indexOf('/photo_search.php?')>-1)) PreCacheAlbumPics();
		return;
	}
	else {
		/*Applies color scheme from last viewed page until the new color scheme comes in*/
		try{
			//ApplyColorSchemeFromLastPage();
			StartTimer("preCache");
			AttemptToPreCacheForNextPage();
			RecordTimer("preCache");
		}
		catch(err) {}
	}
	
	
	if (document.URL.indexOf('/home.php?')>-1) PreCacheChangedProfilePics();
	if ((document.URL.indexOf('/album.php?')>-1) || (document.URL.indexOf('/photo_search.php?')>-1)) PreCacheAlbumPics();

	//  alert('not cached');
	failedBlackAttempts = 0;
	GetColors(photoUrl, true);
	
	
	
	//alert("Time spent in InsertCSS = " + typeof(oldTime) + typeof(currentDate.getTime())));  
	
	
})();


function CacheTypeToCacheName(cacheType) {
	if (cacheType == "photoStream") return "cachedPhotoStreamColors";
	if (cacheType == "profilePic")  return "cachedPageColors";
	if (cacheType == "photoGroupEvent")  return "cachedGroupColors";
	if (cacheType == "photoVideoThumb")  return "cachedVideoColors";
	return "cachedOtherColors";	
}

function UrlToCacheType(url) {
	if (url.indexOf('/object')>-1) return "photoGroupEvent";		// Image from group or event.
	if (url.indexOf('http://photos-')>-1) return "photoStream";		// Image from photo set.
	if (url.indexOf('http://profile.')>-1) return "profilePic";		// Image from profile picture.
	if (url.indexOf('http://vthumb.')>-1) return "photoVideoThumb";	// Image from thumbnail of  videos.
	return "unknown";
}

function RetrieveFromCache(fileName, cacheType) {

	cacheName = CacheTypeToCacheName(cacheType);
	
	var splitFileName = fileName.split("/");
	var fileName = splitFileName[splitFileName.length - 1];
	
	//alert("Attempting to retreive from cacheName: " + cacheName);

	try { var cachedPageColors = GM_getValue(cacheName,''); }
	catch(err) { alert("Error: Can not read from cache.\n\nabout:config key was reset; Restart firefox."); }
	//var cachedPageColors = GM_getValue(cacheName,'');
	
	
	
	if (typeof cachedPageColors != 'undefined') {
		var cachedPageColorsSplit = cachedPageColors.split(',');
		
		//alert("Num of Cached: " + cachedPageColorsSplit.length);
		
		var foundInCached = false;
		for (var i=0; i<cachedPageColorsSplit.length; i++) {
			var temp = cachedPageColorsSplit[i].split('-');
			if (fileName == temp[0]) {
				var colorArray = temp[1].split('*');
				var foundInCached = true;
				
				//alert('Record #' + i + ': ' + fileName + ' : ' + temp[1]);
				
				//move found cached record to end of record set
				for (var x=i; x<cachedPageColorsSplit.length - 1; x++) {
					var holder = cachedPageColorsSplit[x];  
					cachedPageColorsSplit[x] = cachedPageColorsSplit[x+1];
					cachedPageColorsSplit[x+1] = holder;
					if (x > 1000) { alert('Error: Endless Loop'); return null; }
				}
				GM_setValue(cacheName,cachedPageColorsSplit.join(','));
				
				return colorArray;
			}
		}
	}
	return null;
}


function ClearCache() {
	GM_setValue("cachedPhotoStreamColors","");
	GM_setValue("cachedPageColors","");
	GM_setValue("cachedGroupColors","");
	GM_setValue("cachedVideoColors","");
}

function ApplyColorSchemeFromLastPage() {
	var cachedPageColors = GM_getValue('cachedPageColors','');
	if (typeof cachedPageColors != 'undefined') {
		var cachedPageColorsSplit = cachedPageColors.split(',');
		var temp = cachedPageColorsSplit[cachedPageColorsSplit.length-1].split('-');
		try{
			var colorArray = temp[1].split('*');
			startingColorArray = colorArray;      
			InsertCSS(colorArray);
		}
		catch(err) {}
	}
}


function InsertCSSOld(colorArray) {
	//ViewPallet(colorArray);

	StartTimer("Set Style Old"); 
	
	//alert("typeof " + typeof(colorarray));
	
	GM_addStyle("div.profilebox { background:" + colorArray[9] + "; }");
	GM_addStyle(".profileheader h2 {color:" + colorArray[0] + ";}");
	GM_addStyle("#header { background:" + colorArray[5] + "; }"); 
	GM_addStyle("div.profileheader { background:" + colorArray[6] + "; border-top: solid 1px " + colorArray[4] + "; }");
	GM_addStyle("#content { background:" + colorArray[8] + "; }");
	//GM_addStyle("body.profile { background:" + colorArray[8] + "; }");  // enable to reduce some background color
	GM_addStyle("body { background:" + colorArray[7] + "; }");
	GM_addStyle(".info { color: " + colorArray[1] + "; background: " + colorArray[10] + "; }");
	GM_addStyle("h4 { color:" + colorArray[0] + "; }");
	GM_addStyle("A:link { color:" + colorArray[1] + "; }");
	GM_addStyle("A:hover { color:" + colorArray[3] + "; }");
	GM_addStyle("A:visited { color:" + colorArray[2] + "; }");
	GM_addStyle("input.inputtext { border:1px solid " + colorArray[6] + "; background:" + colorArray[10] + "; color:" + colorArray[5] + "; }");
	GM_addStyle(".wallpost .info .header { color: " + colorArray[1] + "; background: " + colorArray[7] + "; border-top: solid 1px " + colorArray[3] + "; border-bottom: dashed 1px " + colorArray[7] + "; }");
	
	//search specific: also wall post textarea, but the text color doesn't work.
	GM_addStyle("textarea { border:1px solid " + colorArray[6] + "; color: " + colorArray[1] + "; background:" + colorArray[9] + "; }"); 
	
	//photo specific
	GM_addStyle("div.photonav { background:" + colorArray[8] + "; }");
	GM_addStyle("#comment { background:" + colorArray[4] + "; }");

    /* START changes due to the addition of "Mini-Feed" */
	// The text color of dates in the mini-feed.
	GM_addStyle(".minifeed .date_divider  { color:" + colorArray[6] + "; border-bottom: solid 1px " + MixTwoHexColors(colorArray[6],colorArray[4],.5) + ";}");
	
	GM_addStyle(".inside_the_box { background:" + colorArray[9] + "; }");
	GM_addStyle(".box_head { background:" + colorArray[6] + "; }");
	GM_addStyle(".when_open .flex_header, .flex_open .box_head { border-top: solid 1px " + colorArray[3] + "; " + "background-image: url('https://mywebspace.wisc.edu/ormont/web/facebook/flex_arrow_open.png')" + "; }");
	
	GM_addStyle(".flex_shut .box_head { background-image: url('https://mywebspace.wisc.edu/ormont/web/facebook/flex_arrow_close.png'); }");
	
	GM_addStyle(".box_subhead { background:" + MixTwoHexColors(colorArray[6],colorArray[9],.5) + "; border-top: solid 1px " + MixTwoHexColors(colorArray[6],colorArray[9],.25) + ";" + "}");
	GM_addStyle(".box_head h2  { color:" + colorArray[3] + "; }");
    /* END changes due to the addition of "Mini-Feed" */
	
			
	//global group specific
	GM_addStyle(".profile .withedit { background: " + colorArray[7] + "; color: " + colorArray[3] + "; border-top: solid 1px " + colorArray[4] + "; }");
	
	 
	//The top facebook bar
	//GM_addStyle("#navigator { background: " + IncreaseSaturation(colorArray[5],100) + "; background-image: url(https://mywebspace.wisc.edu/ormont/web/facebook/pageheaderbg-lighten.png); }");
	GM_addStyle("#navigator { background: " + IncreaseSaturation(colorArray[5],100) + "; background-image: ; }");

	GM_addStyle("#sidebar a.go_home { background: " + IncreaseSaturation(colorArray[5],100) + " url(https://mywebspace.wisc.edu/ormont/web/facebook/facebook_logo_trans.png) no-repeat top left; }");

	GM_addStyle("#sidebar a.go_home:hover {background: " + IncreaseSaturation(colorArray[5],100) + " url(https://mywebspace.wisc.edu/ormont/web/facebook/facebook_logo_trans.png) no-repeat bottom left; }");
	
	//alert(colorArray[0] + ", " + colorArray[9]);

	RecordTimer("Set Style Old");	
}

function InsertCSS(colorArray) {
	//ViewPallet(colorArray);
	
	//InsertCSSOld(colorArray);
	
	
	
	var head = document.getElementsByTagName("head")[0];
	var styleNode = document.createElement("style");
	

	//var styleText = "body{background-color:blue;}";
	
	var styleText =	" \
		div.profilebox { background:" + colorArray[9] + "; }; \
		.profileheader h2 {color:" + colorArray[0] + ";}; \
		#header { background:" + colorArray[5] + "; }\n \
		div.profileheader { background:" + colorArray[6] + "; border-top: solid 1px " + colorArray[4] + "; }\n \
		#content { background:" + colorArray[8] + "; }\n \
	/* body.profile { background:" + colorArray[8] + "; }  enable to reduce some background color */ \n \
		body { background:" + colorArray[7] + "; }\
		.info { color: " + colorArray[1] + "; background: " + colorArray[10] + "; }\
		h4 { color:" + colorArray[0] + "; }\
		A:link { color:" + colorArray[1] + "; }\
		A:hover { color:" + colorArray[3] + "; }\
		A:visited { color:" + colorArray[2] + "; }  \
		input.inputtext { border:1px solid " + colorArray[6] + "; background:" + colorArray[10] + "; color:" + colorArray[5] + "; } \
		.wallpost .info .header { color: " + colorArray[1] + "; background: " + colorArray[7] + "; border-top: solid 1px " + colorArray[3] + "; border-bottom: dashed 1px " + colorArray[7] + "; } \
		.profileTable .label { color: " + colorArray[3] + "; } \
		\
	/* search specific: also wall post textarea, but the text color doesn't work. */ \
		textarea { border:1px solid " + colorArray[6] + "; color: " + colorArray[1] + "; background:" + colorArray[8] + "; }  \
		\
	/* photo specific */ \
		div.photonav { background:" + colorArray[8] + "; }\
		#comment { background:" + colorArray[9] + "; }\
		\
	/* START changes due to the addition of Mini-Feed */ \
		/* The text color of dates in the mini-feed. */ \
		.minifeed .date_divider  { color:" +  ForceContrast(colorArray[6], colorArray[9], 0.15) + "; border-bottom: solid 1px " + MixTwoHexColors(colorArray[6],colorArray[4],.5) + ";} \
		\
		.inside_the_box { background:" + colorArray[9] + "; } \
		.box_head { background:" + colorArray[6] + "; } \
		.when_open .flex_header, .flex_open .box_head { border-top: solid 1px " + colorArray[3] + "; " + "background-image: url('https://mywebspace.wisc.edu/ormont/web/facebook/flex_arrow_open.png')" + "; } \
		\
		.flex_shut .box_head { background-image: url('https://mywebspace.wisc.edu/ormont/web/facebook/flex_arrow_close.png'); } \
		\
		.box_subhead { background:" + MixTwoHexColors(colorArray[6],colorArray[9],.5) + "; border-top: solid 1px " + MixTwoHexColors(colorArray[6],colorArray[9],.25) + ";" + "} \
		.box_head h2  { color:" + ForceContrast(colorArray[3], colorArray[6], 0.15) + "; } \
	/* END changes due to the addition of Mini-Feed */ \
		\
		\
	/* global group specific */  \
		.profile .header {  background: " + colorArray[6] + "; border-top: solid 1px " + colorArray[5] + "; } /* these colors may need to/should be changed */ \
		.profile .header h2 {  color: " + ForceContrast(colorArray[3], colorArray[6], 0.12) + "; } \
		\
		.profile .withedit { background: " + colorArray[7] + "; color: " + colorArray[3] + "; border-top: solid 1px " + colorArray[4] + "; } \
		\
		\
		#navigator { background: " + IncreaseSaturation(colorArray[5],100) + "; background-image: ; } \
		\
		#sidebar a.go_home { background: " + IncreaseSaturation(colorArray[5],100) + " url(https://mywebspace.wisc.edu/ormont/web/facebook/facebook_logo_trans.png) no-repeat top left ! important ; } \
		\
		#sidebar a.go_home:hover {background: " + IncreaseSaturation(colorArray[5],100) + " url(https://mywebspace.wisc.edu/ormont/web/facebook/facebook_logo_trans.png) no-repeat bottom left ! important ;}\
	    "; 
	
	
	if (typeof GM_getValue('clearChatBar','') != 'undefined') var clearChatBar = GM_getValue('clearChatBar','');
	else clearChatBar = "clear";
	
	/* chat bar specific */ 
		if (clearChatBar == "clear") {
			styleText += "#presence_bar{background: url(https://mywebspace.wisc.edu/ormont/web/facebook/chat_bar_bg.png) repeat-x top left;} \
			#presence_popin_bar{background: url(https://mywebspace.wisc.edu/ormont/web/facebook/chat_bar_bg.png) repeat-x top left;}  \
			#presence.full #presence_ui{margin-left:15px;margin-right:15px;border-left:1px solid " + colorArray[3] + "; background: url(https://mywebspace.wisc.edu/ormont/web/facebook/chat_bar_bg.png) repeat-x top left;} \
			\
			#presence .presence_bar_button{padding:5px 6px;height:17px;color:" + ForceContrast(colorArray[2], colorArray[3], 0.12) + ";cursor:pointer;margin-top:-2px;border-left:1px solid " + colorArray[3] + ";border-right:1px solid " + colorArray[6] + ";} \
			\
			#presence_error_bar{border-right:1px solid " + colorArray[3] + ";margin-right:15px;padding:6px 6px 0;background-image:url(https://mywebspace.wisc.edu/ormont/web/facebook/chat_bar_bg.png);}  \
			" 
		}
		else { 
			styleText += "#presence_bar{background: " + colorArray[6] + " url(https://mywebspace.wisc.edu/ormont/web/facebook/chat_bar_bg.png) repeat-x top left;} \
			#presence #chat_status_control_tab{border-right:1px solid " + colorArray[3] + ";} \
			#presence_popin_bar{background: " + colorArray[6] + " url(https://mywebspace.wisc.edu/ormont/web/facebook/chat_bar_bg.png) repeat-x top left;}  \
			#presence.full #presence_ui{margin-left:15px;margin-right:15px;border-left:1px solid " + colorArray[3] + "; background: " + colorArray[6] + " url(https://mywebspace.wisc.edu/ormont/web/facebook/chat_bar_bg.png) repeat-x top left;} \
			\
			#presence .presence_bar_button{padding:5px 6px;height:17px;color:" + ForceContrast(colorArray[2], colorArray[3], 0.12) + ";cursor:pointer;margin-top:-2px;border-left:1px solid " + colorArray[3] + ";border-right:1px solid " + colorArray[6] + ";} \
			\
			#presence_error_bar{border-right:1px solid " + colorArray[3] + ";margin-right:15px;padding:6px 6px 0;background-image:url(https://mywebspace.wisc.edu/ormont/web/facebook/chat_bar_bg.png);}  \
			"
		}



	styleNode.appendChild(document.createTextNode(styleText));
	head.appendChild(styleNode);
	
}


function ViewPallet(colorArray) {
	document.write("<table><tr><td bgcolor=" + colorArray[0] + ">" + colorArray[0] + "</td><td bgcolor=" + colorArray[1] + ">" + colorArray[1] + "</td><td bgcolor=" + colorArray[2] + ">" + colorArray[2] + "</td><td bgcolor=" + colorArray[3] + ">" + colorArray[3] + "</td><td bgcolor=" + colorArray[4] + ">" + colorArray[4] + "</td></tr>");
	document.write("<tr><td bgcolor=" + colorArray[5] + ">" + colorArray[5] + "</td><td bgcolor=" + colorArray[6] + ">" + colorArray[6] + "</td><td bgcolor=" + colorArray[7] + ">" + colorArray[7] + "</td><td bgcolor=" + colorArray[8] + ">" + colorArray[8] + "</td><td bgcolor=" + colorArray[9] + ">" + colorArray[9] + "</td></tr></table>");
}

function GetSaturation(hexColor) {
	
	var red = parseInt(hexColor.substr(1,2), 16)/255;
	var green = parseInt(hexColor.substr(3,2), 16)/255;
	var blue = parseInt(hexColor.substr(5,2), 16)/255;
	
	
	var max = Math.max(red,green,blue);  
	var min = Math.min(red,green,blue);
/*	
	var hue = 221;  
	
	if (red == max) hue = 60*(green-blue)/(max-min);
	if (green == max) hue = 60*(blue-red)/(max-min)+120;
	if (blue == max) hue = 60*(red-green)/(max-min)+240; 
	
	if (max == min) hue = 0;
	
	if (hue > 360) hue = hue - 360;
	if (hue < 0) hue = hue + 360;
	
	*/
	
	//  if ((max + min)/2 < 0.5) var saturation=(max-min)/(max+min) * (100-increasePercent) + increasePercent;
	//  if ((max + min)/2 >=0.5) var saturation=(max-min)/(2.0-max-min) * (100-increasePercent) + increasePercent;
	
	
	var saturation = (1-min/max)*100;
	
	//alert("saturation: " + Math.floor(saturation) );
	
	
	return Math.floor(saturation);
	
}

function GetContrast(hexColor1, hexColor2) {
	/* Contrast should generally be around 500 for background & foreground */
	var red1 = parseInt(hexColor1.substr(1,2), 16)/255;
	var green1 = parseInt(hexColor1.substr(3,2), 16)/255;
	var blue1 = parseInt(hexColor1.substr(5,2), 16)/255;
	
	var red2 = parseInt(hexColor2.substr(1,2), 16)/255;
	var green2 = parseInt(hexColor2.substr(3,2), 16)/255;
	var blue2 = parseInt(hexColor2.substr(5,2), 16)/255;
	
	return (Math.max(red1,red2) - Math.min(red1,red2)) + (Math.max(blue1,blue2) - Math.min(blue1,blue2)) + (Math.max(green1,green2) - Math.min(green1,green2));
}


function GetBrightness(hexColor) {
	/* Contrast should generally be around 500 for background & foreground */
	var red = parseInt(hexColor.substr(1,2), 16)/255;
	var green = parseInt(hexColor.substr(3,2), 16)/255;
	var blue = parseInt(hexColor.substr(5,2), 16)/255;

	return (red*299 + green*587 + blue*114)/1000;
}


function GetLuminance(hexColor) {
	/* Luminance ratio between foreground and background should be 5:1 */
	var red = parseInt(hexColor.substr(1,2), 16)/255;
	var green = parseInt(hexColor.substr(3,2), 16)/255;
	var blue = parseInt(hexColor.substr(5,2), 16)/255;

	if (red <= 0.03928) red = red/12.92;
	else red = ((red+0.055)/1.055)^2.4; 


	if (green <= 0.03928) green = green/12.92;
	else green = ((green+0.055)/1.055)^2.4; 


	if (blue <= 0.03928) blue = blue/12.92;
	else blue = ((blue+0.055)/1.055)^2.4; 

   return 0.2126 * red + 0.7152 * green + 0.0722 * blue 
}



function ForceContrast(hexColor1, hexColor2, contrastMinimum) {
//return hexColor1;
		
	var brightness1 = GetBrightness(hexColor1);
	var brightness2 = GetBrightness(hexColor2);

	var brightnessDiff = Math.abs(brightness1 - brightness2);

	//alert("brightnessDiff = " + brightnessDiff);

	//alert("colors = " + hexColor1 + " " + hexColor2);
	
	var i = 0;
	
	if (brightness1 >= brightness2 && (1 - brightness2) < contrastMinimum) var mixColor = "#FFFFFF";
	else if (brightness1 >= brightness2 && (1 - brightness2) >= contrastMinimum) var mixColor = "#000000";	
	else if (brightness1 < brightness2 && brightness2 < contrastMinimum) var mixColor = "#FFFFFF";
	else if (brightness1 < brightness2 && brightness2 >= contrastMinimum) var mixColor = "#000000";


	while (brightnessDiff < contrastMinimum) {
		i = i+1;
		if (i > 10) { RecordTimer("ForceContrast"); return hexColor1; }
		
		var brightness1 = GetBrightness(hexColor1);
		var brightnessDiff = Math.abs(brightness1 - brightness2);
		
		hexColor1 = MixTwoHexColors(hexColor1, mixColor, 0.005 + 2*Math.abs(contrastMinimum - brightnessDiff)); // The part in th 'abs' just make it converge faster. 
		//alert("brightness = " + brightness1 + " " + brightness2 + "\nratio = " + brightnessDiff + "\ni = " + i + "\nhexColor1 = " + hexColor1);
	}
	
	return hexColor1;

}


function ForceContrastXXX(hexColor1, hexColor2, contrastMinimum) {
	//return hexColor1;
	/* If the colors are too close to each other, move color1 away from color2 */
	var luminance1 = GetLuminance(hexColor1);
	var luminance2 = GetLuminance(hexColor2);

	var luminanceRatio1 = ((luminance1+0.5)/(luminance2+0.5));
	var luminanceRatio2 = ((luminance2+0.5)/(luminance1+0.5));

	alert("luminanceRatio1 = " + luminanceRatio1);

	//alert("colors = " + hexColor1 + " " + hexColor2);
	
	
	var i = 0;
	
	while (luminanceRatio1 < contrastMinimum && luminanceRatio2 < contrastMinimum) {
		i = i+1;
		if (i > 5) return hexColor1;
		
		var luminance1 = GetLuminance(hexColor1);
		var luminance2 = GetLuminance(hexColor2);

		var luminanceRatio1 = ((luminance1+0.5)/(luminance2+0.5));
		var luminanceRatio2 = ((luminance2+0.5)/(luminance1+0.5));
		
		alert("luminance = " + luminance1 + " " + luminance2);
			
		alert("forcing contrast");
		if (luminanceRatio1 < 1) {
			/* Color one is brighter */
			hexColor1 = MixTwoHexColors(hexColor1, "#FFFFFF", 0.1);
			alert("here 1");
		}
		else if (luminanceRatio1 > 1) {
			/* Color one is darker */
			hexColor1 = MixTwoHexColors(hexColor1, "#000000", 0.1);
			alert("here 2");
		}
		else {
			if (hexColor1 == hexColor2) {
				alert("here 3");
				if (hexColor1.toUpperCase() == "#FFFFFF") hexColor1 = "#AAAAAA";
				else if (hexColor1 == "#000000") hexColor1 = "#111111";
				else {
					if (luminance1 > 1.5) hexColor1 = MixTwoHexColors(hexColor1, "#FFFFFF", 0.1); 
					else hexColor1 = MixTwoHexColors(hexColor1, "#000000", 0.1);
				}
			}
			else {
				alert("here 4");
				preHexColor1 = hexColor1;
				alert("before hexColor1 = " + hexColor1);
				hexColor1 = MixTwoHexColors(hexColor1, "#FFFFFF", 0.1);  //this line could be better
				alert("after hexColor1 = " + hexColor1);
				if (preHexColor1 == hexColor1) return hexColor1;
			}
		}
		
	}
	
	return hexColor1;

}

function IncreaseSaturation(hexColor,increasePercent) {
	if (increasePercent < 0) increasePercent = 0;  
	if (increasePercent > 100) increasePercent = 100;  //saturating math...hehe
	
	
	var red = parseInt(hexColor.substr(1,2), 16)/255;
	var green = parseInt(hexColor.substr(3,2), 16)/255;
	var blue = parseInt(hexColor.substr(5,2), 16)/255;
	
	
	var max = Math.max(red,green,blue);  
	var min = Math.min(red,green,blue);
	
	var hue = 221;  
	
	if (red == max) hue = 60*(green-blue)/(max-min);
	if (green == max) hue = 60*(blue-red)/(max-min)+120;
	if (blue == max) hue = 60*(red-green)/(max-min)+240; 
	
	if (max == min) hue = 0;

	if (hue > 360) hue = hue - 360;
	if (hue < 0) hue = hue + 360;
	
	
	
	//if ((max + min)/2 < 0.5) var saturation = (max-min)/(max+min) * (100-increasePercent) + increasePercent;
	//if ((max + min)/2 >=0.5) var saturation = (max-min)/(2.0-max-min) * (100-increasePercent) + increasePercent;
	
	
	
	var saturation = (1-min/max)*100;
	
	
		
	//alert("saturation: " + Math.floor(saturation) + " hue: " + Math.floor(hue) + " lightness: " + Math.floor((max + min)*50));
		
	if (saturation < 40) return "hsl(219,42%,40%)";  //make sure no off gray -> bright color
	
	//saturation = saturation * (100-increasePercent) + increasePercent;
	
	//  alert((max-min)*50);
	//  alert("hsl(" + Math.floor(hue) + "," + Math.floor(saturation) + "%,50%)");
	return "hsl(" + Math.floor(hue) + "," + Math.floor(saturation) + "%," + Math.floor((max + min)*50) + "%)";
	
	/*
		if (blue == max) blue=255*increasePercent/100 + blue*(1-increasePercent/100);
	 else if (red == max) red=255*increasePercent/100 + red*(1-increasePercent/100);
	 else if (green == max) green=255*increasePercent/100 + green*(1-increasePercent/100);
	 
	 if (red == min) red=red*(1-increasePercent/100);
	 else if (green == min) green=green*(1-increasePercent/100);
	 else if (blue == min) blue=blue*(1-increasePercent/100);    
	 return "rgb(" + Math.floor(red) + "," + Math.floor(green) + "," + Math.floor(blue) + ")";
	 */
	
	
	
}

function AverageTwoHexColorsToRGB(hexColor1,hexColor2) {
	var red = Math.floor((parseInt(hexColor1.substr(1,2), 16) + parseInt(hexColor2.substr(1,2), 16))/2);
	var green =  Math.floor((parseInt(hexColor1.substr(3,2), 16) + parseInt(hexColor2.substr(3,2), 16))/2);
	var blue =  Math.floor((parseInt(hexColor1.substr(5,2), 16) + parseInt(hexColor2.substr(5,2), 16))/2);
	
	return "rgb(" + red + "," + green + "," + blue + ")";
}


function MixTwoHexColors(hexColor1,hexColor2,percentColor2)
{
	// ex: var hexFade = MixTwoHexColors("#F1CD00","#12C011",.75);
	
	if (percentColor2 > 1) percentColor2 = 1;
	if (percentColor2 < 0) percentColor2 = 0;
	
	var red = Math.floor((1-percentColor2)*parseInt(hexColor1.substr(1,2), 16) + percentColor2*parseInt(hexColor2.substr(1,2), 16));
	var green =  Math.floor((1-percentColor2)*parseInt(hexColor1.substr(3,2), 16) + percentColor2*parseInt(hexColor2.substr(3,2), 16));
	var blue =  Math.floor((1-percentColor2)*parseInt(hexColor1.substr(5,2), 16) + percentColor2*parseInt(hexColor2.substr(5,2), 16));
	
	/*if (red > 1) red = 1;
	if (blue > 1) blue = 1;
	if (green > 1) green = 1;
	
	if (red < 0) red = 0;
	if (blue < 0) blue = 0;
	if (green < 0) green = 0;*/
	
	return "#" + decimalToHex(red) + decimalToHex(green) + decimalToHex(blue);
	
}


function decimalToHex(decimalValue)  /* works for 0 to 255 */
{
	var high = Math.floor(decimalValue/16);
	var low = Math.round((decimalValue/16 - Math.floor(decimalValue/16))*16);
	
	
	if (high>9) high = String.fromCharCode(high-10+65);
	if (low>9) low = String.fromCharCode(low-10+65);
	
	
	return "" + high + "" + low;
}


function fadeBetweenTwoColorSchemes(currentFadePercent,fadeStep,endingFadePercent,delay)  
{
	var currentFadePercent = currentFadePercent + fadeStep;
	if (currentFadePercent > endingFadePercent) currentFadePercent = endingFadePercent;
	
	fadedColorArray[0] = MixTwoHexColors(startingColorArray[0],finalColorArray[0],currentFadePercent);
	fadedColorArray[1] = MixTwoHexColors(startingColorArray[1],finalColorArray[1],currentFadePercent);
	fadedColorArray[2] = MixTwoHexColors(startingColorArray[2],finalColorArray[2],currentFadePercent);
	fadedColorArray[3] = MixTwoHexColors(startingColorArray[3],finalColorArray[3],currentFadePercent);
	fadedColorArray[4] = MixTwoHexColors(startingColorArray[4],finalColorArray[4],currentFadePercent);
	fadedColorArray[5] = MixTwoHexColors(startingColorArray[5],finalColorArray[5],currentFadePercent);
	fadedColorArray[6] = MixTwoHexColors(startingColorArray[6],finalColorArray[6],currentFadePercent);
	fadedColorArray[7] = MixTwoHexColors(startingColorArray[7],finalColorArray[7],currentFadePercent);
	fadedColorArray[8] = MixTwoHexColors(startingColorArray[8],finalColorArray[8],currentFadePercent);
	fadedColorArray[9] = MixTwoHexColors(startingColorArray[9],finalColorArray[9],currentFadePercent);
	
	InsertCSS(fadedColorArray);
}


function CheckForUpdate() {
	
	//alert("Checking for update");
	
	var versionOnServer = new Array(0,0,0);
	
	GM_xmlhttpRequest({
		method: 'GET',
		url: 'https://mywebspace.wisc.edu/ormont/web/facebook/version.xml',
		headers: {
			'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.3',
			'Accept': 'application/atom+xml,application/xml,text/xml',
		},
		onload: function(responseDetails) {
			var parser = new DOMParser();
			var dom = parser.parseFromString(responseDetails.responseText, "application/xml");
			var entries = dom.getElementsByTagName('version');
			
			var versionOnServerDOM;
			var changes;
			var link;
			for (var i = 0; i < entries.length; i++) {
				versionOnServerDOM = entries[i].getElementsByTagName('versionsegment');
				changes = entries[i].getElementsByTagName('changes');
				link = entries[i].getElementsByTagName('link');
			}
						
			if (entries.length == 0) return;  //no version tag found.        
			
			for(var k = 0; (k < versionOnServerDOM.length); k++) versionOnServer[k] = versionOnServerDOM[k].textContent;
			
			try { 
				if (typeof GM_getValue('lastNotifiedVersion','') != 'undefined') lastNotifiedVersion = GM_getValue('lastNotifiedVersion','').split(",");
				else lastNotifiedVersion = new Array(0,0,0);
			}
			catch(err) { lastNotifiedVersion = new Array(0,0,0); }
			
			
			var alreadyNotifedOfNewVersion = 1;
			for(var j = 0; j < lastNotifiedVersion.length; j++) if (lastNotifiedVersion[j] != versionOnServer[j]) alreadyNotifedOfNewVersion = 0;
			
			if (alreadyNotifedOfNewVersion == 1) return;

			//alert("Version on server = " + versionOnServer.join(".") + ", Running Version = " + thisScriptVersion.join("."));
			for(var j = 0; j < versionOnServer.length; j++) {
				if ((thisScriptVersion[j] < versionOnServer[j]) || ((typeof thisScriptVersion[j] == 'undefined') && (typeof versionOnServer[j] != 'undefined'))) {
					if(confirm("A new version of Facebook Auto-Colorizer was released.\n\n" + changes[0].textContent + "\n\nNotice appears only once per version." + "\n\nGo to Install Page?")) {
						window.location = link[0].textContent;
						return;
					}
					else {
						try { GM_setValue('lastNotifiedVersion',versionOnServer.join(',')); }
						catch(err) { alert("Error: Can not write to version key.\n\nabout:config key was reset; Restart firefox."); }
						SetLastCheckedForVersionVariable();
						return;
					}
				}
				
				if (thisScriptVersion[j] > versionOnServer[j]) {
					//alert("no update needed, thanks for beta testing the new version.");
					SetLastCheckedForVersionVariable();
					return;
				}
			}
			
			//alert("no update needed");
			SetLastCheckedForVersionVariable();
		}
	});
}


function AttemptToPreCacheForNextPage() {
	if (document.URL.indexOf('/photo.php?') == -1) return false;
	try{ 
		var documentSrc=document.body.innerHTML;
		var nextImageOnload = "";
		documentSrcSplit = documentSrc.split('"');
		for (i in documentSrcSplit) {
			if (documentSrcSplit[i].indexOf('(new Image()).src=')>-1) nextImageOnload = documentSrcSplit[i];
		} 
		var nextImage = nextImageOnload.split("'")[1];
		nextImage = nextImage.split('/n').join('/t').split('/s').join('/t').split('/q').join('/t');
		
		StartTimer("Look for PreCache in Cache");
		if (RetrieveFromCache(nextImage, UrlToCacheType(nextImage)) == null) GetColors(nextImage, false);
		RecordTimer("Look for PreCache in Cache");
		
		//alert("nextImage = " + nextImage);
		return true;
	}
	catch(err){return false;}
}

function PreCacheChangedProfilePics() {
	StartTimer("PreCache Changed Profile Pics");
	var entries = document.getElementsByTagName('div');
	var numEntries = entries.length;

	
	for (i = 0; i < numEntries; i++) {
		if (entries[i].className == 'clearfix pictures_container') {
			myImages = entries[i].getElementsByTagName('img');
			var numImages = myImages.length;
			
			for (j = 0; j < numImages; j++) {
				var nextImage = (myImages[j].src).split('/n').join('/t').split('/s').join('/t').split('/q').join('/t');
				
				/* Todo: check the the image is not already cached */
				StartTimer("Look for PreCache Profile Pics in Cache");
				if (RetrieveFromCache(nextImage, UrlToCacheType(nextImage)) == null) GetColors(nextImage, false);
				RecordTimer("Look for PreCache Profile Pics in Cache");
			}
			
		}
	}
	RecordTimer("PreCache Changed Profile Pics");
}


function AddToApplicationsList() {
	StartTimer("Add To Applications List");
	var expandBar = document.getElementById("expandable_more");
	//alert(expandBar.previousSibling.lastChild.lastChild.innerHTML)
	
	var textNode = document.createTextNode("Auto-Colorizer");
	
	var link = document.createElement('a');
	link.setAttribute('href', 'http://www.facebook.com/sharer.php?u=http%3A%2F%2Fuserscripts%2Eorg%2Fscripts%2Fshow%2F3626&t=Facebook+Auto%2DColorizer%3A+Adds+an+awesome+color+scheme+to+every+facebook+page+based+on+you+friend%27s+photo%2E');
	link.setAttribute('class', 'link_title');
	link.setAttribute('onmousedown', 'new track_moveable(this.parentNode, this);');
	link.setAttribute('style',"background-image: url(https://mywebspace.wisc.edu/ormont/web/facebook/color_logo_16x16.png);");

	
	//alert(typeof(stringX));
	//alert(stringX);
	
	//link.setAttribute('onclick', ShowDialog + ";ShowDialog(); return false;");
	link.setAttribute('href', "javascript:" + ShowDialog + ";ShowDialog(); ");
	//link.setAttribute('href', "javascript:alert('here');");
	
	

/*
	link.setAttribute('onmouseover', 
		"var fbAutoColorizerDialog = new pop_dialog(); \
		fbAutoColorizerDialog.show_dialog('<div class=dialog_loading>'+tx('sh01')+'</div>'); \
		getDialog = new Ajax(); \
		getDialog.onDone = function(ajax_obj,responceText){ \
		alert(responceText); \
		} \
		 " );
*/

	//alert(ShowDialog);

	var divInside = document.createElement('div');
	divInside.setAttribute('class', 'container');
	divInside.setAttribute('ID', '2437461801');

	var divOutside = document.createElement('div');
	divOutside.setAttribute('class', 'list_item');	
	
	link.appendChild(textNode);
	divInside.appendChild(link);
	divOutside.appendChild(divInside);
	expandBar.previousSibling.lastChild.appendChild(divOutside);
	
	RecordTimer("Add To Applications List");
}

function IsInChatBar(ImgNode) {
	// Note: "profile_status clearfix" is the class of the parient's parient of a photo in the chat bar. 
	if (ImgNode.getAttribute("class") == "chat_info_pic") return true;
	
	
	return (ImgNode.parentNode.parentNode.getAttribute("class") == "profile_status clearfix");
}

function ShowDialog() {
	var fbAutoColorizerDialog = new pop_dialog(); 
	fbAutoColorizerDialog.show_dialog('<div class=dialog_loading>'+tx('sh01')+'</div>'); 
	//fbAutoColorizerDialog.make_modal();
	//fbAutoColorizerDialog.fade_out(1000);
	
	//fbAutoColorizerDialog.show_choice("hello2","how are you?",tx('pk01'),function(){}, tx('sh02'),function(){});
	
	fbAutoColorizerDialog.show_message("Facebook Auto-Colorizer", "This is work in progress.");
	
	var entries = document.getElementsByTagName('div');
	var numEntries = entries.length;

	var dialogBody = "";
	for (i = 0; i < numEntries; i++) {
		if (entries[i].className == 'dialog_body') {
			dialogBody = entries[i];
		}
	}
	
	if (typeof(dialogBody) == "string") return;  //Can't find the dialog box body div tag.
	
	var div = document.createElement('div');
	
	
	div.innerHTML = "<table><tbody><tr><td><img src='https://mywebspace.wisc.edu/ormont/web/facebook/color_logo_16x16.png' height='64' width='64'></td><td width='64'><br></td><td><p align='right'>\
	<a href='http://www.facebook.com/sharer.php?u=http://userscripts.org/scripts/show/3626&amp;t=Facebook+Auto-Colorizer:+Adds+an+awesome+color+scheme+to+every+facebook+page+based+on+you+friend%27s+photo.'>\
	<img src='http://static.ak.facebook.com/images/share/facebook_share_icon.gif'><img src='http://wisc.facebook.com/images/share_options/share_button_ff_mac.gif'></a><br>\
	<br><a href='http://userscripts.org/scripts/show/3626'>Facebook Auto-Colorizer Install Page</a><br><br><a href='http://wisc.facebook.com/inbox/?compose&amp;id=8638838'>Send Author a Message</a>\
	<br><br>[future: Simple Color Controls]<br>Chat bar: \
	<input type=radio name=clearChatBar onclick="
	+ "'document.cookie = \"clearChatBar=colored; expires=\\\"Fri, 11 Apr 2011 18:30:19 GMT\\\"; path=/\"'"
	+ ">  \
	Colored \
	<input type=radio name=clearChatBar onclick="
	+ "'document.cookie = \"clearChatBar=clear; expires=\\\"Fri, 11 Apr 2011 18:30:19 GMT\\\"; path=/\"'"
	+ ">  \
	Clear</p>\
	<br><center>\
	<form name='_xclick' action='https://www.paypal.com/us/cgi-bin/webscr' method='post'>\
	<input type='hidden' name='cmd' value='_xclick'>\
	<input type='hidden' name='business' value='autocolorizer@gmail.com '>\
	<input type='hidden' name='item_name' value='Thanks for adding color!'>\
	<input type='hidden' name='currency_code' value='USD'>\
	Amount <input type='' name='amount' value='15.00'>\
	<input type='image' src='http://www.paypal.com/en_US/i/btn/btn_donate_LG.gif' border='0' name='submit' alt='Make payments with PayPal - it's fast, free and secure!'>\
	</form></center>\
	\
	</td></tr></tbody></table>";
	
	
	dialogBody.replaceChild(div, dialogBody.firstChild);

	return;


}

function CheckForTemporaryStoredVariables() {
	
	//alert("GetStorage('clearChatBar') == " + GetStorage('clearChatBar'));
	var clearChatBar = GetStorage('clearChatBar');
	if (clearChatBar != null && clearChatBar != "") {
		//alert("Found clearChatBar = " + clearChatBar)
		if ((clearChatBar == "clear") || (clearChatBar == "colored")) {
			//alert("Settings clearChatBar = " + clearChatBar); 
			try { GM_setValue('clearChatBar',clearChatBar); } 
			catch(err) { alert("Error: Can not write to storage.\n\nabout:config key was reset; Restart firefox."); }
		}
		DeleteStorage("clearChatBar");
	}
}

function SetStorage(key,value) {
	//globalStorage[''][key] = value;
	
	// Global storage doesn't work with greasemonkey, so using cookies....
	var date = new Date();
	date.setTime(date.getTime()+120);
	document.cookie = key+"="+value+"; expires="+date.toGMTString()+"; path=/";
}

function GetStorage(key) {
	//return globalStorage[''][key];
	
	try {  
		// Global storage doesn't work with greasemonkey, so using cookies....
		var nameEQ = key + "=";
		var ca = document.cookie.split(';');
		for(var i=0;i < ca.length;i++) {
			var c = ca[i];
			while (c.charAt(0)==' ') c = c.substring(1,c.length);
			if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
		}
		return null;
	}
	catch(err) { /* Do Nothing */; }

}

function DeleteStorage(key) {
	//globalStorage[document.location.host || ".localdomain"][key] = null;

	// Global storage doesn't work with greasemonkey, so using cookies....
	document.cookie = key+"=; expires=-1; path=/";
}

function PreCacheAlbumPics() {
	StartTimer("PreCache Album Pics");
	var entries = document.getElementsByTagName('div');
	var numEntries = entries.length;

	var myImages = document.getElementsByTagName('img');
	for (var i=0; i<myImages.length; i++) {
		url = myImages[i].src;
		if (url.indexOf('http://photos-')>-1) {
		
			photoUrl = url.split('/n').join('/t').split('/s').join('/t').split('/q').join('/t');
			
			StartTimer("Look for PreCache Album Pics in Cache");
			if (RetrieveFromCache(photoUrl, UrlToCacheType(photoUrl)) == null) GetColors(photoUrl, false);
			RecordTimer("Look for PreCache Album Pics in Cache");
		
		}
	}
	RecordTimer("PreCache Album Pics");
}

function SetLastCheckedForVersionVariable() {
	var now = new Date();
	try { GM_setValue('lastCheckedForVersion',"" + now.getTime()); } //make sure it's a string as firefox can't store a 42bit integer
	catch(err) { alert("Error: Can not write to version key.\n\nabout:config key was reset; Restart firefox."); }
	return;
}


function StartTimer(timerName) {
	Timer(timerName, true);
}


function ReadTimer(timerName) {
	alert("  " + timerName + " took " + Timer(timerName, false) + " ms.");
}


function RecordTimer(timerName) {
	timerRecord = timerRecord + "  " + timerName + " took " + Timer(timerName, false) + " ms.\n";  

	if (typeof(timersOff) == 'undefined') timersOff = false;
	if (timersOff != true) {
		if (typeof(printTimer) != 'undefined') clearTimeout(printTimer);
		printTimer = setTimeout('alert(unescape("' + escape("Timers:\n" + timerRecord) + '"));',3000)
	}
}


function PrintRecordTimer() {
	if (typeof(timersOff) == 'undefined') timersOff = false;
	if (timersOff != true) {
		alert("Timers:\n" + timerRecord);  
	}
}


function Timer(timerName, startTimer) {
	//timers = new Array();
	if (typeof(timersOff) == 'undefined') timersOff = false;
	if (timersOff != true) {
		if (startTimer == true) { 
			timers[timerName] = new Date(); 
			return -1;
		}
		else {
			currentTime = new Date();
			return (currentTime.getTime() - timers[timerName].getTime());
		}
	}
}


function GetColors(photoUrl, applyColors) {  //  (applyColors =  true) means get color scheme & apply it immediately 
	StartTimer("Server Access");
	GM_xmlhttpRequest({                      //  (applyColors = false) means only precache color scheme for that picture
		method: 'GET',
		url: 'http://pages.cs.wisc.edu/~ormont/cgi-bin/facebook/color-pallette.pl?url=' + photoUrl + '&ver=' + thisScriptVersion.join("."),
		headers: {
			'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
			'Accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
		},
		onload: function(responseDetails) {
			
			//alert(responseDetails.responseText);
			
			var colorStringArray=responseDetails.responseText.split("#");
			
			var colorArray = new Array(10);
			
			var parser = new DOMParser();
			var dom = parser.parseFromString(responseDetails.responseText,"application/xml");
			var entries = dom.getElementsByTagName('color');
			
			for(var k = 0; (k < entries.length); k++) colorArray[k] = "#" + entries[k].textContent;
			
			//added 4-17-06 to make sure it doesn't pull an all black color scheme
			if (colorArray[9] == "#000000") {
				failedBlackAttempts++;
				if (failedBlackAttempts < 5) {
					//  alert("failedBlackAttempts: " + failedBlackAttempts);
					GetColors(photoUrl, true);
					return 1;
				}
				//  else alert("Failed to get colors");
				return 1;
			}
			
			StartTimer("Sort by brightness");
			//sort by brightness
			var x, y, holder, brightness1, brightness2;
			for(x = 0; x < colorArray.length; x++) {
				for(y = 0; y < (colorArray.length-1); y++) {
					brightness1 = (parseInt(colorArray[y].substr(1,2), 16) + parseInt(colorArray[y].substr(3,2), 16) + parseInt(colorArray[y].substr(5,2), 16));
					brightness2 = (parseInt(colorArray[y+1].substr(1,2), 16) + parseInt(colorArray[y+1].substr(3,2), 16) + parseInt(colorArray[y+1].substr(5,2), 16));
					
					if(brightness1 > brightness2) {
						holder = colorArray[y+1];
						colorArray[y+1] = colorArray[y];
						colorArray[y] = holder;
					}
				}
			}
			RecordTimer("Sort by brightness");
			
			
			StartTimer("Add to cache");
	
			cacheType = UrlToCacheType(photoUrl);
			
			var splitUrl = photoUrl.split("/");
			var filename=splitUrl[splitUrl.length - 1];
			
			//alert("cache type: " + cacheType);
			cacheName = CacheTypeToCacheName(cacheType);
			
			
			try { cachedPageColors = GM_getValue(cacheName,''); }
			catch(err) { /*Die silently alert("Error: Can not read from cache.\n\nabout:config key was reset; Restart firefox.");*/ }
			
			
			
			if ((typeof cachedPageColors == 'undefined') || (cachedPageColors.length == 0)) GM_setValue(cacheName,filename + '-' + colorArray.join('*'));
			else {
				var cachedPageColorsSplit = cachedPageColors.split(',');
				
				if (cachedPageColorsSplit.length + 1 >= 70) {
					//  alert("Num of Cached: " + cachedPageColorsSplit.length);
					//  alert('old records: ' + cachedPageColorsSplit.join('\n'));
					
					for(x = 0; x < cachedPageColorsSplit.length - 1; x++) {
						cachedPageColorsSplit[x] = cachedPageColorsSplit[x + 1];
					}
					cachedPageColorsSplit[cachedPageColorsSplit.length - 1] = filename + '-' + colorArray.join('*');
					//  alert('new records: ' + cachedPageColorsSplit.join('\n'));
					GM_setValue(cacheName,cachedPageColorsSplit.join(','));
				}
				else GM_setValue(cacheName,cachedPageColors + "," + filename + '-' + colorArray.join('*'));
			}
			RecordTimer("Add to cache");			
			
			//alert("putting in cache: " + cacheName);
			
			RecordTimer("Server Access");
			
					
			if (applyColors != false) {
				StartTimer("InsertCSS");
				InsertCSS(colorArray);
				RecordTimer("InsertCSS");
			}
			return 0;
		}

	});
}