/*
 * $Log: xbObjects.js,v $
 * Revision 1.2  2003/06/28 20:12:58  thealx
 * *** empty log message ***
 *
 * Revision 1.1  2003/03/20 16:16:31  thealx
 * *** empty log message ***
 *
 * Revision 1.6  2002/07/22 14:28:54  bc6ix
 * fix license path, remove registerFile, hasProperty, and reportError
 *
 * Revision 1.5  2002/07/07 08:23:07  bc6ix
 * fix line endings
 *
 * Revision 1.4  2002/05/14 16:52:53  bc6ix
 * use CVS Log for revision history
 *
 */

/* ***** BEGIN LICENSE BLOCK *****
 * Licensed under Version: MPL 1.1/GPL 2.0/LGPL 2.1
 * Full Terms at /xbProjects-license/mpl-tri-license.txt
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Bob Clary code.
 *
 * The Initial Developer of the Original Code is
 * Bob Clary.
 * Portions created by the Initial Developer are Copyright (C) 2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s): Bob Clary <bc@bclary.com>
 *
 * ***** END LICENSE BLOCK ***** */

/*
ChangeLog: 2001-12-19 - bclary - changed xbException init method to 
           remove possible exception due to permission denied issues
           in gecko 0.9.5+
*/

function _Classes()
{
  if (typeof(_classes) != 'undefined')
    throw('Only one instance of _Classes() can be created');
    
  function registerClass(className, parentClassName)
  {
    if (!className)
      throw(new xbException('registerClass: className not given', 'xbObjects.js', '_Classes::registerClass'));

    if (typeof(_classes[className]) != 'undefined')
      return;
      
    if (className != 'xbObject' && !parentClassName)
      parentClassName = 'xbObject';
      
    if (!parentClassName)
      parentClassName = null;
    else if (typeof(_classes[parentClassName]) == 'undefined')
      throw(new xbException('registerClass: parentClassName ' + parentClassName + ' not defined', 'xbObjects.js', '_Classes::registerClass'));

    var objClass;
    objClass        = _classes[className] = new Object();
    // evaluating and caching the prototype object in registerClass
    // works so long as we are dealing with 'normal' source files
    // where functions are created in the global context and then 
    // statements executed. when evaling code blocks as in xbCOM,
    // this no longer works and we need to defer the prototype caching
    // to the defineClass method
    objClass.prototypeObject = null;
    objClass.parentClass  = parentClassName;
  }
  _Classes.prototype.registerClass = registerClass;

  function defineClass(className, prototype_func)
  {
    var p;

    if (!className)
      throw( new xbException('defineClass: className not given', 'xbObjects.js', '_Classes::defineClass'));
      
    if (typeof(_classes[className]) == 'undefined')
      throw( new xbException('defineClass: className ' + className + ' not registered', 'xbObjects.js', '_Classes::defineClass'));
    
    if (typeof(_classes[className]['prototype_called']) != 'undefined')
      return;
      
    var classRef    = _classes[className];
    if (!classRef.prototypeObject)
      classRef.prototypeObject  = eval( className + '.prototype' );
    var childPrototype  = classRef.prototypeObject;
    var parentClassName = classRef.parentClass;
      
    if (parentClassName)
    {
      if (typeof(_classes[parentClassName]) == 'undefined')
        throw( new xbException('defineClass: parentClassName ' + parentClassName + ' not registered', 'xbObjects.js', '_Classes::defineClass'));

      var parentClassRef = _classes[parentClassName];
      
      if ( typeof(parentClassRef['prototype_called']) == 'undefined')
      {
        // force parent's prototype to be called by creating a dummy instance
        // note constructor must handle 'default' constructor case
        var dummy;
        eval('dummy = new ' + parentClassName + '();');
      }
        
      var parentPrototype = parentClassRef.prototypeObject;
      //var childSuperClass = classRef.superClass;
    
      for (p in parentPrototype)
      {
        if (p != 'isa' && p != 'classRef' && p != 'parentMethod')
          childPrototype[p] = parentPrototype[p];
      }
    }

    prototype_func();
    
    childPrototype['isa']          = className;
    childPrototype['classRef']        = classRef;
    childPrototype['parentMethod']      = xbObject.prototype.parentMethod;
    
    _classes[className].prototype_called  = true;
  }
  _Classes.prototype.defineClass = defineClass;

}

