Average direction in SQL

Given a column of polar directions in degrees, this is a single SQL expression to compute their average, for use in a GROUP BY query. Some functions may be MySQL-specific.

IF(DEGREES(ATAN2(
        -AVG(SIN(RADIANS(direction_deg)))
        ,-AVG(COS(RADIANS(direction_deg))))
    ) < 180
    ,DEGREES(ATAN2(
        -AVG(SIN(RADIANS(direction_deg)))
        ,-AVG(COS(RADIANS(direction_deg))))
    ) + 180
    ,DEGREES(ATAN2(
        -AVG(SIN(RADIANS(direction_deg)))
        ,-AVG(COS(RADIANS(direction_deg))))
    ) - 180
)

Thought someone might like to run across this.

Hacking a 3rd party script for bookmarklet fun

A few weeks ago I created a simple bookmarklet that loads del.icio.us’s PlayTagger script into the current page. This post covers how some problems with this script were worked through.

Too late

The first challenge was that PlayTagger was designed to initialize itself (let’s call this method “init“) on window.onload: If a user fired the bookmarklet after window.onload (99% of the time), playtagger.js would load but init would’ve missed its chance to be called. This means I had to call init manually, but since script elements load asynchronously, I had to wait until init actually existed in the global scope to call it. This was fairly easily accomplished by attaching my code to the new script element’s “load” event (and using some proprietary “readyState” junk for IE).

Too early

If the page takes a long time to load, it’s possible the user will fire the bookmarklet before window.onload. One of two things will occur:

If it’s fired before the DOM is even “ready”, the bookmarklet throws an error when it tries to append the script element. I could use one of the standard “DOMready” routines to run the bookmarklet code a little later, but this case is rare enough to be not worth the effort to support; by the time the user can see there are mp3s on the page, the DOM is usually ready.

Assuming the DOM is ready, playtagger.js gets loaded via a new script element, the bookmarklet fires init, but then, thanks to playtagger’s built-in event attachment, init is called a second time on window.onload, producing a second “play” button per mp3 link. Harmless, but not good enough.

Preventing the 2nd init call

It would be nice if you could sniff whether or not window.onload has fired, but this doesn’t seem to be possible. Maybe via IE junk. Any ideas for a standards based way to tell?

My only hope seemed to be to somehow disable init after manually calling it. The first try was to just redefine init to a null function after calling it:

init();
init = function () {};

I figured out that redefining init would not help here due to the way it’s attached to window.onload:

// simplified
var addLoadEvent = function(f) {
    var old = window.onload;
    window.onload = function() {
        if (old) { old(); }
        f();
    };
};
addLoadEvent(init);

What’s important to notice here is that init is passed to addLoadEvent as f and window.onload is redefined as a new function, capturing f in the closure. So now f holds init‘s original code (because functions are first-class in Javascript), and f, not the global init, is what is really executed at window.onload. As f is private (hidden by the closure), I can’t overwrite it.

Disabling init from the inside by “breaking” Javascript

The second thing I tried was to break init‘s code from the inside. The first thing init does is loop over the NodeList returned by document.getElementsByTagName('a'), so if I could get that function to return an empty array, that would kill init‘s functionality. Because Javascript is brilliantly flexible I can do just that:

// cache for safe keeping
document.gebtn_ = document.getElementsByTagName;
// "break" the native function
document.getElementsByTagName = function(tag) {
    if (tag != 'a') return document.gebtn_(a);

    // called with 'a' (probably from init)
    // "repair" this function for future use
    document.getElementsByTagName = document.gebtn_;
    // return init-busting empty array
    return [];
};

Simplest solution

While the code above works pretty well, I thought of a simpler, more elegant solution: just rewrite window.onload to what it was before playtagger.js was loaded.

And with that here is the final unpacked bookmarklet code:

javascript:(function () {
    if (window.Delicious && (Delicious.Mp3 || window.Mp3))
        return;
    var d = document
        ,s = d.createElement('script')
        ,wo = window.onload
        ,go = function () {
            Delicious.Mp3.go();
            window.onload = wo || null;
        }
    ;
    s.src = 'http://images.del.icio.us/static/js/playtagger.js';
    if (null === s.onreadystatechange) 
        s.onreadystatechange = function () {
            if (s.readyState == 'complete')
                go();
        };
    else 
        s.onload = go;
    d.body.appendChild(s);
})();

1-minute RAM buyer’s guide

There was a time when it seemed you could just order the cheapest RAM you could find on Pricewatch and be done with it, but it just doesn’t work like that, especially when you’re trying to upgrade a PC more than a year old.

