Greasemonkey Hacks/Linkmania!

From WikiContent

< Greasemonkey Hacks
Revision as of 23:10, 11 March 2008 by Docbook2Wiki (Talk)
(diff) ←Older revision | Current revision (diff) | Newer revision→ (diff)
Jump to: navigation, search
Greasemonkey Hacks


Contents

Hacks 13–20: Introduction

The Web revolves around links. Links take you to a site, let you navigate within it, and finally take you somewhere else. But not all links are created equal. Some links launch a new window without your permission. Some launch external applications. Some execute a piece of JavaScript code, which means they could do almost anything. And some links aren't even clickable.

The first step to reclaiming your browser is taking control of links.

Turn Naked URLs into Hyperlinks

Make every URL clickable.

Have you ever visited a page that displayed a naked URL that you couldn't click? That is, the URL is displayed as plain text on the page, and you need to manually copy the text and paste it into a new browser window to follow the link. I run into this problem all the time while reading weblogs, because many weblog publishing systems allow readers to submit comments (including URLs) but just display the comment verbatim without checking whether the comment includes a naked URL. This hack turns all such URLs into clickable links.

The Code

This user script runs on all pages. To ensure that it does not affect URLs that are already linked, it uses an XPath query that includes not(ancestor::a). To ensure that it does affect URLs in uppercase, the XPath query also includes "contains(translate(., 'HTTP', 'http'), 'http')]".

Once we find a text node that definitely contains an unlinked URL, there could be more than one URL within it, so we need to convert all the URLs while keeping the surrounding text intact. We replace the text with an empty <span> element as a placeholder and then incrementally reinsert each non-URL text snippet and each constructed URL link.

