Cover | Table of Contents
http://greasemonkey.mozdev.org. Click the Install Greasemonkey link. Firefox will warn you that it prevented this site from installing software, as shown in Figure 1-1.
http://userscripts.org.http://greasemonkey.mozdev.org. Click the Install Greasemonkey link. Firefox will warn you that it prevented this site from installing software, as shown in Figure 1-1.
http://userscripts.org.http://diveintomark.org/projects/butler/) to see a brief description of the functionality that Butler offers.
Example: Hello World metadata
// ==UserScript==
// @name Hello World
// @namespace http://www.oreilly.com/catalog/greasemonkeyhcks/
// @description example script to alert "Hello world!" on every page
// @include *
// @exclude http://oreilly.com/*
// @exclude http://www.oreilly.com/*
// ==/UserScript==
alert('Hello world!');
// ==UserScript== // // ==/UserScript==
// @name Hello World
@name is optional. If present, it can appear only once. If not present, it defaults to the filename of the user script, minus the .user.js extension.@include and @exclude parameters: URLs with * wildcards that match any number of characters. This might seem like a simple syntax, but combining wildcards to match exactly the set of pages you want is trickier than you think.example.com in the location bar, you get the site at http://example.com. If you visit www.example.com, you get exactly the same site, but the location bar reads http://www.example.com.@include http://example.com/* @include http://www.example.com/*
http://slashdot.org and http://www.slashdot.org. But it also has specialized subdomains, such as http://apache.slashdot.org/, http://apple.slashdot.org/, and so forth.@include http://slashdot.org/* @include http://*.slashdot.org/*
http://slashdot.org. The second line matches when you visit http://www.slashdot.org (the * wildcard matches www). The second line also matches when you visit @include and @exclude lines in this section is the first and easiest way to configure a user script, because the configuration travels with the script code. If you copy the file to someone else's computer or publish it online, other people will pick up the default configuration.
@include and @exclude lines), but you can change them to anything you like before you install the script. Let's say, for example, that you like Butler, but you have no use for it on Froogle, Google's cleverly named product comparison site. Before you install the script, you can modify the configuration to exclude that site but still let the script work on other Google sites.
var elmNewContent = document.createElement('div');
document.body.appendChild(elmNewContent)
id="ads" and removes it:
var elmDeleted = document.getElementById("ads");
elmDeleted.parentNode.removeChild(elmDeleted);
http://adblock.mozdev.org.id="foo":
var elmNewContent = document.createElement('a');
elmNewContent.href = 'http://www.example.com/';
elmNewContent.appendChild(document.createTextNode('click here'));
var elmFoo = document.getElementById('foo');
elmFoo.parentNode.insertBefore(elmNewContent, elmFoo);
nextSibling property:elmFoo.parentNode.insertBefore(elmNewContent, elmFoo.nextSibling);
elmFoo.nextSibling will work even if elmFoo is the last child of its parent (i.e., it has no next sibling). In this case,
function addGlobalStyle(css) {
try {
var elmHead, elmStyle;
elmHead = document.getElementsByTagName('head')[0];
elmStyle = document.createElement('style');
elmStyle.type = 'text/css';
elmHead.appendChild(elmStyle);
elmStyle.innerHTML = css;
} catch (e) {
if (!document.styleSheets.length) {
document.createStyleSheet();
}
document.styleSheets[0].cssText += css;
}
}
document.styleSheets (note the capitalization!). Each item in this collection is an object, representing a single stylesheet. Each stylesheet object has a collection of rules, and methods to add new rules or remove existing rules.insertRule method takes two parameters. The first is the CSS rule to insert, and the second is the positional index of the rule before which to insert the new rule:
document.styleSheets[0].insertRule('html, body { font-size: large }', 0);
deleteRule method. It takes a single parameter, the positional index of the rule to remove. The following code will remove the first rule, which we just inserted with document.getElementsByTagName) and then test each one to see if it's something of interest. With XPath expressions, you can find exactly the elements you want, all in one shot, and then immediately start working with them.http://www.zvon.org/xxl/XPathTutorial/General/examples.html.
document.evaluate function. Here's the basic syntax:
var snapshotResults = document.evaluate('XPath expression',
document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
document. But you can also search just a part of the page. For example, to search within a <div id="foo">, pass document.getElementById("foo") as the second parameter.data: URL. A data: URL allows you to encode an image as printable text, so you can store it as a JavaScript string. And Firefox supports data: URLs natively, so you can insert the graphic directly into a web page by setting an img element's src attribute to the data: URL string. Firefox will display the image without sending a separate request to any remote server.http://software.hixie.ch/utilities/cgi/data/data.img elements that advertisers use to track your movement online. The script filters this list of potential web bugs to include only those images that point to a third-party site, since many sites use 1 x 1-pixel images for spacing in table-based layouts.src attribute of the img element after the fact. The image data is embedded in the script itself.
// ==UserScript==
// @name Web Bug Detector
// @namespace http://diveintomark.org/projects/greasemonkey/
// @description make web bugs visible
// @include *
// ==/UserScript==
var snapImages = document.evaluate("//img[@width='1'][@height='1']",
document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = snapImages.snapshotLength - 1; i >= 0; i--)
var elmImage = snapImages.snapshotItem(i);
var urlSrc = elmImage.src;
var urlHost = urlSrc.replace(/^(.*?):\/\/(.*?)\/(.*)$/, "$2");
if (urlHost == window.location.host) continue;
elmImage.width = '80';
elmImage.height = '80';
elmImage.title = 'Web bug detected! src="' + elmImage.src + '"';
elmImage.src =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAA' +
'AABHNCSVQICAgIfAhkiAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPB' +
'oAAAv3SURBVHic7Zx5kFxVGcV%2FvWVmemaSSSYxiRkSgglECJK4gAtSEjQFLmjEUhEV' +
'CxUs1NJyK5WScisKFbHUwo0q10jQABZoEJBEtgSdArNHkhCyTTKZzNo9S3dm6faPc2%2' +
'FepNP9Xvfrnu5J2afqVXq6X7%2F3vXO%2F%2By3n3g5UUUUVVVRRRRVVVFFFFVVU4Y4p' +
'lTbgTEUAmA%2F8CohU2JYzEtOBbwKDQH1lTTkVwQm%2Bfk0J7hEG5gCrgAFgYbFGlRIT' +
'TWAT0EJx064BuMBc52nzetJgognsB6YBF%2BIvAQSAZuAa4F9AG4qFkwYTTWACxa3lwP' +
'UU7olRYCbwduAp4Ih5P1wqA4vFRBOYBnqAHciLPl%2FgPRuBDwLDwEbgkHmdLq2Z%2Fh' +
'Eqwz1GgVqgA7gT6AOey%2BN7ITRdvwfcAzxh3gsC2yfCUD%2BYaA8EEdgN7AceQuVISx' +
'7fiwJXI2%2F7E5AETgC7mUQeWA4CAWLI89airPo1RJAbpgHXAr9HYaAfxdS%2BCbDPNw' +
'%2FlInAM6ERJ4B%2FADbiXI0FE4CvQdE8gApPASIlsCqAYeyHwfeTlBVcK5SIQREAMuB' +
'dNwVvJ3VXUAPOQlx5HU3cMDUB3kXZMAWYAVwG%2FBR43f%2B8C7qPASqGcBI4CXcBLKC' +
'm8A1iR49wpwNko41rPA4ijsqhQBFHoaAE%2BhWbBPaizuRGFiVZgPbCaAsqkctdT%2FS' +
'ie%2FRl4M%2FAdYAOnk1KPHjaOiB%2F2eb8aRNxS4L2oJKoFHjT3Pgr0mnvMQwkqiUSL' +
'j5NHsnIjcKq5QAjFiySaRinzr59MOIySQBfwU%2BBHwGXA3zPOq0MExsy9Rgu4RxANwJ' +
'tQFn8bsMhc6yHgAeAYGshONEgBc5%2F5wAHkmZ8xNrrCjcAw8DIUhy4zRtSah6vDSQwd' +
'wH9R%2FEjl8YBxROJ2c7wfTSlLUgBN4Zeb80bNvbxQj8haBaxEAgTIy34OPILiaa%2Bx' +
'2w6ORZuxfz5KVH0owbjWnG4E9iCPmYG6gB3m%2FSB6yFpE8BzgQ8CXgc%2BhntWNyCQw' +
'ZB5gB3Almma2PAkjr59rHvSE2wOY714D%2FBhlbtAgPYPEh6fMtftwiMvm0SmUpMZQ%2' +
'FE2i7B%2FAZbZ5xcABFJ%2B6jKENyPsiiMBe4EXg38DFaHqsA25BnpnrxnGk1GwFPgxc' +
'DvxlnE1hROBe3ONfFHgdIi9hrrEVJapBlLF7EHmDeHtyGmhHg3YOiqFFEWgvOmSO48gD' +
'a8wRRVOnFnjSGH6TeYjbgbvRIGRiyDzQduQNq1AcTBqbapEK00vuui%2BCptudxpavoM' +
'GM4xTu4zN4Iegxdi1AuWAiiveTCKOC9CykulwF%2FBARtwf4pPk8E03A61H91QHMMu9P' +
'R3ExjfrgpizfDSLy7jbn3Y48caE5v1Q9fhQls0CuE0pRB46ikW4DDiIv%2FBtSXoZRSd' +
'ABfCPDEBsLd6FYusLYYz0LHE%2FIxEwUO28AHkZlSSfKrn3kl3TywZC5bs5lhFKrMQlz' +
'0zHzuhWRsRDFuQGUZNLmnCZExuWI7EdRnL0Seee9wDZOJXEqcBHwCxSvbkGZ9jDeCccP' +
'bBlVVgGjGTgPuAR4F%2FAYDmmfwBm4eebzFIqvc1C5tNac%2Fx4U3yxqgVcjr%2B0Hrg' +
'NemXHOGY0gSiJxFPz%2FA3wJtW33IVJOoDhZi7zzrah4TaN4uQSVIGngUhxywsBinDLp' +
'W8gTvVSdMwZn43haN%2Bov%2B8zfu4BPA79GD78ZdQUJ8%2Fn4ox15Vwp1E1EUO1tQvE' +
'ujcukS5OkVRc7sUgBCaL3jDpRBN6LKvwfFt5sQEWPA%2FUBzOBx%2By4IFC0KLFy8mGo' +
'0SCoUIBAKMjIywd%2B9e9uzZw%2FDwMCiAfwR4HmX2j6LB%2BCyqEduosLhaLIEtwF2o' +
'5%2BxHGfdpVId1ofKlGbWCNwLT6uvrWbZsGXV1dYTD4axHIBBg9%2B7dtLa2gjxyDfAF' +
'c92bEaH78S8ylAx%2Bs3AjmpJ%2FQEH9eeDbqDXrRBnxCMq6tmcOhUKh5YsWLQJgdHT0' +
'5DE2NnbK36lUiubmZmKxGPF4vBFlZIDvApvQ4lICxcXXosG5FrV0s4EXKJ3w6opCPXA%' +
'2B6nc%2FhnrkBPAbFPsGUL1nJSI7tWYj6WpNfX19ePbs2ad4WyQSyemJfX19bNy40d57' +
'DfAT5JHLUYZeiVq%2BTPQjve9nqAyaMORL4BvQFHo3jmK7BRl4DCWNdnNktk7NaEVtaV' +
'1dHQ0NDZ7EjSdw27ZtADtRsbwMhYMGc%2B2jwD%2BRYNCISqdFwLmoRAL16avNOTvITz' +
'HKG14Engf8DmU8iwGUTTcgsjpQMO8me0D%2FIkowhEIhIpFIXuRFIhEOHz5MZ2cn6KFt' +
'15RE03g9IjaJCt0IksHsMzWgyuAcNM2XoiJ%2FCyrwn0XkF7VE4EZgANVlvxxn%2BKOo' +
'hOhFWfYo2b3Ooha1dVdkfuBGXDgcJh6P093dDSInjDTHx1GWH0Dhw6pFKTR4EaQWRc1r' +
'e6TN%2B8uRanQxas%2B6UdeTzzp1VripMQHUNg2i%2BuuvqEBOoGl7BBHpNiWa0OifBp' +
'swQJ5pyQsGgyQSCUZGTuaAl1BNuRNnda4fkdePCvMRY69deK8zttfiyPonkNc9a85bgh' +
'LgH1FB7ke1cfXAEFo5C6Asm0SFcQciMJ8bXo1iFyC1cyZ6Iiv5dnpfI21sGEbZdwOSrm' +
'Ie37Oibx3yNqtlNuLIcSnUSiaR%2BFEwvDywAZHVhZ61F7l9PoE4hBPsmY8C6hwUqBLm' +
'ggdQRZwDMTQAAyiBvAb10nfgTWDa3CaBws0URGATIrEOJbi1qNhfjjqkguBWB4aQ0HkQ' +
'PWO7MSTfyj%2BCSozrpqMs9CpE4AzzFFbos%2FMxAymk852P%2Bt91SIBdaU5fn6cdFm' +
'NoGtulURsjpqG4ugL14AXBTQ8MINcfRIE87uPaXcDYAhRw5qLWpQUJgNOR5HtW9u8%2F' +
'gLIlKIT0oDi4CRXNfkWENHqmDmNfL%2FLOA%2FhY5vUisAGl%2FhEKr59SKG71zELDPB' +
'cRNg%2FJz43mmMppwXgfKpWS5ivdSO7qRwmlBXVCxWAUzapO5J07ya5%2BuyIfDxzAX8' +
'85gozcnUJD3IyU1bk4kTyIRmhcXDiA2sI2NK3sIk87IvEQzsJVsbtVR3FifAM%2BMrEX' +
'gSH87wxIG4MeO4TmzAk0bYPmgifMsc%2F5zmbg66jmG0IZ8kWUCOLoQXuQthgCfkDxgs' +
'ggInEEH78AcJvzltw0%2FlWPHuCZvbBjMyxtRKyA5k0XitrHJVFZ%2Bd7WmOej%2Bmw1' +
'jmf0mq9NRyRej6b0Op%2F2jbfTToqC4FXG2Crfr7LRjx74tgfh1gOw5FI0zAeBVti3Xz' +
'uktmPiJZKpksD7jA32M1Cs6kLR4GGkdN%2BFem0%2Fm44sxtDAFSzQehG4CU3hQvamjM' +
'coiluHga9uhdlb4YIA9KX1XifODqweFOd6UZ65wnzexqkzwE7l6aiGuxmtznnuY%2FHA' +
'EI76nbdImw%2BBtozxi%2BMoh9j1kGNpZ6XL7hnsNscICh1LkKryhHlv%2FAxIm2vOQr' +
'XgG9HaSbEEQm5BJCe8CLS7qYohMI1i2hCqoW2cGTLXzszydUiyAklR%2FVmuOYhIbAJu' +
'QwvwtmYtBgUvD%2BRDYIdvcxykcLwshKOeZEMUEb0PKTnZtoakjV1NyGM3IVXl%2FhLY' +
'WhC8ypgenMRZKrjtLbQK0EU4slmu%2B9u9OnG0PW5Vac3MD14EHiuXIQY1qF0%2BFy0T' +
'eAkXveY4jmJpOX73cgrcCByl9N7nhSjypA0o02aLf%2BMxhKMJbqECP8h2IzBb7JloNA' +
'LvRBJWjPwGsBeR2Immf1nhRmBJF1%2FyQA2S2ttRMT1%2BZc8NdkNTFxX4MXY5f%2Bbg' +
'hXrgA2jLRwzv6WsxZs61G0HLislE4Cwk1jyHkkche%2FxilGkhPROThcBatOb8CCIvX%' +
'2B%2BzsAtNZd8nM1kIjKL1jg2oOyl0o2QKxc6ye%2BFk%2BOV3AP3wcC%2FyPq%2FFol' +
'yIUYH%2FEmUyeGAt2jz0JO6dhxeK0S19YzIQ2IBqTvtjmEnzY%2Bp8UGkCA0i6egFNwU' +
'oU70Wh0jEwitTnEIWXLpMClfbAqSjwx%2FCfPCqKShIYQr3vIJL3ixFt%2Fy8RRsJp9b' +
'%2B0q6KKKqqooooqfOB%2F6MmP5%2BlO7YkAAAAASUVORK5CYII%3D';
}
@include and @exclude parameters. It loaded the source code of each user script, created a <Script> element, assigned the source code of the user script to the contents of the <Script> element, and inserted the element into the page. Once all the user scripts finished, Greasemonkey cleaned up the page by removing the <Script> elements it had inserted and removing the global properties it had added.DOMNodeInserted event, which the remote page could intercept. Consider a web page with the following JavaScript code. Keep in mind, this is not a user script; this is just regular JavaScript code that is part of the web page in which user scripts are executing.
<script type="text/javascript>
_scripts = [];
_c = document.getElementsByTagName("script").length;
function trapInsertScript(event) {
var doc = event.currentTarget;
var arScripts = doc.getElementsByTagName("script");
if (arScripts.length > _numPreviousScripts) {
_scripts.push(arScripts[_c++].innerHTML);
}
}
document.addEventListener("DOMNodeInserted", trapInsertScript, true);
</script>
not(ancestor::a). To ensure that it does affect URLs in uppercase, the XPath query also includes "contains(translate(., 'HTTP', 'http'), 'http')]".<span> element as a placeholder and then incrementally reinsert each non-URL text snippet and each constructed URL link.
// ==UserScript==
// @name Linkify
// @namespace http://youngpup.net/userscripts
// @description Turn plain-text URLs into hyperlinks
// @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();
}
}
not(ancestor::a). To ensure that it does affect URLs in uppercase, the XPath query also includes "contains(translate(., 'HTTP', 'http'), 'http')]".<span> element as a placeholder and then incrementally reinsert each non-URL text snippet and each constructed URL link.
// ==UserScript==
// @name Linkify
// @namespace http://youngpup.net/userscripts
// @description Turn plain-text URLs into hyperlinks
// @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();
}
}
target attribute of the link so that it opens in a new window.
// ==UserScript==
// @name Offsite Blank
// @namespace http://diveintomark.org/projects/greasemonkey/
// @description force offsite 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";
}
}
javascript: pseudo-protocol to create pop-up windows.
<a href="javascript:popup('http://youngpup.net/')">go to youngpup.net</a>
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.onclick handler to a regular hyperlink:
<a href="http://youngpup.net/"
onclick="window.open(this.href); return false;">
go to youngpup.net
</a>
javascript: links that appear to open pop-up windows and then change them to use an onclick handler instead.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.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.
// ==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/nomiddleman.user.js
for (var i=0; i<document.links.length; i++) {
var link, temp, start, url, 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 redirection 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 != url) {
temp = url;
url = unescape(url);
}
// and we're done
link.href = url.replace(/&/g, '&');
}
}
http://bolinfest.com/targetalert/.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.
// ==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/pdfwarn.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);
}
}
http://en.wikipedia.org/wiki/Slashdot_effect.
// ==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.links.length; i++) {
link = document.links[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 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);
}
<a> element and then constructs a link that points to the appropriate online tracking site.http://www.ups.com
):http://www.fedex.com):http://www.usps.com
):
// ==UserScript==
// @name UPS/FedEx Tracking Linkify
// @namespace http://scripts.slightlyinsane.com
// @description Link package tracking 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 Track
if (UPSRegex.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