/**	
	@author			nathaniel skulic <nate@skulic.name>
	@copyright 		Copyright 2006, Nathaniel Skulic. All Rights Reserved.
	@fileOverview	
*/

// Special thanks to Dean Edwards/Matthias Miller/John Resig
if(!GUI) var GUI = {};

if(!GUI.Event) GUI.Event = {};


// TODO: use require
if(!oil) oil = {};

/**
	@namespace event handling 
*/
oil.event = {};
	
oil.event.mode = oil.event.MODE_W3C = true;

oil.event.MODE_W3C = 1;
/* A general function that associates an object instance with an event
   handler. The returned inner function is used as the event handler.
   The object instance is passed as the - obj - parameter and the name
   of the method that is to be called on that object is passed as the -
   methodName - (string) parameter.
	
	@depreciated
*/

GUI.Event.attach_method_to_object = function(obj, methodName, vararray){
    /* The returned inner function is intended to act as an event
       handler for a DOM element:-
    */
    return (function(e){
        /* The event object that will have been parsed as the - e -
           parameter on DOM standard browsers is normalised to the IE
           event object if it has not been passed as an argument to the
           event handling inner function:-
        */
        e = e||window.event;
        
        /* The event handler calls a method of the object - obj - with
           the name held in the string - methodName - passing the now
           normalised event object and a reference to the element to
           which the event handler has been assigned using the - this -
           (which works because the inner function is executed as a
           method of that element because it has been assigned as an
           event handler):-
        */
        return obj[methodName](e, this, vararray);
    });
};

Function.prototype.bindAsEventListener = function(object) {
  var __method = this;
  return function(event) {
    __method.call(object, event || window.event);
  }
}

/* Function.prototype.handler = function (object, argarray) {
	var method = this;
	return function (event) {
		method.call(object, GUI.Event.get_event(event), argarray);
	};
} */

oil.event.handler = function(object, handler, args){
	return (function(e){
		object[handler].call(object, oil.event.event(e), oil.event.target(e), args);
	});
}
$handler = oil.event.handler;

oil.event.bind = function(object, handler, args){
	args = args || [];
	return (function(e){
		object[handler].apply(object, args);
	});
}
$bind = oil.event.bind;

$listen  = function(event_type, handler){
	return $listen(event_type, this, handler);
}
// missing in IE Event model
if(typeof Event=='undefined'){
	var Event={
		NONE:0,
		CAPTURING_PHASE:1,
		AT_TARGET:2,
		BUBBLING_PHASE:3,
		STOP:4
	};
}


// find item value in array, return index or false
function inArray(a,v){
	var i=0,found=false;
	for(;a.length>i;i++){
		if(a[i]===v){
			found=i;
			break;
		}
	}
	return found;
}

