/************************************************************************
JavaScript.net
File: System.js
Author: Scott Wood
Version: 2.4.0.1
Description: Provides base system framework implementations
This software is provided under the Open Software License v. 2.1 Agreement
=========================================================================
* 2.4.0.1
+ Updated method cases
+ Added Enum contains Method
* 2.4.0.2
+ Added null checking to methods
*************************************************************************/
//Returns true if object has been initialized
Object.prototype.initialized = false;
//Initializes the object and sets the initialized property to true
Object.prototype.initialize = function()
{
	//Prepare the default result
	var result = this.initialized;
	//If the class has not been initialized 
	if(!this.initialized)
	{
		//Initialize the class
		this.initialized = true;
	}
	return result;
}
//The base class the object is inheriting from
Object.prototype.base = null;
//Provides ability to inherit from a base class
Object.prototype.inherit = function($class)
{	
	//Asset method parameters
	if($class == null)
	{
		throw new ArgumentNullException('class');
	}
	if(this.prototype.base != null)
	{
		throw new MultipleInheritanceException();
	}
	//Set the objects prototype construct to the supplied classes
	this.prototype = new $class()
	//Set the base property to the supplied class prototype
	this.prototype.base = $class.prototype
}
//A collection of class implementations the object is implementing
Object.prototype.implementations = [];
//Implements the provided implementation
Object.prototype.implement = function(implementation)
{
	if(implementation == null)
	{
		throw new ArgumentNullException('implementation');
	}
	this.implementation = implementation
	this.implementation()
	this.implementations[implementation] = implementation.prototype;
	delete this.implementation;
}
//Trims characters from the beginning of a string
String.prototype.trimStart = function(character)
{
	character = character || '\\s'
	var regExp = new RegExp('^'+ character +'+','g');	
	return this.replace(regExp, '');
}
//Trims characters from the end of a string
String.prototype.trimEnd = function(character)
{
	character = character || '\\s'
	var regExp = new RegExp(''+ character +'+$','g');	
	return this.replace(regExp, '');
}
//Trims characters from the beginning and end of a string
String.prototype.trim = function(character)
{
	return this.trimStart(character).trimEnd(character)
}
//Extends the error objects toString method to return the actual error message
Error.prototype.toString = function()
{
	return this.message;
}
//Provides keyword to declaring namespace in a script
function namespace(domain,major,minor,build,revision)
{
	//Assert method parameters
	if(domain == null)
	{
		throw new ArgumentNullException('domain');
	}
	major = major || 0;
	minor = minor || 0;
	build = build || 0;
	revision = revision || 0;
	//Initialize local variables
    var elements = domain.split('.'); 
    var root = window;
    //For each element in the elements collection
    for(var index = 0;index < elements.length; index++)
    {
		//If the root at the elements index is undefined
        if(typeof root[elements[index]] == 'undefined'){
			//Set the root at the elements index to a new namespace if the index is greater than zero provide the root as a parent or the current window object.
            root[elements[index]] = new Namespace((index > 0) ? root : window,domain,major,minor,build,revision)
            //If the root contains a version
			if(root.version)
			{
				//check to make sure it is compatible with the root namespace if not throw an exception.
				if(!root.version.isCompatible(root[elements[index]].version))
				{
					throw new IncompatibleNamespaceException(root,root[elements[index]])
				}
			}
        }
        //Set the root to the root at the elements index
        root = root[elements[index]];
    }
    return root;
}
//Provides a keyword to including another script with the executing script
function using(domain,src)
{
	//Assert method parameters
    src = src || System.source + domain + '.js';
    domain = domain.split('.');
    //Initialize local varibles
    var used = false;
    var root = window;
    //For each element in the domain namespace collection
    for(var index = 0;index < domain.length; index++)
    {
		//Reset used flag to false
        used = false;
        //If the foot at the domain index is not undefined
        if(typeof root[domain[index]] != 'undefined')
        {
			//Set the root to the root at the domain index
            root = root[domain[index]];
            //Set the used flag to true the script has been used before in the current window
            used = true;
        }
    }
    //If the script has not been used before then download it and evaluate it
    if(!used)
    {
        var connection = (window.ActiveXObject) ? new ActiveXObject("MSXML2.XMLHTTP") : new XMLHttpRequest();
        connection.open("GET",src,false,null,null);
        connection.send(null);
        eval(connection.responseText);
    }
}
//Represents a namespace
function Namespace(parent,domain,major,minor,build,revision)
{
	if(parent == null)
	{
		throw new ArgumentNullException('parent');
	}
	if(domain == null)
	{
		throw new ArgumentNullException('domain');
	}
    this.parent = parent;
    this.domain = domain;
    this.version = new Version(major,minor,build,revision)
    if(!Namespace.initialize())
    {
		//Overwrite the object toString method and return namespace domain and version.
        Namespace.prototype.toString = function()
        {
			return '' + this.domain + ' Version ' + this.version.toString()
        }
    }
    //Represents a namespace version
	function Version(major,minor,build,revision)
	{
		this.major = (typeof major != 'undefined') ? major : Version.Any;
		this.minor = (typeof minor != 'undefined') ? minor : Version.Any;
		this.build = (typeof build != 'undefined') ? build : Version.Any;
		this.revision = (typeof revision != 'undefined') ? revision : Version.Any;
		if(!Version.initialize()){
			Version.Any = -1;
			//Overwrite the object toString method and return version formated as string.
			Version.prototype.toString = function()
			{
				var result = '';
				result += (this.major == Version.Any) ? '*' : this.major
				result += '.'
				result += (this.minor == Version.Any) ? '*' : this.minor
				result += '.'
				result += (this.build == Version.Any) ? '*' : this.build
				result += '.'
				result += (this.revision == Version.Any) ? '*' : this.revision
				return result;			
			}
			//Returns true of version equal the current instance
			Version.prototype.equals = function(version)
			{
				return (this.major == version.major && this.minor == version.minor && this.build == version.build && this.revision == version.revision)
			}
			//Returns -1 if version is less than the current instance +1 if version is greater and 0 if version is equal.
			Version.prototype.compare = function(version)
			{
				var result = -1;
				if(this.equals(version))
				{
					result ++
				}
				else if(this.major <= version.major && this.minor <= version.minor && this.build <= version.build && version.revision <= this.revision)
				{
					result ++
				}
				return result;			
			}
			//Returns true if the version provided is compatible with the current instance
			Version.prototype.isCompatible = function(version)
			{
				return ((this.major == version.major || this.major == Version.Any || version.major == Version.Any) && (this.minor <= version.minor || this.minor == Version.Any || version.minor == Version.Any))
			}
		}
	}
}
//Represents a loosely coupled event
function Event(sender)
{
	if(sender == null)
	{
		throw new ArgumentNullException('sender');
	}
	this.sender = sender;
    this.subscriptions = [];
    if(!Event.initialize())
    {
		//Raise the event to all subscribers
		Event.prototype.raise = function()
		{
			for(var index = 0; index < this.subscriptions.length; index++)
			{
				this.subscriptions[index](this.sender,arguments);
			}   
		}
		//Add a subscriber handle
		Event.prototype.subscribe = function(handle)
		{
			if(handle == null)
			{
				throw new ArgumentNullException('handle');
			}
			this.subscriptions[this.subscriptions.length] = handle;
		}
		//Remove a subscriber handle
		Event.prototype.unsubscribe = function(handle)
		{
			if(handle == null)
			{
				throw new ArgumentNullException('handle');
			}
			var removed = null;
			for(var index = 0; index < this.subscriptions.length; index++)
			{
				if(this.subscriptions[index] == handel)
				{
					removed = index;
					index ++
				}
				if(removed)
				{
					this.subscriptions[removed] = this.subscriptions[index];
					removed ++;
				}
			}
			if(removed)
			{
				delete this.subscriptions[index];
			}		
		}
    }
}
//Represents an enumerated type
function Enum()
{
	if(!Enum.initialize())
	{
		//Convert the enumerated type to a string
		Enum.prototype.toString = function(value)
		{
			var result = null;
			for(member in this)
			{
				if(this[member] == value)
				{
					result = member;
				}

			}
			return result;
		}
		//Parse the enumerated type to an object
		Enum.prototype.parse = function(value)
		{
			var result = null;
			for(member in this){
				if(member == value)
				{
					result = this[member];
					break;
				}
			}
			return result;
		}
		//Returns true if value is contained in the enumeration
		Enum.prototype.contains = function(value)
		{
			var result = false;
			for(member in this){
				if(this[member] == value)
				{
					result = true;
					break;
				}
			}
			return result;	
		}
	}
}
//Represents an exception
Exception.inherit(Error)
function Exception(message)
{
	message = message || '';
	Error.call(this,message)
	this.message = message;
}
//Represents an exception generated when a namespace is not compaitible with the namespace provided
IncompatibleNamespaceException.inherit(Exception)
function IncompatibleNamespaceException(existing,declared)
{
	if(existing == null)
	{
		throw new ArgumentNullException('existing');
	}
	if(declared == null)
	{
		throw new ArgumentNullException('declared');
	}
	Exception.call(this,'The version provided in '+ declared.toString() +' is not compatible with '+ existing.toString());
	this.existing = existing;
	this.declared = declared;
}
MultipleInheritanceException.inherit(Exception)
function MultipleInheritanceException()
{
	MultipleInheritanceException.call(this,'A class can only inherit from more than one class');
}
ArgumentException.inherit(Exception)
function ArgumentException(message)
{
	Exception.call(this,message);
}
ArgumentNullException.inherit(ArgumentException)
function ArgumentNullException(argument)
{
	ArgumentException.call(this,'The argument '+ argument +' cannot be null.');
	this.argument = argument;
}
namespace('System',2,4,0,2);
System.source = ''
System.Console = new Object()
{
	var $window = null;
	with(System)
	{
		Console.open = function()
		{
			if(!$window || $window.closed)
			{
				$window = window.open('about:blank','Console','location=no,menubar=no,scrollbars=yes,resizable=yes,status=no,titlebar=yes,toolbar=no');
				this.write('<html><head><body><tt>');
			}
		}
		Console.close = function()
		{
			if($window && !$window.closed)
			{
				this.write('</tt></body></head></html>');
				$window.close();
			}
		}
		Console.write = function(message)
		{
			if($window && !$window.closed)
			{
				message = message || '';
				$window.document.write(message)
			}
		}
		Console.writeLine = function(message)
		{
			if($window && !$window.closed)
			{
				message = message || '';
				$window.document.writeln(message +'<br/>')
			}
		}
	}
}