/*******************************************************************************
                        Enorasis 2006-2007 (c)                                  
*******************************************************************************/
/*
	Javascript Class toolkit.
	
	Supports:
		Multiple inheritance modeling
		Type tracking
		Interface abstract programming
		
		Special functions metas:
			Static
			Virtual
			Abstract
			Stub
			Hidden
			Reference
		
		Keywords:
			Class
			Interface
			Enum
			Namespace
		
		Tight keyword-linked _classes:
			IProperty (Interface)
			Property (Class)
			Ref (Class)
		
		Error Reporting Model
		
		Garbage Collection Service
	
	Author: Mantzoukas Alexander
	Company: Enorasis S.A.
	Version: 3.7.4.234
*/

/* Object Cloning: */
function Clone(obj, parent)
{
	if ( obj && (typeof obj) == "object"
		&& !obj.IsStatic )
	{
		try
		{
		    var clonedObject = new obj.constructor();
		    for(prop in obj)
			clonedObject[prop] = Clone(obj[prop], clonedObject);
		    return clonedObject;
		}
		catch(e)
		{
		    return obj;
		}
	}
	else if (obj && obj.IsHidden)
	{
		obj = Hidden( obj.func, obj.newFunc );
		obj._this = parent;
		return obj;
	}
	else return obj;
}

/* Function meta */

function Abstract()
{
	throw("You cannot call an Abstract function. Not implemented.");
}

function Stub()
{
}

function Hidden( func, newFunc )
{
	var retfunc = function()
	{
		return newFunc.apply( this, arguments );
	};
	
	retfunc.Base = function()
	{
		return func.apply( this._this, arguments );
	};
	
	retfunc.func = func;
	retfunc.newFunc = newFunc;
	
	retfunc.IsHidden = true;
	
	return retfunc;
}

function Static( func )
{
	func.IsStatic = true;
	return func;
}

function Virtual( func )
{
	func.IsVirtual = true;
	return func;
}

function Reference( func, _this  )
{
	return function()
	{
		return func.apply(_this, arguments);
	}
}

/** Meta-classes **/

