Source for "Facebook Fixer"

By Vaughan Chandler
Has 18 other scripts.


// ==UserScript==
// @name        Facebook Fixer
// @namespace   http://userscripts.org/people/14536
// @description A collection of enhancements for various aspects of Facebook eg. showing bigger profile pictures, making it easier to view albums, showing people's age and sign, auto-reloading error pages, changing redirecting links to direct links.
// @include     http://facebook.com/*
// @include     http://*.facebook.com/*
// @exclude     *facebook.com/logout.php*
// @exclude     *facebook.com/*logged_out*
// @author      Vaughan Chandler
// ==/UserScript==

// Last updated on 2008-03-09


// Get user's facebook ID.
var links = document.body.getElementsByTagName('a');
var facebookID = -1;
for (i=0; i<links.length; i++) {
	if ((m = links[i].href.match(/profile.php?.*id=(\d+)/)) && links[i].innerHTML.toLowerCase() == 'profile') {
		var facebookID = m[1]-0;
		break;
	}
}
if (facebookID == -1) { GM_log('Unable to get Facebook ID, default preferences will be used.'); }


// Load preferences.
var prefs = {
	featureBPP : getValue('featureBPP', true, 'boolean'),
	featureBAP : getValue('featureBAP', true, 'boolean'),
	featureAGE : getValue('featureAGE', true, 'boolean'),
	featureEPR : getValue('featureEPR', true, 'boolean'),
	featureLNK : getValue('featureLNK', true, 'boolean'),
	featureUPD : getValue('featureUPD', true, 'boolean'),
	bigPicPosition : getValue('bigPicPosition', 'left', ';left;inline;right;'),
	bigPicAutoclose : getValue('bigPicPosition', 'left', ';left;inline;right;')=='inline' ? getValue('bigPicAutoclose', true, 'boolean') : getValue('bigPicAutoclose', false, 'boolean'),
	errorReloadDelay : getValue('errorReloadDelay', 5, 'number')
}


// Custom function for getting stored values.
function getValue(name, defaultValue, validValues) {
	if (facebookID == -1) { return defaultValue; }
	var buf = GM_getValue(facebookID + '-' + name, -1);
	if (buf == -1 || (validValues.indexOf(';' + buf + ';') == -1 && typeof(defaultValue) != validValues)) {
		GM_setValue(facebookID + '-' + name, defaultValue);
		return defaultValue;
	}
	return buf;
}


// Custom function for storing values.
function setValue(name, value) {
	GM_setValue(facebookID + '-' + name, value);
}


// Makes the location easily available throughout the script.
var loc = window.location.href.toLowerCase();


// Adds code related to photo albums.
// 2008-03-02
if (prefs.featureBAP && (loc.indexOf('facebook.com/album.php?') != -1 || loc.indexOf('facebook.com/photo_search.php?') != -1)) {
	try {
		// Add link to show big pictures.
		try {
			var divs = document.getElementById('album').previousSibling.getElementsByTagName('div');
			for (i=0; i<divs.length; i++) {
				if (divs[i].className.indexOf('summary')!=-1) {
					var albumSpan = document.createElement('span');
					albumSpan.innerHTML = ' | <a id="FBF2BigAlbumPics" href="#" onclick="return false;">Show Big Pictures</a>';
					divs[i].firstChild.insertBefore(albumSpan, divs[i].firstChild.lastChild.nextSibling);
					break;
				}
			}
			document.getElementById('FBF2BigAlbumPics').addEventListener('click', function(){makeAlbumPicsBig(false);}, true);
		} catch(e) { GM_log("Unable to add link for showing big pics on the following page:\n" + document.title + '(' + loc + ')\n' + e.message); }
		// Add menu commands for showing big pictures and maximizing the album.
		GM_registerMenuCommand("Show Big Pictures", function(){makeAlbumPicsBig(false);});
		GM_registerMenuCommand("Maximize Album", function(){makeAlbumPicsBig(true);});
		// Replace small albums pics with the bigger versions. When maximize is false pictures will be changed in place.
		function makeAlbumPicsBig(maximize) {
			var html='';
			var pics = document.getElementById('album').getElementsByTagName('img');
			for (i=0; i<pics.length; i++) { html = html + '<a href="' + pics[i].parentNode.href + '"><img src="' + pics[i].src.replace('/s','/n') + '" /></a>'; }
			if (maximize) { document.body.innerHTML = html; }
			else { document.getElementById('album').innerHTML=html; }
		}
	} catch(x) { GM_log('FBF Error (BAP) - ' + x.message); }
}


