/*
 * Copyright (c) 2003 by Hyperwave AG. All rights reserved.
 * EventFramework.js,v 1.1 2003/12/15 19:56:47 sraubal Exp
 */

/**
 * This class implements the Observable pattern (though the terms "event source"
 * and "event listener" are used) by providing methods to add, remove and inform
 * listeners. The DOM specification names such an object an "event target" - but
 * we regard this term as misleading, so we replaced it with "event source".
 *
 * @author sraubal
 *
 * @see ojs.Event
 */
defineClass ( "ojs.EventSource",
              function ( class$ )
{
  /**
   * Constructs a basic event source object.
   */
  class$.constructor = function( theParams )
  {
    if ( theParams == "__proto__")
      return;

    // array of arrays that hold all listeners for all types
    this.listener_ = new Array();
  }
  
  //-----------------------------------------------------------
  /**
   * Attaches a listener function as a callback for events of a specific type.
   * <p>
   * To redirect to the lexical scope of the object use a technique like the 
   * following:
   * <pre>
   * class$.registerAsListener = function( anEventSource )
   * {
   *   var this_ = this;
   *   function myCallback( anEvent )
   *   {
   *     this_.doSomething( anEvent );
   *   }
   *   anEventSource.addEventListener( "myType", myCallback );
   * }
   * </pre>
   * Be aware that the usage of the <code>this</code> keyword does not reference
   * the native event source nor the UDA EventSource!
   *
   * Another possible use case (and very similar to the one above) might be the 
   * registration of an object's method as the callback function:
   * <pre>
   * anEventSource.addEventListener( 
   *   "myType",
   *   function myCallback( anEvent )
   *   {
   *     anObjectRef.doSomething( anEvent );
   *   }
   * );
   * </pre>
   *
   * Mind that these examples are only applicable if they are used only once -
   * to register for several events try something like this:
   * <pre>
   * class$.registerAsListener = function( anEventSource )
   * {
   *   function getCallback( anObjectRef, aType )
   *   {
   *     function redirect( anEvent )
   *     {
   *       anObjectRef["on" + aType](anEvent);
   *     }
   *     return redirect;
   *   }
   * 
   *   for ( var type in this.typeArray_ )
   *     if ( this.typeArray_.hasOwnProperty(type) )
   *       anEventSource.addEventListener( type, getCallback(this, type) );
   * }
   * </pre>
   * 
   * @param aType: String: type of event (usally defined as static constant)
   * @param aCallback: Function: function that is called whenever an event of
   * the given type appears on the node (may be an object's method)
   */
  class$.addEventListener = function( aType, aCallback )
  {
    if ( !this.listener_[aType] )
      this.listener_[aType] = new Array();
    
    var lstnr_fct = this.listener_[aType];
  
    for ( var i = 0; i < lstnr_fct.length; ++i )
      if ( lstnr_fct[i] == aCallback )
        return;
  
    lstnr_fct[lstnr_fct.length] = aCallback;
  }
  
  //-----------------------------------------------------------
  /**
   * Removes a callback function from the list of listeners.
   *
   * @param aType: String: type of event (see static constants)
   * @param aCallback: Function: function that is removed from the list of 
   * callback function
   */
  class$.removeEventListener = function( aType, aCallback )
  {
    var lstnr_fct = this.listener_[aType];
    if ( !lstnr_fct )
      return;
  
    for ( var i = 0; i < lstnr_fct.length; ++i )
    {
      if ( lstnr_fct[i] == aCallback )
      {
        if ( lstnr_fct.splice )
        {
          lstnr_fct.splice(i, 1);
        }
        else
        {
          //splice not supported in IE 5.0
          var start_slice = ( i > 0 ) ? lstnr_fct.slice(0, i) : [];
          this.listener_[aType] = start_slice.concat(lstnr_fct.slice(i+1));
        }
        break;
      }
    }
  }
  
  //-----------------------------------------------------------
  /**
   * Calls all callback functions registered for the given event with
   * the event object as the only argument.
   *
   * @param anEvent: ojs.Event: event that is send to all observers
   */
  class$.dispatchEvent = function( anEvent )
  {
    var lstnr_fct = this.listener_[anEvent.getType()];
  
    if ( lstnr_fct )
      for ( var i = 0; i < lstnr_fct.length; ++i )
      {
        if ( lstnr_fct[i] )
          lstnr_fct[i](anEvent);
      }
  }
  
  //-----------------------------------------------------------
  /**
   * Takes the given event and creates a new <code>ojs.Event</code> object with the same type 
   * but this object as the source.
   *
   * @param anEvent: ojs.Event: event that defines which type of event should be 
   * dispatched to listeners   
   */
  class$.redirectEvent = function( anEvent )
  {
    this.dispatchEvent(new ojs.Event({ source_: this, type_: anEvent.getType() }));
  }

} );

