Free Newsletters
Technology & Business Daily

InfoWorld
Log-in | Register

  Monday, December 30, 2002 

Scripting an interactive service intermediary

The recent discussion about active intermediaries (Sam Ruby, Phil Windley) sent me in an unexpected direction. What I meant to do was revisit some earlier writing on web proxies, email proxies, and SOAP routing, and try to draw some conclusions. Instead, I invented another bookmarklet.

Here was the problem. It's nice that I can now look up a book in my local library, but what if it's not in the collection? My library's OPAC (online public access catalog) enables you to ask the library to acquire a book, but the required fill-in form creates an activation threshold that I am rarely motivated to leap over.

The basic LibraryLookup bookmarklet is a kind of intermediary. It coordinates two classes of services -- Amazon/BN/isbn.nu/AllConsuming and your local library's OPAC -- to facilitate a lookup. I couldn't resist trying to create another intermediary that would facilitate a purchase request.

The solution I'll present here is less general than the basic lookup in several ways, but also interesting in several ways. Here are the ways in which it is less general:

  • Amazon-only. The basic lookup works with any site whose URL matches either /ISBN or isbn=ISBN. But to fill out a purchase request, more information is needed. This solution relies on Amazon-specific markup to find that information.

  • Innovative-only. The basic lookup works with any of four OPAC systems (and potentially others, as users discover and report the URI patterns that can enable them). But since I only have an account at my own library, which uses an Innovative OPAC, that's the only case I could try. Further, the solution is likely not to work with your Innovative OPAC. A little spelunking reveals that the /acquire function (e.g. http://your.library.baseurl/acquire) produces differently-constituted forms from one Innovative OPAC to the next. Sometimes name/password, sometimes PIN and library-card number, etc. Mine uses name and library-card number.

Nevertheless, here are the reasons I find the solution interesting.

  • It works. Specifically, it works for my library, but the geek-inclined should find it easy to adapt it to another Innovative OPAC, and -- presumably -- to other OPACs.

  • It's a simple but compelling demonstration of the JavaScript DOM.

  • It uses JavaScript to set and get Amazon cookies. I'm sure JS hackers take this for granted, but I've never had occasion to try it.

  • It's a live example of the technique (which Derek Robinson mentioned to me and Art Rhyno showed me) that removes the MSIE bookmarklet size limit.

Intermediating a library purchase request

Here is the bookmarklet you can drag to your link toolbar:

Please Acquire

Here is an Amazon page against which to test it: The Eighth Day of Creation: Makers of the Revolution in Biology.

Clicking the bookmarklet's link should bring up a screen like the one shown here. It's OK to click the button. I've neutered the script so it will just pop up a message rather than send the request. To unneuter it, rewrite the form's action= attribute to specify your OPAC's acquisition-request URL.

A few points to note in the code that follows:

  • Amazon's consistent use of the first META tag makes it very easy to pick out the book's title and author, like so:

    var m0 = document.getElementsByTagName('META')[0];
    var titleAuthor = m0.getAttribute('content');

    Gotta love that million-dollar markup!

  • There's no million-dollar markup for the publisher's name and date. Digging that out of the page is feasible, but much harder. I punt, in this case, by referring the librarian to the book's Amazon URL.

  • The setCookie script written into the generated form uses Amazon's domain. I'd never thought about this, but cookies are a two-way street. Amazon can use them to coordinate with me, but I can also use them to coordinate with Amazon. In this example, the generated form looks for Amazon cookies named MyLibraryUserName and MyLibraryUserID. If it finds them, it defaults two fields to their values. Otherwise, whatever you type there is remembered (via the onChange() handler) in Amazon cookies.

All in all, an instructive little exercise. This sort of technique won't replace active intermediaries, including the local kind that work at the level of HTTP or SMTP. Rather, it will complement them. Users need to be able to see, and approve, what intermediaries propose to do on their behalf. I like the idea of an interactive intermediary that prepares a connection between two services, previews it for the user, and then makes the connection.

Update

The script below contains a privacy bomb which, after a few minutes of reflection, I removed from the live version invoked by the bootloader. It's a fascinating scenario, actually:

  1. You don't want Amazon to see your library-card number.

    1. Don't store/send it at all. This, of course, eliminates most of the convenience of the solution.

    2. Store/send an encrypted version.

  2. You do want Amazon to see your library-card number. Does that sound crazy? Maybe not. Reasons I might trust Amazon with that information:

    1. Because it could use it to co-ordinate my library activity with my Amazon activity, and make better-informed Amazon recommendations. In particular, Amazon could emphasize books known not to be available to me in my local library. This would certainly seem to be a fair quid-pro-quo for the use of that handy ISBN in its URI!

    2. Because it could use it to offer me an email-notification service alerting me to overdue library books.