// TODO: modify to use dean edwards
// Internet Explorer
if ( document.attachEvent ) {
    oil.event.add = GUI.Event.add_event_listener = function(object_node, event_type, handler, capture) {
        if(oil.event.MODE_W3C == true){
            // check if capturing is to be enabled
            if(capture && /mouse|click/.test(event_type) && object_node.nodeType==1) object_node.setCapture();

            return object_node.attachEvent('on'+event_type, handler);
        }
        // use classic registration model
        else {
            object_node['$handler_'+event_type] = handler;
            
            // check if capturing is to be enabled
            if(capture && object_node.nodeType == 1) 
                object_node.captureEvents(Event[event_type.toUpperCase()]);
            
            object_node['on'+event_type] = function(event){ 
                object_node['$handler_'+event_type].call(object_node, event); 
            };
            
            return true;
		}
		return false;
    };
    oil.event.remove = GUI.Event.remove_event_listener = function (object_node, event_type, handler, capture) {
        if(oil.event.MODE_W3C == true){
            // check if capturing was enabled
            if(capture && /mouse|click/.test(t) && object_node.nodeType==1) object_node.setCapture();
            
            return event_type.detachEvent('on'+event_type, handler);
        }
        // use classic registration model
        else {
            // check if capturing was enabled
            if(capture && object_node.nodeType == 1) object_node.releaseEvents(Event[event_type.toUpperCase()]);
            
            object_node['on'+event_type] = null;
            
            return true;
        }
		return false;
    };
    oil.event.target = GUI.Event.get_target = function(event){
        event.currentTarget = this;
        event.target        = event.target||event.srcElement;
        event.eventPhase    = (event.target == event.currentTarget) ? Event.AT_TARGET : Event.BUBBLING_PHASE;
        
        if(event.target && (/3|4/).test(event.target.nodeType)) event.target = event.target.parentNode;
        
        var target = (window.event) ? window.event.srcElement : event.srcElement;
        // when a event is attached inline, the window event might have the event object 
        return target;
    };
    oil.event.event = GUI.Event.get_event = function(event){
        if(window.event) return window.event
        else return event;
    };
    oil.event.stop_default = GUI.Event.prevent_default = function(event){
      //if(window.event) window.event.returnValue = false;
      //else 
          event.returnValue = false;
		  return event;
    };
    oil.event.stop_propagation = GUI.Event.stop_propagation = function(event){
      //if(window.event) window.event.cancelBubble = true;
      //else 
          event.cancelBubble = true;
		  return event;
    };
    oil.event.related_target = GUI.Event.related_target = function(event){
        if(!event.relatedTarget){
			if(event.type=='mouseout'){event.relatedTarget=event.toElement;}
			if(event.type=='mouseover'){event.relatedTarget=event.fromElement;}
		}
    }
}
// W3C DOM
else if(document.addEventListener){
    oil.event.add = function(object_node, event_type, handler, capture) {
        if(oil.event.MODE_W3C == true){
			if(!object_node){
				_warn('Trying to attach event handler to empty node');
				_debug(object_node);
				return null;
			}
            return object_node.addEventListener(event_type, handler, capture || false);
        // use classic registration model
        }
		else {
            object_node['$handler_'+event_type] = handler;
            
            // check if capturing is to be enabled
            if(capture && object_node.nodeType == 1) 
                object_node.captureEvents(Event[event_type.toUpperCase()]);
            
            object_node['on'+event_type] = function(event){ 
                object_node['$handler_'+event_type].call(object_node, event); 
            };
            
            return true;
        }
             
    }
    oil.event.remove = function (object_node, event_type, handler, capture) {
        if(oil.event.MODE_W3C == true)
            return object_node.removeEventListener( event_type, handler, capture || false);
        else {
             // check if capturing was enabled
            if(capture && object_node.nodeType == 1) 
                object_node.releaseEvents(Event[event_type.toUpperCase()]);
            
            object_node['on'+event_type] = null;
            
            return true;
        }
    };
    oil.event.target = function(event){
        var target = event.target;
        return target;
    };
    oil.event.event = function(event){
        return event;
    };
    oil.event.stop_default = function(event){
      if(event) event.preventDefault();
    }
    oil.event.stop_propagation = function(event){
      if(event) event.stopPropagation();
    }
	/* TODO: thanks mootools */
	oil.event.related_target = function(event){
		var type = event.type;
		var target = event.target || event.srcElement;
		if (type.match(/over|out/)){
			switch (type){
				case 'mouseover': related = event.relatedTarget || event.fromElement; break;
				case 'mouseout': related = event.relatedTarget || event.toElement;
			}
			// 
			/* try {Event.fix.relatedTarget.call(this);} catch(e){		
+			if (this.target.getTag().match(/^(input|textarea)$/)) this.relatedTarget = this.target;
+		}
 */
			if(false /* gecko18 firefox3<  */) while (related && related.nodeType == 3) related = related.parentNode;
		}
	}
}

oil.event.keycodes = {
       'enter': 13,
       'up': 38,
       'down': 40,
       'left': 37,
       'right': 39,
       'esc': 27,
       'space': 32,
       'backspace': 8,
       'tab': 9,
       'delete': 46
};

/* Shorthand functions */
$event = $listen = function(event_type, object, f){
	return oil.event.add(object, event_type, f);
}

$event = function(object, event_type, f, capture){
	return oil.event.add(object, event_type, f, capture || false);
}

$unevent = function(object, event_type, f, capture){
	return oil.event.remove(object, event_type, f, capture || false);
}

$target = function(event){
	return oil.event.target(event);
}
$stop = oil.event.stop = function(e){
	//GUI.Event.prevent_default(e);
	oil.event.stop_propagation(e);
	return false;
}

