selberg.org Home Home

Archive for March, 2005
3/27/05
3:04 am
How much do I love my car?

So, my car, a 1971 Plymouth Satellite Sebring Plus, has been making some metal-on-metal noises of late… not sure why. My dad and I suspect it may be the shocks (as they’re original, and at 120K miles are, unsurprisingly, shot). I took it to the mechanic up the street, and they noticed that the front-end needs a lot of work. $600 to do the tire ends (inner and outer), another $400 for a new steering box, $300 for 4 Gabriel shocks ($50 a pop) and 1.25 hours to put ‘em in… and we’re looking at nearly $1500 to make the car ride much nicer.

So, I’ve said I’d start looking when I had to pay more than $3,000 at one time to fix the car. I don’t now, but ouch, $1500? Hmmm… at $500 every six months, I don’t mind, as I budget $1000 a year for random car repairs. Perhaps I should budget $2000 now? But we’re only in March, and already going through a lot of repairs.

Decisions decisions… I’ll probably take the car up to the mechanic I’ve been using in Woodinville for a 2nd estimate… see how it compares, although I suspect it’ll be about the same. And I’ll probably get it done over the year (vs now)… hmm… but I do feel weird putting in half the car’s value for some front-end work. Ah well…

3/27/05
2:12 am
Sick! Blech

Both Laura and I have been sick the past few days, although I think she’s getting the worst of it — fevers of 101, 102, and so on. Go go gadget Tylenol!

In other news, I finally found Doogie’s blog of his adventues in the Sandbox. Like everyone back there, my best wishes for coming back walking and in one piece.

3/27/05
1:32 am
msn_tryothers

My first Firefox extension is a GreaseMonkey script. GreaseMonkey is an extension that lets you create user-level scripts and plug them in… it really trims down the level and skill for writing (and learning!).

