Greasemonkeying around

who is that masked manHow would you feel if you came in to the office and read that World War III had started? Or that you had been voted "Least sexist person 2005"? Or the government was banning alcohol? You'd feel pretty bad, I bet. But, what if you could make someone else feel like that? Then you'd be smiling, wouldn't you! With Greasemonkey and Firefox you too can revel in this kind of childish joy!

What we are going to do is "inject" our own stories into proper web sites. So when our annoying co-worker goes to www.times.com the leading story reads "This just in: annoying co-worker is, um, smelly". With accompanying humourous photoshopped image of co-worker. Great stuff!

What do we need? Well first off the person you are aiming the hilarity at (we'll call them "the mark" to sound all professional) needs to be running Mozilla Firefox. If they aren't, do them a favour and install it for them. Second, they need to have the Greasemonkey extension running - because our trick uses this. Thirdly, you need to install our Greasemonkey script that changes the web page. As you can see this rules out 99.99% of internet users, but still - if they aren't running Firefox and Greasemonkey they shouldn't be on the web anyway.

So lets start by examining our Firefox-using, Greasemonkey-installed mark. What's their internet poison? Are they on eBay.com all day long? Do they check newscientist.com fifty times a day? For demonstration purposes I'll be using The Sydney Morning Herald web site, because it's a news site and it's popular. In Sydney.

Now that we have our mark, set up your template script to run on the target web page. My initial script just alerts a message - to test it works all sparkly:

// ==UserScript==
// @name	SMH Hilarity
// @namespace	http://mrspeaker.webeisteddfod.com/greasemonkey
// @description	SMH story injector
// @include	http://www.smh.com.au/
// ==/UserScript==

(function(){
    alert('heres where da code goes');
})();

Next, choose a good area to inject your story/article/item. I'm selecting the "sidebar" news stories. These stories consist of a small image, with some text IN CAPITAL LETTERS for some reason, followed by a short description of the news item. Pretty easy to reproduce, and (perhaps) more believable than a main story spot.

The easiest way to make it look exactly the same as the other stories is to grab the existing HTML. Just go View->Page Source and grab the relevant bits. Selecting the right HTML is tricky, and requires some skillz, but not too much. Change the content to something particularly hilarious, and put the new HTML into a variable in your script. Heres mine:

var story = "<span class='featureNewsStoryPic'> \n";
story += "<a href='http://blogs.smh.com.au/razor/'>";
story += "<img src='http://mrspeaker.webeisteddfod.com/images/whois.jpg' width='200' height='60' /></a>\n";
story += "</span>\n<p>\n";
story += "Mr Speaker has got the entire nation talking... but who is he??? <a href='http://blogs.smh.com.au/'>more</a>\n";



story += "<div class='clear'></div>\n";
story += "</p>\n";

// Now add that killer story to a new DIV object:
var newDiv = document.createElement("div");
newDiv.className = "featureNewsStory"; //I got this class name from the code.
newDiv.innerHTML = story;

I've pointed the image to one I made in photoshop, and stored on my own server. If you look at the bottom part of that code you'll see I've added our hilarious story into a new DIV object using the document.createElement() method - now we need to inject our DIV into the site. This is the tricky bit that requires a lil' knowledge of the Document Object Model. But not too much. You need the DOM to find the parent (the container) of the place where you want to insert your story.

For the SMH site, the parent of the sidebar stories is a DIV object called "contentArea2". The other stories in contentArea2 are also DIVs. To place our DIV at the bottom of the list we could use appendChild(). But we want to be sneaky and bury our story in the middle somewhere, so we will use the more tricky insertBefore() method:

var content2 = document.getElementById( "contentArea2" );
var divs = content2.getElementsByTagName( "div" );
content2.insertBefore( newDiv, divs[2].nextSibling );

who is mrspeakerAnd we're done! To check it out, install the script and then go to the SMH web site. Can you see our story? That looks mintox! If you want to be really tricky you can point the "more" link to an existing story, and repeat this procedure - only overwrite the original story with your very own article. Sensational! Scared the be-jesus out of the designer at work!

There are a couple of good tools to help you on your prankster way; Firefox's DOM inspector (Tools -> DOM Inspector) will help you search out those pesky DOM elements. And the rendered source extension makes it much easier to see what's going on when you view page source code.

Just remember: with such news-making power comes great responsibility. I'm sure you will use this knowledge maturely and wisely.


Update! This script no longer works on SMH because of the swanky redesign... Heres a new (and much simpler - thank you very much web standards!) version which relaces one of the rotating banners with our story! Looks neat-o!

// ==UserScript==
// @name	SMH
// @namespace	http://mrspeaker.webeisteddfod.com/greasemonkey
// @description	SMH
// @include	http://www.smh.com.au/
// ==/UserScript==

(function(){
	var story = document.getElementById('rotwof2');

	story.getElementsByTagName('a')[0].innerHTML = 'Who is Mr Speaker?';
	story.getElementsByTagName('img')[0].src ='http://mrspeaker.webeisteddfod.com/images/whois.jpg';
	story.getElementsByTagName('p')[0].childNodes[2].nodeValue = 'Mr Speaker has got the entire nation talking... but who is he???';
		
})();