function Class()
{
	var ref = function()
	{
		function CallConstructors(_this, Type, args)
		{
			for(var i = 0; i < Type.InheritedConstructors.length; ++i)
			{
				CallConstructors(_this, Type.Inherited[i], args);
				if (Type.InheritedConstructors[i])
					Type.InheritedConstructors[i].apply(_this, args);
			}
		}
		
		// Garbage Collection
		if (__GC)
			__GC.markObject(this);
		
		for(prop in this)
			this[prop] = Clone(this[prop], this);
		
		CallConstructors(this, this.Type, arguments);
		if (this.Constructor) this.Constructor.apply(this, arguments);
	};
	
	ref.Inherited = new Array();
	ref.InheritedConstructors = new Array();
	ref.Implemented = new Array();
	
	ref.Declare = function(decl)
	{
		for(prop in decl)
		if ( (typeof decl[prop]) != "function" )
		{
			this.prototype[prop] = decl[prop];
		}
		else if (prop != "Type")
		{
			if ( this.prototype[prop] && this.prototype[prop].IsStatic )
			    this.prototype[prop] = Static(decl[prop]);
			else if ( this.prototype[prop] && this.prototype[prop].IsVirtual )
			    this.prototype[prop] = Virtual(decl[prop]);
			else if (this.prototype[prop])
			    this.prototype[prop] = Hidden( this.prototype[prop], decl[prop] );
			else this.prototype[prop] = decl[prop];
		}
	}
	
	ref.IsClass = true;
	
	ref.Implement = function(_interface, declarations)
	{
		if (_interface.IsInterface)
		{
			if (this.SupportsDirectly(_interface)) return;
			this.Implemented.push(_interface);
			for(prop in _interface.prototype)
			{
				if (_interface.prototype[prop].IsReference) this.prototype[prop] = _interface.prototype[prop];
				else if ((typeof declarations[prop]) != "function")
					this.prototype[prop] = declarations[prop];
				else if (declarations[prop]) this.prototype[prop] = Virtual( declarations[prop] );
				else this.prototype[prop] = Abstract;
			}
		}
		else
		{
			throw("Only interfaces are implemented.");
		}
	}
	
	ref.Inherit = function(_class)
	{
		if (_class.IsClass)
		{
			if (this.SupportsDirectly(_class)) return;
			this.Inherited.push(_class);
			this.InheritedConstructors.push(_class.prototype.Constructor);
			for(prop in _class.prototype)
			{
				if ( (typeof _class.prototype[prop]) != "function" )
				{
					this.prototype[prop] = _class.prototype[prop];
				}
				else if (prop != "Constructor" && prop != "Type")
				{
					if ( ( this.prototype[prop] && this.prototype[prop].IsStatic ) ||
							( _class.prototype[prop] && _class.prototype[prop].IsStatic) )
						this.prototype[prop] = Static(_class.prototype[prop]);
					else if ( ( this.prototype[prop] && this.prototype[prop].IsVirtual ) ||
							( _class.prototype[prop] && _class.prototype[prop].IsVirtual) )
						this.prototype[prop] = Virtual(_class.prototype[prop]);
					else if (this.prototype[prop])
						this.prototype[prop] = Hidden( this.prototype[prop], _class.prototype[prop] );
					else this.prototype[prop] = _class.prototype[prop];
				}
			}
		}
		else
		{
			throw("Only classes are inherited to other classes.");
		}
	}
	
	ref.Supports = function(type, allowThrow)
	{
		if (this.SupportsDirectly(type, allowThrow) || this.SupportsIndirectly(type, allowThrow))
		{
			return true;
		}
		else if (allowThrow)
		{
			throw("Object does not allow type.");
		}
		
		return false;
	}
	
	ref.SupportsDirectly = function(type, allowThrow)
	{
		if (type.IsInterface)
		{
			for(var i = 0; i < this.Implemented.length; ++i)
			{
				if (this.Implemented[i] == type)
					return true;
			}
		}
		else if (type.IsClass)
		{
			for(var i = 0; i < this.Inherited.length; ++i)
			{
				if (this.Inherited[i] == type)
					return true;
			}
		}
		else if (allowThrow)
		{
			throw("Object does not allow type.");
		}
		
		return false;
	}
	
	ref.SupportsIndirectly = function(type, allowThrow)
	{
		if (type.IsInterface)
		{
			for(var i = 0; i < this.Implemented.length; ++i)
			{
				if (this.Implemented[i].Supports(type))
					return true;
			}
			for(var i = 0; i < this.Inherited.length; ++i)
			{
				if (this.Inherited[i].Supports(type))
					return true;
			}
		}
		else if (type.IsClass)
		{
			for(var i = 0; i < this.Inherited.length; ++i)
			{
				if (this.Inherited[i].Supports(type))
					return true;
			}
		}
		else if (allowThrow)
		{
			throw("Object does not allow type.");
		}
		
		return false;
	}
	
	ref.Extract = function()
	{
		var i = new Interface();
		
		for(prop in this.prototype)
			if (typeof this.prototype[prop] == "function")
				i.prototype[prop] = Abstract;
			else if (this.prototype[prop].IsProperty && 
				!this.prototype[prop].IsReference)
			{
				i.prototype[prop] = this.prototype[prop];
				i.prototype[prop].get = Abstract;
				i.prototype[prop].set = Abstract;
				i.prototype[prop].Constructor = Abstract;
			}
			else if (this.prototype[prop].IsProperty) 
				i.prototype[prop] = this.prototype[prop];
		return i;
	}
	
	ref.prototype.Cast = function(type, allowThrow)
	{
		if ( this.Type.Supports(type) )
		{
			var ref = new Ref( type );
			ref.set( this );
			return ref;
		}
		else if (allowThrow)
		{
			throw("Bad cast exception.");
		}
		
		return null;
	}
	
	ref.prototype.Type = ref;
	
	return ref;
}