if(GUI.Event.add_event_listener && GUI.Event.remove_event_listener){
    GUI.Event.add_event_handler = GUI.Event.add_event_listener;
    GUI.Event.remove_event_handler = GUI.Event.remove_event_listener;
    
    GUI.Event.chain_event_handler = function(object_node, event_type, handler, capture){
        // initialize the event chain object and handlers array
        object_node.$event_chain = object_node.$event_chain || {};
        object_node.$event_chain[event_type] = object_node.$event_chain[event_type] || {handlers:[], capture:capture};
        
        var event_chain = object_node.$event_chain[event_type];
        
        // TODO: optimize to check for function names or unique id
        if(inArray(event_chain.handlers, handler)===false){
            
            // add the handler to the chain
            event_chain.handlers.push(handler);
            
            // if there is only one element in the chain, set up the chained handlers
            if(event_chain.handlers.length === 1){
                // root handlers
                object_node.$handlers = object_node.$handlers || {};
                object_node.$handlers[event_type] = function(event){
                    GUI.Event.run_event_chain.call(object_node, event);
                };
                return GUI.Event.add_event_listener(object_node, event_type, object_node.$handlers[event_type], capture); 
            }
            
            return true;
        }
        return false;
    };
    GUI.Event.run_event_chain = function(event){
        event_type = event.type;
        var event_chain = this.$event_chain[event_type];
        // process chain in lifo order
        if(event_chain.capture === true){
            for(i = event_chain.handlers.length -1; i >= 0; i--){
                if(typeof event_chain.handlers[i]=='function'){
                    
                    var handler_value = event_chain.handlers[i].call(this, event);
                    
                    if(handler_value===false) break;
                }
            }
        }
        // process chain in FIFO order
        else {
            for(i = 0; event_chain.handlers.length > i; i++){
                if(typeof event_chain.handlers[i]=='function'){
                    
                    var handler_value = event_chain.handlers[i].call(this, event);
                    
                    if(handler_value===false) break;
                }
            }
        }
        
        // process chain in lifo order
        //for(i=f.items.length-1;i>=0;i--){
        //    if(typeof f.items[i]=='function'){
        //        r=f.items[i].call(this,e);
        //        if(r===false){break;}
        //    }
        //}
    }
    // NOTE: Handlers can only be removed when the handler parameter is a function name, anonymous functions will stay forever
    GUI.Event.unchain_event_handler = function(object_node, event_type, handler, capture){
        
		if(object_node.$event_chain && object_node.$event_chain[event_type]){
            
            var event_chain = object_node.$event_chain[event_type];
            
			var key = inArray(event_chain.handlers, handler);
			if(key !== false){
                
				event_chain.handlers.splice(key,1);
				
                if(event_chain.handlers.length === 0){
					var r = remove_event_listener(object_node, event_type, object_node.$handlers[event_type], capture);
					delete object_node.$event_chain[event_type];
					return r;
				}
                else{
					return true;
				}
			}
		}           
		return false;
	};
}

// written by Dean Edwards, 2005
// with input from Tino Zijdel - crisp@xs4all.nl
// http://dean.edwards.name/weblog/2005/10/add-event/
function addEvent(element, type, handler)
{
	if (element.addEventListener)
		element.addEventListener(type, handler, false);
	else
	{
		if (!handler.$$guid) handler.$$guid = addEvent.guid++;
		if (!element.events) element.events = {};
		var handlers = element.events[type];
		if (!handlers)
		{
			handlers = element.events[type] = {};
			if (element['on' + type]) handlers[0] = element['on' + type];
			element['on' + type] = handleEvent;
		}
	
		handlers[handler.$$guid] = handler;
	}
}
addEvent.guid = 1;

function removeEvent(element, type, handler)
{
	if (element.removeEventListener)
		element.removeEventListener(type, handler, false);
	else if (element.events && element.events[type] && handler.$$guid)
		delete element.events[type][handler.$$guid];
}

function handleEvent(event)
{
	event = event || fixEvent(window.event);
	var returnValue = true;
	var handlers = this.events[event.type];

	for (var i in handlers)
	{
		if (!Object.prototype[i])
		{
			this.$$handler = handlers[i];
			if (this.$$handler(event) === false) returnValue = false;
		}
	}

	if (this.$$handler) this.$$handler = null;

	return returnValue;
}

function fixEvent(event)
{
	event.preventDefault = fixEvent.preventDefault;
	event.stopPropagation = fixEvent.stopPropagation;
	return event;
}
fixEvent.preventDefault = function()
{
	this.returnValue = false;
}
fixEvent.stopPropagation = function()
{
	this.cancelBubble = true;
}