I wrote up one called TryOthers for MSN that just puts in a “Try your search on these engines:” on the page. It’s based on the Butler script that does the same to Google. It’s still a big longer and more intimidating than it needs to be, but here’s the useful bit:


        // add other search sites to web search
        addOtherWebSearches: function() {

            var header = document.evaluate("//div[@id='header']/h5″, documen\
t, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
            if (!header) return;

           this.addGlobalStyle(’#tryOthers {margin-bottom:1em;}’);

            // it’d be nice to use document.qf, but MSN uses id= vs name=
            var q = escape(document.forms[0].q.value);
            var other = document.createElement(’div’);
                    other.setAttribute(”id”, “tryOthers”);
            var s = ”;
            s += ‘Try your search on:’;
            s += TryOthersServices._otherWebSearches(q);
            s += ‘
‘; other.innerHTML = s; header.parentNode.insertBefore(other, header.nextSibling); }, }

A couple things to note… first, we add some global CSS style info there. Second, we’re looking for a particular node designated by a particular class — a H5 in a DIV with id=”header. Once we have that, we then stick the TryOthers string in front of it. Simple as that.

The hardest thing about this script is identifying the node you want to alter… it’s really easy if the person writing the page gave you a DIV with an appropriate ID, but usually you are going after something else, so it can take a bit of troubleshooting. And, there’s no real debugger (that I’ve found), so it’s a lot of trial-and-error and logging via the alert() call. Bleah.

3/26/05
4:24 pm
msnpreview

I was home for a few days sick, and in my Advil Cold & Sinus-induced haze I decided I’d hack about in an embedded scripting language that relied heavily on text-file bindings in XML. Yup, Firefox extensions!

I checked out GooglePreview, and modified it for use with MSN Search. — called MSNPreview.

Cracking open the .xpi

Firefox extensions are .xpi files, which is a fancy .zip file that Firefox knows contains a bunch of files to install. Cracking open msnpreview.xpi, we have:


>unzip -l msnpreview.xpi
Archive:  msnpreview.xpi
  Length     Date   Time    Name
 --------    ----   ----    ----
        0  03-26-05 16:06   chrome/
    43690  03-26-05 16:08   chrome/msnpreview.jar
      224  03-26-05 16:08   install.js
      996  03-26-05 16:08   install.rdf
 ——–                   ——-
    44910                   4 files

install.js is a legacy file for older Firefox installs, and can probably get deleted. install.rdf is the instructions to install this extension, and chrome/msnpreview.jar is the contents of the actual extension.

.jar files are also just fancy .zip files. Normally, in a Java world (jars are Java ARchives), they contain a MANIFEST and some other files to make them useful. But, as it turns out, this .jar is just a .zip file. Cracking it open, we have:


>unzip -l msnpreview.jar
Archive:  msnpreview.jar
  Length     Date   Time    Name
 --------    ----   ----    ----
    11081  03-26-05 16:08   content/msnpreview/browserOverlay.js
      700  03-25-05 16:55   content/msnpreview/browserOverlay.xul
      777  03-25-05 16:35   content/msnpreview/contents.rdf
      206  03-25-05 16:35   content/msnpreview/gp.png
       43  03-25-05 16:35   content/msnpreview/pixel.gif
     1200  03-25-05 17:14   content/msnpreview/options.xul
      864  03-25-05 16:35   content/msnpreview/msnoff.png
      343  03-25-05 16:38   content/msnpreview/msnpreview.css
      226  03-25-05 16:35   content/msnpreview/nopreview.gif
     1243  03-25-05 17:38   content/msnpreview/options.js
      644  03-25-05 16:35   content/msnpreview/msnon.png
    13731  03-25-05 16:35   content/msnpreview/msnoff.psd
    10390  03-25-05 16:35   content/msnpreview/msnon.psd
 ——–                   ——-
    41448                   13 files

Key Files

  • content/msnpreview/browserOverlay.js - this is where most of the code is
  • content/msnpreview/browserOverlay.xul - XML bindings for the code
  • content/msnpreview/contents.rdf - header file for the extensions
  • content/msnpreview/options.xul - XML bindings for the options dialog
  • content/msnpreview/options.js - code for the options (pretty minor)

Support Files / Images

  • content/msnpreview/gp.png - legacy image
  • content/msnpreview/pixel.gif - single transparent pixel
  • content/msnpreview/msnoff.png - status bar icon, butterfly w/ X through it
  • content/msnpreview/nopreview.gif - blank preview thumbnail
  • content/msnpreview/msnon.png - status bar icon, butterfly
  • content/msnpreview/msnoff.psd - Photoshop PSD for msnoff.png. Not used normally, just for dev.
  • content/msnpreview/msnon.psd - Photoshop PSD for msnon.png. Not used normally, just for dev.

Key Code

Take a look at content/msnpreview/browserOverlay.js
. In it, you’ll see the following bit of key code:


 } else if (isMsn(url)) {
    //    alert("Activating MSN Thumbnails");
    /***************************************************/
    /* MSN Search Pic Insertions                       */
    /***************************************************/
    var i = 0;
    var results = getDocument().getElementById("results");
    var a = results.getElementsByTagName("a")[i++];
    while (a != null) {
      var href = a.href;

      url = href;
      //      if (url) href = unescape(url[1]);

      if ((href.indexOf(”http://”) == 0) &&
          (href.indexOf(”cc.msnscache.com”) == -1) &&
          (href.indexOf(”.msn.com”) == -1)) {
        var aParent = a.parentNode;
        if (”done” != a.getAttribute(”done”)) {
          var thumb = createThumbnail(href);
          var linka = getDocument().createElement(”a”);
          linka.href = href;
          linka.insertBefore(thumb, linka.firstChild);
          aParent.insertBefore(linka, aParent.firstChild);
          a.setAttribute(”done”, “done”);
        }
      }
      a = results.getElementsByTagName(”a”)[i++];
    }
    head = getDocument().getElementsByTagName(”head”)[0];
    style = getDocument().createElement(”style”);
    style.setAttribute(”type”, ‘text/css’);
    style.innerHTML = “\n#results>UL>LI { height: 100px; }\n”;
    head.insertBefore(style, head.lastChild);
  }

Welcome to the wide world of extension scripting! OK, here’s the model: the input is a DOM tree parsed from the HTML document. It assumes that there is a heavy use of <DIV>’s, name, id, and class attributes (used by style sheets). Effectively, what we’re going to do is find the DOM objects we want, create some new ones, and insert them into the tree in appropriate places. In this case, we’re going to look for anchors in the results section that point outside of msn.com and stick in image in front of ‘em. Plus, we’ll muck about with the global style sheet to ensure that the height of a snippet is tall enough for the picture, so we don’t get some weird indentation effects.

This assumes a tight binding with the underlying HTML input. If that changes, this breaks. Horribly.

Let’s go through it. First off, look at the initialization:


var i = 0;
var results = getDocument().getElementById("results");
var a = results.getElementsByTagName("a")[i++];

What we’re gonna do is find the DOM object identified by id="results", or in this case <div id="results>. Then, we’ll work from within that node of the tree. We’re then going to look for all elements in that subtree that are anchors, i.e. <a href=…>.

Now let’s look at that loop:


while (a != null) {
      var href = a.href;

      if ((href.indexOf("http://") == 0) &&
          (href.indexOf("cc.msnscache.com") == -1) &&
          (href.indexOf(".msn.com") == -1)) {
        var aParent = a.parentNode;
        if ("done" != a.getAttribute("done")) {
          var thumb = createThumbnail(href);
          var linka = getDocument().createElement("a");
          linka.href = href;
          linka.insertBefore(thumb, linka.firstChild);
          aParent.insertBefore(linka, aParent.firstChild);
          a.setAttribute("done", "done");
        }
      }
      a = results.getElementsByTagName("a")[i++];

All this does is check to make sure that the href in question doesn’t point to msn.com or msnscache.com, and tack on the image. The get/set “done” attribute is just to make sure we don’t duplicate the effort, in case we’re somehow doubly-installed or we see the same URL somewhere else.

So, aside from a fair amount of glue code, that’s about it. Pretty sweet, huh?

But wait, there’s more!

Didja notice the following bit of code:


function amazonifiy() {
        var allLinks = getDocument().getElementsByTagName("a");
        for(i=0; i < alllinks .length; i++) {
                var href = allLinks[i].href;
                href = getRealURL(href);
                if (isAmazonCOM(href)) {
                        var n = “http://www.amazon.com/exec/obidos/redirect?\
tag=gp04-20&path=” + escape(getAPath(href));
                        allLinks[i].setAttribute(”href”, n);
                }
                else if (isAmazonDE(href)) {
                        var n = “http://www.amazon.de/exec/obidos/redirect?t\
ag=gp0409-21&path=” + escape(getAPath(href));
                        allLinks[i].setAttribute(”href”, n);
                }
                else if (isAmazonUK(href)) {
                        var n = “http://www.amazon.co.uk/exec/obidos/redirec\
t?tag=gp04-21&path=” + escape(getAPath(href));
                        allLinks[i].setAttribute(”href”, n);
                }
        }
}

Amazonify? What’s up with this?

Well, as it turns out… there’s an interesting economy with Firefox extensions and Amazon. See, Amazon is pretty liberal about who can be an affiliate. So, what people have done is they’ve put in code in extensions to use their Amazon Affiliate tag on Amazon links. Thus, if you install their extension, when you shop at Amazon you’re gonna give them their 5% or whatever it is. In fact, there’s even a Greasemonkey script that let’s you override all this nonesense and put in your own Affiliate ID so you can save your own 5%.

I’m not fully sure about the ethics of this… seems somewhat dodgy, but also OK (and surprisingly entrepreneurial). But it seems that lots of people are glomming onto the idea — which means if you create the dominant search plugin for X, you also get the Amazon Affiliate Credits unless somebody does more work to stop you… which is unlikely to happen, as most people probably don’t even know it’s going on unless they’re extension authors themselves!

Conclusions

It took me about two days to get my environment set up, figure out what everything means, and be productive. Most of that was in understanding the Javascript classes and methods available. That’s really not a very steep learning curve. But from that I was able to do all sorts of cool things. This means that lots of other people will too — there are already a bunch, and more coming seemingly daily. What will really spur things is the Amazon thing. Nothing prompts scratching an itch like the possibility of making a few nickels while doing it. This also means there’s a competition for the “best” plugin of a type — there is already another GooglePreview-like extension which is a bit nicer, although more obnoxious about the Amazon tie-in.

3/26/05
4:22 pm
msnhacks

This is a page describing various hacks on MSN Search that I’ve done.

3/20/05
10:44 pm
Pictoral Resume

MK and I went on an Agosy Cruise sponsored by Microsoft & CMU (does seem to be an undergrad weekend!). I snapped this picture which shows where I used to work in Seattle. Now, I’m over in Redmond, which, while it has a nice view of the Cascades, isn’t quite the same.

Seattle Waterfront - Erik's Jobs

3/19/05
11:36 pm
Blast from the past

A good friend of mine, Bill, from my CMU days was in town, so I and another two pals from CMU (whom I never seem to see, even though we all work for the same company… ) all trucked to Ray’s for dinner. We had a great time, then headed back to Chez Erik for some South Park & Daily Show, along with some Blanton’s Bourbon.

Nothing terribly profound, but it was nice to have a good dinner with people that I went to CMU with some 10 years ago. Sure, we don’t see each other all that often as we all have our different lives, but it’s refreshing every now and then to relive some of the past and find out where the future has taken us. For example… me, a Math/CS major from CMU, is now working for Microsoft. No big surprise. The Physics major is also now working for Microsoft in the advanced hardware division. Again, not a surprise. Guess who’s been at MS the longest? That’d be the industrial design major. Go figure. :)

As for Bill, who also did Physics and even went to grad school for a bit, he’s now the CIO at an architectural firm in Boston. Careers sure can be crazy at times.

3/16/05
11:28 pm
Wordpress Theme Contest

I submitted this theme (ok, a slight variation) to Alex King’s Theme Competition. I noticed it doesn’t quite look as good as it does here… looks like I need to make a minor update or three. We’ll see how it goes. :)

3/16/05
1:46 am
New email notification

Well, Meg wanted it, so we have it… sign up and get e-mail notifications. Or, just grab the RSS feed from the bottom of the page!

3/16/05
1:14 am
Computers are Evil (I)

OK… so, you’d think that maybe a 1971 Plymouth Satellite Sebring Plus might not run as well as a 4-year-old PC. Welp… no.

So over the weekend, my new 60G drive on my main Windows box failed. I haven’t ripped it out to see what the failure is, or if the disk is under warranty (I suspect not), but what a drag. Luckily, no real data was lost — just a ton of installed apps that I’ll now have to install _again_. Bah.

Also, my work notebook decided to randomly lose the taskbar for most of today when the battery died and the thing shut down. Why? I don’t know. But then it suddenly fixed itself. Bah.

Just to show that Linux isn’t immune, I foolishly upgraded something on my storage server, and suddenly KDE (the Windows of Linux) and LDAP (used for authentication) weren’t working. They’re fixed now, but man what a pain to troubleshoot.

Grump.