Hack 59. Use Recent Searches and Google SearchKeys Together

 < Day Day Up > 

Recent Searches needs an update to play nicely with Google SearchKeys.

I stumbled onto this hack by accident. I had been running Recent Searches [Hack #57] for a few weeks, and I heard about Google SearchKeys [Hack #58] on the Greasemonkey mailing list. I went to install it and immediately fell in love with it, but after a few searches, I realized that my recent searches list wasn't being updated anymore.

After investigating, I discovered that, because of the way the Google SearchKeys user script works, it was never calling the onclick handler I Had defined for the search result links. Instead, Google SearchKeys simply parsed out the URL of each search result link and assigned it to window.location.href, thus loading the result page and creating the illusion of "following" the link. The illusion was almost perfect, except that my Recent Searches script was assuming that the only way to follow a result link was to click it (or navigate to it with the keyboard and press Enter, but either way would trigger the link's onclick handler).

After a little intensive research, I had a solution so ingenious that my editor agreed it was worthy of its own hack. JavaScript has a feature called watchpoints. On every object, you can set a watchpoint on one of the object's properties. When that property is about to be changed, the JavaScript engine will call a callback function of your choosing, with the property name, the old value, and the new value.

By setting watchpoints on document.location, document.location.href, window. location, and window.location.href, we can notice when a script (such as Google SearchKeys) is trying to move to a different page programmatically. We can save the URL of the new page to our recent changes database and then let the script go about its merry way.

6.14.1. The Code

This user script runs on all Google pages, because it displays the list of recent searches on the Google home page as well as on search results pages. The bulk of this script is the same as "Remember Recent Google Searches" [Hack #57]; the changes are listed in boldface.

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

 // ==UserScript== // @name Recent Searches // @namespace http://diveintomark.org/projects/greasemonkey/ // @description remember and display recent Google searches // @include http://www.google.*/search* // ==/UserScript== // based on code by Adam Langley // and included here with his gracious permission // http://www.imperialviolet.org/page24.html 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> &middot; '; 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; if (typeof(event) == 'string') { sHref = event; sTitle = ''; } else { 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); } function watchLocation(sPropertyName, sOldValue, sNewValue) { trackClick(sNewValue); return sNewValue; } if (/^\/search/.test(window.location.pathname)) { injectRecentSearches( ); addCurrentSearch( ); document.addEventListener('click', trackClick, true); var unsafeDocument = document.wrappedJSObject || document; unsafeDocument.watch('location', watchLocation); unsafeDocument.location.watch('href', watchLocation); unsafeWindow.watch('location', watchLocation); unsafeWindow.location.watch('href', watchLocation); } else if (/^\/$/.test(window.location.pathname)) { injectRecentSearches( ); } 

6.14.2. Running the Hack

As I mentioned before, the only reason for this hack is to get two previous hacks to play nicely with each other. After you install this script (Tools Install This User Script), you also need to install [Hack #58].

Now, go to http://www.google.com and search for anything. In the search results page, you will see the numbers to the left of each search result, as shown in Figure 6-22.

You won't see any recent searches unless you've previously installed the Recent Searches hack and executed a search.


Figure 6-22. Google search with keyboard shortcuts


Click through to one of the search result pages by typing the number next to the link.

Go back to http://www.google.com. Below the search box is the list of recent searches, as shown in Figure 6-23.

Figure 6-23. Recent searches executed with the keyboard


The list of recent searches includes the result link you just followed by typing the Google SearchKeys keyboard shortcut.

     < Day Day Up > 


    Greasemonkey Hacks
    Greasemonkey Hacks: Tips & Tools for Remixing the Web with Firefox
    ISBN: 0596101651
    EAN: 2147483647
    Year: 2005
    Pages: 168
    Authors: Mark Pilgrim

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net