Save the following user script as linkify.user.js:

	// ==UserScript==
	// @name		  Linkify
	// @namespace	  http://youngpup.net/userscripts
	// @description   Turn plain-linkstext URLstext URLs into linkshyperlinks
	// @include *
	// ==/UserScript==

	// based on code by Aaron Boodman
	// and included here with his gracious permission

	var urlRegex = /\b(https?:\/\/[^\s+\"\<\>]+)/ig;
	var snapTextElements = document.evaluate("//text()[not(ancestor::a) " + 
		"and not(ancestor::script) and not(ancestor::style) and " + 
		"contains(translate(., 'HTTP', 'http'), 'http')]", 
		document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
	for (var i = snapTextElements.snapshotLength - 1; i >= 0; i--) {
		var elmText = snapTextElements.snapshotItem(i);
		if (urlRegex.test(elmText.nodeValue)) {
			var elmSpan = document.createElement("span");
			var sURLText = elmText.nodeValue;
			elmText.parentNode.replaceChild(elmSpan, elmText);
			urlRegex.lastIndex = 0;
			for (var match = null, lastLastIndex = 0;
				 (match = urlRegex.exec(sURLText)); ) { 
				elmSpan.appendChild(document.createTextNode(
				sURLText.substring(lastLastIndex, match.index))); 
				var elmLink = document.createElement("a"); 
				elmLink.setAttribute("href", match[0]); 
				elmLink.appendChild(document.createTextNode(match[0])); 
				elmSpan.appendChild(elmLink); 
				lastLastIndex = urlRegex.lastIndex;
			}
			elmSpan.appendChild(document.createTextNode(
				sURLText.substring(lastLastIndex)));
			elmSpan.normalize();
		}
	}

Running the Hack

Before installing the user script, go to http://www.mnot.net/blog/2005/05/18/WADL, an article published by Mark Nottingham on his weblog. I followed Mark's weblog for many years, and the only thing I disliked was that the links I posted in the comments section would be displayed as plain text (i.e., not as hyperlinks). The comments section at the end of this article has several contributed URLs that display as plain text, as shown in Figure 2-1.

Figure 2-1. Comments with plain-text URLs

Comments with plain-text URLs

Now, install the user script (Tools → Install This User Script), and refresh http://www.mnot.net/blog/2005/05/18/WADL. All the URLs in the comments section are now real hyperlinks, as shown in Figure 2-2.

Hacking the Hack

You might want to distinguish between links that were part of the original page and links that were created by this script. You can do this by adding a custom style to the elmLink element.

Change this line:

	var elmLink = document.createElement("a");

to this:

	var elmLink = document.createElement("a");
	elmLink.title = 'linkified by Greasemonkey!';
	elmLink.style.textDecoration = 'none';
	elmLink.style.borderBottom = '1px dotted red';

The linkified URLs will now be underlined with a dotted red line, as shown in Figure 2-3.

Figure 2-2. Comments with clickable URLs

Comments with clickable URLs

Figure 2-3. Custom styles on linkified URLs

Custom styles on linkified URLs

Force Offsite Links to Open in a New Window

Keep your browser organized by automatically opening each site in its own window.

I originally wrote this user script after someone posted a request to the Greasemonkey script repository. I personally like to open links in a new tab in the current window, but some people prefer to open a separate window for each site. Offsite Blank lets you do this automatically, by forcing offsite links to open in a new window.

The Code

This user script runs on remote web sites (but not, for example, on HTML documents stored on your local machine that you open from the File → Open menu). Since search engines exist to provide links to other pages, and I find it annoying for search result links to open new links, I've excluded Google and Yahoo! by default.

The code itself breaks down into four steps:

  1. Get the domain of the current page.
  2. Get a list of all the links on the page.
  3. Compare the domain of each link to the domain of the page.
  4. If the domains don't match, set the target attribute of the link so that it opens in a new window.

Save the following user script as offsiteblank.user.js:

	// ==UserScript==
	// @name		  Offsite Blank
	// @namespace	  http://diveintomark.org/projects/greasemonkey/
	// @description	  force linksoffsite linksoffsite links to open in a new window
	// @include		  http*://*
	// @exclude       http://*.google.tld/*
	// @exclude       http://*.yahoo.tld/*
	// ==/UserScript==

	var sCurrentHost = location.host;
	var arLinks = document.links;
	for (var i = arLinks.length - 1; i >= 0; i--) {
		var elmLink = arLinks[i];
		if (elmLink.host && elmLink.host != sCurrentHost) {
				elmLink.target = "_blank";
		}
	}

Running the Hack

After installing the user script (Tools → Install This User Script), go to http://www.fsf.org. Click on one of the links in the navigation bar, such as "About us." The link will open in the same window, as normal.

Go back to http://www.fsf.org, scroll to the bottom of the page, and click the Plone Powered link to visit http://plone.org. Since the link points to a page on another site, Firefox will automatically open the link in a new window, as shown in Figure 2-4.

Figure 2-4. Offsite link opened in a new window

Offsite link opened in a new window

Hacking the Hack

This hack is somewhat naive about what constitutes an offsite link. For example, if you visit http://www.slashdot.org and click a link on http://developers.slashdot.org, the script will force the link to open in a new window, because it considers developers.slashdot.org to be a different site than www.slashdot.org. We can fix this by modifying the user script to compare only the last part of the domain name.

Save the following user script as offsiteblank2.user.js:

	// ==UserScript==
	// @name          Offsite Blank 2
	// @namespace	  http://diveintomark.org/projects/greasemonkey/
	// @description   force offsite linksoffsite links to open in a new window	
	// @include		  http*://*
	// @exclude		  http*://*.google.tld/*
	// @exclude		  http*://*.yahoo.tld/*
	// ==/UserScript== 

	var NUMBER_OF_PARTS = 2; 

	var sCurrentHost = window.location.host;
	var arParts = sCurrentHost.split('.');
	if (arParts.length > NUMBER_OF_PARTS) {
		sCurrentHost = [arParts[arParts.length - NUMBER_OF_PARTS],
				arParts[arParts.length - 1]].join('.');
	}
	var linksarLinks = document.getElementsByTagName('a');
	for (var i = arLinks.length - 1; i >= 0; i--) {
		var elmLink = arLinks[i];
		var sHost = elmLink.host;
		if (!sHost) { continue; }
	var arLinkParts = sHost.split('.');
		if (arLinkParts.length > NUMBER_OF_PARTS) {
			sHost = [arLinkParts[arLinkParts.length - NUMBER_OF_PARTS],
				 arLinkParts[arLinkParts.length - 1]].join('.');
		}
	if (sHost != sCurrentHost) {
			elmLink.target = "_blank";
        }
	}

This script is still naive about what constitutes an offsite link; it's just naive in a different way than the first script. On sites such as http://www.amazon.co.uk, this script thinks the current domain is co.uk, instead of amazon.co.uk. You can further refine this behavior by changing the NUMBER_OF_PARTS constant at the top of the script from 2 to 3.

Fix Broken Pop-up Links

Change javascript: pseudo-protocol pop-up windows into normal hyperlinks.

Advanced browser users do more than just click hyperlinks. They also right-click them, print them, and save them to disk. All these additional behaviors are broken when web developers incorrectly use the javascript: pseudo-protocol to create pop-up windows.

A broken pop up looks like this:

	<a href="javascript:popup('http://youngpup.net/')">go to youngpup.net</a>

In this example, the web developer is attempting to create a link that opens in a new pop-up window. Unfortunately, the value of the href attribute is not a valid URL. It's just JavaScript, which works only in the context of the current page. This means that if a user right-clicks the link and tries to open it in a new window, the popup function is undefined and the user gets an error message. Likewise, if the user attempts to save or print the contents of the hyperlink, the browser first has to download it, which it cannot do because the href doesn't contain a URL; it contains a random JavaScript statement.

There is no reason for web developers ever to do this. You can easily write annoying pop-up windows and retain the benefits of regular hyperlinks, by adding an onclick handler to a regular hyperlink:

	<a href="http://youngpup.net/" 
           onclick="window.open(this.href); return false;"> 
		 go to youngpup.net 
	</a> 

Using Greasemonkey, we can scan for javascript: links that appear to open pop-up windows and then change them to use an onclick handler instead.

The Code

This user script runs on every page. It loops through every link on the page, looking for javascript: URLs. If the link's href attribute begins with javascript:, the script checks whether it appears to open a pop-up window by looking for something that looks like a URL after the javascript: keyword. Since the overwhelming majority of web authors that use "javascript:" links use them to open pop-up windows, this should not have too many false positives.

If the script determines that the link is trying to open a pop-up window, it attempts to reconstruct the target URL. It changes the surrounding <a> element to use a normal href attribute and move the JavaScript code to the onclick event handler.

Save the following user script as popupfixer.user.js:

	// ==UserScript==
	// @name           Popup Window Fixer
	// @namespace	   http://youngpup.net/userscripts
	// @description	   Fixes javascript: pseudo-links that popup windows
	// @include        *
	// ==/UserScript==

	const urlRegex = /\b(https?:\/\/[^\s+\"\<\>\'\(\)]+)/ig; 
	var candidates = document.getElementsByTagName("a");

	for (var cand = null, i = 0; (cand = candidates[i]); i++) {
		if (cand.getAttribute("onclick") == null &&        
			cand.href.toLowerCase().indexOf("javascript:") == 0) { 
			var match = cand.href.match(urlRegex); 
			if (!match) { continue; } 
			cand.href = match[0];
		} 
	}

Running the Hack

Before installing the script, create a file called testpopup.html with the following contents:

	<html>
	<head>
	<script type="text/javascript">
	function popup(url) { window.open(url); }
	</script>
	</head>
	<body>
	<a href="javascript:popup('http://youngpup.net/')">Aaron's home page</a>
	</body>
	</html>

Save the file and open it in Firefox (File → Open…). When you hover over the link, the status bar displays the JavaScript. When you click the link, it does indeed open http://youngpup.net. However, if you right-click the link and select "Open in new tab," the link fails, because the pop-up function is defined only on the current page, not in the new tab.

Now, install the user script (Tools → Install This User Script) and refresh the test page. Hover over the link again, and you will see in the status bar that the link now points directly to the URL, as shown in Figure 2-5.

Figure 2-5. Fixed pop-up link

Fixed pop-up link

This means you can right-click the link and open it in a new tab, save the target page to your local computer, or print it. When you click the link directly, it still opens an annoying pop-up window, as the developer intended.

Hacking the Hack

There are various problems with this code, all stemming from the fact that it is hard to know for sure that a "javascript:" link is supposed to open a pop up. For example, this script cannot detect any of these pop-up links:

	<a href="javascript:popup('foo.html')">
	<a href="javascript:popup('foo/bar/')">
	<a href="javascript:popupHomePage()">

All we can say for sure is that hyperlinks that use the "javascript:" pseudo-protocol are not really hyperlinks. So, maybe they shouldn't look like hyperlinks. If they didn't, you'd be less confused when they didn't act like hyperlinks.

Save the following user script as popupstyler.user.js:

	var candidates = document.links;
	for (var cand = null, i = 0; (cand = candidates[i]); i++) {
		if (cand.href.toLowerCase().indexOf("javascript:") == 0) {
			with (cand.style) {
				background = "#ddd";
				borderTop = "2px solid white";
				borderLeft = "2px solid white";
				borderRight = "2px solid #999";
				borderBottom = "2px solid #999";
				padding = ".5ex 1ex";
				color = "black";
				textDecoration = "none";
			}
		}
	}

Uninstall popupfixer.user.js, install popupstyler.user.js, and refresh the test page. (In general, you can run the two scripts simultaneously, just not in this demonstration.) The "javascript:" link now appears to be a button, as shown in Figure 2-6.

Figure 2-6. Styled pop-up link

Styled pop-up link

There is no complete solution that will work with every possible "javascript:" pop-up link, since there are so many variations of JavaScript code to open a new window. In theory, you could redefine the window.open function and manually call the JavaScript code in each link, but this could have serious unintended side effects if the link did something other than open a window. Your best bet is a combination of fixing the links you can fix and styling the rest.

Aaron Boodman

Remove URL Redirections

Cut out the middleman and make links point directly to where you want to go.

Many portal sites use redirection links for links that point to other sites. The link first goes to a tracking page on the portal, which logs your click and sends you on your way to the external site. Not only is this an invasion of privacy, but it's also slower, since you need to load the tracking page before you are redirected to the page you actually want to read. This hack detects such redirection links and converts them to direct links that take you straight to the final destination.

The Code

This user script runs on all pages, except for a small list of pages where it is known to cause problems with false positives. It uses the document.links collection to find all the links on the page and checks whether the URL of the link includes another URL within it. If it finds one, it extracts it and unescapes it, and replaces the original URL.

Save the following user script as nomiddleman.user.js:

	// ==UserScript==
	// @name        NoMiddleMan
	// @namespace   http://0x539.blogspot.com/
	// @description Rewrites URLs to remove redirection scripts
	// @include     *
	// @exclude		http://del.icio.us/*
	// @exclude     http://*bloglines.com/*
	// @exclude		http://web.archive.org/*
	// @exclude		http://*wists.com/*
	// ==/UserScript==

	// based on code by Albert Bachand
	// and included here with his gracious permission
	// http://kungfoo.webhop.org/code samplesnomiddleman.user.jsnomiddleman.user.js

	for (var i=0; i<document.linkslinks.length; i++) { 
		 var link, temp, start, linksURL redirectionurl, qindex, end; 
		 link = document.links[i];

		 // Special case for Google results (assumes English language)
		 if (link.text == 'Cached' ||
			 /Similar.*?pages/.exec(link.text)) {
			 continue;
		 }

		 temp = link.href.toLowerCase();

		 // ignore javascript links and GeoURL
		 if (temp.indexOf('javascript:') == 0 ||
		 temp.indexOf('geourl.org') != -1) {
			 continue;
		 }

		 // find the start of the (last) real url
		 start = Math.max(temp.lastIndexOf('http%3a'),
				  temp.lastIndexOf('http%253a'),
				  temp.lastIndexOf('http:'));

		 if (start <= 0) { 
			 // special case: handle redirect url without a 'http:' part 
			 start = link.href.lastIndexOf('www.'); 
			 if (start < 10) {
				 start = 0;
			 } else { 
				 link.href = link.href.substring(0, start) + 
				 'http://' + link.href.substring(start);
			 }
		 }

		 // we are most likely looking at a URLsredirectionredirection link
		 if (start > 0) {
			 url = link.href.substring(start);

			 // check whether the real url is a parameter 
			 qindex = link.href.indexOf('?'); 
			 if (qindex > -1 && qindex < start) {
			     // it's a parameter, extract only the url
				 end = url.indexOf('&');
				 if (end > -1) {
				 url = url.substring(0, end);
				 }
			 }
			 // handle Yahoo's chained redirections
			 var temp = url;
			 url = unescape(url);

			 while (temp != linksURL redirectionurl) {
				 temp = url;
				 url = unescape(url);
			 }
			 // and we're done
			 link.href = url.replace(/&amp;/g, '&');
		 }
	}

Running the Hack

Before installing the user script, go to http://www.yahoo.com and search for greasemonkey. In the list of search results, each linked page is really a redirect through Yahoo!'s servers, as shown in Figure 2-7.

Figure 2-7. Yahoo! Redirection URL

Yahoo! Redirection URL

Now, install the user script (Tools → Install This User Script), go back to http://www.yahoo.com, and execute the same search. The link to the search result page now points directly to http://dream.sims.berkeley.edu/~ryanshaw/wordpress/2005/02/18/greasemonkey-stole-your-job-and-your-business-model/, as shown in Figure 2-8.

Figure 2-8. Yahoo! Direct URL

Yahoo! Direct URL

There are a variety of ways that sites can redirect links through a tracking page. This script doesn't handle all of them, but it handles the most common cases and a few special cases used by popular sites (such as Yahoo!). The author maintains a weblog at http://0x539.blogspot.com/ where you can check for updates to this script.

Warn Before Opening PDF Links

Make your browser double-check that you really want to open that monstrous PDF.

How many times has this happened to you? You're searching for something, or just browsing, and click on a promising-looking link. Suddenly, your browser slows to a crawl, and you see the dreaded "Adobe Acrobat Reader" splash screen. Oh no, you've just opened a PDF link, and your browser is launching the helper application from hell.

This hack saves you the trouble, by popping up a dialog box when you click on a PDF file to ask you if you're sure you want to continue. If you cancel, you're left on the original page and can continue browsing in peace.

Tip

This hack is derived from a Firefox extension called TargetAlert, which offers more features and customization options. Download it at http://bolinfest.com/targetalert/.

The Code

This user script runs on all pages. It iterates through the document.links collection, looking for links pointing to URLs ending in .pdf. For each link, it attaches an onclick handler that calls the window.confirm function to ask you if you really want to open the PDF document.

Save the following user script as pdfwarn.user.js:

	// ==UserScript==
	// @name          PDF Warn
	// @namespace     http://www.sencer.de/
	// @description   Ask before opening PDF links
	// @include       *
	// ==/UserScript==

	// based on code by Sencer Yurdagül and Michael Bolin
	// and included here with their gracious permission
	// http://www.sencer.de/code/greasemonkey/code samplespdfwarn.user.jspdfwarn.user.js

	for (var i = document.links.length - 1; i >= 0; i--) {
		var elmLink = document.links[i];
		if (elmLink.href && elmLink.href.match(/^[^\\?]*pdf$/i)) {
			var sFilename = elmLink.href.match(/[^\/]+pdf$/i); 
			elmLink.addEventListener('click', function(event) {
				if (!window.confirm('Are you sure you want to ' + 
						'open the PDF file "' + 
						sFilename + '"?')) {


						event.stopPropagation();
						event.preventDefault();
				}
			}, true);
		}
	}

Running the Hack

After installing the user script (Tools → Install This User Script), go to http://www.google.com and search for census filetype:pdf. At the time of this writing, the first search result is a link to a PDF file titled "Income, Poverty, and Health Insurance Coverage in the United States." Click the link, and Firefox will pop up a warning dialog asking you to confirm opening the PDF, as shown in Figure 2-9.

Figure 2-9. PDF confirmation dialog

PDF confirmation dialog

If you click OK, the link will open, Firefox will launch the Adobe Acrobat plug-in, and you will see the PDF without further interruption. If you click Cancel, you'll stay on the search results page, where you can click "View as HTML" to see Google's version of the PDF file converted to HTML.

Avoid the Slashdot Effect

Add web cache links to Slashdot articles.

Reading Slashdot is one of my guilty pleasures. It is a guilty pleasure that I share with tens of thousands of other tech geeks. People who have been linked from a Slashdot article report that Slashdot sends as many as 100,000 visitors to their site within 24 hours. Many sites cannot handle this amount of traffic. In fact, the situation of having your server crash after being linked from Slashdot is known as the Slashdot effect.

Tip

Read more about the Slashdot effect at http://en.wikipedia.org/wiki/Slashdot_effect.

This hack tries to mitigate the Slashdot effect by adding links to Slashdot articles that point to various global web caching systems. Instead of visiting the linked site, you can view the same page through a third-party proxy. If the Slashdot effect has already taken hold, the linked page might still be available in one of these caches.

The Code

This user script runs on all Slashdot pages, including the home page. The script adds a number of CSS rules to the page to style the links we're about to add. Then, it constructs three new links—one to Coral Cache, one to MirrorDot, and one to the Google Cache—and adds them after each external link in the Slashdot article.

Save the following user script as slashdotcache.user.js:

	// ==UserScript==
	// @name        Slashdot Cache
	// @namespace   http://www.cs.uni-magdeburg.de/~vlaube/Projekte/
	GreaseMonkey/
	// @description Adds links to web caches on Slashdot
	// @include     http://slashdot.tld/*
	// @include     http://*.slashdot.tld/*
	// ==/UserScript==

	// based on code by Valentin Laube
	// and included here with his gracious permission

	var coralcacheicon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA'+ 
	'oAAAAKCAYAAACNMs%2B9AAAAgUlEQVQY042O0QnCQBQEZy0sFiEkVVxa8GxAuLOLgD3cV'+ 
	'RKwAytYf05JkGgGFt7H8nZkG10UgBNwZE0F7j77JiIJGPlNFhGzgwOQd%2FQytrEJdjtb'+ 
	'rs%2FORAqRZBvZBrQxby2nv5iHniqokquUgM%2FH8Hadh57HNG05rlMgFXDL0vE%2FL%2'+ 
	'BEXVN83HSenAAAAAElFTkSuQmCC'; 

	var mirrordoticon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAo'+ 
	'AAAAKCAYAAACNMs%2B9AAAAbklEQVQY05WQMRKEMAwDNzzqUobWv%2BBedvcK3EKZV4km'+ 
	'BiYFE9RYI3mssZIkRjD1Qnbfsvv2uJjdF6AApfELkpDEZ12XmHcefpJEiyrAF%2Fi1G8H'+ 
	'3ajZPjOJVdPfMGV3N%2FuGlvseopprNdz2NFn4AFndcO4mmiYkAAAAASUVORK5CYII%3D'; 
	var googleicon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAA'+ 
	'AKCAIAAAACUFjqAAAAiklEQVQY02MUjfmmFxPFgAuIxnz7jwNcU9BngSjae%2FbDxJUPj'+ 
	'1z%2BxMDAYKPLlx8u72wswMDAwASRnrjyIQMDw%2BoW3XfbbfPD5SFchOGCHof2nHmPaT'+ 
	'gTpmuEPA8LeR6GsKHSNrp8E1c%2B3Hv2A8QKG10%2BiDjUaRD7Qmsuw51GlMcYnXcE4Aq'+ 
	'SyRn3Abz4culPbiCuAAAAAElFTkSuQmCC'; 
	var backgroundimage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA'+ 
	'DEAAAAOCAYAAACGsPRkAAAAHXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBUaGUgR0lNUO'+ 
	'9kJW4AAAC7SURBVEjH7daxDYMwEEbhh11cAxKSKYEV0qeKMgETZBbPkgmYIEqVPisAJZa'+ 
	'QTOPCUprQZYAY8Sb4P11zGcD9dT0BFuhIpx6wt%2FPjnX0BTxEpjako8uLv1%2FvV49xM'+ 
	'CGEBLgqwIlI2dZsEAKDIC5q6RURKwCqgM6ZCa01Kaa0xpgLo1CZLsW23YgcdiANxIH4g%'+ 
	'2FOqTHL%2FtVkDv3EyMMSlAjBHnZoBeATaEsIzTkMxF%2FOoZp2F7O2y2hwfwA3URQvMn'+ 
	'dliTAAAAAElFTkSuQmCC';

	function addGlobalStyle(css) { 
		var head, style; 
		head = document.getElementsByTagName('head')[0]; 
		if (!head) { return; } 
		style = document.createElement('style'); 
		style.type = 'text/css'; 
		style.innerHTML = css; 
		head.appendChild(style);
	}

	addGlobalStyle('' +
	'a.coralcacheicon, a.mirrordoticon, a.googleicon { \n' +
	' padding-left: 15px; background: center no-repeat; \n' +
	'} \n' +
	'a.coralcacheicon { \n' +
	' background-image: url(' +coralcacheicon + '); \n' +
	'} \n' +
	'a.mirrordoticon { \n' +
	' background-image: url(' + mirrordoticon + '); \n' +
	'} \n' +
	'a.googleicon { \n' +
	' background-image: url(' + googleicon + '); \n' +
	'} \n' +
	'a.coralcacheicon:hover, a.mirrordoticon:hover, ' +
	'a.googleicon:hover { \n' +
	' opacity: 0.5; \n' +
	'} \n' +
	'div.backgroundimage { \n' +
	'  display:inline; \n' +
	'  white-space: nowrap; \n' +
	'  padding:3px; \n' +
	'  background:url(' +  backgroundimage + ') center no-repeat; \n' +
	'}');

	var link, anchor, background;
	for (var i=0; i<document.linkslinks.length; i++) {

	link = document.linkslinks[i];

	// filter relative links
	if(link.getAttribute('href').substring(0,7) != 'http://') {
		continue;
	}

	// filter all other links    
	if(link.parentNode.nodeName.toLowerCase() != 'i' &&       
       (link.parentNode.nodeName.toLowerCase() != 'font' || 
	   link.parentNode.color != '#000000' || link.parentNode.size == '2') 
&&
	   (!link.nextSibling || !link.nextSibling.nodeValue ||
	   link.nextSibling.nodeValue.charAt(1) != '[')) {
	   continue;

	}
	
	// add background
	background = document.createElement('div');
	background.className = 'backgroundimage';
	link.parentNode.insertBefore(background, link.nextSibling);

	//add mirrordot link
	anchor = document.createElement('a');
	anchor.href = 'http://www.mirrordot.com/find-mirror.html?' + link.href;
	anchor.title = 'MirrorDot - Solving the Slashdot effectSlashdot Effect';
	anchor.className = 'mirrordoticon';
	background.appendChild(anchor);

	//add coral cache link
	anchor = document.createElement('a');
	anchor.href = link.href;
	anchor.host += '.nyud.net:8090';
	anchor.title = 'Coral - The NYU Distribution Network';
	anchor.className = 'coralcacheicon';
	background.appendChild(anchor);

	//add google cache link
	anchor = document.createElement('a');
	anchor.href = 'http://www.google.com/search?q=cache:' + link.href;
	anchor.title = 'Google Cache';
	anchor.className = 'googleicon';
	background.appendChild(anchor);

	// add a space so it wraps nicely 
	link.parentNode.insertBefore(document.createTextNode(' '), 
		link.nextSibling);
}

Running the Hack

After installing the user script (Tools → Install This User Script), go to http://slashdot.org. In the summary of each article, you will see a set of small icons next to each link, as shown in Figure 2-10.

Figure 2-10. Slashdot cache links

Slashdot cache links

The first icon points to the MirrorDot cache for the linked page, the second icon points to the Coral Cache version of the link, and the third points to the Google Cache version. If the linked page is unavailable because of the Slashdot effect, you can click any of the cache links to attempt to view the link. For example, the MirrorDot link takes you to a page on MirrorDot (http://www.mirrordot.com) that looks like Figure 2-11.

The Coral Cache system works on demand: the page is not cached until someone requests it. MirrorDot works by polling Slashdot frequently to find new links before the Slashdot effect takes the linked site down. Google Cache works in conjunction with Google's standard web crawlers, so brand-new pages might not be available in the Google Cache if Google had not indexed them before they appeared on Slashdot.

Figure 2-11. MirrorDot cache of Slashdotted link

MirrorDot cache of Slashdotted link

Convert UPS and FedEx Tracking Numbers to Links

Make it easier to track packages.

All major package-delivery companies have web sites that allow you to track the status of packages. This is especially useful for online shoppers. Unless you're buying downloadable software, pretty much everything you buy online needs to be shipped one way or another. Unfortunately, not all online retailers are as web-savvy as one might hope.

This hack scans web pages for package tracking numbers and then converts them to links that point to the page on the delivery company's web site that shows the shipment's current status.

The Code

This user script runs on all pages. It is similar to "Turn Naked URLs into Hyperlinks" [Hack #13]. It scans the page for variations of package numbers that are not already contained in an <a> element and then constructs a link that points to the appropriate online tracking site.

These patterns are converted into links to UPS (http://www.ups.com ):

  • 1Z 999 999 99 9999 999 9
  • 9999 9999 999
  • T999 9999 999

This pattern is converted into a link to FedEx (http://www.fedex.com):

  • 9999 9999 9999

The following patterns are converted into links to the United States Postal Service (http://www.usps.com ):

  • 9999 9999 9999 9999 9999 99
  • 9999 9999 9999 9999 9999

Save the following user script as tracking-linkify.user.js:

	// ==UserScript==
	// @name UPS/FedEx tracking numbersFedEx Tracking Linkify
	// @namespace http://scripts.slightlyinsane.com
	// @description Link package tracking numberstracking numbers to appropriate site
	// @include *
	// ==/UserScript==

	// Based on code by Justin Novack and Logan Ingalls
	// and included here with their gracious permission
	// Originally licensed under a Create Commons license
	// Visit http://creativecommons.org/licenses/by-sa/2.0/ for details

	var UPSRegex = new RegExp('/\b(1Z ?[0-9A-Z]{3} ?[0-9A-Z]{3} ?[0-9A-Z]{'+
	'2} ?[0-9A-Z]{4} ?[0-9A-Z]{3} ?[0-9A-Z]|[\\dT]\\d\\d\\d ?\\d\\d\\d\\d '+
	'?\\d\\d\\d)\\b', 'ig');
	var FEXRegex = new RegExp('\\b(\\d\\d\\d\\d ?\\d\\d\\d\\d ?\\d\\d\\d\\'+
	'd)\\b', 'ig');
	var USARegex = new RegExp('\\b(\\d\\d\\d\\d ?\\d\\d\\d\\d ?\\d\\d\\d\\'+
	'd ?\\d\\d\\d\\d ?\\d\\d\\d\\d ?\\d\\d|\\d\\d\\d\\d ?\\d\\d\\d\\d ?\\d'+
	'\\d\\d\\d ?\\d\\d\\d\\d ?\\d\\d\\d\\d)\\b', 'ig');

	function UPSUrl(t) {
		return 'http://wwwapps.ups.com/WebTracking/processInputRequest?sor'+
			't_by=status&tracknums_displayed=1&TypeOfInquiryNumber=T&loc=e'+
			'n_US&InquiryNumber1=' + String(t).replace(/ /g, '') +
			'&track.x=0&track.y=0';
	}

	function FEXUrl(t) {
		return 'http://www.fedex.com/cgi-bin/tracking?action=track&languag'+
			'e=english&cntry_code=us&initial=x&tracknumbers=' +
			String(t).replace(/ /g, '');
	}

	function USAUrl(t) {
	return 'http://trkcnfrm1.smi.usps.com/netdata-cgi/db2www/cbd_243.d'+

		'2w/output?CAMEFROM=OK&strOrigTrackNum=' +
		String(t).replace(/ /g, '');
	}

	// tags we will scan looking for un-hyperlinked urls
	var allowedParents = [
		'abbr', 'acronym', 'address', 'applet', 'b', 'bdo', 'big',
		'blockquote', 'body', 'caption', 'center', 'cite', 'code',
		'dd', 'del', 'div', 'dfn', 'dt', 'em', 'fieldset', 'font',
		'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'i', 'iframe',
		'ins', 'kdb', 'li', 'object', 'pre', 'p', 'q', 'samp',
		'small', 'span', 'strike', 's', 'strong', 'sub', 'sup',
		'td', 'th', 'tt', 'u', 'var'];

	var xpath = '//text()[(parent::' + allowedParents.join(' or parent::') +
		')]';

	var candidates = document.evaluate(xpath, document, null,
		XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

	//var tO = new Date().getTime();
	for (var cand = null, i = 0; (cand = candidates.snapshotItem(i)); i++) {

	// UPS tracking numbersUPS Track
	if (linksUPS and FedEx tracking numbersUPSRegex.test(cand.nodeValue)) {
		var span = document.createElement('span');
		var source = cand.nodeValue;

		cand.parentNode.replaceChild(span, cand);
	
		UPSRegex.lastIndex = 0;
		for (var match = null, lastLastIndex = 0;
			 (match = UPSRegex.exec(source)); ) {
			span.appendChild(document.createTextNode(
				source.substring(lastLastIndex, match.index)));

			var a = document.createElement('a');
			a.setAttribute('href', UPSUrl(match[0]));
			a.setAttribute('title', 'Linkified to UPS');
			a.appendChild(document.createTextNode(match[0]));
			span.appendChild(a);

			lastLastIndex = UPSRegex.lastIndex;
		}
		
		span.appendChild(document.createTextNode(
			source.substring(lastLastIndex)));
		span.normalize();
	}

	// USPS Track
	if (USARegex.test(cand.nodeValue)) {
		var span = document.createElement('span');

		var source = linksUPS and FedEx tracking numberscand.nodeValue;
		
		cand.parentNode.replaceChild(span, cand);

		USARegex.lastIndex = 0;
		for (var match = null, lastLastIndex = 0;
			 (match = USARegex.exec(source)); ) {
			span.appendChild(document.createTextNode(
				source.substring(lastLastIndex, match.index)));

			var a = document.createElement('a');
			a.setAttribute('href', USAUrl(match[0]));
			a.setAttribute('title', 'Linkified to USPS');
			a.appendChild(document.createTextNode(match[0]));
			span.appendChild(a);

			lastLastIndex = USARegex.lastIndex;
		}

		span.appendChild(document.createTextNode(
			source.substring(lastLastIndex)));
		span.normalize();
	}

	// FedEx tracking numbersFedEx Track
	if (FEXRegex.test(cand.nodeValue)) {
		var span = document.createElement('span');
		var source = cand.nodeValue;

		cand.parentNode.replaceChild(span, cand);

		FEXRegex.lastIndex = 0;
		for (var match = null, lastLastIndex = 0;
			 (match = FEXRegex.exec(source)); ) {
			span.appendChild(document.createTextNode(
				source.substring(lastLastIndex, match.index)));

			var a = document.createElement('a');
			a.setAttribute('href', FEXUrl(match[0]));
			a.setAttribute('title', 'Linkified to FedEx');
			a.appendChild(document.createTextNode(match[0]));
			span.appendChild(a);

			lastLastIndex = FEXRegex.lastIndex;
		}

		span.appendChild(document.createTextNode(
			source.substring(lastLastIndex)));
		span.normalize();
	}
  }

Running the Hack

Before installing the script, create a file called testlinkify.html with the following contents:

	<html>
	<head>
	<title>Test Linkify</title>
	</head>
	<body>
	<p>UPS tracking numbersUPS tracking numberstracking numbers:</p>
	<ul>
	<li>Package 1Z 999 999 99 9999 999 9 sent</li>
	<li>Package 9999 9999 999 sent </li>
	<li>Package T999 9999 999 sent</li>
	</ul>
	<p>FedEx tracking numbersFedEx tracking numbers:</p>
	<ul>
	<li>Package 9999 9999 9999 sent</li>
	</ul>
	<p>USPS tracking numbers:</p>
	<ul>
	<li>Package 9999 9999 9999 9999 9999 99 sent</li>
	</ul>
	</body>
	</html>

Save the file and open it in Firefox (File → Open…). It lists a number of variations of (fake) package tracking numbers in plain text, as shown in Figure 2-12.

Figure 2-12. Package tracking numbers

Package tracking numbers

Now, install the user script (Tools → Install This User Script), and refresh the test page. The script has converted the package tracking numbers to links to their respective online tracking sites, as shown in Figure 2-13.

Figure 2-13. Package tracking links

Package tracking links

If you hover over a link, you will see a tool tip that lets you know that the tracking number was automatically converted to a link.

Follow Links Without Clicking Them

Hover over any link for a short time to open it in a new tab in the background.

This hack was inspired by dontclick.it (http://dontclick.it), a site that demonstrates some user interaction techniques that don't involve clicking. The site is written in Flash, which annoys me, but it gave me the idea of lazy clicking: the ability to open links just by moving the cursor over the link and leaving it there for a short time. I don't claim that it will cure your carpal tunnel syndrome, but it has changed the way I browse the Web.

The Code

This user script runs on nonsecure web pages. By default, it will not run on secure web pages, because it has been my experience that most secure sites, such as online banking sites, are very unweblike and don't support opening links in new tabs.

The script gets a list of all the links (which Firefox helpfully maintains for us in the document.links collection) and attaches three event handlers to each link:

Mouseover
When you move your cursor to a link, the script starts a timer that lasts for 1.5 seconds (1500 milliseconds). When the timer runs down, it calls GM_openInTab to open the link in a new tab.
Mouseout
If you move your cursor off a link within 1.5 seconds, the onmouseout event handler cancels the timer, so the link will not open.
Click
If you actually click a link within 1.5 seconds, the onclick event handler cancels the timer and removes all three event handlers. (Note that you can click a link without leaving the page; for example, holding down the Ctrl key while clicking will open the link in a new tab.) This means that if you manually follow a link, the auto-open behavior disappears and the link will not open twice.

Save the following user script as autoclick.user.js:

	// ==UserScript==
	// @name AutoClick
	// @namespace http://diveintomark.org/projects/greasemonkey/
	// @description hover over linkslinks for 1.5 seconds to open in a new tab
	// @include http://*
	// ==/UserScript==

	var _clickTarget = null;
	var _autoClickTimeoutID = null;

	function mouseover(event) {
		_clickTarget = event.currentTarget;
		_autoclickTimeoutID = window.setTimeout(autoclick, 1500);
	}

	function mouseout(event) {
		_clickTarget = null;
		if (_autoclickTimeoutID) {
			window.clearTimeout(_autoclickTimeoutID);
		}
	}

	function clear(elmLink) {
		if (!elmLink) { return; }
		elmLink.removeEventListener('mouseover', mouseover, true);
		elmLink.removeEventListener('mouseout', mouseout, true);
		elmLink.removeEventListener('click', click, true);
	}

	function click(event) {
		var elmLink = event.currentTarget;
		if (!elmLink) { return false; }
		clear(elmLink);
		mouseout(event);
	}

	function autoclick() {
		if (!_clickTarget) { return; }

		GM_openInTab(_clickTarget.href);
		clear(_clickTarget);
	}

	for (var i = document.linkslinks.length - 1; i >= 0; i--) {
		var elmLink = document.links[i];
		if (elmLink.href && elmLink.href.indexOf('javascript:') == -1) {
			elmLink.addEventListener('mouseover', mouseover, true);
			elmLink.addEventListener('mouseout', mouseout, true);
			elmLink.addEventListener('click', click, true);
		}
	}

Running the Hack

Before running this hack, you'll need to set up Firefox so that it doesn't bring new tabs to the front. Go to Tools → Options → Advanced. Under Tabbed Browsing, make sure "Select new tabs opened from links" is not checked, as shown in Figure 2-14.

Figure 2-14. Firefox tab options

Firefox tab options

Now, install the user script (Tools → Install This User Script), and go to http://del.icio.us/popular/. Hover over any link for a short time (1.5 seconds to be precise), and the link will open in a new tab in the background, as shown in Figure 2-15.

Figure 2-15. Auto-opened link

Auto-opened link

The script is smart enough not to reopen links you've already opened. If you move your cursor away from the link you just opened, then move it back to the link, it will not open a second time.

The script is also smart enough not to auto-open links you've already clicked. If you move to another link and click while holding down the Ctrl key (or the Command key on Mac OS X), Firefox will open the link in a new tab in the background. If you move away from the link and then move back, it will not auto-open no matter how long you hover over it.

Personal tools