// This little snippet fixes the problem that the onload attribute on the body-element will overwrite
// previous attached events on the window object for the onload event
if (!window.addEventListener)
{
	document.onreadystatechange = function()
	{
		if (window.onload && window.onload != handleEvent)
		{
			addEvent(window, 'load', window.onload);
			window.onload = handleEvent;
		}
	}
}



// simulateEvent
//
// simulate a user action
//
function simulateEvent(eventType, targetElement) {
    var event;
    targetElement = $(targetElement);
    
    if (targetElement) {
     // check for IE
     if (window.ActiveXObject) {
         event = document.createEventObject();
         targetElement.fireEvent("on"+eventType,event);
     } else {
         switch (eventType) {
             case "abort":
             case "blur":
             case "change":
             case "error":
             case "focus":
             case "load":
             case "reset":
             case "resize":
             case "scroll":
             case "select":
             case "submit":
             case "unload":
                 event = document.createEvent("HTMLEvents");
                 event.initEvent(eventType, "true", "true");
                 break;
             case "click":
             case "mousedown":
             case "mousemove":
             case "mouseout":
             case "mouseover":
             case "mouseup":
                 event = document.createEvent("MouseEvents");
                 event.initMouseEvent(eventType, true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
                 break;
         }
         targetElement.dispatchEvent(event);
     }
    }
};

GUI.Event.content_loaded_handler = function(parameters){
    if (parameters.callee.done) return;
    
    parameters.callee.done = true;
    // kill the timer
    if (window._timer) clearInterval(_timer);
}


GUI.Event.install_content_loaded_handler = function(handler){
    // mozilla/opera9
    if (document.addEventListener)
        return document.addEventListener("DOMContentLoaded", handler, false);
    // safari
    if (/WebKit/i.test(navigator.userAgent)) { // sniff
        var _timer = setInterval(function() {
            if (/loaded|complete/.test(document.readyState)) {
                clearInterval(_timer);
                handler(); // call the onload handler
            }
        }, 10);
    }
    // ie
    if (window.attachEvent){
/*        var head = document.getElementsByTagName('HEAD')[0];
        var script = document.createElement('script');
            script.setAttribute('defer','defer');
            script.appendChild(document.createTextNode(handler()));
            
        
            head.appendChild(script);*/
        document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
        var script = document.getElementById("__ie_onload");
        script.onreadystatechange = function() {
            if (this.readyState == "complete") {
                handler(); // call the onload handler
            }
        };
    }
    window.onload = handler;
}





// written by Dean Edwards, 2005
// http://dean.edwards.name/

function addEvent(element, type, handler) {
    // assign each event handler a unique ID
    if (!handler.$$GUId) handler.$$GUId = addEvent.GUId++;
    // create a hash table of event types for the element
    if (!element.events) element.events = {};
    // create a hash table of event handlers for each element/event pair
    var handlers = element.events[type];
    if (!handlers) {
        handlers = element.events[type] = {};
        // store the existing event handler (if there is one)
        if (element["on" + type]) {
            handlers[0] = element["on" + type];
        }
    }
    // store the event handler in the hash table
    handlers[handler.$$GUId] = handler;
    // assign a global event handler to do all the work
    element["on" + type] = handleEvent;
};
// a counter used to create unique IDs
addEvent.GUId = 1;

function removeEvent(element, type, handler) {
    // delete the event handler from the hash table
    if (element.events && element.events[type]) {
        delete element.events[type][handler.$$GUId];
    }
};

function handleEvent(event) {
    var returnValue = true;
    // grab the event object (IE uses a global event object)
    event = event || fixEvent(window.event);
    // get a reference to the hash table of event handlers
    var handlers = this.events[event.type];
    // execute each event handler
    for (var i in handlers) {
        this.$$handleEvent = handlers[i];
        if (this.$$handleEvent(event) === false) {
            returnValue = false;
        }
    }
    return returnValue;
};

function fixEvent(event) {
    // add W3C standard event methods
    event.preventDefault = fixEvent.preventDefault;
    event.stopPropagation = fixEvent.stopPropagation;
    return event;
};
fixEvent.preventDefault = function() {
    this.returnValue = false;
};
fixEvent.stopPropagation = function() {
    this.cancelBubble = true;
};