// Add Age and Sign to profiles if appropriate data is available.
// 2008-03-02
if (prefs.featureAGE && loc.indexOf('profile.php?')!=-1) {
	try {
		var info='';
		var birthdayTD;
		var bday = new Date();
		// Determine age, if possible.
		birthdayTD = document.getElementById('Birthday').getElementsByTagName('td')[1];
		var monthDay = birthdayTD.getElementsByTagName('a')[0].innerHTML;
		try {
			var year = birthdayTD.getElementsByTagName('a')[1].innerHTML;
			bday.setTime(Date.parse(monthDay+', '+year));
			var now = new Date();
			var age = now.getFullYear()-bday.getFullYear();
			if (now.getMonth()<bday.getMonth()) { age-=1; }
			else if (now.getMonth()==bday.getMonth() && now.getDate()<bday.getDate()) { age-=1; }
			info = age+' years old';
		} catch(x) {
			bday.setTime(Date.parse(monthDay+', 2007'));
		}
		// Determine sign.
		var signs = new Array("Capricorn","Aquarius","Pisces","Aries","Taurus","Gemini","Cancer","Leo","Virgo","Libra","Scorpio","Sagittarius");
		var endDates = new Array(19,18,20,19,20,21,22,22,22,22,21,21);
		var m = bday.getMonth();
		var d = bday.getDate();
		var sign = m;
		if (d>endDates[m]) { sign = (sign + 1) % 12; }
		info = (info=='' ? '' : info+', ') + signs[sign];
		// Show age/sign.
		if (info!='') {
			var ageSpan = document.createElement('span');
			ageSpan.innerHTML = info;
			birthdayTD.appendChild(ageSpan);		
		}
	} catch(x) { GM_log('FBF Error (AGE) - ' + x.message); }
}


