<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://commons.oreilly.com/wiki/skins/common/feed.css?97"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>http://commons.oreilly.com/wiki/index.php?title=Greasemonkey_Hacks/Web_Mail&amp;action=history&amp;feed=atom</id>
		<title>Greasemonkey Hacks/Web Mail - Revision history</title>
		<link rel="self" type="application/atom+xml" href="http://commons.oreilly.com/wiki/index.php?title=Greasemonkey_Hacks/Web_Mail&amp;action=history&amp;feed=atom"/>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=Greasemonkey_Hacks/Web_Mail&amp;action=history"/>
		<updated>2013-06-19T17:19:46Z</updated>
		<subtitle>Revision history for this page on the wiki</subtitle>
		<generator>MediaWiki 1.11.0</generator>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=Greasemonkey_Hacks/Web_Mail&amp;diff=9636&amp;oldid=prev</id>
		<title>Docbook2Wiki: Initial conversion from Docbook</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=Greasemonkey_Hacks/Web_Mail&amp;diff=9636&amp;oldid=prev"/>
				<updated>2008-03-11T23:10:08Z</updated>
		
		<summary type="html">&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 23:10, 11 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</summary>
		<author><name>Docbook2Wiki</name></author>	</entry>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=Greasemonkey_Hacks/Web_Mail&amp;diff=7959&amp;oldid=prev</id>
		<title>Docbook2Wiki: Initial conversion from Docbook</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=Greasemonkey_Hacks/Web_Mail&amp;diff=7959&amp;oldid=prev"/>
				<updated>2008-03-11T20:29:52Z</updated>
		
		<summary type="html">&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Greasemonkey Hacks/TOC}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Hacks 60–66: Introduction ==&lt;br /&gt;
&lt;br /&gt;
One of the most popular uses for the Internet in the early 1990s was email. Personal email, email-based newsletters, and email-based discussion groups all drove people onto the Internet. It was the ''killer app'': the one feature people couldn't live without.&lt;br /&gt;
&lt;br /&gt;
Then the Web exploded onto the scene, and pundits and self-proclaimed experts declared that the killer app of the Web was interactive TV. And then it was search. And then it was shopping. And then it was interactive TV again. (Somewhere in there was that whole 3D VRML craze that lasted about five minutes. Boy, that was fun…not.)&lt;br /&gt;
&lt;br /&gt;
And here we are, in the year 2005. What's the killer app of the Web? What's the most impressive, most fantastic, most mind-bogglingly useful thing to hit the Web in the past 10 years? Gmail, a web-based email service. God, I love the Internet.&lt;br /&gt;
&lt;br /&gt;
== Force Gmail to Use a Secure Connection ==&lt;br /&gt;
&lt;br /&gt;
'''Protect your inbox by automatically redirecting Gmail to an https:// address'''.&lt;br /&gt;
&lt;br /&gt;
You can use Google's web mail service through an unsecured connection (an ''http://'' address) or a secure connection (an ''https://'' address). When I'm out and about and browsing the Web on an untrusted network (such as an Internet cafe), I try to remember to use the ''https://'' address. But why bother remembering, when Greasemonkey can remember it for me?&lt;br /&gt;
&lt;br /&gt;
=== The Code ===&lt;br /&gt;
&lt;br /&gt;
This user script is literally one line of code. The reason it can be so small is that we configure it to run only on ''http://mail.google.com'', the insecure address of Gmail.&lt;br /&gt;
&lt;br /&gt;
Save the following user script as ''securewebmail.user.js'':&lt;br /&gt;
&lt;br /&gt;
 	// ==UserScript==&lt;br /&gt;
 	// @name          Secure web mail&amp;lt;nowiki&amp;gt;Webmail&lt;br /&gt;
	// @namespace	  http://diveintomark.org/projects/greasemonkey/&lt;br /&gt;
	// @description	  force webmail to use secure connection&lt;br /&gt;
	// @include		  http://mail.google.com/*&lt;br /&gt;
	// ==/UserScript==&lt;br /&gt;
&lt;br /&gt;
	window.location.href = window.location.href.replace(/^http:/, 'https:');&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running the Hack ===&lt;br /&gt;
&lt;br /&gt;
After installing the user script (Tools → Install This User Script), go to ''http://mail.google.com/mail''. Your browser will automatically redirect to ''https://mail.google.com/mail''. Firefox will change the background color of the location bar to pale yellow (as shown in [[Greasemonkey Hacks/Web Mail#greasemonkeyhks-CHP-7-FIG-1|Figure 7-1]]) to indicate that you are now browsing a secure site.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;greasemonkeyhks-CHP-7-FIG-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 7-1. A secure connection to Gmail'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Greasemonkey Hacks_I_7_tt269.png|A secure connection to Gmail]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hacking the Hack ===&lt;br /&gt;
&lt;br /&gt;
Many online applications offer the same service on an ''http://'' or an ''https://'' address. This script will work unmodified on any such site. There is nothing Gmail-specific about the code itself; all it does is redirect from an ''http://'' address to the corresponding ''https://'' address.&lt;br /&gt;
&lt;br /&gt;
If you use Yahoo! Mail instead of (or in addition to) Gmail, all you need to do is change the script's configuration to tell Greasemonkey to run the script when you visit Yahoo! Mail. Under the Tools menu, select Manage User Scripts. In the list of scripts, select Gmail Secure. You will see the current configuration of where the script should run. Under &amp;quot;Included pages,&amp;quot; click Add…and type ''http://mail.yahoo.com/*'', as shown in [[Greasemonkey Hacks/Web Mail#greasemonkeyhks-CHP-7-FIG-2|Figure 7-2]].&lt;br /&gt;
&lt;br /&gt;
Now, visit Yahoo! Mail at ''http://mail.yahoo.com''. You will immediately be redirected to ''https://mail.yahoo.com'', and you can sign in to Yahoo! Mail securely.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;greasemonkeyhks-CHP-7-FIG-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 7-2. Secure Yahoo! Mail configuration'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Greasemonkey Hacks_I_7_tt270.png|Secure Yahoo! Mail configuration]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Warn Before Replying to Multiple Recipients in Gmail ==&lt;br /&gt;
&lt;br /&gt;
'''Don't embarrass yourself by sending private replies to everyone'''.&lt;br /&gt;
&lt;br /&gt;
Using any email program, it's all too easy to accidentally hit the Reply All button and end up saying something to a large group that was meant for just one person. But the problem isn't limited to the Reply All button. If there are multiple people in the To: list, it's even easier to accidentally reply to them all, because the Reply button replies to everyone by default.&lt;br /&gt;
&lt;br /&gt;
=== The Code ===&lt;br /&gt;
&lt;br /&gt;
This user script runs in the Compose frame of Gmail, which can be identified by its query string parameter &amp;lt;tt&amp;gt;view=cv&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
Use the View Frame Info menu command in the context menu to see the URL of a &amp;lt;tt&amp;gt;&amp;lt;frame&amp;gt;&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;&amp;lt;iframe&amp;gt;&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The script uses the following algorithm to detect a possible reply-all snafu:&lt;br /&gt;
&lt;br /&gt;
# Listen for all click events on the page by using &amp;lt;tt&amp;gt;document.addEventListener&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# If the click event originated from a Send button, check the number of recipients. Recipients are usually separated by a comma followed by a space, but since you can type any amount of space around the comma, this script uses a regular expression.&lt;br /&gt;
# If there is more than one recipient, warn the user.&lt;br /&gt;
# If the user decides not to proceed, cancel the form submission. The script accomplishes this by calling the event object's &amp;lt;tt&amp;gt;stopPropagation&amp;lt;/tt&amp;gt; method, which prevents the event from bubbling up to the &amp;lt;tt&amp;gt;&amp;lt;form&amp;gt;&amp;lt;/tt&amp;gt; element where it would submit the form and send the message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
You can use &amp;lt;tt&amp;gt;addEventListener&amp;lt;/tt&amp;gt; at any level in the document tree. Sometimes, listening at a high level and filtering by the &amp;lt;tt&amp;gt;target&amp;lt;/tt&amp;gt; property is easier than finding a specific element and attaching an event listener to it.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Save the following user script as ''dontreplyall.user.js'':&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
	// ==UserScript==&lt;br /&gt;
	// @name		 Don't Reply-All&lt;br /&gt;
	// @namespace    http://youngpup.net/&lt;br /&gt;
	// @description  &amp;lt;/nowiki&amp;gt;Gmailwarn before multiple repliesWarn before replying to multiple recipients in web mailGmail&lt;br /&gt;
 	// @include      http*://mail.google.com/mail/?*&amp;amp;view=cv*&lt;br /&gt;
 	// ==/UserScript==&lt;br /&gt;
 &lt;br /&gt;
 	// based on code by Aaron Boodman&lt;br /&gt;
 	// and included here with his gracious permission&lt;br /&gt;
 &lt;br /&gt;
 	var recipient_separator = /\s*\,\s*/g;&lt;br /&gt;
 &lt;br /&gt;
 	document.addEventListener(&amp;quot;click&amp;quot;, function(e) {&lt;br /&gt;
 		if (e.target.id == &amp;quot;send&amp;quot;) {&lt;br /&gt;
 			var form = document.getElementById(&amp;quot;compose_form&amp;quot;);&lt;br /&gt;
 			var to = removeEmptyItems(&lt;br /&gt;
 				form.elements.namedItem('to').value.split(recipient_separator)); &lt;br /&gt;
 			var cc = removeEmptyItems( &lt;br /&gt;
 				form.elements.namedItem('cc').value.split(recipient_separator)); &lt;br /&gt;
 			var bcc = removeEmptyItems( &lt;br /&gt;
 				form.elements.namedItem('bcc').value.split(recipient_separator));&lt;br /&gt;
 &lt;br /&gt;
 			if ((to.length + cc.length + bcc.length) &amp;gt; 1) {&lt;br /&gt;
 			  if (!confirm(&amp;quot;WARNING!\n&amp;quot; + &lt;br /&gt;
 				   &amp;quot;Do you really want to reply to all these people?\n\n&amp;quot; + &lt;br /&gt;
 				   &amp;quot;To: &amp;quot; + to.join(&amp;quot;, &amp;quot;) + &amp;quot;\n&amp;quot; + &lt;br /&gt;
 				   &amp;quot;CC: &amp;quot; + cc.join(&amp;quot;, &amp;quot;) + &amp;quot;\n&amp;quot; + &lt;br /&gt;
 				   &amp;quot;BCC: &amp;quot; + bcc.join(&amp;quot;, &amp;quot;))) {&lt;br /&gt;
 				 e.stopPropagation();&lt;br /&gt;
 			  }&lt;br /&gt;
 		   }&lt;br /&gt;
 		}&lt;br /&gt;
 	}, true);&lt;br /&gt;
 &lt;br /&gt;
 	function removeEmptyItems(arr) {&lt;br /&gt;
 		var result = [];&lt;br /&gt;
 		for (var i = 0, item; item = arr[i]; i++) {&lt;br /&gt;
 			if (/\S/.test(item)) {&lt;br /&gt;
 			   result.push(item);&lt;br /&gt;
 			}&lt;br /&gt;
 		}&lt;br /&gt;
 		&lt;br /&gt;
 		return result;&lt;br /&gt;
 	}&lt;br /&gt;
