//<SCRIPT LANGUAGE="Javascript">
///////////////////////////////////////////////////////////////////////
// oop.js
//
// implement pseudo object inheritance in Javascript
// Copyright (c) 2000 by Bob Clary. All rights reserved.

registerFile('oop.js', 'browser.js');

/*
function redirectFunction(method)
{
 var f = new Function('arg0', 'arg1', 'arg2', 'arg3', 'arg4', 'arg5', 'arg6', 'arg7', 'arg8', 'arg9', 'var retVal = this.parentMethod("' + method + '", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); if (typeof(retVal) != "undefined") return retVal;')
 return f;
}
*/


function _Classes()
{
	if (typeof(_classes) != 'undefined')
		throw('Only one instance of _Classes() can be created');
		
	_Classes.prototype.registerClass = registerClass;
	function registerClass(className, parentClassName)
	{
		if (!className)
			throw (new bcException('registerClass: className not given', 'oop.js', '_Classes::registerClass'));
			
		if (className in _classes)
			return;
			
		if (className != 'bcObject' && !parentClassName)
			parentClassName = 'bcObject';
			
		if (!parentClassName)
			parentClassName = null;
		else if ( !(parentClassName in _classes))
			throw (new bcException('registerClass: parentClassName ' + parentClassName + ' not defined', 'oop.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 bcCOM,
		// this no longer works and we need to defer the prototype caching
		// to the defineClass method
		objClass.prototypeObject = null;
		objClass.parentClass	= parentClassName;
	}

	_Classes.prototype.defineClass = defineClass;
	function defineClass(className, prototype_func)
	{
		var p;

		if (!className)
			throw ( new bcException('defineClass: className not given', 'oop.js', '_Classes::defineClass'));
			
		if (!(className in _classes))
			throw ( new bcException('defineClass: className ' + className + ' not registered', 'oop.js', '_Classes::defineClass'));
		
		if ('prototype_called' in _classes[className])
			return;
			
		var classRef		= _classes[className];
		if (!classRef.prototypeObject)
			classRef.prototypeObject	= eval( className + '.prototype' );
		var childPrototype	= classRef.prototypeObject;
		var parentClassName = classRef.parentClass;
			
		if (parentClassName)
		{
			if ( !( parentClassName in _classes) )
				throw ( new bcException('defineClass: parentClassName ' + parentClassName + ' not registered', 'oop.js', '_Classes::defineClass'));

			var parentClassRef = _classes[parentClassName];
			
			if ( ! parentClassRef.prototype_called )
			{
				// 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']			= bcObject.prototype.parentMethod;
		
		_classes[className].prototype_called	= true;
	}

	_Classes.prototype.registerInterface =	registerInterface;
	function registerInterface(interfaceName, parentInterfaceName)
	{
		if (!interfaceName)
			throw ( new bcException('registerInterface: interfaceName not given', 'oop.js', '_Classes::registerInterface'));
			
		if (_classes[interfaceName])
			return;
			
		if (!parentInterfaceName)
			parentInterfaceName = null;
		else if ( ! _classes[parentInterfaceName] )
			throw ( new bcException('registerInterface: parentInterfaceName ' + parentInterfaceName + ' not defined', 'oop.js', '_Classes::registerInterface'));

		var objInterface;
		objInterface				= _classes[interfaceName] = new Object();
		objInterface.prototypeObject	= eval( interfaceName + '.prototype' );			
		objInterface.parentClass	= parentInterfaceName;
	}

	_Classes.prototype.defineInterface = defineInterface;
	function defineInterface(interfaceName, prototype_func)
	{
		var p;

		if (!interfaceName)
			throw (new bcException('defineInterface: interfaceName not given', 'oop.js', '_Classes::defineInterface'));
			
		if ( !_classes[interfaceName])
			throw ( new bcException('defineInterface: interfaceName ' + interfaceName + ' not registered', 'oop.js', '_Classes::defineInterface'));
		
		if ( _classes[interfaceName].prototype_called )
			return;
			
		var classRef			= _classes[interfaceName];
		var childPrototype		= classRef.prototypeObject;
		var parentClassName		= classRef.parentClass;

		if (parentClassName)
		{
			if ( typeof(_classes[parentClassName]) == 'undefined' )
				throw ( new bcException('defineInterface: parentClassName ' + parentClassName + ' not registered', 'oop.js', '_Classes::defineInterface'));

			var parentClassRef = _classes[parentClassName];
			
			if ( ! parentClassRef.prototype_called )
			{
				// 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']					= interfaceName;
		childPrototype['classRef']				= classRef;
		childPrototype['parentMethod']			= bcObject.prototype.parentMethod;
		
		_classes[interfaceName].prototype_called	= true;
	}

	_Classes.prototype.implementInterface = implementInterface;
	function implementInterface(className, interfaceName)
	{
		var p;
		
		if (!className)
			throw (new bcException('implementInterface: className not given', 'oop.js', '_Classes::implementInterface'));
			
		if ( typeof(_classes[className].prototype_called) != 'undefined' )
			return;
			
		if (!interfaceName)
			throw ( new bcException('implementInterface: interfaceName not given', 'oop.js', '_Classes::implementInterface'));
			
		if ( ! _classes[interfaceName] )
			throw ( new bcException('implementInterface: interfaceName ' + interfaceName + ' not registered', 'oop.js', '_Classes::implementInterface'));
		
		if ( ! _classes[interfaceName].prototype_called )
		{
			var dummy;
			eval('dummy = new ' + interfaceName + '();');
		}
		
		var interfacePrototype	= _classes[interfaceName].prototypeObject;
		var classPrototype		= _classes[className].prototypeObject;
				
		for (p in interfacePrototype)
			 classPrototype[p] = interfacePrototype[p];
	}
}

// create global instance
var _classes = new _Classes();

		
// register root class bcObject
_classes.registerClass('bcObject');

function bcObject()
{
	_classes.defineClass('bcObject', _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...
		bcObject.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!!!! ***
		bcObject.prototype.classRef			= null;
		
		bcObject.prototype.init				= init;
		function init() { }
		
		bcObject.prototype.destroy			= destroy;
		function destroy() {}

		bcObject.prototype.parentMethod		= parentMethod;
		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 != 'bcObject' && 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;
			
			if (typeof(retVal) != 'undefined')
				return retVal;
		}
	}
}

_classes.registerClass('bcException');

function bcException(errorMessage, errorFile, errorFunction, exception)
{
	_classes.defineClass('bcException', _prototype_func);
	
	this.init(errorMessage, errorFile, errorFunction, exception);
	
	function _prototype_func()
	{
		bcException.prototype.init = init;
		function init(errorMessage, errorFile, errorFunction, exception)
		{
			if (typeof(exception) == 'object' && typeof(exception.isa) != 'undefined' && exception.isa == 'bcException')
			{
				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;
							
					for (p in exception)
						if (typeof(exception[p]) == 'string' || typeof(exception[p]) == 'number')
							this.nativeMessage += p + ' = ' + exception[p] + ' ';
							
					this.nativeException = exception;
				}
			}
		}
		
		bcException.prototype.toString = toString;
		function toString()
		{
			var s = 'bcException(' + this.errorFile + ' : ' + this.errorFunction + ') ' +
			        this.errorMessage + ' / ' + this.nativeMessage;
			return s;
		}
	}
}

// bcArray 

_classes.registerClass('bcArray');

function bcArray()
{
	_classes.defineClass('bcArray', _prototype_func);
	
	this.init();

	function _prototype_func()
	{
		bcArray.prototype.init			= init;
		function init()
		{
			this.parentMethod('init');
			this.list = new Array();
			this.length = 0;
		}

		bcArray.prototype.destroy		= destroy;
		function destroy()
		{
			var i;
			
			for (i = 0; i < this.list.length; i++)
			{
				if (this.list[i].destroy)
					this.list[i].destroy();
				else
					this.list[i] = null;
			}
			this.list = null;
			this.parentMethod('destroy');
		}
		
		bcArray.prototype.item			= item;
		function item(index)
		{
			if (index >= this.length)
				return null;
			else if (index < 0)
				return null;
			else
				return this.list[index];
		}
		
		bcArray.prototype.concat		= concat;
		function concat(array2)
		{
			if (array2.list.length == 0)
				return this;
			
			var newList  = new bcArray();
			newList.list = this.list.concat(array2.list);
			newList.length = newList.list.length;
			
			return newList;
		}
		
		bcArray.prototype.join			= join;
		function join(str) { return this.list.join(str); }

		bcArray.prototype.pop			= pop;
		function pop()
		{
			if (this.length == 0)
				return null;
				
			var node = this.list[this.length - 1];
			
			if (this.length == 1)
				this.list = new Array();
			else
				this.list = this.list.slice(0, this.length - 1);
				
			--this.length;
			
			return node;
		}
		
		bcArray.prototype.push			= push;
		function push(node)
		{
			this.list[this.length] = node;
			++this.length;
			
			return node;
		}
		
		bcArray.prototype.top = top;
		function top()
		{
			if (this.length == 0)
				return null;
				
			return this.list[this.length-1];
		}
		
		bcArray.prototype.reverse		= reverse;
		function reverse()
		{
			this.list = this.list.reverse();
			return this;
		}
		
		bcArray.prototype.shift		= shift;
		function shift()
		{
			if (this.length == 0)
				return null;
				
			var node = this.list[0];
			
			if (this.length == 1)
				this.list = new Array();
			else
				this.list = this.list.slice(1);
				
			--this.length;
			
			return node;
		}

		bcArray.prototype.unshift		= unshift;
		function unshift(node)
		{
			var tempArray = new Array();
			tempArray[0] = node;
			
			this.list = tempArray.concat(this.list);
			
			++this.length;
			
			return this.length;
		}
				
		bcArray.prototype.slice		= slice;
		function slice(startIndex, stopIndex)
		{
			var newList = new bcArray();
			if (!stopIndex)
				stopIndex = this.length;
				
			newList.list = this.list.slice(startIndex, stopIndex);
			newList.length = newList.list.length;
			
			return newList;
		}
		
		bcArray.prototype.sort			= sort;
		function sort(cmp)
		{
			var newList = new bcArray();
			newList.list = this.list.sort(cmp);
			newList.length = this.length;
			
			return newList;
		}

		bcArray.prototype.getLength	= getLength;
		function getLength()	{ return this.length; }
	}
}

_classes.registerClass('bcCookies');

function bcCookies()
{
	_classes.defineClass('bcCookies', _prototype_func);
	
	this.init();
	
	function _prototype_func()
	{
		bcCookies.prototype.init = init;
		function init()
		{
			this.parentMethod('init');
			
			var q = document.cookie;
			this.value = new Array();
			this.secure = false;

			if (q)
			{
				var pairs = q.split(';');

				for (var i = 0; i < pairs.length; ++i)
				{
					var name_value = pairs[i].split('=');
					
					this.value[name_value[0]] = name_value[1];
					for (var j = 2; j < name_value.length; j++)
						this.value[name_value[0]] += '=' + name_value[j];
						
					this.value[name_value[0]] = unescape(this.value[name_value[0]]);
				}
			}
		}
		
		bcCookies.prototype.save = save;
		function save()
		{
			var p;
			var s = '';
			
			for (p in this.value)
				s += p + '=' + escape(this.value[p]) + ';';
				
			if (this.secure)
				s += 'secure;';
				
			document.cookie = s;
		}
		
		bcCookies.prototype.setExpires = setExpires;
		function setExpires(date)
		{
			this.value['expires'] = date.toGMTString();
		}
		
		bcCookies.prototype.setPath = setPath;
		function setPath(path)
		{
			this.value['path'] = path;
		}
		
		bcCookies.prototype.setDomain = setDomain;
		function setDomain(domain)
		{
			this.value['domain'] = domain;
		}
		
		bcCookies.prototype.setSecure = setSecure;
		function setSecure()
		{
			this.secure = true;
		}
		
	}
}


// functionToString() is a replacement for the standard Function.toString()
// so that only the function name and argument list are returned rather than
// all of the source code for the function.
function functionToString()
{
	var s = this.oldToString();
	var i;
	var t;
	
	if (s == null || typeof(s) != 'string' || s.length == 0)
		return '';
	
	i = s.indexOf('native code');
	if (i != -1  && s.indexOf('functionToString') == -1)
		return s;
		
	i = s.indexOf(')');
	if (i == -1)
		t = s;
	else
		t = s.substr(0, i+1) + '{ [user code] }';
		
	return t;
}

Function.prototype.oldToString = Function.prototype.toString;
Function.prototype.toString    = functionToString;

// add some useful method to String for handling HTML...

function escapeHTML()
{
	var s = this.toString();
	
	s = s.replace(/\</g, '&lt;');
	s = s.replace(/\>/g, '&gt;');
	s = s.replace(/\&/g, '&amp;');
	
	return s;
}

function unescapeHTML()
{
	var s = this.toString();
	
	s = s.replace(/\&lt;/g,  '<');
	s = s.replace(/\&gt;/g,   '>');
	s = s.replace(/\&amp;/g, '&');
	
	return s;
}
	
String.prototype.escapeHTML   = escapeHTML;
String.prototype.unescapeHTML = unescapeHTML;
	

// eof: oop.js
//</SCRIPT>
