How to Remember Recent Google Searches with User Scripts
Remember Recent Google Searches
Track what you search for and which search results you follow.
Google recently added yet another beta service: My Search History (http://www.google.com/searchhistory/). In a nutshell, you log into your Google account, and My Search History remembers which keywords you search for and which search results you end up following. A nice idea, but it has some limitations that disappointed me when I tried it. My Search History isn't immediately available on the Google home page. Also, clicking a previous search simply reexecutes the search, instead of actually taking me to the result I followed last time. How is that useful? I remember what I searched for; what I want to know is what I found!
This hack lets me do what I had hoped the "My Search History" tool would do.
The Code
This user script runs on all Google pages. The code itself breaks down into three distinct parts:
-
The SavedSearches function and associated prototype methods are used to create a persistent array-i.e., an Array class that saves its data to the Firefox preferences database.
-
The getCurrentSearchText, addCurrentSearch, clearSavedSearches, and injectRecentSearches functions handle the basic operations of the script. Whenever you execute a Google search, the script adds your keywords to its persistent array, and then alters the search results page to include a list of your recent searches.
-
The trackClick function is where the real magic happens. On search result pages, we register TRackClick as a global onclick event handler. When you click on anything on the search results page, TRackClick is called. It looks at where you clicked, and if you clicked on a search result, it stores the title and URL of the link before following it.
The end result is seamless: you search, click a search result, and visit the result page. But invisibly, behind the scenes, the user script has tracked and stored your every movement.
|
Save the following user script as recentsearches.user.js:
// ==UserScript== // @name Recent Searches // @namespace http://diveintomark.org/projects/greasemonkey/ // @description remember and display recent Google searches // @include http://www.google.com/* // ==/UserScript== function SavedSearches() { var iCount = GM_getValue('count') || 0; for (var i = 0; i < iCount; i++) { this.push({ "searchtext": GM_getValue('searchtext.' + i, ''), "searchresult": GM_getValue('searchresult.' + i, '')}); } } SavedSearches.prototype = new Array( ); SavedSearches.prototype.find = function(sSearchText) { for (var i = this.length - 1; i >= 0; i--) { if (this[i] == sSearchText) { return i; } } return -1; }; SavedSearches.prototype.append = function(sSearchText) { GM_setValue('searchtext.' + this.length, sSearchText); this.push({"searchtext": sSearchText}); GM_setValue('count', this.length); }; var arSavedSearches = new SavedSearches( ); function getCurrentSearchText( ) { var elmForm = document.forms.namedItem('gs'); if (!elmForm) { return; } var elmSearchBox = elmForm.elements.namedItem('q'); if (!elmSearchBox) { return; } var sKeyword = elmSearchBox.value; if (!sKeyword) { return; } return sKeyword; } function addCurrentSearch( ) { var sCurrentSearchText = getCurrentSearchText( ); if (!sCurrentSearchText) { return; } var sLastSearch = null; if (arSavedSearches.length) { sLastSearch = arSavedSearches[arSavedSearches.length - 1]; } if (sLastSearch && (sLastSearch['searchtext'] == sCurrentSearchText)) { return; } arSavedSearches.append(sCurrentSearchText); } function clearSavedSearches( ) { for (var i = 0; i < arSavedSearches.length; i++) { GM_setValue('searchtext.' + i, ''); GM_setValue('searchresult.' + i, ''); } GM_setValue('count', 0); arSavedSearches = new SavedSearches( ); var elmRecentSearches = document.getElementById('recentsearcheslist'); if (elmRecentSearches) { elmRecentSearches.innerHTML = ''; } } function injectRecentSearches( ) { if (!arSavedSearches.length) { return; } var elmFirst = document.evaluate("//table[@bgcolor='#e5ecf9']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if (!elmFirst) { elmFirst = document.evaluate("//form[@name='f']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; } if (!elmFirst) { return; } var htmlRecentSearches = '<p style="font-size: small"> Recent searches: '; var iDisplayedCount = 0; for (var i = arSavedSearches.length - 1; (iDisplayedCount <10) && (i >= 0); i--) { var oSearch = arSavedSearches[i]; if (!oSearch['searchresult']) { continue; } var sSearchResult = oSearch['searchresult']; var iSpacePos = sSearchResult.indexOf(' '); var sHref = sSearchResult.substring(0, iSpacePos); var sTitle = sSearchResult.substring(iSpacePos + 1); htmlRecentSearches += '<a href="' + shref + '" title="' + sTitle + '">' + oSearch['searchtext'] + '</a> · '; iDisplayedCount++; } if (!iDisplayedCount) { return; } htmlRecentSearches += '[<a ' + 'title="Clear saved searches" href="#">clear</a>]</p>'; var elmWrapper = document.createElement('div'); elmWrapper.id = "recentsearcheslist"; elmWrapper.innerHTML = htmlRecentSearches; elmFirst.parentNode.insertBefore(elmWrapper, elmFirst.nextSibling); window.addEventListener('load', function( ) { var elmClearLink = document.getElementById('clearsavedsearches'); elmClearLink.addEventListener('click', clearSavedSearches, true); }, true); } function trackClick(event) { var sHref, sTitle; var elmTarget = event.target; while ((elmTarget.nodeName != 'A') && (elmTarget.nodeName != 'BODY')) { elmTarget = elmTarget.parentNode; } if (elmTarget.nodeName != 'A') { return; } var elmParent = elmTarget.parentNode; while ((elmParent.nodeName != 'P') && (elmParent.nodeName != 'BODY')) { elmParent = elmParent.parentNode; } if (elmParent.nodeName != 'P') { return; } if (elmParent.getAttribute('class') != 'g') { return; } sHref = elmTarget.href; sTitle = elmTarget.textContent; var iSearchIndex = arSavedSearches.find(getCurrentSearchText( )); if (iSearchIndex == -1) { addCurrentSearch( ); iSearchIndex = arSavedSearches.length - 1; } GM_setValue('searchresult.' + iSearchIndex, sHref + ' ' + sTitle); } if (/^\/search/.test(location.pathname)) { injectRecentSearches( ); addCurrentSearch( ); document.addEventListener('click', trackClick, true); } else if (/^\/$/.test(location.pathname)) { injectRecentSearches( ); }
Running the Hack
After installing the user script (Tools Install This User Script), go to http://www.google.com and search for something. Click on an interesting search result. Lather, rinse, and repeat. Each time you revisit the Google home page, you will see a growing list of your recent searches, as shown in Figure 6-19. Clicking on a recent search term will take you to the same link you followed when you originally executed the search.
Figure 6-19. Recent searches on the Google home page

You will also see the list of recent searches on the search results page itself. Hovering over a recent search displays the title of the linked page, as shown in Figure 6-20.
Figure 6-20. Recent searches on Google's search results page

Hacking the Hack
The links in the recent searches list go directly to the search result you clicked when you searched. But what if you want to rerun the search and go somewhere else? That's easy enough. In the injectRecentSearches function, find these two lines:
var sHref = sSearchResult.substring(0, iSpacePos); var sTitle = sSearchResult.substring(iSpacePos + 1);
And change them like this:
var sHref = 'http://www.google.com/search?q=' + escape(sSearchResult); var sTitle = 'previously found ' + sSearchResult.substring(iSpacePos + 1) + '\n' + sSearchResult.substring(0, iSpacePos);
Now, if you hover over a link in the recent searches list, the tool tip will display the title and URL of the page you went to last time. If you click the link, it will reexecute the search so that you can choose a different search result this time.