Event.Publisher = Class.create();
Object.extend( Event.Publisher, 
{
    _ls_event_targets: null,
    
    _event_source_id: null,
    
    _fl_trace_events: false,
    
    getEventSourceId: function()
    {
        if ( typeof this._event_source_id == 'function' )
            return this._event_source_id();
        else
            return this._event_source_id;
    },
    
    getEventTarget: function( event_name )
    {
        if ( ! this._ls_event_targets )
            this._ls_event_targets = new Array();
        
        if ( ! this._ls_event_targets[ event_name ] )
            document.body.appendChild(
                this._ls_event_targets[ event_name ] = document.createElement( 'A' )
            );
        
        return this._ls_event_targets[ event_name ];
    },
    
    addEventListener: function( event_name, callback_func, capturing )
    {
        var targ = this.getEventTarget( event_name );
        
        Event.observe( targ, 'click', callback_func, capturing );
        
        if ( this._fl_trace_events )
        {
            var data = 
            {
                publisher: this.getEventSourceId(),
                event_name: event_name,
                listener: callback_func,
                capturing: capturing,
                event_source_proxy: targ
            };
            
            this.dispatchEvent( 'eventListenerAdded', data, true, true );
        }
    },
    removeEventListener: function( event_name, callback_func, capturing )
     { 
     var targ = this.getEventTarget( event_name ); 
     Event.stopObserving( targ, 'click', callback_func, capturing ); 
     if ( this._fl_trace_events ) { 
		var data = { 
					publisher: this.getEventSourceId(), 
					event_name: event_name, 
					listener: callback_func, 
					capturing: capturing, 
					event_source_proxy: targ 
				}; 
		this.dispatchEvent( 'eventListenerRemoved', data, true, true ); 
		} 
	}, 
	dispatchEvent: function( event_name, data, can_bubble, cancelable ) { 
		var targ = this.getEventTarget( event_name ); 
		var event_data = { event_name: event_name, event_target: this, data: data ? data : null }; 
		if ( ! can_bubble ) can_bubble = false; 
		if ( ! cancelable ) cancelable = false; 
		var event = Event.create( targ, event_data, can_bubble, cancelable, true ); 
		if ( this._fl_trace_events ) { 
			if ( event_name.match( /event(?:ListenerAdded|ListenerRemoved|Dispatched|Received)/ ) ) return; 
			var data = { 
						publisher: this.getEventSourceId(), 
						event_name: event_name, 
						event_data: event_data, 
						can_bubble: can_bubble, 
						cancelable: cancelable, 
						event_source_proxy: targ, 
						result: event 
						}; 
			this.dispatchEvent( 'eventDispatched', data, true, true ); 
		} 
	}, 
	toggleEventsTrace: function() { 
		var trace = Event.Tracer.findTracer(); 
		if ( ! trace || ! this._fl_trace_events ) { 
			this._fl_trace_events = true; 
			trace = Event.Tracer.startTrace(); 
			trace.registerPublisher( this ); 
		} else { 
			this._fl_trace_events = false; 
			if ( trace ) trace.unregisterPublisher( this ); 
		} 
		return this._fl_trace_events; 
	}, 
	isEventsTraceActive: function() { return this._fl_trace_events; } 
} );