I find (2b) especially intriguing. It's not really in Amazon's interest for me to be aware of what's available in the local library, and it's not really in the library's interest for me to be made promptly aware of fines accumulating there. By yoking them together, I might be able to play the two services off against one another to my benefit -- and to theirs.


The bookmarklet's bootloader

javascript:void((function() {var%20element=document.createElement('script'); element.setAttribute('src', 'http://weblog.infoworld.com/udell/gems/acquire.js'); document.body.appendChild(element)})())

The script loaded by the bootloader

var setCookieScript = 'function setCookie(Name1, Value1) { var expires = new Date(); expires.setFullYear(expires.getFullYear()+1); var cookie = Name1 + '=' + escape(Value1) + ';domain=amazon.com;path=/;expires=' +expires.toGMTString(); alert(cookie); document.cookie = cookie; }';

function getCookie(Name)
  {
  var s = '; '+document.cookie+';';
  var i = s.indexOf('; '+Name+'=');
  if (i == -1)
    { return ''; }
  else
    {
    i += 3 + Name.length;
    var j = s.indexOf(';', i);
    return unescape(s.substring(i, j));
    }
  }

var myLibraryUserID = getCookie('MyLibraryUserID');
var myLibraryUserName = getCookie('MyLibraryUserName');
var m0 = document.getElementsByTagName('META')[0];
var titleAuthor = m0.getAttribute('content');
var re = /(.+),s*([^,]+)$/;

re.test(titleAuthor);
var title = RegExp.$1;
var author = RegExp.$2;

var win = window.open('','LibraryAcquisitionRequest', 'resizable=1,scrollable=1,width=600,height=400');

win.document.write('<html><head><title>Request acquisition of: ' + titleAuthor + '</title><scr' + 'ipt>' + setCookieScript + '</scr' + 'ipt></head><body>');

win.document.write('<p>Request acquisition of: ' + titleAuthor + '</p>');

win.document.write('<form name="acquire" method="post" action="javascript:alert(\'Demonstration only!\');"><table><tr><td align="right">Author: </td> <td><input name="author" value="' + author + '" size="40" maxlength="255"></td> </tr><tr><td align="right">Title:</td> <td><input name="title" value="' + title + '" size="40" maxlength="255"></td> </tr><tr><td align="right">Where/when published:</td> <td><input name="publish" value="See Amazon: ' + location.href + '" size="60" maxlength="255"></td> </tr><tr><td align="right">Where mentioned:</td> <td><input name="mention" value="Amazon" size="60" maxlength="255"></td> </tr><tr><td align="right">Other info:</td> <td><input name="other" value="Intermediated by the LibraryLookup project" size="40" maxlength="255"></td> <tr><tr><td align="right">Your name:</td> <td><input name="name" value="' + myLibraryUserName + '" size="40" maxlength="255" onChange="javascript:setCookie (\'MyLibraryUserName\',forms[0].name.value);"></td> </tr> <tr><td align="right">14-digit library card #:</td> <td><input name="barcode" type="text" value="' + myLibraryUserID + '" size="40" maxlength="40" /* use with caution! onChange="javascript:setCookie (\'MyLibraryUserID\',forms[0].barcode.value);" */></td> </tr><tr><td align="left" colspan="2"><br><input name="submit" type="submit" value="Ask library to acquire this book"></td> </tr></table></form><p></body></html>');

win.document.close();

 


Recent Entries


















































Sponsored Technology Links

 
 
 HOME  NEWS  BLOGS  PODCASTS  VIDEOS  TECHNOLOGIES  TEST CENTER  EVENTS  CAREERS  IT EXEC-CONNECT   About | Advertise | Awards | RSS | Contact Us 

Copyright © 2008, Reprints, Permissions, Licensing, IDG Network, Privacy Policy, Terms of Service.
All Rights reserved. InfoWorld is a leading publisher of technology information and product reviews on topics including viruses,
phishing, worms, firewalls, security, servers, storage, networking, wireless, databases, and web services.

CIO :: ComputerWorld :: CSO :: Demo :: GamePro :: Games.net :: IDG Connect :: IDG World Expo
Industry Standard :: IT World :: JavaWorld :: LinuxWorld :: MacUser :: Macworld :: Network World :: PC World :: Playlist