Jul 062010
 

In my Greasemonkey scripts, I sometimes find it necessary to alert the user in some way without using the alert() function. In any other web application, I would normally use Facebox, a wonderfully free jQuery plugin that displays Facebook style pop-ups. However, there are several obstacles to adding any (well, most) jQuery plugins to Greasemonkey scripts:

  • The plugin itself is a script which must be kept somewhere. You can directly download the script from the Facebox server with Ajax, but that might result in an obscene amount requests to the server (which is not proper internet etiquette).
  • Some plugins require external media (i.e. images, CSS sheets, etc.), which, as described above, shouldn’t be downloaded directly from the owning server.
  • Media can sometimes be embedded in the script, but plugins can sometimes be too big or awkward to store inline.

Luckily, Greasemonkey and JavaScript contains the features necessary to resolve these issues.

The first and third issues can be solved with Greasemonkey’s GM_getValue() and GM_setValue() functions and a bit of Ajax. The idea is to download the plugin script once, and cache it using GM_setValue(). Then, the next time the script runs we can use GM_getValue() to get it.

The second issue can be solved using Firefox’s handy data URI feature. I used this website to upload the plugin’s image files and convert them to a data URI. In the code that injects Facebox, the references to these files in the plugin code is replaced with the file data, inline. Note that the Facebox CSS was minified as well, although I have misplaced the link to the website I used. Here is the code (or, alternatively, download it from here):

 // Facebox: Copyright 2007, 2008 Chris Wanstrath [ [email protected] ]

var loadingImg = "data:image/gif;base64,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";

var closeImg = "data:image/gif;base64,R0lGODlhQgAWAOYAAMDAwMHBwZ2dnVlZWdLS0oODg6+vr/X19eTk5FBQULi4uIyMjJSUlFRUVFZWVtzc3FJSUsnJyVFRUVhYWFVVVezs7FNTU/b29v39/erq6vr6+qampldXV6KiokxMTE9PT2ZmZvf3993d3czMzGBgYHt7e6Ghofz8/PDw8N7e3mxsbLW1tfv7+5WVlc3NzcrKynh4eOvr60tLS+Dg4F9fX7m5ud/f37S0tIaGhnBwcGNjY+Xl5ZKSkoiIiKmpqW5ubpeXl29vb9XV1Y6Oju3t7WVlZXZ2dpiYmG1tbXx8fO/v7+fn57GxsbOzs/Hx8Xl5eVpaWmRkZE1NTXNzc2dnZ5aWloeHh+Pj405OTmlpadvb23p6ev////7+/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAABCABYAAAf/gFyCg4SFhoeIiYqLjI2Oj4xdkpOUlZaXl5BcJ42Ynp+YkZJcLEdCXJZcIwIaqKCvoKJdIUAeUSKuXYIuNB5VF5QVWgiwXQjDlwdaWrqLo00yAxZFWq6qJA0DHj6SBAtb4AsEXQBbzJUABeBbBQCUCusFL1wB6+uDoxlbHwMJVDaCRtCwMAALDhRdImxZEEFLhAIGyJmrZGALAwJaCDDYElGigYwbLtDjGKBkAHz5SiQY0EDFDBfYBkixEgJVgQIHJuWUeE4Sgi0CKgmYOJSSoHoPDhmtUIIfBSTYJiToIbKLli3uLJXrybPSVQVdKirYeXRLUkOVuMRIAmGAAw4D/yAMqSlp6yW7k4paAtqlgrotGxCUbSGgsAKURlEYoTBgQoMOrSbhRTcx75ZLfLscAPDN7EjChhFP4qIkCGOWDHRNIoD1bmVJ8MZRYp1VMseRZwulnTElGwUHcU1gcHXg5k5jOSdbzWyZmOZJQHErHZ1ChQRpWUA4mAChw3BJChk6LLBgM0cA6JlVvJhxI9iw7DdsOVnPgMkARARRB3E9AYwrLziwnQTCueJNPOOUY09W6ayzQG0NgnPYSPZ4Zo0IIEAARQIlZCBIDQJOIEEHJ1ByjHOvnHjJMjlFwsUDOqzkXwyoCLKCBBxwIMMNxfSYijNcOMEABB9s4aFRK1iQQDAOIvjo5CjO6MICD09UkAuUTPyww5VPwiILFxpUdQkGS3DZ5SuyNOOJfmf6mGaPgQAAOw==";