function Interface()
{
	var ref = function() { throw("You cannot instantiate an interface"); };
	
	ref.Define = function(decl)
	{
		for(prop in decl)
			if ( (decl[prop] == Abstract) ||		/* Methods */
				decl[prop].IsProperty ||		/* Properties */
					decl[prop].IsReference )	/* References */
			{
				if (decl[prop].IsProperty &&
					!decl[prop].IsReference)
				{
					decl[prop].get = Abstract;
					decl[prop].set = Abstract;
				}
				this.prototype[prop] = decl[prop];
			}
			// else throw("Bad interface declaration."); // Temporary fix 19/05/07
	}
	
	ref.Inherited = new Array();
	ref.IsInterface = true;
	
	ref.Inherit = function(_interface)
	{
		if (_interface.IsInterface)
		{
			if (this.Supports(_interface)) return;
			this.Define(_interface.prototype);
			this.Inherited.push(_interface);
		}
		else
		{
			throw("Error in interface inheritance.");
		}
	}
	
	ref.Supports = function(type, allowThrow)
	{
		if (this.SupportsDirectly(type, allowThrow) || this.SupportsIndirectly(type, allowThrow))
		{
			return true;
		}
		else if (allowThrow)
		{
			throw("Object does not allow type.");
		}
		
		return false;
	}
	
	ref.SupportsDirectly = function(type, allowThrow)
	{
		if (type.IsInterface)
		{
			for(var i = 0; i < this.Inherited.length; ++i)
			{
				if (this.Inherited[i] == type)
					return true;
			}
		}
		else if (allowThrow)
		{
			throw("Object does not allow type.");
		}
		
		return false;
	}
	
	ref.SupportsIndirectly = function(type, allowThrow)
	{
		if (type.IsInterface)
		{
			for(var i = 0; i < this.Inherited.length; ++i)
			{
				if (this.Inherited[i].Supports(type))
					return true;
			}
		}
		else if (allowThrow)
		{
			throw("Object does not allow type.");
		}
		
		return false;
	}
	
	ref.MakeStubClass = function()
	{
		var c = new Class();
		var objDecl = {};
		for(prop in this.prototype)
			if (this.prototype[prop] == Abstract)
				objDecl[prop] = Stub;
			else if ((this.prototype[prop].IsProperty &&
					this.prototype[prop].get == Abstract &&
					this.prototype[prop].set == Abstract)
				)
				objDecl[prop] = IProperty.MakeStubClass();
			else if (this.prototype[prop].IsReference)
				objDecl[prop] = this.prototype[prop];
		
		c.Implement(this, objDecl);
		return c;
	}
	
	return ref;
}

function Enum(decl)
{
	var ref = function( value )
	{
		var prop = new Property();
		prop.EnumType = this.Type;
		prop.set = function(value)
		{
			if ( this.EnumType.Exists(value) )
				this.Data = value;
			else throw("Type mismatch in enumeration property set.");
		}
		
		if (value)
		{
			prop.set(value);
		}
		
		return prop;
	};
	
	ref.Declare = function(decl)
	{
		for(prop in decl)
		{
			if ( typeof decl[prop] == "number" )	/* Number Fields */
			{
				this[prop] = decl[prop];
			}
			else if ( typeof decl[prop] != "function" )
				throw("Bad enumeration declaration.");
		}
	}
	
	ref.Exists = function(num)
	{
		for(prop in decl)
		{
			if ( (typeof decl[prop] == "number" ) && num == decl[prop] )
			{
				return true;
			}
		}
		
		return false;
	}
	
	ref.IsEnum = true;
	
	ref.prototype.Type = ref;
	
	if (decl) ref.Declare(decl);
	
	return ref;
}

function Namespace()
{
	return { IsNamespace: true };
}

/** Properties **/

var IProperty = new Interface();

IProperty.Define(
	{
		get: Abstract,
		set: Abstract,
		Constructor: Abstract
	}
); // IProperty Interface

var Property = new Class();

Property.Implement( IProperty,
	{
		get: function() { return this.Data; },
		set: function(x) { this.Data = x; },
		Constructor: function() {}
	}
); // Property::IProperty Interface implementation

Property.Declare(
	{
		IsProperty: true,
		Data: null
	}
); // Property Class

var ArrayProperty = new Class();
ArrayProperty.Inherit( Property );

