/**
 * A function that does nothing: to be used when resetting callback handlers.
 * @final
 */
EMPTY_FUNCTION = function()
{
  // does nothing.
}

var Class =
{
  /**
   * Defines a new class from the specified instance prototype and class
   * prototype.
   *
   * @param {Object} instancePrototype the object literal used to define the
   * member variables and member functions of the instances of the class
   * being defined.
   * @param {Object} classPrototype the object literal used to define the
   * static member variables and member functions of the class being
   * defined.
   *
   * @return {Function} the newly defined class.
   */
  define: function(instancePrototype, classPrototype)
  {
    /* This is the constructor function for the class being defined */
    var base = function()
    {
      if (!this.__prototype_chaining 
          && base.prototype.initialize instanceof Function)
        base.prototype.initialize.apply(this, arguments);
    }

    base.prototype = instancePrototype || {};

    if (!base.prototype.initialize)
      base.prototype.initialize = EMPTY_FUNCTION;

    for (var property in classPrototype)
    {
      if (property == 'initialize')
        continue;

      base[property] = classPrototype[property];
    }

    if (classPrototype && (classPrototype.initialize instanceof Function))
      classPrototype.initialize.apply(base);

    function augment(method, derivedPrototype, basePrototype)
    {
      if (  (method == 'initialize')
          &&(basePrototype[method].length == 0))
      {
        return function()
        {
          basePrototype[method].apply(this);
          derivedPrototype[method].apply(this, arguments);
        }
      }

      return function()
      {
        this.base = function()
        {
          return basePrototype[method].apply(this, arguments);
        };

        return derivedPrototype[method].apply(this, arguments);
        delete this.base;
      }
    }

    /**
     * Provides the definition of a new class that extends the specified
     * parent class.
     *
     * @param {Function} parent the class to be extended.
     * @param {Object} instancePrototype the object literal used to define
     * the member variables and member functions of the instances of the
     * class being defined.
     * @param {Object} classPrototype the object literal used to define the
     * static member variables and member functions of the class being
     * defined.
     *
     * @return {Function} the newly defined class.
     */
    function extend(parent, instancePrototype, classPrototype)
    {
      var derived = function()
      {
        if (!this.__prototype_chaining
            && derived.prototype.initialize instanceof Function)
          derived.prototype.initialize.apply(this, arguments);
      }

      parent.prototype.__prototype_chaining = true;

      derived.prototype = new parent();

      delete parent.prototype.__prototype_chaining;

      for (var property in instancePrototype)
      {
        if (  (instancePrototype[property] instanceof Function)
            &&(parent.prototype[property] instanceof Function))
        {
          derived.prototype[property] = augment(property, instancePrototype, parent.prototype);
        }
        else
          derived.prototype[property] = instancePrototype[property];
      }

      derived.extend =  function(instancePrototype, classPrototype)
      {
        return extend(derived, instancePrototype, classPrototype);
      }

      for (var property in classPrototype)
      {
        if (property == 'initialize')
          continue;

        derived[property] = classPrototype[property];
      }

      if (classPrototype && (classPrototype.initialize instanceof Function))
        classPrototype.initialize.apply(derived);

      return derived;
    }

    base.extend = function(instancePrototype, classPrototype)
    {
      return extend(base, instancePrototype, classPrototype);
    }
    return base;
  }
}

var Base = Class.define(
{
  initialize: function(value) // Java constructor equivalent
  {
    this.property = value;
  }, 

  property: undefined, // member variable

  getProperty: function() // member variable accessor
  {
    return this.property;
  }, 

  foo: function()
  {
    alert('inside Base.foo');
    // do something
  }, 

  bar: function()
  {
    alert('inside Base.bar');
    // do something else
  }
}, 
{
  initialize: function() // Java static initializer equivalent
  {
    this.property = 'Base';
  },

  property: undefined, // static member variables can have the same
                       // name as non static member variables

  getProperty: function() // static member functions can have the same
  {                       // name as non static member functions
    return this.property;
  }
});

var Derived = Base.extend(
{
  initialize: function()
  {
    this.base('derived'); // chain with parent class's constructor
  }, 

  property: undefined, 

  getProperty: function()
  {
    return this.property;
  }, 

  foo: function() // override foo
  {
    alert('inside Derived.foo');
    this.base(); // call parent class implementation of foo
                 // do some more treatments
  }
}, 
{
  initialize: function()
  {
    this.property = 'Derived';
  }, 

  property: undefined, 

  getProperty: function()
  {
    return this.property;
  }
});


var b = new Base('base');
alert('b instanceof Base returned: ' + (b instanceof Base));
alert('b.getProperty() returned: ' + b.getProperty());
alert('Base.getProperty() returned: ' + Base.getProperty());

b.foo();
b.bar();

var d = new Derived('derived');
alert('d instanceof Base returned: ' + (d instanceof Base));
alert('d instanceof Derived returned: ' + (d instanceof Derived));
alert('d.getProperty() returned: ' + d.getProperty());  
alert('Derived.getProperty() returned: ' + Derived.getProperty());

d.foo();
d.bar();