//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
/// Verified in: IE6, IE7, IE8, FF, Chrome, Safari, Opera
/////////////////////
/*
	Events
*/

///////////////////////////////////////
/**************************************
Event CLASS
**************************************/
///////////////////////////////////////
EVENTS = {};
EVENTS.Event = function()
{
	this.mCount = 0;
}
EVENTS.Event.prototype.dispose = function()
{
    /// deleting handlers ///
    if (this.handlers !== null)
    {
        var l = this.handlers.length;
        for (var i = 0; i < l; ++i)
        {
            if (this.handlers[i] !== null) this.handlers[i].dispose();
        }
        delete this.handlers;
    }
    this.handlers = null;
    
    this.mIsDisposed = null;
}

///////// VARIABLES
EVENTS.Event.prototype.handlers = null;
EVENTS.Event.prototype.mCount = 0;
EVENTS.Event.prototype.mRemovedHandler = false;
EVENTS.Event.prototype.mIsDisposed = false;

///////// METHODS
EVENTS.Event.prototype.dispatch = function ()
{
    if (this.mIsDisposed) return;
    if (this.handlers === null) return;

    //// applying all handlers ////
    var l = this.handlers.length;
    for (var i = 0; i < l; ++i)
    {
        // there's a chance that this object (event) is disposed
        // by one of the handlers
        if (this.mIsDisposed) return;

        // invoking handler
        var handler = this.handlers[i];
        try
        {
            if (handler !== null) handler.handle.apply(handler, arguments);
        }
        catch (e)
        {
            alert(e);
        }
    }

    // there's a chance that this object (event) is disposed
    // by one of the handlers
    if (this.mIsDisposed) return;

    //// cleaning 'disposed' handlers ////
    this.cleanDisposedHandlers();
}

EVENTS.Event.prototype.cleanDisposedHandlers = function ()
{
    if (!this.mRemovedHandler) return;
    var l = this.handlers.length;
    for (var i = 0; i < l; ++i)
    {
        if (this.handlers[i] === null)
        {
            this.handlers.splice(i, 1);
            --i;
            --l;
        }
    }
    this.mRemovedHandler = false; // setting flag
}

EVENTS.Event.prototype.addHandler = function (func, context)
{
    if (this.mIsDisposed) return;
    if (this.handlers === null) this.handlers = new Array();

    /// verifying the handler doesn't exist already
    var l = this.handlers.length;
    for (i = 0; i < l; ++i)
    {
        var handle = this.handlers[i];
        if (handle !== null && handle.func === func && handle.context === context) return;
    }

    // adding the handler
    var handler = new EVENTS.Handler(func, context);
    this.handlers.push(handler);
    ++this.mCount;
    func = null;
    context = null;

    // cleaning disposed handlers
    this.cleanDisposedHandlers();

    return handler;
}

EVENTS.Event.prototype.addHandler_obj = function (handler)
{
	if (this.mIsDisposed) return;
    if (this.handlers === null) this.handlers = new Array();
    
    var l = this.handlers.length;
    for (i = 0; i < l; ++i)
    {
        var handle = this.handlers[i];
        if (handle !== null && handle.func === handler.func && handle.context === handler.context) return;
    }
    
    this.handlers.push(handler);
    ++this.mCount;
    func = null;
    context = null;

    // cleaning disposed handlers
    this.cleanDisposedHandlers();
}

EVENTS.Event.prototype.removeHandlerObject = function (handler)
{
    if (this.handlers === null) return;
    var l = this.handlers.length;
    var handleObject = null;
    var i;
    for (i = 0; i < l; ++i)
    {
        if (this.handlers[i] === handler)
        {
            --this.mCount;
            this.handlers[i] = null;
            break;
        }
    }
    this.mRemovedHandler = true; // raising flag
    handler.dispose();
} 

EVENTS.Event.prototype.removeHandler = function(func, context)
{
	if (this.handlers === null) return;
	
	context = context || null;
	var l = this.handlers.length;
	var handleObject = null;
	var i;
	for (i=0;(i<l && handleObject === null);++i)
	{
		if (this.handlers[i] !== null && this.handlers[i].func === func && this.handlers[i].context === context) 
		{
			handleObject = this.handlers[i];
			this.handlers[i] = null;
			--this.mCount;
			break;
		}
	}
    this.mRemovedHandler = true;  // raising flag
	if (handleObject !== null) handleObject.dispose(); 
	
	func = null;
	context = null;
}
EVENTS.Event.prototype.count = function()
{
    return this.mCount;
}

///////////////////////////////////////
/**************************************
Handler object
**************************************/
///////////////////////////////////////
EVENTS.Handler = function(func, context)
{
	func = func || null;
	context = context || null;
	
	if (typeof(func) !== 'function')
		throw('Error creating event handler: no function is passed');
	
	this.func = func;
	this.context = context;
	
	return this;
}
EVENTS.Handler.prototype.dispose = function()
{
	delete this.func;
	delete this.context;
	this.func = null;
	this.context = null;
}
EVENTS.Handler.prototype.handle = function()
{
	return this.func.apply(this.context, arguments);
}
EVENTS.Handler.prototype.func = null;
EVENTS.Handler.prototype.context = null;
