Decades of C,C++ trained my brain to imagine functions/methods as static entities which are fed objects to operate on, either through a stack frame or a heap-allocated object. C# is similar but abstracts away the object lifetime stuff a bit (and adds the huge .NET library of course)...cool, I'm a big fan of C# on Windows.

Now javascript... since I won't continue on clear-headed without making sure I understand why the code originally looked intuitively wrong (and then you mentioned the closure forcing me to rewrite it) I did some reading and playing around with code. I realized since I started my first javascript SPA using a framework that uses requireJS, I had the lifetime of objects down without ever looking at how it is *functions* in a dynamic environment that are making it feel like "class" and "namespace" type objects and giving me that slightly false sense of C++ security :)  It's the "functions are dynamic objects too" part that my c++-trained brain doesn't intuitively grasp yet.

Anyway, I actually spent a bit of time trying to make that little bit of canvas-detect code look and feel right. I knew I wanted a '$.supportsCanvas' boolean object, set once, that would be around for all the library to use. Solved that, but it still doesn't look right, and neither does the "*Detect event model...*" code below it, and I'm not happy with that.  Now I think the reason is it looks like it was written by a C programmer :) That isCanvasSupported() function is floating out there alone even though it's called only once. The entire surrounding function *was* neat little rows of "$.xxx = ..." blocks setting OpenSeadragon-wide globals, and my code looks out of place.

Getting to the point and feeling like I've learned some stuff , shouldn't I have written it like this...?
```javascript
    $.supportsCanvas = function () {
        var canvasElement = document.createElement( 'canvas' );
        return !!( $.isFunction( canvasElement.getContext ) &&
                   canvasElement.getContext( '2d' ) );
    }();

    /**
     * Detect event model and create appropriate _addEvent/_removeEvent methods
     */
    $._addEvent = function () {
        if ( window.addEventListener ) {
            return function ( element, eventName, handler, useCapture ) {
                element = $.getElement( element );
                element.addEventListener( eventName, handler, useCapture );
            };
        } else if ( window.attachEvent ) {
            return function ( element, eventName, handler, useCapture ) {
                element = $.getElement( element );
                element.attachEvent( 'on' + eventName, handler );
                if ( useCapture && element.setCapture ) {
                    element.setCapture();
                }
            };
        } else {
            throw new Error( "No known event model." );
        }
    }();

    $._removeEvent = function () {
        if ( window.removeEventListener ) {
            return function ( element, eventName, handler, useCapture ) {
                element = $.getElement( element );
                element.removeEventListener( eventName, handler, useCapture );
            };
        } else if ( window.detachEvent ) {
            return function( element, eventName, handler, useCapture ) {
                element = $.getElement( element );
                element.detachEvent( 'on' + eventName, handler );
                if ( useCapture && element.releaseCapture ) {
                    element.releaseCapture();
                }
            };
        } else {
            throw new Error( "No known event model." );
        }
    }();
```