// Bigger profile pics
// 2008-03-08
if (prefs.featureBPP) {
	try {
		// Read preferences.
		if (prefs.bigPicPosition=='inline') {
			if (loc.indexOf('profile.php')==-1) {
				window.addEventListener('load', function() {
					var elms = document.getElementById('content').getElementsByTagName('img');
					for (i=0; i<elms.length; i++) {
						img = elms[i];
						if (img.src.indexOf("http://profile") != -1 && (img.src.indexOf("/s") != -1 || img.src.indexOf("/t") != -1 || img.src.indexOf("/q") != -1)) {
							if (img.parentNode.parentNode.className.toLowerCase().indexOf('wallimage')!=-1) {
								// Skip images in walls which don't seem to work well with 'inline' mode.
								continue;
							}
							bigPicURL = img.src.replace(/\/[qst]([\d_]+)\.jpg/, "/n$1.jpg");
							bigPicDimensions = (img.src.indexOf("/t") != -1 ? 'width="'+(img.width*4)+'" height="'+(img.height*4)+'"' : '');
							newHTML = '<span class="FBFSmallProfilePic"><img src="' + img.src + '"/><img src="' + bigPicURL + '" class="FBFBigProfilePic" ' + bigPicDimensions + '/></span>';
							img.parentNode.innerHTML = newHTML;
							i++; // Because an image was added. Otherwise it will loop indefinitely.
						}
					}	
					GM_addStyle(
						'.FBFSmallProfilePic img { display:inline ! important; z-index:999996;}'+
						'.FBFSmallProfilePic .FBFBigProfilePic { display:none ! important; position:absolute ! important; background:white ! important; z-index:999997;}'+
						'.FBFSmallProfilePic:hover .FBFBigProfilePic { display:inline ! important; border:3px ridge #3399ff ! important; }'
					);
				}, true);
			}
		} else {
			// Add styles required.
			GM_addStyle(
				'#FBPPdiv { display:none; position:fixed !important; top:2px !important; ' + prefs.bigPicPosition + ':2px !important; border:1px solid #003366; background:#3b5998; padding:2px 4px; min-width:130px; z-index:99999 !important;}'+
				'#FBPPheader, #FBPPloading { text-align:center; color:white; font-variant:small-caps; font-weight:bold !important; }'+
				'#FBPPclose { text-align:right; color:#ffaaaa; cursor:pointer; font-weight:bold; height:1px; }'+
				'#FBPPclose:hover { color:#aa6666; }'+
				'#FBPPimg { text-align:center; }'+
				'#FBPPimg img { color:white; }'
			);
			// Create placeholder for images.
			var div = document.createElement('div');
			div.id = 'FBPPdiv';
			div.innerHTML = (!prefs.bigPicAutoclose ? '<div id="FBPPclose" title="Close">x</div>' : '') + '<div id="FBPPheader">Big Profile Pic</div><div id="FBPPimg"><span></span></div>';
			document.body.insertBefore(div, document.body.lastChild.nextSibling);
			// Listen for clicks on the x and make pic disappear.
			if (!prefs.bigPicAutoclose) {
				document.getElementById('FBPPclose').addEventListener('click', function() { document.getElementById('FBPPdiv').style.display='none'; }, false);
			}
			// Add event listeners
			function addEventListeners() {
				var images = document.getElementsByTagName('img');
				var s;
				var newSrc;
				var profileLink;
				for (var i=0, j=0; i<images.length; i++) {
					s = images[i].src;
					// Determine if the image is a small profile picture.
					if (s.indexOf("http://profile") != -1 && (s.indexOf("/s") != -1 || s.indexOf("/t") != -1 || s.indexOf("/q") != -1)) {
						// Listen for mouse overs to show the big pic.
						images[i].addEventListener('mouseover', function() {
							newSrc = this.src.replace(/\/[qst]([\d_]+)\.jpg/, "/n$1.jpg");
							profileLink = 'http://www.facebook.com/profile.php?id=' + newSrc.match(/\/n(\d+)_\d+\.jpg/)[1];
							document.getElementById('FBPPimg').innerHTML = '<a href="' + profileLink + '"><img src="' + newSrc + '" alt="Loading Pic..." /></a>';
							document.getElementById('FBPPdiv').style.display = 'block';
						}, false);
						// If autoclosing, listen for mouse outs to hide the big pic.
						if (prefs.bigPicAutoclose) {
							images[i].addEventListener('mouseout', function() {
								document.getElementById('FBPPdiv').style.display = 'none';
							}, false);
						}
					}
				}
			}
			addEventListeners();
			// Handle special cases on the friends pages
			var needsUpdating = false;
			if (loc.indexOf('http://www.facebook.com/friends') != -1) {
				// If you change the view then refresh the page, the images load after the page so the listeners must be updated.
				if (loc.indexOf('#') != -1) { needsUpdating = true; }
				// If you change the view the listeners must be updated.
				var links = document.getElementById('ftabs').getElementsByTagName('a');
				for (var i=0; i<links.length; i++) {
					links[i].addEventListener('click', function() { needsUpdating = true; }, false);
					try {
						// If you use the dropdown on the "more" page the listeners must be updated.
						document.getElementById('friends_nk').addEventListener('change', function() { needsUpdating = true; 	}, false);
					} catch(x){}
				}
				// Updated the listeners if required.
				document.getElementById('friends_target').addEventListener('mouseover', function() {
					if (needsUpdating) {
						addEventListeners();
						needsUpdating = false;
					}
				}, false);
			}
		}
	} catch(x) { GM_log('FBF Error (BPP) - ' + x.message); }
}


// Make redirecting links direct
if (prefs.featureLNK) {
	for (i=0; i<document.links.length; i++) {
		if (m = document.links[i].href.match(/\/(note|share)_redirect\.php\?.*url=([^&]+)/)) {
			document.links[i].href = unescape(m[2]);
		}
	}
}


