Source for "XISBN LibraryLookup"

By Hippopottoman
Has 2 other scripts.


// ==UserScript==
// @name        XISBN LibraryLookup
// @namespace   http://userscripts.org/people/4764
// @description Check book availability in one or more libraries
// @include     http://*.amazon.*
// @include     http://*.chapters.indigo.ca/*
// @include     http://*.powells.com/*
// @include     http://*allconsuming.net/item/view/*
// @include     http://books.google.*
// ==/UserScript==
 
// This script was inspired by Jon Udell's userscript of similar functionality
// that he posted in his 'Further adventures in lightweight service
// composition' post of 30 January, 2006
// (http://weblog.infoworld.com/udell/2006/01/30.html).
//
// I've modified the code in the following ways: 
// - it's more understandable to me - your mileage may vary, but I found the
//   background DOM modification and associated event a little confusing
// - it works against the Waterloo Public Library instead of the Keene
//   libraries
// - it only hits XISBN if the original ISBN doesn't appear in the library
// - if no versions of the book are found in the library, that fact is noted
//   on the Amazon page
//
// The latest version of this script is always available at 
// http://userscripts.org/scripts/show/3393
//
// Updates:
//
// 2006.03.30
// - added Region of Waterloo Library as last-checked library
// 
// 2006.03.20
// - Bugfixes, thanks to Kevin Yezbick's sharp eyes
//   - better detection of whether books are at the WPL & KPL
//   - persisting extra ISBN list until all libraries have been checked 
// - updated ISBN & title detection at Chapters
// 
// 2006.03.15
// - added support for fallback libraries - if the book isn't found
//   in the library, look in others. This could still use some work,
//   since we're relooking up the XISBNs
// - added fallback library of Kitchener Public Library
// - added support for Google Books, as requested by Kevin Yezbick
//
// 2006.03.09, later on
// - added All Consuming
//
// 2006.03.09
// - fixed broken ISBN detection on Powells
// 
// 2006.03.08
// - Added builtin support for Powells web pages (a US bookseller)
// - Fixed up some logs. A little.
//
// 2006.03.05
// - split 'source page' (formerly Amazon site) functionality into separate objects
//   and added builtin support for Chapters web pages (a Canadian bookseller)

