/*
max by ProgramMax is licensed under a Creative Commons Attribution 3.0 Unported License:
http://creativecommons.org/licenses/by/3.0/

To learn more visit http://www.programmax.net/projects/max
*/

if( typeof max == "undefined" ) {
	var max = { };
}

if( typeof max.exceptions == "undefined" ) {
	max.exceptions = { };
}

max.exceptions.CouldNotAttachEvent = function( ) {
};

if( typeof max.events == "undefined" ) {
	max.events = function( ){

		var registeredEvents = [ ];
	
		var returning = { };

		returning.EventHandler = function( parameters ) {
			max.common.checkParameters({
				"parameters.object": {
							value: parameters.object,
							type: Object
				},
				"parameters.event": {
							value: parameters.event,
							type: String
				},
				"parameters.callback": {
							value: parameters.callback,
							type: Function
				}
			});

			function correctThisKeyword( parameters ) {
				max.common.checkParameters({
					"parameters.object": {
								value: parameters.object,
								type: Object
					},
					"parameters.callback": {
								value: parameters.callback,
								type: Function
					}
				});

				var correctedFunction = parameters.callback;

				// attachEvents does its callback with 'this' refering to window
				// instead of the object who's event fired.
				// This will correct the behavior.

				if( parameters.object.attachEvent ) {
					correctedFunction = function( ) {
						parameters.callback.apply( parameters.object, arguments );
					}
				}

				return correctedFunction;
			}

			this.object = parameters.object;
			this.event = parameters.event;
			this.callback = correctThisKeyword( parameters );
			this.prewrappedCallback = parameters.callback;
		};

		returning.Event = function( parameters ) {
			max.common.checkParameters({
				"parameters.object": {
							value: parameters.object,
							type: Object
				},
				"parameters.event": {
							value: parameters.event,
							type: String
				}
			});

			this.object = parameters.object;
			this.event = parameters.event;
		};

		if( window.addEventListener ) {
			returning.addEventHandler = function( parameters ) {
				max.common.checkParameters({
					"parameters.eventHandler": {
									value: parameters.eventHandler,
									type: returning.EventHandler
					}
				});

				parameters.eventHandler.object.addEventListener( parameters.eventHandler.event, parameters.eventHandler.callback, false );
				// the last param has to do with capturing & bubbling
				// use false (bubbling) since capturing really just causes headaches

				registeredEvents.push( parameters.eventHandler );
			};

			returning.removeEventHandler = function( parameters ) {
				max.common.checkParameters({
					"parameters.eventHandler":
						{ value: parameters.eventHandler, type: returning.EventHandler }
				});

				parameters.eventHandler.object.removeEventListener( parameters.eventHandler.event, parameters.eventHandler.callback, false );
				// the last param has to do with capturing & bubbling
				// use false (bubbling) since capturing really just causes headaches

				var Length = registeredEvents.length;
				for( var i = 0; i < Length; ++i ) {
					if( registeredEvents[ i ] == parameters.eventHandler ) {
						registeredEvents.splice( i, 1 );
						break;
					}
				}
			};

			returning.raiseEvent = function( parameters ) {
				max.common.checkParameters({
					"parameters.event": {
								value: parameters.event,
								type: returning.Event
					}
				});

				var Event = document.createEvent( "Event" );
				Event.initEvent( parameters.event.event, true, true );
				parameters.event.object.dispatchEvent( Event );
			};
		} else if( window.attachEvent ) {
			returning.addEventHandler = function( parameters ) {
				max.common.checkParameters({
					"parameters.eventHandler": {
									value: parameters.eventHandler,
									type: returning.EventHandler
					}
				});

				// Detach first to correct IE's behavior of calling the
				// same event handler twice when registered twice
				parameters.eventHandler.object.detachEvent( "on" + parameters.eventHandler.event, parameters.eventHandler.callback );
				var result = parameters.eventHandler.object.attachEvent( "on" + parameters.eventHandler.event, parameters.eventHandler.callback );
				if( result === false ) {
					throw new max.exceptions.CouldNotAttachEvent( );
				}

				registeredEvents.push( parameters.eventHandler );
			};

			returning.removeEventHandler = function( parameters ) {
				max.common.checkParameters({
					"parameters.eventHandler": {
									value: parameters.eventHandler,
									type: returning.EventHandler
					}
				});

				parameters.eventHandler.object.detachEvent( "on" + parameters.eventHandler.event, parameters.eventHandler.callback );

				var Length = registeredEvents.length;
				for( var i = 0; i < Length; i++ ) {
					if( registeredEvents[ i ] == parameters.eventHandler ) {
						registeredEvents.splice( i, 1 );
						break;
					}
				}
			};

			returning.raiseEvent = function( parameters ) {
				max.common.checkParameters({
					"parameters.event": {
								value: parameters.event,
								type: returning.Event
					}
				});

				var Event = document.createEventObject( );
				parameters.event.object.fireEvent( "on" + parameters.event.event, Event );
			};
		} else {
			// If neither attachEvent or addEventListener is supported use a custom solution

			returning.addEventHandler = function( parameters ) {
				max.common.checkParameters({
					"parameters.eventHandler": {
									value: parameters.eventHandler,
									type: returning.EventHandler
					}
				});

				var oldEvent = parameters.eventHandler.object[ "on" + parameters.eventHandler.event ];

				if( typeof oldEvent != "Function" ) {
					parameters.eventHandler.object[ "on" + parameters.eventHandler.event ] = parameters.eventHandler.callback;
				} else {
					parameters.eventHandler.object[ "on" + parameters.eventHandler.event ] = function( ) {
						oldEvent( arguments );
						parameters.eventHandler.callback( arguments );
					};
				}

				registeredEvents.push( parameters.eventHandler );
			};

			returning.removeEventHandler = function( parameters ) {
				max.common.checkParameters({
					"parameters.eventHandler": {
									value: parameters.eventHandler,
									type: returning.EventHandler
					}
				});

				parameters.eventHandler.object[ "on" + parameters.eventHandler.event ] = null;

				var Length = registeredEvents.length;
				for( var i = 0; i < Length; i++ ) {
					if( registeredEvents[ i ] == parameters.eventHandler ) {
						registeredEvents.splice( i, 1 );
						break;
					}
				}
			};

			returning.raiseEvent = function( parameters ) {
				max.common.checkParameters({
					"parameters.event": {
								value: parameters.event,
								type: returning.Event
					}
				});

				parameters.event.object[ "on" + parameters.event.event ]( );
			};
		}

		returning.stopEvent = function( parameters ) {
			max.common.checkParameters({
				"parameters": {
						value: parameters,
						type: Object
				}
			});

			if( parameters.stopPropagation ) {
				parameters.stopPropagation( );
			}
			parameters.cancelBubble = true;
		};

		returning.cancelEvent = function( parameters ) {
			max.common.checkParameters({
				"parameters": {
						value: parameters,
						type: Object
				}
			});

			if( parameters.preventDefault) {
				parameters.preventDefault();
			}
			parameters.returnValue = false;
		};

		returning.getEventTarget = function( parameters ) {
			max.common.checkParameters({
				"parameters": {
						value: parameters,
						type: Object
				}
			});

			var target;
			if( parameters.srcElement ) {
				target = parameters.srcElement;
			} else if( parameters.target ) {
				target = parameters.target;
			}
			if( target.nodeType == 3 ) { // some browsers set the target as the text node, not the element node
				target = target.parentNode;
			}

			return target;
		};

		returning.addEventHandler({
			eventHandler: new returning.EventHandler({
				object: window,
				event: "unload",
				callback: function( ) {
					var Length = registeredEvents.length;
					while( Length-- > 0 ) {
						returning.removeEventHandler({ eventHandler: registeredEvents[ 0 ] });
					}
				}
			})
		});

		return returning;
	}();
}
