Source: helper/events.js

/**
 * Events abstraction for browsers that may not support it, but which we want to be suported. 
 */
(function(global, scope){

    /**
     * Pseudo event handler to be fired after the DOM was parsed or
     * on window load at last.
     *
     * @author based upon some code by Dean Edwards
     * @author Dean Edwards
     * @link   http://dean.edwards.name/weblog/2006/06/again/
     * @private
     */
    global.fireoninit = function () {
        // quit if this function has already been called
        if (arguments['callee'].done) return;
        // flag this function so we don't do the same thing twice
        arguments['callee'].done = true;
        // kill the timer
        _timer && clearInterval(_timer);
        _timer = null;

        if (typeof global.oninit == 'function') {
            global.oninit();
        }
    };

    /***
     * Run the fireoninit function as soon as possible after
     * the DOM was loaded, using different methods for different
     * Browsers
     *
     * @author Dean Edwards
     * @link   http://dean.edwards.name/weblog/2006/06/again/
     */
    // for Mozilla
    if (document.addEventListener) {
        document.addEventListener("DOMContentLoaded", global.fireoninit, null);
    }

    // for Internet Explorer (using conditional comments)
    /*@cc_on
    @if (@_win32)
    document.write("<scr" + "ipt id=\"__ie_init\" defer=\"true\" src=\"//:\"><\/script>");
    var script = document.getElementById("__ie_init");
    script.onreadystatechange = function() {
        if (this.readyState == "complete") {
            global.fireoninit(); // call the onload handler
        }
    };
    @end @*/

    // for Safari
    if (/WebKit/i.test(navigator.userAgent)) { // sniff
        var _timer = setInterval(function () {
            if (/loaded|complete/.test(document.readyState)) {
                global.fireoninit(); // call the onload handler
            }
        }, 10);
    }

    // for other browsers
    global.onload = global.fireoninit;

    /**
     * This is a pseudo Event that will be fired by the fireoninit
     * function above.
     *
     * Use addInitEvent to bind to this event!
     *
     * @author Andreas Gohr <andi@splitbrain.org>
     * @see fireoninit()
     * @private
     */
    global.oninit = function () {};

    scope.Events = {

        /**
         * Add an event to a given object
         * 
         * @param {Element} obj the Element object to add the event to
         * @param {string} type the event name to add a handler for
         * @param {function} fn the function to run when the event triggers
         * @param {object} opt  additional options to send along the event
         */
        addEvent: function( obj, type, fn, opt, eventOpts ) {
            obj['e'+type+fn] = fn;
            obj[type+fn] = function(){ obj['e'+type+fn]( global.event, opt ); }
            if ( obj.attachEvent ) {
                obj.attachEvent( 'on'+type, obj[type+fn] );
            } else {
                obj.addEventListener( type, opt?function(e){fn(e,opt)}:fn, eventOpts || false );
            }
        },

        /**
         * Remove an event from a given object
         * 
         * @param {Element} obj the Element to object to remove an event from
         * @param {string} type the event name to remove from the handler
         * @param {function} fn the function that needs to be removed
         */
        removeEvent: function( obj, type, fn, eventOpts ) {
            if ( obj.detachEvent ) {
                obj.detachEvent( 'on'+type, obj[type+fn] );
            } else {
              obj.removeEventListener( type, fn, eventOpts || false );
            }
            obj[type+fn] = null;
            obj['e'+type+fn] = null;
        },

        /**
         * Clear all events of a type from an element
         * @param {object} element the element to clear an event type from
         * @param {string} type    the event type
         */
        clearEvents: function (obj, type) {
			Object.keys(obj).forEach(function(_type){
				_type.indexOf(type) == 0 && obj.removeEvent(type, obj[_type]);
			});
        },

        /**
         * Bind a function to the global.init pseudo event
         *
         * @author Simon Willison
         * @see http://simon.incutio.com/archive/2004/05/26/addLoadEvent
         * @param {function} the function to add to the init handler
         */
        addInitEvent: function( fn ) {
            var oldoninit = global.oninit;
            if (typeof global.oninit != 'function') {
                global.oninit = fn;
            } else {
                global.oninit = function () {
                    oldoninit();
                    fn();
                };
            }
        },

        /**
         * Fire an event on a given object
         * 
         * @param {Element} el the element to fire an event on
         * @param {string} eventName the event name to fire
         * @param {object} options options to send along
         */
        fireEvent: function(el, eventName, options) {
            var event;
            if (window.CustomEvent) {
                try {
                    // Set cancelable and bubble options, otherwise e.g FF will not stop the event
                    options = options || {};
                    options.cancelable = true;
                    options.bubbles = true;
                    event = new CustomEvent( eventName, options);
                } catch ( e ) {
                    // IE is really a bad person!
                }
            }
            
            if ( !event ) {
                event = document.createEvent('CustomEvent');
                event.initCustomEvent( eventName, true, true, options);
            }

            return !el.dispatchEvent(event);
        },

        /**
         * Prevent further interaction with the given event
         * 
         * @param {Event} e the event to stop
         */
        stopEvent: function( event, returnValue ) {
            event = event || window.event;

            if (event) {
                event.cancelBubble = true;
                event.returnValue = !!returnValue;
                if (event.stopPropagation) {
                    event.stopPropagation();
                }
                if (event.preventDefault) {
                    event.preventDefault();
                }
            }
            return !!returnValue;
        }
    };

    var create = function(_){

        var handleEvent = function( event, argument ) {
            var args = [].slice.call(argument);
            args.splice( 0, 0, this );
            event.apply( null, args );
            return this;
        };

        _.addEvent || ( _.addEvent = function() {
            return handleEvent.call( this, scope.Events.addEvent, arguments );
        });

        _.removeEvent || ( _.removeEvent = function() {
            return handleEvent.call( this, scope.Events.removeEvent, arguments );
        });

        _.clearEvents || ( _.clearEvents = function() {
            return handleEvent.call( this, scope.Events.clearEvents, arguments );
        });

        _.fireEvent || ( _.fireEvent = function() {
            return handleEvent.call( this, scope.Events.fireEvent, arguments );
        });

        _.addInputListener || ( _.addInputListener = function( listener ) {
            return this.addEvent( 'input', listener)
            .addEvent( 'change', listener)
            .addEvent( 'cut', listener)
            .addEvent( 'paste', listener)
            .addEvent( 'drop', listener)
            .addEvent( 'keydown', listener)
            .addEvent( 'focus', listener);
        });
    };

	create(Element.prototype);
	create(global);

})(window, window.htmlviewer || window.$ || (window.$ = {}));