// Check for Updates (thanks to Jarett - http://userscripts.org/users/38602)
// 2008-03-09
if (prefs.featureUPD) {
	var version_scriptNum = 8861;
	var version_timestamp = 1205044498741;
	function updateCheck(forced) {if((forced)||(parseInt(GM_getValue("lastUpdate", "0")) + 86400000 <= (new Date().getTime()))) {try {GM_xmlhttpRequest({method: "GET",url: "http://userscripts.org/scripts/review/" + version_scriptNum + "?" + new Date().getTime(),headers: {'Cache-Control': 'no-cache'},onload: function(xhrResponse) {GM_setValue("lastUpdate", new Date().getTime() + ""); var rt = xhrResponse.responseText.replace(/&nbsp;?/gm, " ").replace(/<li>/gm, "\n").replace(/<[^>]*>/gm, ""); var scriptName = (/@name\s*(.*?)\s*$/m.exec(rt))[1]; GM_setValue("targetScriptName", scriptName); if (parseInt(/version_timestamp\s*=\s*([0-9]+)/.exec(rt)[1]) > version_timestamp) {if (confirm("There is an update available for the Greasemonkey script \"" + scriptName + ".\"\nWould you like to go to the install page now?")) {GM_openInTab("http://userscripts.org/scripts/show/" + version_scriptNum);}} else if (forced) {alert("No update is available for \"" + scriptName + ".\"");}}});} catch (err) {if (forced) {alert("An error occurred while checking for updates:\n" + err);}}}}
	updateCheck(false);
}


// Automatically reload error pages.
// 2008-03-09
if (prefs.featureEPR && loc.indexOf('apps.facebook.com') != -1) {
	var delay = prefs.errorReloadDelay;
	var interval;
	try {
		if (document.getElementById('error_message').innerHTML.toLowerCase().indexOf('error while loading page from') != -1) {
			try {
				var cancelButton = document.createElement('input');
				cancelButton.type = 'button';
				cancelButton.value = 'Do Not Try Again';
				cancelButton.className = 'inputbutton';
				cancelButton.addEventListener('click', function() {
					tryAgainButton.value = 'Try Again';
					cancelButton.disabled = true;
					clearInterval(interval);
					this.style.background = "#999999";
				}, true);
				var tryAgainButton = document.getElementById('try_again_button');
				tryAgainButton.parentNode.insertBefore(cancelButton, tryAgainButton.nextSibling);
				delay++;
				interval = setInterval(function(){
					delay--;
					if (delay==0) {
						tryAgainButton.value = 'Trying Now';
						tryAgainButton.click();
						clearInterval(interval);
					} else {
						tryAgainButton.value = 'Trying in ' + delay + ' seconds';
					}
				}, 1000);
			} catch(x) { GM_log('FBF Error (EPR) - ' + x.message); }
		}
	} catch(x) {}
}