&lt;br /&gt;
=== Running the Hack ===&lt;br /&gt;
&lt;br /&gt;
After installing the user script (Tools → Install This User Script), log into Gmail at ''http://mail.google.com'' and open any message. Replace the To: field with multiple test addresses and press Send. The script will display a dialog to confirm that you want to send your message to multiple recipients, as shown in [[Greasemonkey Hacks/Web Mail#greasemonkeyhks-CHP-7-FIG-3|Figure 7-3]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;greasemonkeyhks-CHP-7-FIG-3&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 7-3. Confirmation message before replying to multiple recipients'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Greasemonkey Hacks_I_7_tt272.png|Confirmation message before replying to multiple recipients]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you hit OK, Gmail will send the message as usual. If you hit Cancel, you will stay in the message composition window and can edit the To: or Cc: list to trim the number of recipients.&lt;br /&gt;
&lt;br /&gt;
—''Aaron Boodman''&lt;br /&gt;
&lt;br /&gt;
== Warn Before Sending Gmail Messages with Missing Attachments ==&lt;br /&gt;
&lt;br /&gt;
'''Never again forget to attach a file to your email'''.&lt;br /&gt;
&lt;br /&gt;
It's too easy to forget to attach files when sending email. You send off the message, and a few minutes later you get a puzzled reply asking, &amp;quot;Was there supposed to be a file with this?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Some desktop mail applications use heuristics to check for this condition and prompt you before sending your message. However, I have never seen this incorporated in a web mail application. Using Greasemonkey, we can guess with pretty good accuracy that a message was supposed to contain an attachment if it contains words such as ''attachment'' or ''files''. If such a message actually contains no files, then we can show a warning before sending the message.&lt;br /&gt;
&lt;br /&gt;
=== The Code ===&lt;br /&gt;
&lt;br /&gt;
This user script runs in both the Reply and Compose frames in Gmail. These can be identified by their &amp;lt;tt&amp;gt;view=cw&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;view=page&amp;lt;/tt&amp;gt; query string parameters.&lt;br /&gt;
&lt;br /&gt;
The script listens for click events on the Send buttons. When the user clicks one of them, the script gets the contents of the message text box and scans each line of the message for any occurrences of specific keywords. Since messages can contain quoted text copied from a previous message, we intentionally ignore lines that start with &amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If we find any occurrences of ''attachment'' or ''files'', we check whether there are any attachments. If there are no attachments, we prompt the user to confirm that she really wants to send the message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
You can access forms, form elements, images, and links by their elements' &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; attributes as well as their &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt; attributes. If the element you need to modify is one of these types but doesn't have a unique &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt; attribute, check to see if it has a unique &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; attribute instead.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Save the following user script as ''missingattachments.user.js'':&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
	// ==UserScript==&lt;br /&gt;
	// @name		 Missing Attachment&lt;br /&gt;
	// @namespace	 http://youngpup.net/&lt;br /&gt;
	// @description  Warn before sending Gmail messages without attachments&lt;br /&gt;
	// @include      http*://mail.google.com/mail/?*view=cv*&lt;br /&gt;
	// @include      http*://mail.google.com/mail/?*view=page*&lt;br /&gt;
	// ==/UserScript==&lt;br /&gt;
&lt;br /&gt;
	// based on code by Aaron Boodman&lt;br /&gt;
	// and included here with his gracious permission&lt;br /&gt;
&lt;br /&gt;
	// add more keywords here if necessary&lt;br /&gt;
	var words = [&amp;quot;attach&amp;quot;, &amp;quot;attachment&amp;quot;, &amp;quot;attached&amp;quot;, &amp;quot;file&amp;quot;, &amp;quot;files&amp;quot;];&lt;br /&gt;