var faceCss = "#facebox .b{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAQCAYAAADwMZRfAAAAG0lEQVQ4jWMICgraQClmGDVk1JBRQ0YNCdoAAHYawHDC5WlcAAAAAElFTkSuQmCC')}#facebox .tl{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAS0lEQVR42o3OoQ0AIAxEUZYi7NEluggewwy1dMNyBgIJCSe+uTxxKSKuRKQgRRV1ZGicIKOG/NVGa/jB9oPrkzNQWVhZ2FloLBwMnD51rC95s060AAAAAElFTkSuQmCC')}#facebox .tr{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAARElEQVR42o3KoREAIAwEMJbi2KNLdBE8pjPUlg3Lo8BwvIhLEZEAB4MOCi0zy23H+TCg/uNR2TjYuDU2Khs7G42NzsZYRf6sL6b2F1EAAAAASUVORK5CYII=')}#facebox .bl{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQ0lEQVQY02MICgpaD8QbCGEGILGMWIVTiFXYQqzCdGIVmhOl8P///yDF/cQqNCVKIZLifoIKkTSYQz3YAg06UDivBwBLtawvNrYbVAAAAABJRU5ErkJggg==')}#facebox .br{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAQ0lEQVQYlY3KoRUAIAhFUZbyuMdbgkXoFmaw6oaYyP5w2zXgCo6Jcasx1RhqdDVOJa6qMiWOX1ydOh5gAwkE4MDs0B5TPqwv1+d6zQAAAABJRU5ErkJggg==')}#facebox{position:absolute;top:0;left:0;z-index:100;text-align:left}#facebox .popup{position:relative}#facebox table{border-collapse:collapse}#facebox td{border-bottom:0;padding:0}#facebox .body{padding:10px;background:#fff;width:370px}#facebox .loading{text-align:center}#facebox .image{text-align:center}#facebox img{border:0;margin:0}#facebox .footer{border-top:1px solid #DDD;padding-top:5px;margin-top:10px;text-align:right}#facebox .tl,#facebox .tr,#facebox .bl,#facebox .br{height:10px;width:10px;overflow:hidden;padding:0}#facebox_overlay{position:fixed;top:0;left:0;height:100%;width:100%}.facebox_hide{z-index:-100}.facebox_overlayBG{background-color:#000;z-index:99}* html #facebox_overlay{position:absolute;height:expression(document.body.scrollHeight>document.body.offsetHeight ? document.body.scrollHeight:document.body.offsetHeight+'px')}";

GM_addStyle(faceCss);
GM_addStyle("#facebox {font-family: 'lucida grande',tahoma,verdana,arial,sans-serif}");

// Cache the facebox JS to avoid bombarding the hosting site
var cache = GM_getValue("facebox", null);
if (cache == null)
{
	console.log("Script not in cache... fetching remote version");
	GM_xmlhttpRequest({
		'method':'GET',
		'url':"http://defunkt.github.com/facebox/facebox.js",
		'onload':function(d){
			GM_setValue("facebox", d.responseText);
			injectAndContinue(d.responseText);
		}
	});
}
else {
	console.log("Script already in cache... loading cached version");
	injectAndContinue(cache);
}

function injectAndContinue(script)
{
	script = script.replace("loadingImage : '/facebox/loading.gif'","loadingImage : '" + loadingImg + "'");
	script = script.replace("closeImage   : '/facebox/closelabel.gif'", "closeImage : '" + closeImg + "'");

	// Inject script
	$("<!--mce:0-->").appendTo('head');

	main();
}

Also note that this script assumes that the website the script is included in already uses jQuery. If not, you might want to inject it yourself.

Hopefully in the near future I will roll out a tool which automates the process of making jQuery plugins (and other scripts) suitable for inclusion in Greasemonkey scripts.

 Posted by at 7:22 pm