You can easily spend hours reading about RAM density, ECC, buffering, registration, DDR(2), clock timing, et.al. and more hours doing awkward eBay and Google searches to find the right features just to end up with RAM that still won’t work with your system. Please save yourself the hassle.

  • Look up your PC/motherboard at Crucial, Kingston and 4AllMemory, and note the model numbers recommended.
  • Also check for these models at other reputable stores. NewEgg is known for excellent service, but also search Shopping.com and Froogle. Crucial is usually on the high side, but a safe choice.

BTW, pray you don’t end up needing a particular flavor of Rambus RAM.

Rhythm guitar geniuses

Ivy frequently has stunning rhythm guitar parts–a little bit of extra texture, a little melody. Two examples: “Blame In On Yourself” from Long Distance, and Apartment Life’s “Quick, Painless, and Easy” The latter I’ve been casually wondering how to play for some time so I finally sat down and figure it out.

Ivy “Quick, Painless, and Easy” (rhythm guitar)

First, raise the B string to C so C and E can ring open together (it’s possible with standard tuning but the E+ is a pain to get your fingers in and out of). Everywhere but the chorus the A string quiet drones. The named chord is the effective harmony with the electric bass in the recording (how you might play it on piano, etc.)

Tuning = E-A-D-G-C-E (raise B to C)

Intro/Verse
  Amaj7      x-0-6-6-4-0
  Cmaj7/G    x-0-5-4-0-0
  Fmaj7      x-0-3-2-0-0
  E+         x-0-2-1-0-0
  Am7        x-0-7-5-7-0
  Cmaj7/G    x-0-5-4-0-0
  Fmaj7      x-0-3-2-0-0
  Cmaj7/G    x-0-5-4-0-0

Pre-chorus (twice)
  Amaj7      x-0-6-6-4-0
  Cmaj7/G    x-0-5-4-0-0
  D6add9/F#  x-0-4-4-2-0
  Fmaj7      x-0-3-2-0-0

Chorus
  C          3-3-2-0-0-0
  D          x-0-0-2-2-2
  C          3-3-2-0-0-0
  D          x-0-0-2-2-2
  C          3-3-2-0-0-0
  D          x-0-0-2-2-2
  Fmaj7      1-3-3-2-0-0
  G6add4     3-5-5-4-0-0

Opera folks: Make it easy to test in your browser

Opera wants to know what features web developers would most like to see. I posted this in response (but it never seemed to show up on the page):

“Testing” version: offer devs a download pre-configured for more easily testing site compatibility with Opera. Marketshare will only rise if more sites are tested in Opera, so this has to be dead-easy for devs used to testing in IE/FF.

Strict-mode JS Notices: tell devs when they’ve attempted to use a common interface that IE/FF supports but Opera doesn’t. Link to a knowledgebase article, which leads me to…

Documentation: Be public and upfront about lack of support for popular proprietary features that IE/FF support and that web devs are itching to use, and list practical alternatives and workarounds in knowledgebase articles. In fact, publish usable compatibility scripts if possible, not just secret browser.js fixes.

In the 7.5 days this would’ve been XMLHTTPRequest. Yes, Opera devs were working on it, but web devs were in the cold for knowledge of the current level of support (eg. you couldn’t POST until 7.6 so many sites half worked). If support is in the works, let us know how it’s coming along! Not just in an odd hint drop on the forums.

This also goes for Opera-specific tech; I shouldn’t have to figure out myself that UserJS doesn’t fire in panels (why not?) or search forums for the most basic widget methods. Devs need thorough docs. Take the PHP docs as a model.

JS Panel: I sorely miss not being able to watch JS errors in real-time in a panel, as I did with v8. In fact, it should be a top-notch panel developed by Opera that ships with the browser.

DOM Inspector: devs love them, plenty of bookmarklet versions exist out there. Grab the best, improve the hell out of it and ship it in a panel or publish it as an official Opera bookmarklet. Grab one of your widget developers for two days and you’ll have this.

Detect a blocked pop-up window

Since reliable info doesn’t seem to be out there, this Javascript detects if a pop-up window was blocked at least by IE6/XPsp2’s built-in blocker, FF or Opera 8.

var popWin; // global for easy reference later
function pop(url, name, features, replace) {
    popWin = open(url, name, features, replace);
    if (popWin == null) { // nice blocking browsers return null
        alert('window blocked, redirect or whatever');
    } else {
        setTimeout(function() {
            if (typeof popWin.parent == 'undefined') { // opera
                alert('window blocked, redirect or whatever');
            }
        }, 200);
    }
}

How about Safari?

Build what you see

I’m really digging this agile software development idea, which boils down to:

  1. Create the user interface.
  2. Build the code to power it.

Without a UI, you just have a bunch of developers and stakeholders, each having a vague notion of what the app will do and look like, and who it will serve. This leads to:

  • frustration for all parties when the finished product doesn’t match the stakeholders’ vision
  • wasted time on features that no-one will use
  • missed opportunities for features that would’ve been obvious with a UI in hand