&lt;br /&gt;
	// creates a regex like of the form /\b(foo|bar|baz)\b/i&lt;br /&gt;
	var regex = new RegExp(&amp;quot;\\b(&amp;quot; + words.join(&amp;quot;|&amp;quot;) + &amp;quot;)\\b&amp;quot;, &amp;quot;i&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	var form = document.getElementById(&amp;quot;compose_form&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	document.addEventListener(&amp;quot;click&amp;quot;, function(e) { &lt;br /&gt;
		if (e.target.id != &amp;quot;send&amp;quot;) { return true; } &lt;br /&gt;
		var allLines = form.elements.namedItem('msgbody').value.split(&amp;quot;\n&amp;quot;); &lt;br /&gt;
		for (var i = 0, line; line = allLines[i]; i++) {&lt;br /&gt;
			// by convention, reply lines start with &amp;quot;&amp;gt;&amp;quot;. Some people like&lt;br /&gt;
			// to be clever and use other characters. If you encounter this,&lt;br /&gt;
			// you can test for those characters as well.&lt;br /&gt;
			if (line[0] == &amp;quot;&amp;gt;&amp;quot;) { continue; }&lt;br /&gt;
			if (!line.match(regex)) { continue; }&lt;br /&gt;
			if (isFileAttached()) { continue; }&lt;br /&gt;
			if (!window.confirm(&amp;quot;&amp;lt;/nowiki&amp;gt;Gmailwarn before sending without attachmentsWARNING\n\n&amp;quot; +&lt;br /&gt;
 				&amp;quot;This message mentions attachments, but none &amp;quot; +&lt;br /&gt;
 				&amp;quot;are included.\n\n&amp;quot; +&lt;br /&gt;
 				&amp;quot;Really send?\n\n&amp;quot; +&lt;br /&gt;
 				&amp;quot;Suspicious line:\n&amp;quot; +&lt;br /&gt;
 				&amp;quot;\&amp;quot;&amp;quot; + line + &amp;quot;\&amp;quot;&amp;quot;)) {&lt;br /&gt;
 				e.stopPropagation();&lt;br /&gt;
 			}&lt;br /&gt;
 			break;&lt;br /&gt;
 		}&lt;br /&gt;
 	}, true);&lt;br /&gt;
 &lt;br /&gt;
 	function isFileAttached() {&lt;br /&gt;
 		var iter = document.evaluate(&amp;quot;.//input[@type='file']&amp;quot;,&lt;br /&gt;
 			form, null, XPathResult.ANY_TYPE, null);&lt;br /&gt;
 		var input;&lt;br /&gt;
 		while (input = iter.iterateNext()) {&lt;br /&gt;
 			if (input.value != &amp;quot;&amp;quot;) {&lt;br /&gt;
 				return true;&lt;br /&gt;
 			}&lt;br /&gt;
 		}&lt;br /&gt;
 		return false;&lt;br /&gt;
 	}&lt;br /&gt;
&lt;br /&gt;
The word-boundary regular expression assertion &amp;lt;tt&amp;gt;\b&amp;lt;/tt&amp;gt; is the best way to tell the difference between &amp;lt;tt&amp;gt;foo&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;foobar&amp;lt;/tt&amp;gt;. The word boundary matches when a word character (&amp;lt;tt&amp;gt;a-z, A-Z, 0-9&amp;lt;/tt&amp;gt;, -and _) is preceded or followed by a nonword character.&lt;br /&gt;
&lt;br /&gt;
It's fast and easy to create regular expressions in JavaScript using the literal form (i.e., &amp;lt;tt&amp;gt;/foobar/&amp;lt;/tt&amp;gt;). But you might need to construct an expression dynamically, from a string that you don't know beforehand. To do this, create an instance of the RegExp object, which takes a string argument. This creates an additional problem: backslashes have special meaning inside JavaScript strings. To insert a backslash in the regular expression defined as a string, you need ''two'' backslashes. So, &amp;lt;tt&amp;gt;/hello\?/&amp;lt;/tt&amp;gt; becomes &amp;lt;tt&amp;gt;&amp;quot;hello\\?&amp;quot;&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;/c:\\/&amp;lt;/tt&amp;gt; becomes &amp;lt;tt&amp;gt;&amp;quot;c:\\\\&amp;quot;&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Running the Hack ===&lt;br /&gt;
&lt;br /&gt;
After installing the user script (Tools → Install This User Script), log into Gmail at ''http://mail.google.com'' and start a new message. Add some text to the message body, such as, &amp;quot;Hi Bob, the spreadsheet you requested is attached. Please review.&amp;quot; Press the Send button without attaching any files. The script pops up a dialog to confirm that you really intended to send the message without any attachments, as shown in [[Greasemonkey Hacks/Web Mail#greasemonkeyhks-CHP-7-FIG-4|Figure 7-4]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;greasemonkeyhks-CHP-7-FIG-4&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 7-4. Confirming sending without attachments'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Greasemonkey Hacks_I_7_tt274.png|Confirming sending without attachments]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you click OK, Gmail will send the message as usual. If you click Cancel, you will stay on the message composition page, and you can click &amp;quot;Attach a file.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
—''Aaron Boodman''&lt;br /&gt;
&lt;br /&gt;
== Compose Your Mail in Gmail ==&lt;br /&gt;
&lt;br /&gt;
'''Make mailto: links open the Gmail compose page'''.&lt;br /&gt;
&lt;br /&gt;
The Web comprises many kinds of resources: web pages, newsgroups, IRC channels, FTP sites, and so on. Each kind of resource has a ''scheme'', such as the &amp;lt;tt&amp;gt;http&amp;lt;/tt&amp;gt;: in ''http://mozilla.org'' or the &amp;lt;tt&amp;gt;irc&amp;lt;/tt&amp;gt;: in ''irc://irc.mozilla.org/firefox''.&lt;br /&gt;
&lt;br /&gt;
You've probably seen &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: links on contact pages; when you click the link, it launches an external email program.&lt;br /&gt;
&lt;br /&gt;
But what if you use a web mail service such as Gmail? Normally, getting &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: links to launch a web-based email application is nontrivial. You would basically need to write an external ''mail'' program that switched back to your browser and opened the appropriate URL. What a pain! This hack solves the problem another way, by rewriting &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: links to point to the Gmail Compose page.&lt;br /&gt;
&lt;br /&gt;
=== The Code ===&lt;br /&gt;
&lt;br /&gt;
This user script runs on all pages. From a high-level view, it sounds deceptively simple. Just find all the &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: links, parse them, and replace them with links to Gmail. When you click the link, the browser just redirects to the Gmail Compose page instead of launching a separate application.&lt;br /&gt;
&lt;br /&gt;
Of course, it's not really that simple. The problem is that &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: links can be complex. RFC 2368, entitled &amp;quot;The mailto URL scheme,&amp;quot; specifies the format. The overall structure is &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;:&amp;lt;tt&amp;gt;''&amp;lt;recipient&amp;gt;''&amp;lt;/tt&amp;gt;?&amp;lt;tt&amp;gt;''&amp;lt;querystring&amp;gt;''&amp;lt;/tt&amp;gt;, where &amp;lt;tt&amp;gt;''&amp;lt;querystring&amp;gt;''&amp;lt;/tt&amp;gt; is a list of &amp;lt;tt&amp;gt;&amp;lt;name&amp;gt;=&amp;lt;value&amp;gt;&amp;lt;/tt&amp;gt; pairs separated by ampersands (&amp;lt;tt&amp;gt;&amp;amp;&amp;lt;/tt&amp;gt;). We want to pass these name/value pairs to Gmail, but, of course, Gmail's Compose page uses a different syntax for encoding them in the URL. So, we need to parse them out and map them individually.&lt;br /&gt;
&lt;br /&gt;
The most important values that we want to transfer to Gmail are the To: and Cc: recipients, the subject line, and the email body. All of these can be encoded in that simple-looking &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: link! The script parses them out one by one, stores them temporarily, and then uses them to construct the URL of the Gmail Compose page.&lt;br /&gt;
&lt;br /&gt;
One last thing worth mentioning: this script intentionally delays its own processing of the page to take place after the page is loaded, by hooking into the window's &amp;lt;tt&amp;gt;onload&amp;lt;/tt&amp;gt; event. Many sites use JavaScript to write out &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: links (to protect them from spammers). By the time the &amp;lt;tt&amp;gt;onload&amp;lt;/tt&amp;gt; event fires, the &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: links should be set up and available to use in the DOM.&lt;br /&gt;
&lt;br /&gt;
Save the following user script as ''mailto-compose-in-gmail.user.js'':&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
	// ==UserScript==&lt;br /&gt;
	// @name         Mailto Compose In Gmail&lt;br /&gt;
	// @namespace    http://blog.monstuff.com/archives/cat_greasemonkey.html&lt;br /&gt;
	// @description  Rewrites &amp;quot;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;mailto: links&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;mailto:&amp;quot; links to Gmail compose links&lt;br /&gt;
    // @include		 *&lt;br /&gt;
	// ==/UserScript==&lt;br /&gt;
&lt;br /&gt;
	// based on code by Julien Couvreur&lt;br /&gt;
	// and included here with his gracious permission&lt;br /&gt;
&lt;br /&gt;
	function &amp;lt;/nowiki&amp;gt;web mailprocessMailtoLinks() {&lt;br /&gt;
 		var xpath = &amp;quot;//a[starts-with(@href,'&amp;lt;nowiki&amp;gt;mailto: links&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;mailto:')]&amp;quot;;&lt;br /&gt;
		var res = document.evaluate(xpath, document, null,&lt;br /&gt;
			XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);&lt;br /&gt;
&lt;br /&gt;
		var linkIndex, mailtoLink;&lt;br /&gt;
		for (linkIndex = 0; linkIndex &amp;lt; res.snapshotLength; linkIndex++) {&lt;br /&gt;
			mailtoLink = res.snapshotItem(linkIndex);&lt;br /&gt;
&lt;br /&gt;
			var href = mailtoLink.href;&lt;br /&gt;
			var matches = href.match(/^mailto:([^\?]*)(\?([^?]*))?/);&lt;br /&gt;
			var emailTo, params, emailCC, emailSubject, emailBody;&lt;br /&gt;
&lt;br /&gt;
			emailTo = matches[1];&lt;br /&gt;
			params = matches[3];&lt;br /&gt;
			if (params) {&lt;br /&gt;
				var splitQS = params.split('&amp;amp;');&lt;br /&gt;
				var paramIndex, param;&lt;br /&gt;
&lt;br /&gt;
				for (paramIndex = 0; paramIndex &amp;lt; splitQS.length; paramIndex++)&lt;br /&gt;
	 {&lt;br /&gt;
				param = splitQS[paramIndex];&lt;br /&gt;
				nameValue = param.match(/([^=]+)=(.*)/);&lt;br /&gt;
				if (nameValue &amp;amp;&amp;amp; nameValue.length == 3) {&lt;br /&gt;
				switch(nameValue[1]) {&lt;br /&gt;
				case &amp;quot;to&amp;quot;:&lt;br /&gt;
					emailTo += &amp;quot;%2C%20&amp;quot; + nameValue[2];&lt;br /&gt;
					break;&lt;br /&gt;
				case &amp;quot;cc&amp;quot;:&lt;br /&gt;
					emailCC = nameValue[2];&lt;br /&gt;
					break;&lt;br /&gt;
				case &amp;quot;subject&amp;quot;:&lt;br /&gt;
					emailSubject = nameValue[2];&lt;br /&gt;
					break;&lt;br /&gt;
				case &amp;quot;body&amp;quot;:&lt;br /&gt;
					emailBody = nameValue[2];&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				}&lt;br /&gt;
				}&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			var newUrl = &amp;quot;https://mail.google.com/mail?view=cm&amp;amp;tf=0&amp;quot; +&lt;br /&gt;
				(emailTo ? (&amp;quot;&amp;amp;to=&amp;quot; + emailTo) : &amp;quot;&amp;quot;) +&lt;br /&gt;
				(emailCC ? (&amp;quot;&amp;amp;cc=&amp;quot; + emailCC) : &amp;quot;&amp;quot;) +&lt;br /&gt;
				(emailSubject ? (&amp;quot;&amp;amp;su=&amp;quot; + emailSubject) : &amp;quot;&amp;quot;) +&lt;br /&gt;
				(emailBody ? (&amp;quot;&amp;amp;body=&amp;quot; + emailBody) : &amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
			mailtoLink.href = newUrl;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		window.addEventListener(&amp;quot;load&amp;quot;, &amp;lt;/nowiki&amp;gt;web mailprocessMailtoLinks, false);&lt;br /&gt;
&lt;br /&gt;
=== Running the Hack ===&lt;br /&gt;
&lt;br /&gt;
Before installing this script, go to ''http://blog.monstuff.com'' and hover your cursor over the link on the left titled &amp;quot;Contact me.&amp;quot; You will see a &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: address in the status bar, as shown in [[Greasemonkey Hacks/Web Mail#greasemonkeyhks-CHP-7-FIG-5|Figure 7-5]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;greasemonkeyhks-CHP-7-FIG-5&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 7-5. A mailto: link'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Greasemonkey Hacks_I_7_tt276.png|A mailto: link]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, install the script (Tools→Install This User Script), and refresh the page. Again, hover your cursor over the &amp;quot;Contact me&amp;quot; link, and you will see that the URL has been changed to point to Gmail, as shown in [[Greasemonkey Hacks/Web Mail#greasemonkeyhks-CHP-7-FIG-6|Figure 7-6]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;greasemonkeyhks-CHP-7-FIG-6&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 7-6. A &amp;quot;mailto:&amp;quot; link transformed to Gmail'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Greasemonkey Hacks_I_7_tt277.png|A &amp;quot;mailto:&amp;quot; link transformed to Gmail]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hacking the Hack ===&lt;br /&gt;
&lt;br /&gt;
Rewriting &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: links to regular &amp;lt;tt&amp;gt;http&amp;lt;/tt&amp;gt;: links has one disadvantage. Normally, Firefox lets you right-click on &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: links and select &amp;quot;Copy email address&amp;quot; from the context menu, but this hack interferes with that feature by turning the &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: link into something else.&lt;br /&gt;
&lt;br /&gt;
One solution is not to rewrite the link, but attach an &amp;lt;tt&amp;gt;onclick&amp;lt;/tt&amp;gt; event handler. This lets you copy the email address using Firefox's context menu, but you would still redirect to Gmail when you actually click the link.&lt;br /&gt;
&lt;br /&gt;
This works, except for one thing: it won't let you open the Gmail Compose page in a new tab. But we can solve that problem with Greasemonkey, too. One of the new features in Greasemonkey 0.5 is the &amp;lt;tt&amp;gt;GM_openInTab&amp;lt;/tt&amp;gt; function, which does exactly what it sounds like.&lt;br /&gt;
&lt;br /&gt;
In the previous script, replace this line:&lt;br /&gt;
&lt;br /&gt;
 	mailtoLink.href = newUrl;&lt;br /&gt;
&lt;br /&gt;
with this code snippet:&lt;br /&gt;
&lt;br /&gt;
 	mailtoLink.addEventListener('click', function(e) {&lt;br /&gt;
 		GM_openInTab(newUrl);&lt;br /&gt;
 		e.preventDefault();&lt;br /&gt;
 	}&lt;br /&gt;
&lt;br /&gt;
When you click the link, Greasemonkey will open the Gmail Compose page in a new tab. You can still right-click and select items from the regular &amp;lt;tt&amp;gt;mailto&amp;lt;/tt&amp;gt;: context menu, including &amp;quot;Copy email address.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
—''Julien Couvreur''&lt;br /&gt;
&lt;br /&gt;
== Add a Delete Button to Gmail ==&lt;br /&gt;
&lt;br /&gt;
'''Improve the Gmail interface with the most requested feature that Google doesn't want you to have'''.&lt;br /&gt;
&lt;br /&gt;
Many netizens have gotten a Gmail account and been wowed with the interface, but noticed one critical missing piece: there is no easy way to delete a message. Deletion in the standard Gmail interface requires opening the drop-down actions box and selecting the last item, which is cumbersome for a frequent activity.&lt;br /&gt;
&lt;br /&gt;
This hack alters the user interface of Gmail to include an extra button that lets the user delete any message with one simple click.&lt;br /&gt;
&lt;br /&gt;
=== The Code ===&lt;br /&gt;
&lt;br /&gt;
This script is split into five parts.&lt;br /&gt;
&lt;br /&gt;
# The &amp;lt;tt&amp;gt;_gd_gmail_delete&amp;lt;/tt&amp;gt; function performs the actual message deletion. It simply searches for a &amp;lt;tt&amp;gt;&amp;lt;select&amp;gt;&amp;lt;/tt&amp;gt; element within its parent container and then finds the appropriate action within that menu. If it is found, the script triggers the &amp;lt;tt&amp;gt;onchange&amp;lt;/tt&amp;gt; handler of the select box, which launches the standard Gmail code to delete the selected messages.&lt;br /&gt;
# The &amp;lt;tt&amp;gt;_gd_make_dom_button&amp;lt;/tt&amp;gt; function is notable because it supports multiple languages. There is only one word that the script adds, but this function attempts to autodetect the language in the Gmail interface, and sets the Delete button caption to the proper translation. It also attaches the &amp;lt;tt&amp;gt;_gd_gmail_delete&amp;lt;/tt&amp;gt; function as the &amp;lt;tt&amp;gt;onclick&amp;lt;/tt&amp;gt; event handler for the button.&lt;br /&gt;
# The &amp;lt;tt&amp;gt;_gd_insert_button&amp;lt;/tt&amp;gt; function does the real magic in this script. It calls the &amp;lt;tt&amp;gt;_gd_make_dom_button&amp;lt;/tt&amp;gt; function to create a new element to be injected into the page, does a little checking for the most attractive place to put it, and injects the element into the page.&lt;br /&gt;
# The &amp;lt;tt&amp;gt;_gd_place_delete_buttons&amp;lt;/tt&amp;gt; function is the driver function. It calls the &amp;lt;tt&amp;gt;_gd_element&amp;lt;/tt&amp;gt; function to check for the four places where it might be appropriate to place the Delete button. The best thing to look for is the drop-down actions menu, which is tagged with one of four IDs, depending on the page. If we find the drop-down menu, we call the &amp;lt;tt&amp;gt;_gd_insert_button&amp;lt;/tt&amp;gt; function with a reference to the container of that box.&lt;br /&gt;
# Finally, the main block of the script checks for the &amp;lt;tt&amp;gt;document.location.search&amp;lt;/tt&amp;gt; element. The Gmail interface is split into multiple frames, but the frame where all the user interaction occurs will always contain a search box. If it makes sense to add a Delete button on the current page, we call the &amp;lt;tt&amp;gt;_gd_place_delete_buttons&amp;lt;/tt&amp;gt; function to add it. Because of the way Gmail constantly re-creates parts of its user interface, we also need to register an event handler to re-add our Delete button after all mouse and keyboard actions.&lt;br /&gt;
&lt;br /&gt;
Save the following user script as ''gmail-delete-button.user.js'':&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
	// ==UserScript==&lt;br /&gt;
	// @name		 Gmail Delete Button&lt;br /&gt;
	// @namespace    http://www.arantius.com/&lt;br /&gt;
	// @description  Add a &amp;quot;Delete&amp;quot; button to Gmail's interface&lt;br /&gt;
	// @include      http*://mail.google.com/*mail*?*&lt;br /&gt;
	// ==/UserScript==&lt;br /&gt;
&lt;br /&gt;
	// based on code by Anthony Lieuallen&lt;br /&gt;
	// and included here with his gracious permission&lt;br /&gt;
	// http://www.arantius.com/article/arantius/gmail+delete+button/&lt;br /&gt;
	&lt;br /&gt;
	function _gd_element(id) {&lt;br /&gt;
		try {&lt;br /&gt;
			var el=document.getElementById(id);&lt;br /&gt;
		} catch (e) {&lt;br /&gt;
			GM_log(&lt;br /&gt;
				&amp;quot;Gmail Delete Button:\nThere was an error!\n\n&amp;quot;+&lt;br /&gt;
				&amp;quot;Line: &amp;quot;+e.lineNumber+&amp;quot;\n&amp;quot;+&lt;br /&gt;
				e.&amp;lt;/nowiki&amp;gt;Gmailadding a delete buttonname+&amp;quot;: &amp;quot;+e.message+&amp;quot;\n&amp;quot;&lt;br /&gt;
 			);&lt;br /&gt;
 			return false;&lt;br /&gt;
 			&lt;br /&gt;
 		}&lt;br /&gt;
 		if (el) return el;&lt;br /&gt;
 		return false;&lt;br /&gt;
 	}&lt;br /&gt;
 &lt;br /&gt;
 	function _gd_web mail&amp;lt;nowiki&amp;gt;gmail_delete(delete_button) {&lt;br /&gt;
		//find the command box&lt;br /&gt;
		var command_box = delete_button.parentNode.&lt;br /&gt;
	getElementsByTagName('select')[0];&lt;br /&gt;
		var real_command_box = command_box.wrappedJSObject || command_box;&lt;br /&gt;
		real_command_box.onfocus();&lt;br /&gt;
&lt;br /&gt;
		//find the command index for 'move to trash'&lt;br /&gt;
		var delete_index=-1;&lt;br /&gt;
		for (var i=0; i&amp;lt;command_box.options.length; i++) {&lt;br /&gt;
			if ('tr'==command_box.options[i].value &amp;amp;&amp;amp;&lt;br /&gt;
				!command_box.options[i].disabled ) {&lt;br /&gt;
				delete_index=i;&lt;br /&gt;
				break;&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		//don't try to continue if we can't move to trash now&lt;br /&gt;
		if (-1==delete_index) {&lt;br /&gt;
			var box=_gd_element('nt1'); if (box) {&lt;br /&gt;
				try {&lt;br /&gt;
				//if we find the box put an error message in it&lt;br /&gt;
				box.firstChild.style.visibility='visible';&lt;br /&gt;
				box.getElementsByTagName('td')[1].innerHTML= '' +&lt;br /&gt;
				'Could not delete. Make sure at least one ' +&lt;br /&gt;
				'conversation is selected.';&lt;br /&gt;
				} catch (e) {}&lt;br /&gt;
			}&lt;br /&gt;
			return;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		//set the command index and fire the change event&lt;br /&gt;
		command_box.selectedIndex=delete_index;&lt;br /&gt;
		real_command_box = command_box.wrappedJSObject || command_box;&lt;br /&gt;
		real_command_box.onchange();&lt;br /&gt;
	}&lt;br /&gt;
	function _gd_make_dom_button(id) {&lt;br /&gt;
		var delete_button= document.createElement('button');&lt;br /&gt;
		delete_button.setAttribute('class', 'ab');&lt;br /&gt;
		delete_button.setAttribute('id', '_gd_delete_button'+id);&lt;br /&gt;
		delete_button.addEventListener('click', function() {&lt;br /&gt;
			_gd_gmail_delete(delete_button);&lt;br /&gt;
		}, true);&lt;br /&gt;
		//this is &amp;lt;/nowiki&amp;gt;Gmailadding a delete button&amp;lt;nowiki&amp;gt;a little hack-y, but we can find the language code here&lt;br /&gt;
		var lang='';&lt;br /&gt;
		try {&lt;br /&gt;
			var urlToTest=window.top.document.getElementsByTagName('frame')[1].&lt;br /&gt;
	src;&lt;br /&gt;
			lang=urlToTest.match(/html\/([^\/]*)\/loading.html$/)[1];&lt;br /&gt;
		} catch (e) {}&lt;br /&gt;
		//now check that language, and set the button text&lt;br /&gt;
		var buttonText='Delete';&lt;br /&gt;
		switch (lang) {&lt;br /&gt;
		case 'it': buttonText='Elimina'; break;&lt;br /&gt;
		case 'es': buttonText='Borrar'; break;&lt;br /&gt;
		case 'fr': buttonText='Supprimer'; break;&lt;br /&gt;
		case 'pt-BR': buttonText='Supressão'; break;&lt;br /&gt;
		case 'de': buttonText='Löschen'; break;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		delete_button.innerHTML='&amp;lt;b&amp;gt;'+buttonText+'&amp;lt;/b&amp;gt;';&lt;br /&gt;
		return delete_button;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function _gd_insert_button(insert_container, id) {&lt;br /&gt;
		if (!insert_container) return false;&lt;br /&gt;
		if (_gd_element('_gd_delete_button'+id)) {&lt;br /&gt;
			return false;&lt;br /&gt;
		}&lt;br /&gt;
		&lt;br /&gt;
		//get the elements&lt;br /&gt;
		var spacer, delete_button;&lt;br /&gt;
		delete_button=_gd_make_dom_button(id);&lt;br /&gt;
		spacer=insert_container.firstChild.nextSibling.cloneNode(false);&lt;br /&gt;
&lt;br /&gt;
		//pick the right place to put them, depending on which page we're on&lt;br /&gt;
		var insert_point=insert_container.firstChild;&lt;br /&gt;
		if (2==id || 3==id) {&lt;br /&gt;
			// 2 and 3 are inside the message and go at a different place&lt;br /&gt;
			insert_point=insert_point.nextSibling.nextSibling;&lt;br /&gt;
		}&lt;br /&gt;
		if (document.location.search.match(/search=query/)) {&lt;br /&gt;
			//inside the search page the button goes in a different place&lt;br /&gt;
			if (0==id) {&lt;br /&gt;
				spacer=insert_container.firstChild.nextSibling.nextSibling.&lt;br /&gt;
	cloneNode(false);&lt;br /&gt;
				insert_point=insert_container.firstChild.nextSibling.&lt;br /&gt;
	nextSibling.nextSibling;&lt;br /&gt;
			}&lt;br /&gt;
			if (1==id) spacer=document.createElement('span');&lt;br /&gt;
		} else if (document.location.search.match(/search=sent/)) {&lt;br /&gt;
			//inside the sent page the button goes in yet another place&lt;br /&gt;
			if (0==id) {&lt;br /&gt;
				spacer=document.createTextNode(' ');&lt;br /&gt;
				insert_point=insert_container.firstChild.nextSibling.&lt;br /&gt;
	nextSibling;&lt;br /&gt;
			}&lt;br /&gt;
			if (1==id) spacer=document.createElement('span');&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		insert_container.insertBefore(spacer, insert_point);&lt;br /&gt;
		insert_container.insertBefore(delete_button, spacer);&lt;br /&gt;
	}&lt;br /&gt;
	function _gd_place_delete_buttons() {&lt;br /&gt;
		if (!window || ! document || ! document.body) return;&lt;br /&gt;
		var top_menu=_gd_element('tamu');&lt;br /&gt;
		if (top_menu) _gd_insert_button(top_menu.parentNode, 0);&lt;br /&gt;
		var bot_menu=_gd_element('bamu');&lt;br /&gt;
		if (bot_menu) _gd_insert_button(bot_menu.parentNode, 1);&lt;br /&gt;
		var mtp_menu=_gd_element('ctamu');&lt;br /&gt;
		if (mtp_menu) _gd_insert_button(mtp_menu.parentNode, 2);&lt;br /&gt;
		var mbt_menu=_gd_element('cbamu');&lt;br /&gt;
		if (mbt_menu) _gd_insert_button(mbt_menu.parentNode, 3);&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if (document.location.search) {&lt;br /&gt;
		var s=document.location.search;&lt;br /&gt;
		if (s.match(/\bsearch=(inbox|query|cat|all|starred|sent)\b/) ||&lt;br /&gt;
			( s.match(/view=cv/) &amp;amp;&amp;amp; !s.match(/search=(trash|spam)/) )&lt;br /&gt;
		) {&lt;br /&gt;
			// Insert the main button&lt;br /&gt;
			try {&lt;br /&gt;
				_gd_place_delete_buttons();&lt;br /&gt;
			} catch (e) {&lt;br /&gt;
				GM_log(e.message);&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			// Set events to try &amp;lt;/nowiki&amp;gt;Gmailadding a delete buttonadding buttons after user actions&lt;br /&gt;
 			var buttonsInAMoment = function() {&lt;br /&gt;
 				try {&lt;br /&gt;
 				_gd_place_delete_buttons();&lt;br /&gt;
 				}&lt;br /&gt;
 				catch (e) {&lt;br /&gt;
 				GM_log(e.message);&lt;br /&gt;
 				}&lt;br /&gt;
 			};&lt;br /&gt;
 			window.addEventListener('mouseup', buttonsInAMoment, false);&lt;br /&gt;
 			window.addEventListener('keyup', buttonsInAMoment, false);&lt;br /&gt;
 		}&lt;br /&gt;
 	}&lt;br /&gt;
&lt;br /&gt;
=== Running the Hack ===&lt;br /&gt;
&lt;br /&gt;
Before installing the user script, log into Gmail at ''http://mail.google.com''. The default view is your inbox, as shown in [[Greasemonkey Hacks/Web Mail#greasemonkeyhks-CHP-7-FIG-7|Figure 7-7]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;greasemonkeyhks-CHP-7-FIG-7&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 7-7. Unmodified Gmail interface'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Greasemonkey Hacks_I_7_tt281.png|Unmodified Gmail interface]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, install the script (Tools → Install This User Script) and refresh the page. You will see a Delete button next to the Archive button, as shown in [[Greasemonkey Hacks/Web Mail#greasemonkeyhks-CHP-7-FIG-8|Figure 7-8]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;greasemonkeyhks-CHP-7-FIG-8&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 7-8. Gmail with added Delete button'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Greasemonkey Hacks_I_7_tt282.png|Gmail with added Delete button]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can select one or more messages and click Delete, and Gmail will move the messages directly to the Trash folder.&lt;br /&gt;
&lt;br /&gt;
Certain actions within Gmail will completely rebuild the page. This is most obvious when deleting or archiving a previously unread message. Our Delete button will momentarily disappear from the interface when this happens. Don't worry, though; your next mouse click or key press will restore it in time to delete your next message.&lt;br /&gt;
&lt;br /&gt;
—''Anthony Lieuallen''&lt;br /&gt;
&lt;br /&gt;
== Select Your Yahoo! ID from a List ==&lt;br /&gt;
&lt;br /&gt;
'''Add a drop-down menu on Yahoo!'s login form to select your username'''.&lt;br /&gt;
&lt;br /&gt;
Do you have multiple Yahoo! accounts? Are you tired of typing them over and over when switching back and forth between them? Are you sick of hacks that begin with rhetorical questions? I can't help you with that last one, but here's a hack that gives you a drop-down menu of all your Yahoo! IDs in the Yahoo! login form.&lt;br /&gt;
&lt;br /&gt;
=== The Code ===&lt;br /&gt;
&lt;br /&gt;
This user script runs on all Yahoo! pages, but the first thing it does is check for the existence of the Yahoo! login form. If it doesn't find a login form, it just exits without doing anything. If it does find the login form, it sets a short timer to replace the username input box with a drop-down menu of your Yahoo! IDs.&lt;br /&gt;
&lt;br /&gt;
Edit the following user script to include your Yahoo! IDs, and then save it as ''yahoo-select.user.js'':&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
	// ==UserScript==&lt;br /&gt;
	// @name         Yahoo! User Persitance Thing&lt;br /&gt;
	// @namespace    http://www.rhyley.org/gm/&lt;br /&gt;
	// @description  Add a drop-down box to the Yahoo login form&lt;br /&gt;
	// @include      http*://*.yahoo.tld/*&lt;br /&gt;
	// ==/UserScript==&lt;br /&gt;
&lt;br /&gt;
	// based on code by Jason Rhyley&lt;br /&gt;
	// and included here with his gracious permission&lt;br /&gt;
&lt;br /&gt;
	// ** Replace this array with your Yahoo IDs **&lt;br /&gt;
	var gUserIDs = new Array(&amp;quot;Put&amp;quot;,&amp;quot;Your&amp;quot;,&amp;quot;User&amp;quot;,&amp;quot;ID&amp;quot;,&amp;quot;Here&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	var login = null;&lt;br /&gt;
	var password = null;&lt;br /&gt;
	&lt;br /&gt;
	function buildLoginThing() {&lt;br /&gt;
		if (gUserIDs[0] == 'Put'){&lt;br /&gt;
			alert('You must configure the script before it will \n' +&lt;br /&gt;
				  'work propery. Go to &amp;quot;Manage User Scripts&amp;quot; and\n' +&lt;br /&gt;
				  'click the \&amp;quot;Edit\&amp;quot; button to configure the script.');&lt;br /&gt;
			return;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var elmSelect = document.createElement(&amp;quot;select&amp;quot;);&lt;br /&gt;
		elmSelect.id = &amp;quot;username&amp;quot;;&lt;br /&gt;
		elmSelect.name = &amp;quot;login&amp;quot;;&lt;br /&gt;
		elmSelect.className = &amp;quot;yreg_ipt&amp;quot;;&lt;br /&gt;
		elmSelect.addEventListener('change', function() {&lt;br /&gt;
			if (this.&amp;lt;/nowiki&amp;gt;web mailselect a web mail ID from a listselectedIndex == this.options.length-1) {&lt;br /&gt;
 				window.setTimeout(function() {&lt;br /&gt;
 				var elmNew = document.createElement(&amp;quot;input&amp;quot;);&lt;br /&gt;
 				elmNew.type = &amp;quot;text&amp;quot;;&lt;br /&gt;
 				elmNew.id = &amp;quot;username&amp;quot;;&lt;br /&gt;
 				elmNew.name = &amp;quot;login&amp;quot;;&lt;br /&gt;
 				elmNew.className = &amp;quot;yreg_ipt&amp;quot;;&lt;br /&gt;
 				login.parentNode.replaceChild(elmNew, login);&lt;br /&gt;
 				login = elmNew;&lt;br /&gt;
 				login.focus();&lt;br /&gt;
 				}, 0);&lt;br /&gt;
 			} else {&lt;br /&gt;
 				password.focus();&lt;br /&gt;
 			}&lt;br /&gt;
 		}, true);&lt;br /&gt;
 		var arOptions = new Array();&lt;br /&gt;
 		web mailselect a web mail ID from a listfor Yahoo!for (var i in gUserIDs) {&lt;br /&gt;
 			arOptions[i] = document.createElement(&amp;quot;option&amp;quot;);&lt;br /&gt;
 			arOptions[i].value = gUserIDs[i];&lt;br /&gt;
 			arOptions[i].text = gUserIDs[i];&lt;br /&gt;
 			elmSelect.appendChild(arOptions[i]);&lt;br /&gt;
 		}&lt;br /&gt;
 		arOptions[i] = document.createElement(&amp;quot;option&amp;quot;);&lt;br /&gt;
 		arOptions[i].text = &amp;quot;Other…&amp;quot;;&lt;br /&gt;
 		elmSelect.appendChild(arOptions[i]);&lt;br /&gt;
 		login.parentNode.replaceChild(elmSelect, login);&lt;br /&gt;
 		login = elmSelect;&lt;br /&gt;
 	}&lt;br /&gt;
 &lt;br /&gt;
 	if (document.forms.length) {&lt;br /&gt;
 		for (var k = 0; k &amp;lt; document.forms.length; k++) {&lt;br /&gt;
 			var elmForm = document.forms[k];&lt;br /&gt;
 			if (elmForm.action.indexOf('login.yahoo.com') != -1) {&lt;br /&gt;
 				elmForm.addEventListener('submit', function(e) {&lt;br /&gt;
 				e.stopPropagation();&lt;br /&gt;
 				e.preventDefault();&lt;br /&gt;
 				}, true);&lt;br /&gt;
 				login = elmForm.elements.namedItem('login');&lt;br /&gt;
 				password = elmForm.elements.namedItem('passwd');&lt;br /&gt;
 				break;&lt;br /&gt;
 			}&lt;br /&gt;
 		}&lt;br /&gt;
 	}&lt;br /&gt;
 &lt;br /&gt;
 	if (!login) { return; }&lt;br /&gt;
 	if (location.href.indexOf(&amp;quot;web mail&amp;lt;nowiki&amp;gt;mail.yahoo.com&amp;quot;) != -1) {&lt;br /&gt;
		location.href = &amp;quot;http://login.yahoo.com/config/login?.done=&amp;quot; +&lt;br /&gt;
			&amp;quot;http%3a%2f%2fmail%2eyahoo%2ecom&amp;quot;;&lt;br /&gt;
	} else {&lt;br /&gt;
		buildLoginThing();&lt;br /&gt;
		setTimeout(function() { password.focus(); }, 100);&lt;br /&gt;
	}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Running the Hack ===&lt;br /&gt;
&lt;br /&gt;
After editing this script to include your Yahoo! IDs, install it (Tools → Install This User Script) and go to ''http://mail.yahoo.com'' . (Log out if you're already logged in, and then go back to the login page.) In the login form, instead of the normal username text box, you'll see a drop-down menu, as shown in [[Greasemonkey Hacks/Web Mail#greasemonkeyhks-CHP-7-FIG-9|Figure 7-9]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;greasemonkeyhks-CHP-7-FIG-9&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 7-9. Drop-down menu of Yahoo! IDs'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Greasemonkey Hacks_I_7_tt284.png|Drop-down menu of Yahoo! IDs]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Select a Yahoo! ID from the list, and the script will automatically set focus to the password field. Or, you can also select Other…to replace the drop-down menu with the regular input box and type your username manually. (This option is useful if you let other people use your computer and they want to check their Yahoo! mail, too.)&lt;br /&gt;
&lt;br /&gt;
=== Hacking the Hack ===&lt;br /&gt;
&lt;br /&gt;
Do you use Google instead of Yahoo!? With some simple modifications, we can do the same thing on the Google login form.&lt;br /&gt;
&lt;br /&gt;
Edit the following user script to include your Google IDs, and then save it as ''google-select.user.js'':&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
	// ==UserScript==&lt;br /&gt;
	// @name		 Google User Persitance Thing&lt;br /&gt;
	// @namespace    http://www.rhyley.org/gm/&lt;br /&gt;
	// @description  Add a drop-down box with your Google IDs&lt;br /&gt;
	// @include      http*://*.google.tld/*&lt;br /&gt;
	// ==/UserScript==&lt;br /&gt;
&lt;br /&gt;
	// based on code by Jason Rhyley&lt;br /&gt;
	// and included here with his gracious permission&lt;br /&gt;
	// ** Replace this array with your Yahoo IDs **&lt;br /&gt;
	var gUserIDs = new Array(&amp;quot;Put&amp;quot;,&amp;quot;Your&amp;quot;,&amp;quot;User&amp;quot;,&amp;quot;ID&amp;quot;,&amp;quot;Here&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	var login = null;&lt;br /&gt;
	var password = null;&lt;br /&gt;
&lt;br /&gt;
	function buildLoginThing() {&lt;br /&gt;
		if (gUserIDs[0] == 'Put') {&lt;br /&gt;
			alert('You must configure the script &amp;lt;/nowiki&amp;gt;web mailselect a web mail ID from a listfor Googlebefore it will \n' +&lt;br /&gt;
 				  'work propery. Go to &amp;quot;Manage User Scripts&amp;quot; and\n' +&lt;br /&gt;
 				  'click the &amp;quot;Edit&amp;quot; button to configure the script.');&lt;br /&gt;
 			return;&lt;br /&gt;
 		}&lt;br /&gt;
 &lt;br /&gt;
 		var Gmailselect a web mail ID from a listelmSelect = document.createElement(&amp;quot;select&amp;quot;);&lt;br /&gt;
 		elmSelect.name = &amp;quot;web mailEmail&amp;quot;;&lt;br /&gt;
 		elmSelect.style.width = &amp;quot;10em&amp;quot;;&lt;br /&gt;
 		elmSelect.addEventListener('change', function() {&lt;br /&gt;
 			if (this.selectedIndex == this.options.length-1) {&lt;br /&gt;
 			window.setTimeout(function() {&lt;br /&gt;
 				var elmNew = document.createElement(&amp;quot;input&amp;quot;);&lt;br /&gt;
 				elmNew.type = &amp;quot;text&amp;quot;;&lt;br /&gt;
 				elmNew.name = &amp;quot;Email&amp;quot;;&lt;br /&gt;
 				elmNew.style.width = &amp;quot;10em&amp;quot;;&lt;br /&gt;
 				login.parentNode.replaceChild(elmNew, login);&lt;br /&gt;
 				login = elmNew;&lt;br /&gt;
 				login.focus();&lt;br /&gt;
 			}, 0);&lt;br /&gt;
 		}&lt;br /&gt;
 		else {&lt;br /&gt;
 			password.focus();&lt;br /&gt;
 		}&lt;br /&gt;
 	}, true);&lt;br /&gt;
 &lt;br /&gt;
 	var arOptions = new Array();&lt;br /&gt;
 	var i;&lt;br /&gt;
 	for (i in gUserIDs) {&lt;br /&gt;
 		 arOptions[i] = document.createElement(&amp;quot;option&amp;quot;);&lt;br /&gt;
 		 arOptions[i].setAttribute(&amp;quot;value&amp;quot;,gUserIDs[i]);&lt;br /&gt;
 		 arOptions[i].text = gUserIDs[i];&lt;br /&gt;
 		 elmSelect.appendChild(arOptions[i]);&lt;br /&gt;
 	}&lt;br /&gt;
 	arOptions[i] = document.createElement(&amp;quot;option&amp;quot;);&lt;br /&gt;
 	arOptions[i].text = &amp;quot;Other…&amp;quot;;&lt;br /&gt;
 	elmSelect.appendChild(arOptions[i]);&lt;br /&gt;
 	login.parentNode.replaceChild(elmSelect, login);&lt;br /&gt;
 	login = elmSelect;&lt;br /&gt;
  }&lt;br /&gt;
 &lt;br /&gt;
  if (document.forms.length) {&lt;br /&gt;
 	 for (var k = 0; k &amp;lt; document.forms.length; k++) {&lt;br /&gt;
 		  var elmForm = document.forms[k];&lt;br /&gt;
 		  if (elmForm.action.indexOf('ServiceLogin') != -1) {&lt;br /&gt;
 				login = web mailselect a web mail ID from a listfor GoogleelmForm.elements.namedItem('Email');&lt;br /&gt;
 				password = elmForm.elements.namedItem('Passwd');&lt;br /&gt;
 				break;&lt;br /&gt;
 		  }&lt;br /&gt;
 	  }&lt;br /&gt;
  }&lt;br /&gt;
 	&lt;br /&gt;
  if (!login) { return; }&lt;br /&gt;
  password.style.width = &amp;quot;10em&amp;quot;;&lt;br /&gt;
  buildLoginThing();&lt;br /&gt;
  setTimeout(function() { password.focus(); }, 100);&lt;br /&gt;
&lt;br /&gt;
Now, go to ''http://mail.google.com''. (Again, log out if you're already logged in, and then go back to ''http://mail.google.com'' to see the login form.) In place of the usual username box, you'll see a drop-down menu of your Google IDs, as shown in [[Greasemonkey Hacks/Web Mail#greasemonkeyhks-CHP-7-FIG-10|Figure 7-10]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;greasemonkeyhks-CHP-7-FIG-10&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 7-10. Drop-down menu of Google IDs'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Greasemonkey Hacks_I_7_tt286.png|Drop-down menu of Google IDs]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As with the Yahoo! script, you can select a Google ID from the menu, or select Other…to type your Google ID manually.&lt;br /&gt;
&lt;br /&gt;
== Add Saved Searches to Gmail ==&lt;br /&gt;
&lt;br /&gt;
'''Keep often-used searches at your fingertips'''.&lt;br /&gt;
&lt;br /&gt;
Gmail is Google's web mail application. In addition to the large amount of space that it provides, the main thing that sets it apart from the competition is the fact that its user interface is search-driven. It is therefore unfortunate that you must retype searches that you perform frequently. Many client-side email applications, such as Mozilla's Thunderbird, Gnome's Evolution, and Apple's Mail allow saved searches (also known as ''persistent searches'' or ''smart folders''). This hack adds a similar feature to Gmail.&lt;br /&gt;
&lt;br /&gt;
=== The Code ===&lt;br /&gt;
&lt;br /&gt;
This user script runs on the Gmail domain only. Initialization is rather complex, since the hack must create its own Gmail sidebar module. To make it easier to match the appearance of our sidebar with the rest of the Gmail interface, we use the CSS rules array to create a consistent set of CSS &amp;lt;tt&amp;gt;rules&amp;lt;/tt&amp;gt; that we can reference later.&lt;br /&gt;
&lt;br /&gt;
Each saved search is represented by a &amp;lt;tt&amp;gt;PersistentSearch&amp;lt;/tt&amp;gt; object. Since searches must be saved across sessions, each object can be serialized to and deserialized from a string that we can then use with &amp;lt;tt&amp;gt;GM_getValue&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;GM_setValue&amp;lt;/tt&amp;gt;. Additionally, each search can display how many results match it. To accomplish this, we use an &amp;lt;tt&amp;gt;XMLHttpRequest&amp;lt;/tt&amp;gt; object to actually invoke the search URL, and then we parse the number of results from the response text. We cache the number of results to minimize hits on the Gmail server. Finally, we execute the search by calling Gmail's own &amp;lt;tt&amp;gt;_MH_OnSearch&amp;lt;/tt&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
To support editing of saved searches, we must override the main Gmail display and show our own interface instead. We must also do our own event handling, to deal with clicks on form buttons and other events. To make saved searches even more useful, we add some additional search operators that the user can enter, such as &amp;lt;tt&amp;gt;after:oneweekago&amp;lt;/tt&amp;gt;. These are dynamically converted to absolute dates when the search is executed.&lt;br /&gt;
&lt;br /&gt;
Save the following user script as ''gmailsavedsearches.user.js'':&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
	// ==UserScript==&lt;br /&gt;
	// @name         Gmail Saved Searches&lt;br /&gt;
	// @namespace    http://persistent.info/greasemonkey&lt;br /&gt;
	// @description  Adds persistent seaches to Gmail&lt;br /&gt;
	// @include      http*://mail.google.com/*&lt;br /&gt;
	// ==/UserScript==&lt;br /&gt;
&lt;br /&gt;
	// based on code by Mihai Parparita&lt;br /&gt;
	// and included here with his gracious permission&lt;br /&gt;
	&lt;br /&gt;
	// Utility functions&lt;br /&gt;
	function getObjectMethodClosure(object, method) {&lt;br /&gt;
		return function() {&lt;br /&gt;
			return object[method].apply(object, arguments);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function getDateString(date) {&lt;br /&gt;
		return date.getFullYear() + &amp;quot;/&amp;quot; +&lt;br /&gt;
			   (date.getMonth() + 1) + &amp;quot;/&amp;quot; +&lt;br /&gt;
			   date.getDate();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	// Shorthand&lt;br /&gt;
	var newNode = getObjectMethodClosure(document, &amp;quot;createElement&amp;quot;);&lt;br /&gt;
	var newText = getObjectMethodClosure(document, &amp;quot;createTextNode&amp;quot;);&lt;br /&gt;
	var getNode = getObjectMethodClosure(document, &amp;quot;getElementById&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	// Contants&lt;br /&gt;
	const RULES = new Array(&lt;br /&gt;
		// Block in sidebar&lt;br /&gt;
		&amp;quot;.&amp;lt;/nowiki&amp;gt;Gmailsaved searchessearchesBlock {-moz-border-radius: 5px; background: #fad163; margin:&lt;br /&gt;
 	20px 7px 0 0; padding: 3px;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.refreshButton {display: block; cursor: pointer; float: right; margin-&lt;br /&gt;
 	top:-2px;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.searchesBlockList {background: white;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.listItem {color: #ca9c22;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.editLink {text-align: right; color: #ca9c22; padding: 2px 5px 5px 0;}&amp;quot;,&lt;br /&gt;
 &lt;br /&gt;
 		// Edit page&lt;br /&gt;
 		&amp;quot;.searchesContainer {-moz-border-radius: 10px; background: #fad163;&lt;br /&gt;
 	padding: 10px;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.innerContainer {background: #fff7d7; text-align: center; padding:&lt;br /&gt;
 	10px;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.searchesList {width: 100%;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.searchesList th {text-align: left; font-size: 90%;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.searchesList td {padding: 10px 0 10px 0; vertical-align: bottom;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.searchesList td.divider {background: #fad163; height: 3px; padding:&lt;br /&gt;
 	0;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.editItem {font-size: 80%;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.labelCell {width: 210px;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.labelCell input {width: 200px;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.cancelButton {margin-right: 5px;}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.editCell {}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.editCell input {width: 100%}&amp;quot;,&lt;br /&gt;
 		&amp;quot;.saveButton {margin-left: 5px; font-weight: bold;}&amp;quot;&lt;br /&gt;
 	);&lt;br /&gt;
 &lt;br /&gt;
 	const REFRESH_IMAGE = &amp;quot;data:image/&lt;br /&gt;
 	gif;base64,R0lGODlhDQAPANU5AM%2BtUs6sUunDX&amp;quot; +&lt;br /&gt;
 		&amp;quot;PfPYt65WK%2BTRaiMQvXNYfDJX9m1VtSxVIBrM7GURsKiTZqBPeS%2FWo94OZmAPebBW6WK&amp;quot;&lt;br /&gt;
 	+&lt;br /&gt;
 		&amp;quot;QbiaSdOwU35qMpV9O4t0N4NuNI12OIFsM9u3V7mbSaaLQtazVcyqUZ6EP%2BC7WX1oMbudS&amp;quot;&lt;br /&gt;
 	+&lt;br /&gt;
 		&amp;quot;semT62QRPjPYuvFXXtmMbSXR%2BK9WohyNvLKYOfBXPPLYJB4Ob6fS5R8O%2B3GXqGGQK%2&amp;quot;&lt;br /&gt;
 	+&lt;br /&gt;
 		&amp;quot;BSRauPROG9WW5cK%2F%2F%2F%2FwAAAAAAAAAAAAAAAAAAAAAAACH5BAEAADkALAAAAAANA&amp;quot;&lt;br /&gt;
 	+&lt;br /&gt;
 		&amp;quot;A8AAAZvwJxwSMzdiKcAg8YIDEyG4QPjABAUhgUuInQtAsQaDqcRwj7EUmY8yiUuReJtQInF&amp;quot;&lt;br /&gt;
 	+&lt;br /&gt;
 		&amp;quot;h5JEAXQX3mwzD305FSRGBAN3Eys5HWM4LAdDIiFCCmMbAkMcMghCBDgpEAUNKg4eL0MoFgI&amp;quot;&lt;br /&gt;
 	+&lt;br /&gt;
 		&amp;quot;tAA0AnkQHmoNBADs%3D&amp;quot;;&lt;br /&gt;
 		const RESULT_SIZE_RE = /D\(\[&amp;quot;ts&amp;quot;,(\d+),(\d+),(\d+),/;&lt;br /&gt;
 &lt;br /&gt;
 		const DEFAULT_Gmailsaved searchesSEARCHES = {&lt;br /&gt;
 			&amp;quot;has:attachment&amp;quot;: &amp;quot;Attachments&amp;quot;,&lt;br /&gt;
 			&amp;quot;after:today&amp;quot;: &amp;quot;Today&amp;quot;,&lt;br /&gt;
 			&amp;quot;after:oneweekago&amp;quot;: &amp;quot;Last Week&amp;quot;&lt;br /&gt;
 		};&lt;br /&gt;
 &lt;br /&gt;
 		const SEARCHES_PREF = &amp;quot;PersistentSearches&amp;quot;;&lt;br /&gt;
 		const SEARCHES_COLLAPSED_PREF = &amp;quot;PersistentSearchesCollapsedCookie&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 		const ONE_DAY = 24 * 60 * 60 * 1000;&lt;br /&gt;
 		&lt;br /&gt;
 		// Globals&lt;br /&gt;
 		var searches = new Array();&lt;br /&gt;
 		var searchesBlock = null;&lt;br /&gt;
 		var searchesBlockHeader = null;&lt;br /&gt;
 		var triangleImage = null;&lt;br /&gt;
 		var searchesBlockList = null;&lt;br /&gt;
 		var editLink = null;&lt;br /&gt;
 		&lt;br /&gt;
 		var hiddenNodes = null;&lt;br /&gt;
 		var searchesContainer = null;&lt;br /&gt;
 		var searchesList = null;&lt;br /&gt;
 		&lt;br /&gt;
 		function initializePersistentSearches() {&lt;br /&gt;
 			var labelsBlock = getNode(&amp;quot;nb_0&amp;quot;);&lt;br /&gt;
 			&lt;br /&gt;
 			if (!labelsBlock) {&lt;br /&gt;
 				return;&lt;br /&gt;
 			}&lt;br /&gt;
 &lt;br /&gt;
 			searchesBlock = newNode(&amp;quot;div&amp;quot;);&lt;br /&gt;
 			searchesBlock.id = &amp;quot;nb_9&amp;quot;;&lt;br /&gt;
 			searchesBlock.className = &amp;quot;searchesBlock&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 			// header&lt;br /&gt;
 			searchesBlockHeader = newNode(&amp;quot;div&amp;quot;);&lt;br /&gt;
 			searchesBlockHeader.className = &amp;quot;s h&amp;quot;;&lt;br /&gt;
 			searchesBlock.appendChild(searchesBlockHeader);&lt;br /&gt;
 &lt;br /&gt;
 			var refreshButton = newNode(&amp;quot;img&amp;quot;);&lt;br /&gt;
 			refreshButton.src = REFRESH_IMAGE;&lt;br /&gt;
 			refreshButton.className = &amp;quot;refreshButton&amp;quot;;&lt;br /&gt;
 			refreshButton.width = 13;&lt;br /&gt;
 			refreshButton.height = 15;&lt;br /&gt;
 			refreshButton.addEventListener('click', refreshPersistentSearches, true);&lt;br /&gt;
 			searchesBlockHeader.appendChild(refreshButton);&lt;br /&gt;
 			&lt;br /&gt;
 			triangleImage = newNode(&amp;quot;img&amp;quot;);&lt;br /&gt;
 			triangleImage.src = &amp;quot;/web mailmail/images/opentriangle.gif&amp;quot;;&lt;br /&gt;
 			triangleImage.width = 11;&lt;br /&gt;
 			triangleImage.height = 11;&lt;br /&gt;
 			triangleImage.addEventListener('click', Gmailsaved searchestogglePersistentSearches, true);&lt;br /&gt;
 			searchesBlockHeader.appendChild(triangleImage);&lt;br /&gt;
 &lt;br /&gt;
 			var searchesText = newNode(&amp;quot;span&amp;quot;);&lt;br /&gt;
 			searchesText.appendChild(newText(&amp;quot; Searches&amp;quot;));&lt;br /&gt;
 			searchesText.addEventListener('click', togglePersistentSearches, true);&lt;br /&gt;
 			searchesBlockHeader.appendChild(searchesText);&lt;br /&gt;
 &lt;br /&gt;
 			// searches list&lt;br /&gt;
 			searchesBlockList = newNode(&amp;quot;div&amp;quot;);&lt;br /&gt;
 			searchesBlockList.className = &amp;quot;searchesBlockList&amp;quot;;&lt;br /&gt;
 			searchesBlock.appendChild(searchesBlockList);&lt;br /&gt;
 			&lt;br /&gt;
 			editLink = newNode(&amp;quot;div&amp;quot;);&lt;br /&gt;
 			editLink.appendChild(newText(&amp;quot;Edit searches&amp;quot;));&lt;br /&gt;
 			editLink.className = &amp;quot;lk cs editLink&amp;quot;;&lt;br /&gt;
 			editLink.addEventListener('click', editPersistentSearches, true);&lt;br /&gt;
 			searchesBlockList.appendChild(editLink);&lt;br /&gt;
 &lt;br /&gt;
 			if (GM_getValue(SEARCHES_PREF)) {&lt;br /&gt;
 				restorePersistentSearches();&lt;br /&gt;
 			} else {&lt;br /&gt;
 				for (var query in DEFAULT_SEARCHES) {&lt;br /&gt;
 				  addPersistentSearch(new PersistentSearch(query, DEFAULT_&lt;br /&gt;
 		SEARCHES[query]));&lt;br /&gt;
 				}&lt;br /&gt;
 			}&lt;br /&gt;
 &lt;br /&gt;
 			insertSearchesBlock();&lt;br /&gt;
 &lt;br /&gt;
 			if (GM_getValue(SEARCHES_COLLAPSED_PREF) == &amp;quot;1&amp;quot;) {&lt;br /&gt;
 				togglePersistentSearches();&lt;br /&gt;
 			}&lt;br /&gt;
 &lt;br /&gt;
 			checkSearchesBlockParent();&lt;br /&gt;
 		 }&lt;br /&gt;
 &lt;br /&gt;
 		 function refreshPersistentSearches() {&lt;br /&gt;
 			for (var i=0; i &amp;lt; searches.length; i++) {&lt;br /&gt;
 				searches[i].getResultSize(true);&lt;br /&gt;
 			}&lt;br /&gt;
 &lt;br /&gt;
 			return false;&lt;br /&gt;
 		 }&lt;br /&gt;
 &lt;br /&gt;
 		 function insertSearchesBlock() {&lt;br /&gt;
 			var labelsBlock = getNode(&amp;quot;nb_0&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
 			if (!labelsBlock) {&lt;br /&gt;
 				return;&lt;br /&gt;
 			}			&lt;br /&gt;
 				getNode(&amp;quot;nav&amp;quot;).insertBefore(Gmailsaved searchessearchesBlock, labelsBlock.nextSibling);&lt;br /&gt;
 			}&lt;br /&gt;
 			&lt;br /&gt;
 			// For some reason, when moving back to the Inbox after viewing a message,&lt;br /&gt;
 			// we seem to get removed from the nav section, so we have to add ourselves&lt;br /&gt;
 			// back. This only happens if we're a child of the &amp;quot;nav&amp;quot; div, and nowhere&lt;br /&gt;
 			// else (but that's the place where we're supposed to go, so we have no&lt;br /&gt;
 			// choice)&lt;br /&gt;
 			function checkSearchesBlockParent() {&lt;br /&gt;
 				if (searchesBlock.parentNode != getNode(&amp;quot;nav&amp;quot;)) {&lt;br /&gt;
 				  insertSearchesBlock();&lt;br /&gt;
 				}&lt;br /&gt;
 				&lt;br /&gt;
 				window.setTimeout(checkSearchesBlockParent, 200);&lt;br /&gt;
 			 }&lt;br /&gt;
 &lt;br /&gt;
 			 function restorePersistentSearches() {&lt;br /&gt;
 				var serializedSearches = GM_getValue(SEARCHES_PREF).split(&amp;quot;|&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
 				for (var i=0; i &amp;lt; serializedSearches.length; i++) {&lt;br /&gt;
 				var search = PersistentSearch.prototype.&lt;br /&gt;
 			 fromString(serializedSearches[i]);&lt;br /&gt;
 &lt;br /&gt;
 				addPersistentSearch(search);&lt;br /&gt;
 			   }&lt;br /&gt;
 		     }&lt;br /&gt;
 &lt;br /&gt;
 			 function savePersistentSearches() {&lt;br /&gt;
 				var serializedSearches = new Array();&lt;br /&gt;
 &lt;br /&gt;
 				for (var i=0; i &amp;lt; searches.length; i++) {&lt;br /&gt;
 				serializedSearches.push(searches[i].toString());&lt;br /&gt;
 				}&lt;br /&gt;
 &lt;br /&gt;
 				GM_setValue(SEARCHES_PREF, serializedSearches.join(&amp;quot;|&amp;quot;));&lt;br /&gt;
 			 }&lt;br /&gt;
 &lt;br /&gt;
 			 function clearPersistentSearches() {&lt;br /&gt;
 				for (var i=0; i &amp;lt; searches.length; i++) {&lt;br /&gt;
 				var item = searches[i].getListItem();&lt;br /&gt;
 				if (item.parentNode) {&lt;br /&gt;
 				item.parentNode.removeChild(item);&lt;br /&gt;
 				}&lt;br /&gt;
 				}&lt;br /&gt;
 				searches = new Array();&lt;br /&gt;
 			}&lt;br /&gt;
 &lt;br /&gt;
 			function addPersistentSearch(search) {&lt;br /&gt;
 				searches.push(search);&lt;br /&gt;
 				searchesBlockList.insertBefore(search.getListItem(), editLink);&lt;br /&gt;
 &lt;br /&gt;
 				savePersistentSearches();&lt;br /&gt;
 			}&lt;br /&gt;
 			function Gmailsaved searches&amp;lt;nowiki&amp;gt;editPersistentSearches(event) {&lt;br /&gt;
				var container = getNode(&amp;quot;co&amp;quot;);&lt;br /&gt;
			&lt;br /&gt;
				hiddenNodes = new Array();&lt;br /&gt;
&lt;br /&gt;
				for (var i = container.firstChild; i; i = i.nextSibling) {&lt;br /&gt;
				hiddenNodes.push(i);&lt;br /&gt;
				i.style.display = &amp;quot;none&amp;quot;;&lt;br /&gt;
				}&lt;br /&gt;
&lt;br /&gt;
				searchesContainer = newNode(&amp;quot;div&amp;quot;);&lt;br /&gt;
				searchesContainer.className = &amp;quot;searchesContainer&amp;quot;;&lt;br /&gt;
				searchesContainer.innerHTML += &amp;quot;&amp;lt;b&amp;gt;Persistent Searches&amp;lt;/b&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
				container.appendChild(searchesContainer);&lt;br /&gt;
			&lt;br /&gt;
				var innerContainer = newNode(&amp;quot;div&amp;quot;);&lt;br /&gt;
				innerContainer.className = &amp;quot;innerContainer&amp;quot;;&lt;br /&gt;
				innerContainer.innerHTML +=&lt;br /&gt;
				'&amp;lt;p&amp;gt;Use &amp;lt;a href=&amp;quot;http://mail.google.com/support/bin/answer.&lt;br /&gt;
			&amp;lt;/nowiki&amp;gt;web mailpy?answer=7190&amp;quot; target=&amp;quot;_blank&amp;quot;&amp;gt;operators&amp;lt;/a&amp;gt; ' +&lt;br /&gt;
 				'to specify queries. &amp;amp;lt;code&amp;gt;today&amp;amp;lt;/code&amp;gt;, &amp;amp;lt;code&amp;gt;yesterday&amp;amp;lt;/code&amp;gt; and&lt;br /&gt;
 			 &amp;amp;lt;code&amp;gt;oneweekago&amp;amp;lt;/code&amp;gt; ' +&lt;br /&gt;
 				'are also supported as values for the &amp;amp;lt;code&amp;gt;before:&amp;amp;lt;/code&amp;gt; and &amp;amp;lt;code&amp;gt;&lt;br /&gt;
 			 after:&amp;amp;lt;/code&amp;gt; ' +&lt;br /&gt;
 				'operators. Delete an item\'s query to remove it.&amp;amp;lt;/p&amp;gt;';&lt;br /&gt;
 			 searchesContainer.appendChild(innerContainer);&lt;br /&gt;
 &lt;br /&gt;
 			 searchesList = newNode(&amp;quot;table&amp;quot;);&lt;br /&gt;
 			 searchesList.className = &amp;quot;searchesList&amp;quot;;&lt;br /&gt;
 			 innerContainer.appendChild(searchesList);&lt;br /&gt;
 &lt;br /&gt;
 			 var headerRow = newNode(&amp;quot;tr&amp;quot;);&lt;br /&gt;
 			 searchesList.appendChild(headerRow);&lt;br /&gt;
 			 headerRow.appendChild(newNode(&amp;quot;th&amp;quot;)).appendChild(newText(&amp;quot;Label&amp;quot;));&lt;br /&gt;
 			 headerRow.appendChild(newNode(&amp;quot;th&amp;quot;)).appendChild(newText(&amp;quot;Query&amp;quot;));&lt;br /&gt;
 &lt;br /&gt;
 			 for (var i=0; i &amp;lt; searches.length; i++) {&lt;br /&gt;
 				searchesList.appendChild(searches[i].getEditItem());&lt;br /&gt;
 				&lt;br /&gt;
 				var dividerRow = newNode(&amp;quot;tr&amp;quot;);&lt;br /&gt;
 				var dividerCell = dividerRow.appendChild(newNode(&amp;quot;td&amp;quot;));&lt;br /&gt;
 				dividerCell.className = &amp;quot;divider&amp;quot;;&lt;br /&gt;
 				dividerCell.colSpan = 3;&lt;br /&gt;
 &lt;br /&gt;
 				searchesList.appendChild(dividerRow);&lt;br /&gt;
 			 }&lt;br /&gt;
 			&lt;br /&gt;
 			 var newSearch = new PersistentSearch(&amp;quot;&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
 			 var newItem = newSearch.getEditItem();&lt;br /&gt;
 			 newItem.firstChild.innerHTML =&lt;br /&gt;
 				&amp;quot;&amp;amp;lt;h4&amp;gt;Create a new persistent search:&amp;amp;lt;/h4&amp;gt;&amp;quot; +&lt;br /&gt;
 				newItem.firstChild.innerHTML;&lt;br /&gt;
 				Gmailsaved searchessearchesList.appendChild(newItem);&lt;br /&gt;
 &lt;br /&gt;
 				var cancelButton = newNode(&amp;quot;button&amp;quot;);&lt;br /&gt;
 				cancelButton.appendChild(newText(&amp;quot;Cancel&amp;quot;));&lt;br /&gt;
 				cancelButton.className = &amp;quot;cancelButton&amp;quot;;&lt;br /&gt;
 				cancelButton.addEventListener('click', cancelEditPersistentSearches,&lt;br /&gt;
 			true);&lt;br /&gt;
 				innerContainer.appendChild(cancelButton);&lt;br /&gt;
 &lt;br /&gt;
 				var saveButton = newNode(&amp;quot;button&amp;quot;);&lt;br /&gt;
 				saveButton.appendChild(newText(&amp;quot;Save Changes&amp;quot;));&lt;br /&gt;
 				saveButton.className = &amp;quot;saveButton&amp;quot;;&lt;br /&gt;
 				saveButton.addEventListener('click', saveEditPersistentSeaches, true);&lt;br /&gt;
 				innerContainer.appendChild(saveButton);&lt;br /&gt;
 				&lt;br /&gt;
 				// Make clicks outside the edit area hide it&lt;br /&gt;
 				getNode(&amp;quot;nav&amp;quot;).addEventListener('click', cancelEditPersistentSearches,&lt;br /&gt;
 			true);&lt;br /&gt;
 &lt;br /&gt;
 				// Since we're in a child of the &amp;quot;nav&amp;quot; element, the above handler will get&lt;br /&gt;
 				// triggered immediately unless we stop this event from propagating&lt;br /&gt;
 				event.stopPropagation();&lt;br /&gt;
 				&lt;br /&gt;
 				return false;&lt;br /&gt;
 			}&lt;br /&gt;
 &lt;br /&gt;
 			function cancelEditPersistentSearches() {&lt;br /&gt;
 				searchesContainer.parentNode.removeChild(searchesContainer);&lt;br /&gt;
 				searchesContainer = null;&lt;br /&gt;
 &lt;br /&gt;
 				for (var i=0; i &amp;lt; hiddenNodes.length; i++) {&lt;br /&gt;
 				hiddenNodes[i].style.display = &amp;quot;&amp;quot;;&lt;br /&gt;
 				}&lt;br /&gt;
 				getNode(&amp;quot;nav&amp;quot;).removeEventListener('click', cancelEditPersistentSearches,&lt;br /&gt;
 			true);&lt;br /&gt;
 		&lt;br /&gt;
 				return true;&lt;br /&gt;
 			}&lt;br /&gt;
 &lt;br /&gt;
 			function saveEditPersistentSeaches() {&lt;br /&gt;
 				clearPersistentSearches();&lt;br /&gt;
 				&lt;br /&gt;
 				for (var row = searchesList.firstChild; row; row = row.nextSibling) {&lt;br /&gt;
 				var cells = row.getElementsByTagName(&amp;quot;td&amp;quot;);&lt;br /&gt;
 				if (cells.length != 2) {&lt;br /&gt;
 				continue;&lt;br /&gt;
 				}&lt;br /&gt;
 				var label = cells[0].getElementsByTagName(&amp;quot;input&amp;quot;)[0].value;&lt;br /&gt;
 				var query = cells[1].getElementsByTagName(&amp;quot;input&amp;quot;)[0].value;&lt;br /&gt;
 &lt;br /&gt;
 				if (label &amp;amp;&amp;amp; query) {&lt;br /&gt;
 				var search = new PersistentSearch(query, label);&lt;br /&gt;
 				addPersistentSearch(search);&lt;br /&gt;
 				}	&lt;br /&gt;
 				}&lt;br /&gt;
 &lt;br /&gt;
 				// cancelling just hides everything, which is what we want to do&lt;br /&gt;
 				Gmailsaved searchescancelEditPersistentSearches();&lt;br /&gt;
 			}&lt;br /&gt;
 &lt;br /&gt;
 			function togglePersistentSearches() {&lt;br /&gt;
 				if (searchesBlockList.style.display == &amp;quot;none&amp;quot;) {&lt;br /&gt;
 				searchesBlockList.style.display = &amp;quot;&amp;quot;;&lt;br /&gt;
 				triangleImage.src = &amp;quot;/web mail&amp;lt;nowiki&amp;gt;mail/images/opentriangle.gif&amp;quot;;&lt;br /&gt;
				GM_setValue(SEARCHES_COLLAPSED_PREF, &amp;quot;0&amp;quot;);&lt;br /&gt;
				} else {&lt;br /&gt;
				searchesBlockList.style.display = &amp;quot;none&amp;quot;;&lt;br /&gt;
				triangleImage.src = &amp;quot;/mail/images/triangle.gif&amp;quot;;&lt;br /&gt;
				GM_setValue(SEARCHES_COLLAPSED_PREF, &amp;quot;1&amp;quot;);&lt;br /&gt;
				}&lt;br /&gt;
			&lt;br /&gt;
				return false;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			function PersistentSearch(query, label) {&lt;br /&gt;
				this.query = query;&lt;br /&gt;
				this.label = label;&lt;br /&gt;
&lt;br /&gt;
				this.totalResults = -1;&lt;br /&gt;
				this.unreadResults = -1;&lt;br /&gt;
				&lt;br /&gt;
				this.listItem = null;&lt;br /&gt;
				this.editItem = null;&lt;br /&gt;
				this.resultSizeItem = null;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			PersistentSearch.prototype.toString = function() {&lt;br /&gt;
				var serialized = new Array();&lt;br /&gt;
				&lt;br /&gt;
				for (var property in this) {&lt;br /&gt;
				if (typeof(this[property]) != &amp;quot;function&amp;quot; &amp;amp;&amp;amp;&lt;br /&gt;
				typeof(this[property]) != &amp;quot;object&amp;quot;) {&lt;br /&gt;
				  serialized.push(property + &amp;quot;=&amp;quot; + this[property]);&lt;br /&gt;
				}&lt;br /&gt;
				}&lt;br /&gt;
			&lt;br /&gt;
				return serialized.join(&amp;quot;&amp;amp;&amp;quot;);&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			PersistentSearch.prototype.fromString = function(serialized) {&lt;br /&gt;
				var properties = serialized.split(&amp;quot;&amp;amp;&amp;quot;);&lt;br /&gt;
				&lt;br /&gt;
				var search = new PersistentSearch(&amp;quot;&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
			for (var i=0; i &amp;lt; properties.length; i++) {&lt;br /&gt;
				var keyValue = properties[i].split(&amp;quot;=&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
				search[keyValue[0]] = keyValue[1];&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			return search;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		PersistentSearch.prototype.getListItem = function() {&lt;br /&gt;
			if (!this.listItem) {&lt;br /&gt;
				this.listItem = newNode(&amp;quot;div&amp;quot;);&lt;br /&gt;
				this.listItem.className = &amp;quot;lk cs listItem&amp;quot;;&lt;br /&gt;
				this.listItem.appendChild(newText(this.label));&lt;br /&gt;
				this.resultSizeItem = newNode(&amp;quot;span&amp;quot;);&lt;br /&gt;
				this.listItem.appendChild(this.resultSizeItem);&lt;br /&gt;
				this.getResultSize(false);&lt;br /&gt;
				var _this = this;&lt;br /&gt;
				this.listItem.addEventListener('click', function() {&lt;br /&gt;
			getObjectMethodClosure(_this, &amp;quot;execute&amp;quot;)(); }, true);&lt;br /&gt;
				}&lt;br /&gt;
&lt;br /&gt;
				return this.listItem;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			PersistentSearch.prototype.getEditItem = function() {&lt;br /&gt;
				if (!this.editItem) {&lt;br /&gt;
				this.editItem = newNode(&amp;quot;tr&amp;quot;);&lt;br /&gt;
				this.editItem.className = &amp;quot;editItem&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
				var labelCell = newNode(&amp;quot;td&amp;quot;);&lt;br /&gt;
				labelCell.className = &amp;quot;labelCell&amp;quot;;&lt;br /&gt;
				var labelInput = newNode(&amp;quot;input&amp;quot;);&lt;br /&gt;
				labelInput.value = this.label;&lt;br /&gt;
				labelCell.appendChild(labelInput);&lt;br /&gt;
				this.editItem.appendChild(labelCell);&lt;br /&gt;
				&lt;br /&gt;
				var editCell = newNode(&amp;quot;td&amp;quot;);&lt;br /&gt;
				editCell.className = &amp;quot;editCell&amp;quot;;&lt;br /&gt;
				var queryInput = newNode(&amp;quot;input&amp;quot;);&lt;br /&gt;
				queryInput.value = this.getEditableQuery();&lt;br /&gt;
				editCell.appendChild(queryInput);&lt;br /&gt;
				this.editItem.appendChild(editCell);&lt;br /&gt;
				}&lt;br /&gt;
		&lt;br /&gt;
				return this.editItem;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			PersistentSearch.prototype.execute = function() {&lt;br /&gt;
				var searchForm = getNode(&amp;quot;s&amp;quot;);&lt;br /&gt;
				searchForm.elements.namedItem('q').value = this.getRunnableQuery();&lt;br /&gt;
				top.js._MH_OnSearch(unsafeWindow, 0);&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			PersistentSearch.prototype.getRunnableQuery = function() {&lt;br /&gt;
				var query = this.query;&lt;br /&gt;
&lt;br /&gt;
				var today = new Date();&lt;br /&gt;
				var yesterday = new Date(today.getTime() - ONE_DAY);&lt;br /&gt;
				var oneWeekAgo = new Date(today.getTime() - 7 * ONE_DAY);&lt;br /&gt;
&lt;br /&gt;
				query = query.replace(/:today/g, &amp;quot;:&amp;quot; + getDateString(today));&lt;br /&gt;
				query = query.replace(/:yesterday/g, &amp;quot;:&amp;quot; + getDateString(yesterday));&lt;br /&gt;
				query = query.replace(/:oneweekago/g, &amp;quot;:&amp;quot; + getDateString(oneWeekAgo));&lt;br /&gt;
&lt;br /&gt;
				return query;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			PersistentSearch.prototype.getEditableQuery = function() {&lt;br /&gt;
				return this.query;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			PersistentSearch.prototype.getResultSize = function(needsRefresh) {&lt;br /&gt;
				if (this.totalResults == -1 || this.unreadResults == -1) {&lt;br /&gt;
				needsRefresh = true;&lt;br /&gt;
				} else {&lt;br /&gt;
				this.updateResultSizeItem();&lt;br /&gt;
				}&lt;br /&gt;
&lt;br /&gt;
				if (needsRefresh) {&lt;br /&gt;
				this.resultSizeItem.style.display = &amp;quot;none&amp;quot;;&lt;br /&gt;
				this.runQuery(this.getRunnableQuery(),&lt;br /&gt;
					getObjectMethodClosure(this, &amp;quot;getUnreadResultSize&amp;quot;));&lt;br /&gt;
				   }&lt;br /&gt;
				}&lt;br /&gt;
&lt;br /&gt;
				PersistentSearch.prototype.runQuery = function(query, continuationFunction)&lt;br /&gt;
				{&lt;br /&gt;
				var queryUrl = &amp;quot;http://&amp;lt;/nowiki&amp;gt;web mail&amp;lt;nowiki&amp;gt;mail.google.com/mail?search=query&amp;amp;q=&amp;quot; +&lt;br /&gt;
				escape(query) + &amp;quot;&amp;amp;view=tl&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
				GM_xmlhttpRequest({method: 'GET', url: queryUrl,&lt;br /&gt;
				onload: function(oResponseDetails) {&lt;br /&gt;
				var match = RESULT_SIZE_RE.exec(oResponseDetails.responseText);&lt;br /&gt;
				if (match) {&lt;br /&gt;
				var resultSize = match[3];&lt;br /&gt;
				continuationFunction(resultSize);&lt;br /&gt;
				} else {&lt;br /&gt;
				alert(&amp;quot;Couldn't find result size in search query.&amp;quot;);&lt;br /&gt;
				}}});&lt;br /&gt;
				}&lt;br /&gt;
&lt;br /&gt;
				PersistentSearch.prototype.getUnreadResultSize = function(totalResults) {&lt;br /&gt;
				this.totalResults = totalResults;&lt;br /&gt;
				&lt;br /&gt;
				this.runQuery(this.getRunnableQuery() + &amp;quot; is:unread&amp;quot;,&lt;br /&gt;
						getObjectMethodClosure(this, &amp;quot;updateResultSize&amp;quot;));&lt;br /&gt;
				}&lt;br /&gt;
&lt;br /&gt;
				PersistentSearch.prototype.updateResultSize = function(unreadResults) {&lt;br /&gt;
				this.unreadResults = unreadResults;&lt;br /&gt;
				&lt;br /&gt;
				&amp;lt;/nowiki&amp;gt;Gmailsaved searchessavePersistentSearches();&lt;br /&gt;
 				&lt;br /&gt;
 				this.updateResultSizeItem();&lt;br /&gt;
 				}&lt;br /&gt;
 &lt;br /&gt;
 				PersistentSearch.prototype.updateResultSizeItem = function() {&lt;br /&gt;
 				if (this.resultSizeItem) {&lt;br /&gt;
 				// Clear existing contents&lt;br /&gt;
 				var child;&lt;br /&gt;
 				&lt;br /&gt;
 				this.resultSizeItem.style.display = &amp;quot;&amp;quot;;&lt;br /&gt;
 				&lt;br /&gt;
 				while (child = this.resultSizeItem.firstChild) {&lt;br /&gt;
 				this.resultSizeItem.removeChild(child);&lt;br /&gt;
 				}&lt;br /&gt;
 &lt;br /&gt;
 				// Update with new values&lt;br /&gt;
 				this.resultSizeItem.appendChild(newText(&amp;quot; (&amp;quot;));&lt;br /&gt;
 				var unread = newNode(this.unreadResults &amp;gt; 0 ? &amp;quot;b&amp;quot; : &amp;quot;span&amp;quot;);&lt;br /&gt;
 				unread.appendChild(newText(this.unreadResults));&lt;br /&gt;
 				this.resultSizeItem.appendChild(unread);	&lt;br /&gt;
 				this.resultSizeItem.appendChild(newText(&amp;quot;/&amp;quot; + this.totalResults + &amp;quot;)&amp;quot;));&lt;br /&gt;
 				}&lt;br /&gt;
 				}&lt;br /&gt;
 &lt;br /&gt;
 				function initializeStyles() {&lt;br /&gt;
 				var styleNode = newNode(&amp;quot;style&amp;quot;);&lt;br /&gt;
 				&lt;br /&gt;
 				document.body.appendChild(styleNode);&lt;br /&gt;
 			&lt;br /&gt;
 				var styleSheet = document.styleSheets[document.styleSheets.length - 1];&lt;br /&gt;
 &lt;br /&gt;
 				for (var i=0; i &amp;lt; RULES.length; i++) {&lt;br /&gt;
 				styleSheet.insertRule(RULES[i], 0);&lt;br /&gt;
 				}&lt;br /&gt;
 				}&lt;br /&gt;
 &lt;br /&gt;
 				initializeStyles();&lt;br /&gt;
 				initializePersistentSearches();&lt;br /&gt;
&lt;br /&gt;
=== Running the Hack ===&lt;br /&gt;
&lt;br /&gt;
After installing the user script (Tools → Install This User Script), log into your Gmail account at ''http://mail.google.com''. You will see a yellow box in the sidebar, between Labels and Invites.&lt;br /&gt;
&lt;br /&gt;
By default, the new box displays three searches. Click a search to execute it, and the results will display in the standard messages pane, as shown in [[Greasemonkey Hacks/Web Mail#greasemonkeyhks-CHP-7-FIG-11|Figure 7-11]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;greasemonkeyhks-CHP-7-FIG-11&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 7-11. Results of saved search'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Greasemonkey Hacks_I_7_tt288.png|Results of saved search]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Each saved search shows the number of unread and total messages that match it. These are cached; to update them, click on the refresh icon in the upper-right corner of the box.&lt;br /&gt;
&lt;br /&gt;
You can also edit your saved searches by clicking the &amp;quot;Edit searches&amp;quot; link, as shown in [[Greasemonkey Hacks/Web Mail#greasemonkeyhks-CHP-7-FIG-12|Figure 7-12]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;greasemonkeyhks-CHP-7-FIG-12&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 7-12. Editing saved searches'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Greasemonkey Hacks_I_7_tt289.png|Editing saved searches]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can use standard Gmail search syntax in your saved searches, as well as a few custom operators such as &amp;lt;tt&amp;gt;before&amp;lt;/tt&amp;gt;: and &amp;lt;tt&amp;gt;after&amp;lt;/tt&amp;gt;:.&lt;br /&gt;
&lt;br /&gt;
—''Mihai Parparita''&lt;/div&gt;</summary>
		<author><name>Docbook2Wiki</name></author>	</entry>

	</feed>