ArrayProperty.Declare(
	{
		Data: new Array(),
		length: 0,
		
		get: function( i )
		{
			return this.Data[i];
		},
		
		set: function( i, value )
		{
			this.Data[i] = value;
			return this;
		},
		
		add: function( value )
		{
			this.Data[ this.Data.length ] = value;
			this.length = this.Data.length;
			return this;
		},
		
		addBeforeIndex: function( i, value )
		{
			this.Data.splice(i, 0, value);
			this.length = this.Data.length;
			return this;
		},
		
		removeIndex: function( i )
		{
			this.Data.splice(i, 1);
			this.length = this.Data.length;
			return this;
		},
		
		remove: function( value, all )
		{
			for(var i = 0; i < this.Data.length; ++i)
			{
				if (this.Data[i] == value)
				{
					this.Data.splice(i, 1);
					this.length = this.Data.length;
					if (!all) break;
				}
			}
			return this;
		},
		
		exists: function( value )
		{
			var times = 0;
			for(var i = 0; i < this.Data.length; ++i)
				if (this.Data[i] == value)
					++times;
			return times;
		},
		
		existIndexes: function( value )
		{
			var indexes = new Array();
			for(var i = 0; i < this.Data.length; ++i)
				if (this.Data[i] == value)
					indexes[ indexes.length ] = i;
			return indexes;
		}
	}
); // ArrayProperty Class

/** Reference **/

var Ref = new Class();
Ref.Inherit( Property );

Ref.Declare(
	{
		IsReference: true,
		RefType: null,
		RefObj: null,
		
		Constructor: function( type )
		{
			this.RefType = type;
			if (!this.RefType || 
				!(this.RefType.IsClass || this.RefType.IsInterface) )
				throw("Error: Undefined reference type.");
		},
		
		get: function()
		{
			return this.RefObj;
		},
		
		set: function( obj )
		{
			if (!this.RefType) throw("Error: Undefined reference type.");
			if (!obj) throw("Error: Undefined object to be referenced.");
			/*if (!obj.Type) throw("Error: Undefined object's type that is to be referenced.");*/
			if ( obj.Type && !(obj.Type.Supports(this.RefType)) )
				throw("Error: Object type does not support reference type.");
			
			this.RefObj = obj;
			
			var _type = this.RefType.prototype;
			for(prop in _type)
			{
				if ( _type[prop] == null || ((typeof _type[prop]) != "function" && !_type[prop].IsProperty) )
					this[prop] = this.RefObj[prop];
				else if ( (typeof _type[prop]) == "function" && prop != "Constructor" && prop != "Type" )
					this[prop] = Reference( this.RefObj[prop], this.RefObj );
				else if ( _type[prop].IsProperty )
				{
					this[prop] = new Property();
					this[prop].get = Reference( this.RefObj[prop].get, this.RefObj[prop] );
					this[prop].set = Reference( this.RefObj[prop].set, this.RefObj[prop] );
				}
			}
			
			this.Type = this.RefType;
		}
	}
); // Ref _class

Array.prototype.IsArray = function() { return true }; // marker
Array.prototype.forEach = function( iter )
{
	for(var i = 0; i < this.length; ++i)
		if (iter( this[i], i )) break;
};

var Enumerator = new Class();

Enumerator.Declare(
	{
		objByName: new Object(),
		obj: new Array(),
		objNow: -1,
		objName: new Array(),
		length: 0,
		
		Constructor: function( obj, filterFunc )
		{
			if (obj.IsArray)
			{
				for(var i = 0; i < obj.length; ++i)
					if (obj[i] != null)
					{
						if (!filterFunc || filterFunc(i, obj[ i ]) )
						{
							this.obj[ this.obj.length ] = obj[ i ];
							this.objName[ this.objName.length ] = i;
							this.objByName[ String(i) ] = obj[i];
						}
					}
				return;
			}
			
			for(prop in obj)
			{
				if (obj[prop] != null)
				{
					if (!filterFunc || filterFunc(prop, obj[ prop ]) )
					{
						this.obj[ this.obj.length ] = obj[ prop ];
						this.objName[ this.objName.length ] = prop;
						this.objByName[ prop ] = obj[prop];
					}
				}
			}
			
			this.length = this.obj.length;
		},
		
		Next: function()
		{
			++this.objNow;
			if (this.obj.length <= this.objNow) return null;
			return this.obj[this.objNow];
		},
		
		Peak: function()
		{
			if (this.obj.length <= this.objNow
				|| this.objNow < 0) return null;
			return this.obj[this.objNow];
		},
		
		PeakName: function()
		{
			if (this.objName.length <= this.objNow
				|| this.objNow < 0) return null;
			return this.objName[this.objNow];
		},
		
		GetByName: function( name )
		{
			return this.objByName[ name ];
		},
		
		Init: function()
		{
			this.objNow = -1;
		}
	}
);