(
   function()
   {
      var xisbnQuery = 'http://labs.oclc.org/xisbn/';
      
      var isbnREplain = /(\d{7,9}[\dXx])/g;
      var isbnREdelimited = /[^\d](\d{7,9}[\dXx])[^\dXx]/;

      var extraIsbns = [];

      // Libraries -----------------------------------------------------------

      var waterlooRegion =
      {
            name: 'Region of Waterloo Library',

            createSearchUrl: function(isbn)
            {
               // var prefix = 'http://www.regionofwaterloo.canlib.ca/uhtbin/cgisirsi/uOtJAQjVct/HEADQUARTR/262040012/5/0/?searchdata1=';
               var prefix = 'http://www.regionofwaterloo.canlib.ca/uhtbin/cgisirsi/uOtJAQjVct/HEADQUARTR/x/5/0/?searchdata1=';
               return prefix + isbn
            },
            
            // Get the results of a lookup - mark up the page and return true
            // if there's a hit, otherwise return false.
            processLookup: function(isbn, results)
            {
               var notFound = /found no matches in the library you searched/;

               page = results.responseText;
               if ( notFound.test(page) )
               { 
                  GM_log('couldn\'t find ' + isbn + ' at the ' + this.name);
                  return false;
               }
               else
               {
                  return true;
               }
            },
            
      } // end waterlooRegion

      var wpl = 
         {
            name: 'Waterloo Public Library',

            createSearchUrl: function(isbn)
            {
               var prefix = 'http://books.wpl.ca/search/?searchtype=i&searcharg=';
               var suffix = '&searchscope=3&SORT=D&extended=0&SUBMIT=Search&searchlimits=';
               return prefix + isbn + suffix;
            },
            
            // Get the results of a lookup - mark up the page and return true
            // if there's a hit, otherwise return false.
            processLookup: function(isbn, results)
            {
               var notFound = /No matches found; nearby ISBN[/]ISSN/;

               page = results.responseText;
               if ( notFound.test(page) )
               { 
                  GM_log('couldn\'t find ' + isbn + ' at the ' + this.name);
                  return false;
               }
               else
               {
                  return true;
               }
            },
            
         } // end wpl

      var kpl = 
         {
            name: 'Kitchener Public Library',
            createSearchUrl: function(isbn)
            {
               var prefix = 'http://books.wpl.ca/search/?searchtype=i&searcharg=';
               var suffix = '&searchscope=1&SORT=D&extended=0&SUBMIT=Search&searchlimits=';
               return prefix + isbn + suffix;
            },

            // the Kitchener Public Library shares a catalog with the WPL, so
            // just copy their processLookup
            processLookup: wpl.processLookup,
         } // end kpl

      // End of Libraries ----------------------------------------------------

      // Figure out which site the source page comes from to add a new one,
      // make a new block like the "chapters" and "amazon" variables below,
      // and extend the "if...else..." block at the bottom of this function.
      function whichSiteIsThis()
      {
         var chapters =
            {
               getIsbn: function()
               {
                  var isbn = null;
                  isbnLinkNode = document.evaluate
                     (
                        "//div[@class='itemPublishingDetails']/a",
                        document,
                        null, 
                        XPathResult.FIRST_ORDERED_NODE_TYPE, null
                        ).singleNodeValue;
                  if ( isbnLinkNode )
                  {
                     isbn = isbnLinkNode.firstChild.nodeValue;
                  }
                  return isbn;
               },

               getOriginalTitle: function()
               {
                  var origTitle = document.evaluate
                     (
                        "//div[@class='productCoreInfo']/h1/span", 
                        document,
                        null, 
                        XPathResult.FIRST_ORDERED_NODE_TYPE, null
                        ).singleNodeValue;
                  return origTitle;
               }
            }
      
         var allconsuming =
            {
               getIsbn: function()
               {
                  var isbn = null;
                  isbnLinkNode = document.evaluate
                     (
                        "//div[@class='item-header-body']/a[@class='amazon-link']/@href", 
                        document,
                        null, 
                        XPathResult.FIRST_ORDERED_NODE_TYPE, null
                        ).singleNodeValue;
                  if ( isbnLinkNode )
                  {
                     isbn = isbnLinkNode.firstChild.nodeValue.match(isbnREdelimited)[1];
                  }
                  return isbn;
               },

               getOriginalTitle: function()
               {
                  var origTitle = document.evaluate
                     (
                        "//div[@class='item-header-body']/strong", 
                        document,
                        null, 
                        XPathResult.FIRST_ORDERED_NODE_TYPE, null
                        ).singleNodeValue;
                  return origTitle;
               }
            }
      
         var amazon =
            {
               getIsbn: function()
               {
                  try
                  {
                     return location.href.match(isbnREdelimited)[1];
                  }
                  catch ( e ) 
                  {
                     return null;
                  }
               },

               getOriginalTitle: function()
               {
                  var origTitle = document.evaluate
                     (
                        "//b[@class='sans']", 
                        document,
                        null, 
                        XPathResult.FIRST_ORDERED_NODE_TYPE, null
                        ).singleNodeValue;
                  return origTitle;
               }
            }
      
         var powells =
            {
               getIsbn: function()
               {
                  try
                  {
                     return location.href.match(isbnREdelimited)[1];
                  }
                  catch ( e ) 
                  {
                     return null;
                  }
               },

               getOriginalTitle: function()
               {
                  var origTitle = document.evaluate
                     (
                        "//h1[@class='title']", 
                        document,
                        null, 
                        XPathResult.FIRST_ORDERED_NODE_TYPE, null
                        ).singleNodeValue;
                  return origTitle;
               }
            }
      
         var googleBooks =
            {
               getIsbn: function()
               {
                  try
                  {
                     return location.href.match(isbnREdelimited)[1];
                  }
                  catch ( e ) 
                  {
                     return null;
                  }
               },

               getOriginalTitle: function()
               {
                  var origTitle = document.evaluate
                     (
                        "//span[@class='title']", 
                        document,
                        null, 
                        XPathResult.FIRST_ORDERED_NODE_TYPE, null
                        ).singleNodeValue;
                  return origTitle;
               }
            }
      
         // figure out what site we're looking at
         if ( location.href.match(/chapters/) )
         {
            return chapters;
         }
         else if ( location.href.match(/allconsuming/) )
         {
            return allconsuming;
         }
         else if ( location.href.match(/powells/) )
         {
            return powells;
         }
         else if ( location.href.match(/google/) )
         {
            return googleBooks;
         }
         else
         {
            // Amazon's pretty popular - make it the default
            return amazon;
         }
      }

      // ---------------------------------------------------------------------

      var xisbnCache =
         {
            cache: {},
            
            get: function(isbn)
            {
               if ( null == this.cache[isbn] )
               {
                  return null;
               }
               else
               {
                  return this.cache[isbn].copy()
              }
            },

            set: function(isbns)
            {
               var isbn = isbns.shift();
               
               if ( ! isbns )
               {
                  isbns = [];
               }

               GM_log('caching ' + isbns.length + ' items: [' + isbns + '] for ' + isbn);
               this.cache[isbn] = isbns.copy();
               return this.get(isbn);
            }
         }

      // ---------------------------------------------------------------------
      var libraryLookup = 
         {
            originalTitle: null,
            infoSpan: null,

            getLibrary: function()
            {
               return this.libraries[0];
            },

            insertContent: function(content)
            {
               if ( ! this.infoSpan )
               {
                  this.infoSpan = document.createElement("span");
                  var sp = document.createElement('br');
                  this.originalTitle.appendChild(sp);
                  this.originalTitle.appendChild(this.infoSpan);
               }

               this.infoSpan.innerHTML = content;
            },

            // Look up the alternate isbns for this one.  Store the results in
            // extraIsbns and start looking them up.
            lookUpAlternates: function(isbn)
            {
               alts = xisbnCache.get(isbn);
               if ( alts )
               {
                  extraIsbns = alts;
                  libraryLookup.keepLooking();
                  return;
               }
                  
               GM_log('loading extra ISBNs for ' + isbn);
               GM_xmlhttpRequest
                  (
                  {
                    method:  'GET',
                                url:     xisbnQuery + isbn,
                                onload:  function(results)
                                {
                                   var foundIsbns = results.responseText.match(isbnREplain);
                                   
                                   extraIsbns = xisbnCache.set(foundIsbns);
                                   
                                   GM_log('extra ISBNs = ' + extraIsbns);
                                   libraryLookup.keepLooking();
                                }
                  }
                  );
            },

            // Lookup ISBNs from the libraryLookup.extraIsbns list. If there
            // aren't any extra ISBNs left, give up in disgust. Otherwise, try
            // the first ISBN. If it doesn't work out, call keepLooking again.
            keepLooking: function()
            {
               if ( 0 == extraIsbns.length && 1 == this.libraries.length )
               {
                  this.insertContent("isn't in the library");

                  return;
               }
               else if ( 0 == extraIsbns.length )
               {
                  GM_log('time to try a new library');
                  this.libraries.shift();
                  extraIsbns = null;
                  this.lookupBook(originalIsbn);
                  return;
               }

               var isbn = extraIsbns.shift();

               // look up the next book
               GM_xmlhttpRequest
                  (
                  {
                    method:  'GET',
                                url:     libraryLookup.getLibrary().createSearchUrl(isbn),
                                onload:  function(results)
                                {
                                   var found = libraryLookup.getLibrary().processLookup(isbn, results);
                                   if ( found )
                                   {
                                      var library = libraryLookup.getLibrary();
                                      libraryLookup.insertContent('<a href="' +
                                                               library.createSearchUrl(isbn) +
                                                               '">Hey! The ' +
                                                               library.name + 
                                                               ' has this!');
                                      GM_log('found ' + isbn + ' - no more looking');
                                   }
                                   else
                                   {
                                      libraryLookup.keepLooking();
                                   }
                                }
                    }
                     )
            },

            // Look up an original ISBN - if this fails, fall back to the
            // alternate ISBNs provided by xisbn
            lookupBook: function(isbn)
            {
               GM_log('looking up ' + isbn + ' in the ' + this.getLibrary().name);
               this.insertContent('looking in the ' + this.getLibrary().name);
               GM_xmlhttpRequest
                  (
                  {
                    method:  'GET',
                                url:     libraryLookup.getLibrary().createSearchUrl(isbn),
                                onload:  function(results)
                                {
                                   var found = libraryLookup.getLibrary().processLookup(isbn, results);
                                   if ( found )
                                   {
                                      var library = libraryLookup.getLibrary();
                                      libraryLookup.insertContent('<a href="' +
                                                                  library.createSearchUrl(isbn) +
                                                                  '">Hey! The ' +
                                                                  library.name + 
                                                                  ' has this!');
                                      GM_log('found ' + isbn + ' - no more looking');
                                   }
                                   else
                                   {
                                      libraryLookup.lookUpAlternates(isbn);
                                   }
                                }
                    }
                    )
              },
         } // end of libraryLookup


      var theSite = whichSiteIsThis();

      originalIsbn = theSite.getIsbn();
      if ( originalIsbn )
      {
         GM_log('found isbn = "' + originalIsbn + '" on source page');
      }
      else
      {
         return;
      }

      libraryLookup.originalTitle = theSite.getOriginalTitle();
      if ( ! libraryLookup.originalTitle )
      { 
         GM_log("couldn't find the original title");
         return;
      }

      // make sure we can copy arrays
      if ( typeof Array.prototype.copy==='undefined' ) 
      {
         Array.prototype.copy = function() 
         {
            var newArray = [];
            var i = this.length;
            while( i-- ) 
            {
               newArray[i] = typeof this[i].copy !== 'undefined' ? this[i].copy() : this[i];
            }
            return newArray;
         };
      }

      // Change this to add, delete, or change order in which libraries are
      // checked
      libraryLookup.libraries = [wpl, kpl, waterlooRegion];
      libraryLookup.lookupBook(originalIsbn);
   }
)();