// Show config screen
// 2008-03-09
if (facebookID != -1) { 
	GM_registerMenuCommand('Configure Facebook Fixer', function() {
		try {
			try { document.getElementById('FBPPdiv').style.display = 'none'; } catch(x){}
			GM_addStyle(
				'#FBFconfig { margin:25px; background:white; }'+
				'#FBFconfig h3 { font-size:14px; text-align:center; }'+
				'#FBFconfig h4 { font-size:12px; border-bottom:1px solid #cccccc; margin-top:30px; }'+
				'#FBFconfig div { text-align:center; }'+
				'#FBFconfig input { margin:0; }'+
				'.FBFnote { color:#888888; }'+
				'#errorReloadDelay { width:50px; }'
			);
			var html = '<h3>Facebook Fixer Preferences</h3>'+
				'<div>(Changes are saved immediately)</div>'+
				'<h4>Script Features</h4>'+
				'<p><input type="checkbox" id="featureBAP" ' + (prefs.featureBAP ? ' checked="checked"' : '') + ' /><label for="featureBAP"> Big Album Pictures</label><br /><span class="FBFnote">Lets you view full-size versions of album pictures all on the same page.</span></p>'+
				'<p><input type="checkbox" id="featureBPP" ' + (prefs.featureBPP ? ' checked="checked"' : '') + ' /><label for="featureBPP"> Big Profile Pictures</label><br /><span class="FBFnote">Shows a bigger version of a profile picture when you put the mouse over the small version.</span></p>'+
				'<p><input type="checkbox" id="featureAGE" ' + (prefs.featureAGE ? ' checked="checked"' : '') + ' /><label for="featureAGE"> Birthday Info</label><br /><span class="FBFnote">Shows a persons age and sign on their profile (if they provide their birthdate).</span></p>'+
				'<p><input type="checkbox" id="featureEPR" ' + (prefs.featureEPR ? ' checked="checked"' : '') + ' /><label for="featureEPR"> Error Page Reloader</label><br /><span class="FBFnote">Automatically reload application error pages.</span></p>'+
				'<p><input type="checkbox" id="featureLNK" ' + (prefs.featureLNK ? ' checked="checked"' : '') + ' /><label for="featureLNK"> Fix Redirecting Links</label><br /><span class="FBFnote">Changes redirecting links to direct links.</span></p>'+
				'<p><input type="checkbox" id="featureUPD" ' + (prefs.featureUPD ? ' checked="checked"' : '') + ' /><label for="featureUPD"> Check for Updates</label><br /><span class="FBFnote">Check Userscripts.org daily for updates to Facebook Fixer.</span></p>'+
				'<h4>Big Profile Pics</h4>'+
				'<p><input type="checkbox" id="bigPicAutoclose" ' + (prefs.bigPicAutoclose ? ' checked="checked"' : '') + (prefs.bigPicPosition=='inline' ? ' disabled="disabled"' : '') + ' /><label for="bigPicAutoclose"> Only show big profile pics while the mouse is over the small version.</label><br /><span class="FBFnote">This option is always used when big pictures are shown next to the small pictures.</span></p>'+
				'<p><span style="font-weight:bold; color:#777777;">Where should the big profile pic appear?</span> <select id="bigPicPosition">'+
					'<option value="left"'+ (prefs.bigPicPosition=='left' ? ' selected="selected"' : '') + '>On the left of the screen</option>'+
					'<option value="right"'+ (prefs.bigPicPosition=='right' ? ' selected="selected"' : '') + '>On the right of the screen</option>'+
					'<option value="inline"'+ (prefs.bigPicPosition=='inline' ? ' selected="selected"' : '') + '>Next to the small picture</option>'+
				'</select><br /><span class="FBFnote">The script works much better when showing the picture on the left or the right.</span></p>'+
				'<h4>Error Page Reloader</h4>'+
				'<p>Number of seconds to wait before reloading: <input type="text" id="errorReloadDelay" value="' + prefs.errorReloadDelay + '" /></p>'+
				'<br /><br />'+
				'<div><input type="button" value="Return to previous page" onclick="window.location=window.location" /></div>';
			document.getElementById('content').innerHTML = '<div id="FBFconfig">' + html + '</div>';
			document.getElementById('featureBPP').addEventListener('click', function() { setValue('featureBPP', this.checked); }, false);
			document.getElementById('featureBAP').addEventListener('click', function() { setValue('featureBAP', this.checked); }, false);
			document.getElementById('featureAGE').addEventListener('click', function() { setValue('featureAGE', this.checked); }, false);
			document.getElementById('featureEPR').addEventListener('click', function() { setValue('featureEPR', this.checked); }, false);
			document.getElementById('featureLNK').addEventListener('click', function() { setValue('featureLNK', this.checked); }, false);
			document.getElementById('featureUPD').addEventListener('click', function() { setValue('featureUPD', this.checked); }, false);
			document.getElementById('bigPicAutoclose').addEventListener('click', function() { setValue('bigPicAutoclose', this.checked); }, false);
			document.getElementById('bigPicPosition').addEventListener('change', function() {
				setValue('bigPicPosition', this.options[this.selectedIndex].value);
				if (this.options[this.selectedIndex].value == 'inline') {
					document.getElementById('bigPicAutoclose').checked='checked';
					document.getElementById('bigPicAutoclose').disabled='disabled';
					setValue('bigPicAutoclose', true);
				} else {
					document.getElementById('bigPicAutoclose').disabled='';
				}
			}, false);
			document.getElementById('errorReloadDelay').addEventListener('keyup', function() {
				try {
					setValue('errorReloadDelay', Math.abs(this.value.split('.')[0]));
				} catch(x) {}
			}, false);
		} catch(x) {
			window.alert('Unable to show preferences, please refresh the current page and try again');
			GM_log('FBF Error (PREF) - ' + x.message);
		}
	});
}