﻿/* $Id: Events.js,v 1.2 2005/10/31 18:17:30 justin Exp $ */
/**
  * Crossbrowser event handling functions.
  *
  * A set of functions to easily attach and detach event handlers to HTML elements.
  * These functions work around the shortcomings of the traditional method ( element.onevent = function; )
  * where only 1 handler could be attached for a certain event on the object, and mimic the DOM level 2
  * event methods addEventListener and removeEventListener for browsers that do not support these
  * methods (e.g. Internet Explorer) without resorting to propriety methods such as attachEvent and detachEvent
  * that have a whole set of their own shortcomings.
  * Created as an entry for the 'contest' at quirksmode.org: http://www.quirksmode.org/blog/archives/2005/09/addevent_recodi.html
  *
  * @author Tino Zijdel ( crisp@xs4all.nl )
  * @version 1.2
  * @date 2005-10-21
  * @see http://therealcrisp.xs4all.nl/upload/addEvent2
  */


/**
  * addEvent
  *
  * Generic function to attach event listeners to HTML elements.
  * This function does NOT use attachEvent but creates an own stack of function references
  * in the DOM space of the element. This prevents closures and therefor possible memory leaks.
  * Also because of the way the function references are stored they will get executed in the
  * same order as they where attached - matching the behavior of addEventListener.
  *
  * @param obj The object to which the event should be attached.
  * @param evType The eventtype, eg. 'click', 'mousemove' etcetera.
  * @param fn The function to be executed when the event fires.
  * @param useCapture (optional) Whether to use event capturing, or event bubbling (default).
  */
function addEvent(obj, evType, fn, useCapture)
{
        //-- Default to event bubbling
        if (!useCapture) useCapture = false;

        //-- DOM level 2 method
        if (obj.addEventListener)
        {
                obj.addEventListener(evType, fn, useCapture);
        }
        else
        {
                //-- event capturing not supported
                if (useCapture)
                {
                        alert('This browser does not support event capturing!');
                }
                else
                {
                        var evTypeRef = '__' + evType;

                        //-- create function stack in the DOM space of the element; seperate stacks for each event type
                        if (obj[evTypeRef])
                        {
                                //-- check if handler is not already attached, don't attach the same function twice to match behavior of addEventListener
                                if (array_search(fn, obj[evTypeRef]) > -1) return;
                        }
                        else
                        {
                                //-- create the stack if it doesn't exist yet
                                obj[evTypeRef] = [];

                                //-- if there is an inline event defined store it in the stack
                                if (obj['on'+evType]) obj[evTypeRef][0] = obj['on'+evType];

                                //-- attach helper function using the DOM level 0 method
                                obj['on'+evType] = IEEventHandler;
                        }

                        //-- add reference to the function to the stack
                        obj[evTypeRef][obj[evTypeRef].length] = fn;
                }
        }
}

/**
  * removeEvent
  *
  * Generic function to remove previously attached event listeners.
  *
  * @param obj The object to which the event listener was attached.
  * @param evType The eventtype, eg. 'click', 'mousemove' etcetera.
  * @param fn The listener function.
  * @param useCapture (optional) Whether event capturing, or event bubbling (default) was used.
  */
function removeEvent(obj, evType, fn, useCapture)
{
        //-- Default to event bubbling
        if (!useCapture) useCapture = false;

        //-- DOM level 2 method
        if (obj.removeEventListener)
        {
                obj.removeEventListener(evType, fn, useCapture);
        }
        else
        {
                var evTypeRef = '__' + evType;

                //-- Check if there is a stack of function references for this event type on the object
                if (obj[evTypeRef])
                {
                        //-- check if function is present in the stack
                        var i = array_search(fn, obj[evTypeRef]);
                        if (i > -1)
                        {
                                try
                                {
                                        delete obj[evTypeRef][i];
                                }
                                catch(e)
                                {
                                        obj[evTypeRef][i] = null;
                                }
                        }
                }
        }
}

/**
  * IEEventHandler
  * 
  * IE helper function to execute the attached handlers for events.
  * Because of the way this helperfunction is attached to the object (using the DOM level 0 method)
  * the 'this' keyword will correctely point to the element that the handler was defined on.
  *
  * @param e (optional) Event object, defaults to window.event object when not passed as argument (IE).
  */
function IEEventHandler(e)
{
        e = e || window.event;
        var evTypeRef = '__' + e.type, retValue = true;

        //-- iterate through the stack and execute each function in the scope of the object by using function.call
        for (var i = 0, j = this[evTypeRef].length; i < j; i++)
        {
                if (this[evTypeRef][i])
                {
                        if (Function.call)
                        {
                                retValue = this[evTypeRef][i].call(this, e) && retValue;
                        }
                        else
                        {
                                //-- IE 5.0 doesn't support call or apply, so use this
                                this.__fn = this[evTypeRef][i];
                                retValue = this.__fn(e) && retValue;
                        }
                }
        }

        if (this.__fn) try { delete this.__fn; } catch(e) { this.__fn = null; }

        return retValue;
}

/**
  * array_search
  * 
  * Searches the array for a given value and returns the (highest) corresponding key if successful, -1 if not found.
  *
  * @param val The value to search for.
  * @param arr The array to search in.
  */
function array_search(val, arr)
{
        var i = arr.length;

        while (i--)
                if (arr[i] && arr[i] === val) break;

        return i;
}
/*
Array.prototype.positionOf = function(val) {
	var i = this.length;
	while(i--) {
		if(this[i] && this[i] === val) {
			break;
		}
	}
	return i;
}
*/
/**
 * Gets the target element from an <code>Event</code> object.
 *
 * @param e The event
 * @return The target element node
 */
function getTargetFromEvent(e) {
	var targ;
    if (!e) var e = window.event;
    if (e.target) targ = e.target;
    else if (e.srcElement) targ = e.srcElement;
    if (targ.nodeType == 3) { // defeat Safari bug
            targ = targ.parentNode;
    }
    return targ;
}