// create global instance
var _classes = new _Classes();

    
// register root class xbObject
_classes.registerClass('xbObject');

function xbObject()
{
  _classes.defineClass('xbObject', _prototype_func);

  this.init();
  
  function _prototype_func()
  {
    // isa is set by defineClass() to the className
    // Note that this can change dynamically as the class is cast
    // into it's ancestors...
    xbObject.prototype.isa        = null;  
    
    // classref is set by defineClass() to point to the 
    // _classes entry for this class. This allows access 
    // the original _class's entry no matter how it has 
    // been recast. 
    // *** This will never change!!!! ***
    xbObject.prototype.classRef      = null;
    
    function init() { }
    xbObject.prototype.init        = init;
    
    function destroy() {}
    xbObject.prototype.destroy      = destroy;

    function parentMethod(method, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
    {
      var className  = this.isa;
      var parentClass  = _classes[className].parentClass;
      var tempMethod  = _classes[parentClass].prototypeObject[method];
      
      // find who implemented this method
      while (parentClass != 'xbObject' && tempMethod == this[method])
      {
        parentClass = _classes[parentClass].parentClass;
        tempMethod  = _classes[parentClass].prototypeObject[method];
      }
        
      this.tempMethod  = tempMethod;
      this.isa    = parentClass;
      var retVal    = this.tempMethod(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
      this.isa    = className;
      
      return retVal;
    }
    xbObject.prototype.parentMethod    = parentMethod;
  }
}

_classes.registerClass('xbException');

function xbException(errorMessage, errorFile, errorFunction, exception)
{
  _classes.defineClass('xbException', _prototype_func);
  
  this.init(errorMessage, errorFile, errorFunction, exception);
  
  function _prototype_func()
  {
    function init(errorMessage, errorFile, errorFunction, exception)
    {
      // turn off error handling inside xbExceptions
      var oldonerror = null;
      if (typeof(window.onerror) == 'function')
        oldonerror = window.onerror;
      window.onerror = null;

      if (typeof(exception) == 'object' && typeof(exception.isa) != 'undefined' && exception.isa == 'xbException')
      {
        this.errorMessage  = exception.errorMessage;
        this.errorFile = exception.errorFile;
        this.errorFunction = exception.errorFunction;
        this.nativeMessage  = exception.nativeMessage;
        this.nativeException = exception.nativeException;
      }
      else
      {
        this.errorMessage  = errorMessage;
        this.errorFile = errorFile;
        this.errorFunction = errorFunction;
        this.nativeMessage  = '';

        if (typeof(exception) == 'object')
        {
          var p;
              
          /*
          This causes exceptions in some cases related to
          XPCWrappedNative_NoHelper in Gecko 0.9.5+
          related to permission denied to access filename,
          lineNumber, columnNumber. Revert to the public toString()
          interface.

          for (p in exception)
            if (typeof(exception[p]) == 'string' || typeof(exception[p]) == 'number')
              this.nativeMessage += p + ' = ' + exception[p] + ' ';
          */
          this.nativeMessage = exception + '';
              
          this.nativeException = exception;
        }
      }
      // turn error handlng back on
      window.onerror = oldonerror;
    }
    xbException.prototype.init = init;
    
    function toString()
    {
      // turn off error handling inside xbExceptions
      var oldonerror = null;
      if (typeof(window.onerror) == 'function')
        oldonerror = window.onerror;
      window.onerror = null;

      var s = 'xbException(' + this.errorFile + ' : ' + this.errorFunction + ') ' +
              this.errorMessage + ' / ' + this.nativeMessage;
      // turn error handlng back on
      window.onerror = oldonerror;
      return s;
    }
    xbException.prototype.toString = toString;
  }
}
// eof: xbObjects.js
//</SCRIPT>