////////////////////////////// Error reporting \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
var ErrorHandlerClass = new Class();

ErrorHandlerClass.Declare(
	{
		LineStart: -1,
		LineEnd: -1,
		IsUniqueHandler: false,
		Url: '',
		
		Constructor: function( handler, unique, url, lineStart, lineEnd )
		{
			this.onHandleError = handler;
			this.LineStart = lineStart ? lineStart : -1;
			this.LineEnd = lineEnd ? lineEnd : -1;
			if (url) this.Url = url;
			if (unique) this.IsUniqueHandler = unique;
		},
		
		CheckFireError: function( msg, url, line )
		{
			url = url.replace(/^http:\/\/.*?\//, "/");
			
			if ( ( this.LineStart <= line || this.LineStart < 1 ) &&
				( this.LineEnd >= line || this.LineEnd < 1 ) &&
				(url.indexOf(this.Url) > -1 || this.Url.indexOf(url) > -1) )
			{
				if ( this.onHandleError(msg, url, line, this) )
					return this.IsUniqueHandler;
			}
			
			return false;
		},
		
		onHandleError: function(msg, url, line, errorHandler) {}
	}
);

var ErrorClass = new Class();

ErrorClass.Declare(
	{
		ErrorHandlers: new ArrayProperty(),
		
		Constructor: function()
		{
			try
			{
				window.on("onerror", Reference(this.errorHandler, this));
			}
			catch(e)
			{
				window.onerror = Reference(this.errorHandler, this);
			}
		},
		
		errorHandler: function(msg, url, line)
		{
			for(var i = this.ErrorHandlers.length-1; i >= 0; --i)
			{
				var errorHandler = this.ErrorHandlers.get(i);
				if ( errorHandler.CheckFireError(msg, url, line) )
					return true;
			}
			
			return false;
		}
	}
);

///////////////////////////// Garbage Managment \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
var GarbageCollectorClass = new Class();

GarbageCollectorClass.Declare(
	{
		Objects: Static(new Array()),
		Events: Static(new Array()),
		
		Constructor: function()
		{
			try
			{
				window.on("onunload", Reference(this.OnPageUnload, this) );
			}
			catch(e)
			{
				window.onunload = Reference(this.OnPageUnload, this);
			}
		},
		
		OnPageUnload: function()
		{
			for(var i = 0; i < this.Events.length; ++i)
				removeEventListener( this.Events[i].Name, 
					this.Events[i].Value, this.Events[i].Element );
			
			// this.GarbageCollectElements("body");
			// this.GarbageCollectElements(null, true); // Very slow
			
			for(var i = 0; i < this.Objects.length; ++i)
				this.GarbageCollectObjects( this.Objects[i] );
		},
		
		markObject: function( obj )
		{
			this.Objects[ this.Objects.length ] = obj;
			return;
		},
		
		markEvent: function( name, value, element )
		{
			this.Events[ this.Events.length ] = {
				Value: value,
				Name: name,
				Element: element
			};
			return;
		},
		
		GarbageCollectObjects: function( obj )
		{
			if (obj && typeof obj == "object")
			{
				for(var prop in obj)
				{
					if (typeof obj[prop] == "object")
					{
						//if (isIE())
						//	this.GarbageCollectObjects(obj[prop]);
						delete obj[prop];
						obj[prop] = null;
					}
				}
				
				delete obj;
			}
		},
		
		GarbageCollectElements: function( el, collectChildren )
		{
			el = $element(el);
			if (!el) el = $element("html");
			for(var prop in el)
			{
				try
				{
					if ( el[prop] &&
						( typeof el[prop] == "function" || typeof el[prop] == "object" )
						)
					{
						delete el[prop];
						el[prop] = null;
					}
				}
				catch(e)
				{};
			}
			
			if (collectChildren)
				for(var i = 0; i < el.childNodes.length; ++i)
					this.GarbageCollectElements( el.childNodes[i] );
		}
	}
);

var __GC = new GarbageCollectorClass();

