
/*************************************************************************
Name:	event.js
Desc:	handler for events (onclick, onpress)
        allows events to be removed more efficiently
		solves closures and cyclic dependancies
		frees up memory on older browsers
        
        global handler 'EventListener' at EOF

TODO:
*************************************************************************/

//constructor
function EventJS() {
    this.registry    = null;
    this.last_label  = 0;
    this.detectBrowser();
}

//init the listener (attached unload function to window to remove old events)
EventJS.prototype.init = function() {
    if (this.registry == null) {
        this.registry = new Array();
        this.add(window, 'unload', this.removeAll);	//unload all events if window unloads
    }
}

//shortcut to remove ON part of event name (ie: onclick -> click)
EventJS.prototype.removeEventPrefix = function(name) {
    if (name.substr(0, 2).toLowerCase() == 'on') {
        return name.substr(2, name.length-2).toLowerCase();
    } else {
        return name.toLowerCase();
    }
}

//sets flag for either MOZILLA or IE (each requires events to be attached differently)
EventJS.prototype.detectBrowser = function() {
    var obj = document.createElement('div');	//temp object
	
	//detect browser by props on object
    if (obj.addEventListener) {
        this.browser = 'BROSWER_MOZILLA';
    } else if (obj.attachEvent) {
        this.browser = 'BROSWER_IE';
    } else {
		this.browser = null;
	}
	
    this.obj = null;	//clear temp
}

/**
 * Add an event listener to an object
 *
 * @param object    obj         The object to attach to
 * @param string    eventType   The type of event to attach to
 * @param string    label       <i>(Opt)</i> Provide a virtual label so removing listeners is easier
 *
 * @return The virtual label used to register this event (so you may remove it later with ease), or false on failure
 */
 
 //NOTE: //using label will override existing event
EventJS.prototype.add = function(obj, eventType, func, label) {
    //init here when window available (not on construct)
    this.init();
    
    if (typeof obj == 'object' && typeof func == 'function') {
		//create label if none exists
        if (!label) {label = 'EVENT_' + (this.last_label++);}
		
		//remove if this label already exists (overriding)
        this.remove(label, true);
        
		//remove 'ON' prefix from event, and create one WITH 'ON'
        eventType = this.removeEventPrefix(eventType);
        var onEventType = 'on'+eventType;
        var success = true;
        var closure = null;
        
        //create event function depending on browser
        switch (this.browser) {
            case 'BROSWER_IE':
                closure = function() { func.call(obj); }
                success = obj.attachEvent(onEventType, closure);
                break;
            case 'BROSWER_MOZILLA':
                obj.addEventListener(eventType, func, false);
                break;
            default:
                if (obj[onEventType]) {
                    var oldListener = obj[onEventType];
                    closure = function() { func.call(obj); oldListener.call(obj); }
                }
                obj[onEventType] = closure;
                break;
        }
        
        //add event function to registry
        if (success) {
            if (typeof closure == 'function') {
                this.registry[label] = {obj: obj, eventType: eventType, func: closure};
            } else {
                this.registry[label] = {obj: obj, eventType: eventType, func: func};
            }
            return label;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

/**
 * Remove an event listener with the label it was added to the registry with
 *
 * @param string    label       The label the event was added to the registry with
 * @param bool      forceDel    <i>(Opt)</i> If the browser doesn't support listener functions, we tried
 *                              to solve it by explicitly setting the event listener. In this case, we
 *                              cannot  choose to remove a single listener, we have to remove all of them. Defaults to true.
 *
 * @return True on success, false on failure
 */
EventJS.prototype.remove = function(label, forceDel) {
    var done = false;
    
    if (this.registry[label]) {
        var reg = this.registry[label];
        var eventType = this.removeEventPrefix(reg.eventType);
        var onEventType = 'on'+eventType;
        
        //remove listeners
        switch (this.browser) {
            case 'BROSWER_IE':
                reg.obj.detachEvent(onEventType, reg.func);
                done = true;
                break;
            case 'BROSWER_MOZILLA':
                reg.obj.removeEventListener(eventType, reg.func, false);
                done = true;
                break;
            default:
                if (forceDel) {
                    reg.obj[onEventType] = null;
                    done = true;
                }
                break;
        }
        
        // Remove from reg
        if (done) {
            this.registry[label] = null;
        }
    }

    return done;
}

//unloads ALL registered events
EventJS.prototype.removeAll = function() {
    for (var label in EventListener.registry) {
        if (EventListener.registry[label] == null) {continue;}	//skip loop if event is null
        EventListener.remove.call(EventListener, label, true);	//remove event
    }
	EventListener.registry = null;	//clear the listener
}

//HANDLER - instance of class that we use as a global variable to add Event
var EventListener = new EventJS();