/**
 * Use this object to create new "classes".
 * It takes care that any newly created object's
 * 'Initialize' method gets called - just like a 
 * constructor.
 */
var Class = 
{
	Create: function() 
	{
		return function() 
		{ 
			if(this.Initialize && typeof this.Initialize == 'function') 
			{ 
				this.Initialize.apply(this, arguments); 
			} 
		}
	}
}

/**
 * Simulates inheritance - copies all properties
 * from source to destination object.
 *
 * Why don't use 'prototype' and 'new' to assign
 * the Src's constructor? Because it doesn't work
 * like you would expect: 
 * It kills the old prototype's properties.
 */
Object.Extend = function(Dst, Src)
{
	for(Property in Src) {
		Dst[Property] = Src[Property];
	}
	return Dst;
}

Object.Inspect = function($Object, $ShowProperties) 
{
	try 
	{
		if($Object == undefined) { 
			return 'undefined';
		}
		
		if($Object == null) {
			return 'null';
		}
		
		if($ShowProperties) 
		{
			$Properties = '';
			
			for(var $Property in $Object) {
				$Properties += $Property + ': ' + $Object[$Property] + '\n';	
			}
			
			alert($Properties);
		}
		
		return $Object.Inspect ? $Object.Inspect() : $Object.toString();
	}
	catch($e)
	{
		alert($e.name + ' ' + $e.message);
	}
}

/**
 * Disabled because this would break the hash-map
 * abilities of an object.
 *
Object.prototype.Extend = function(Src)
{
	return Object.Extend(this, Src);
}
*/

// -- Array Extensions --------------------------------------------------------

/**
 * Some browsers don't support Array.push()..
 */
if(!Array.prototype.push)
{
	Array.prototype.push = function() 
	{
		var CurLength	= this.length;
		
		for(var i = 0; i < arguments.length; ++i) {
			this[CurLength + i] = arguments[i];
		}
		
		return this.length;
	}
}

/**
 * There's a weird bug with Array.concat():
 * It should add all elements from the HTMLCollection
 * list you get using getElementByName/TagName, 
 * but it just adds the "array" itself..!?
 *
 * I guess, it just does not regcognize a NodeList or
 * HTMLCollection as an array.
 */
Array.prototype.concat = function()
{
	var NewArray = new Array();
	
	for(var i = 0; i < this.length; ++i) {
		NewArray.push(this[i]);
	}
	
	for(var i = 0; i < arguments.length; ++i) 
	{
		var Argument = arguments[i];
		
		// Is the argument an array itself?
		// So add all of it's elements.
		//
//		if(	Argument instanceof Array ||
//			Argument instanceof NodeList ||
//			Argument instanceof HTMLCollection) 

		// Unfortunately IE sucks again. It doesn't know about NodeList
		// and HTMLCollection neither about whether item or namedItem is
		// a function's instance. So we have to fake this by checking for
		// length and item() to be defined.. :(
		// So let's cross our fingers and hope this will work in most cases.
		//
		if(Argument instanceof Array || (Argument.length != 'undefined' && Argument.item != 'undefined'))
		{
			for(var k = 0; k < Argument.length; ++k) {
				NewArray.push(Argument[k]);
			}	
		} 
		else 
		{
			NewArray.push(Argument);	
		}	
	}
	
	return NewArray;
}


// -- Function Extensions -----------------------------------------------------

/**
 * Binds a function to an object
 * using a closure (partial function).
 */
Function.prototype.Bind = function(object)
{
	var __Method = this;
	return function() { return __Method.apply(object, arguments); }
}

Function.prototype.BindAsEventListener = function($Object)
{
	var $Method = this;
	return function($Event) { return $Method.call($Object, $Event || window.event); };
}

/**
 * Some older browsers (IE5..) don't support
 * Function's apply() - let's add it.
 */
if(!Function.prototype.apply)
{
	Function.prototype.apply = function($Object, $Arguments)
	{
		if(!$Object) {
			$Object = window;
		}
		
		if(!$Arguments) {
			$Arguments = [];
		}
		
		$Parameters = [];

		for(var $i = 0; $i < $Arguments.length; ++$i) {
			$Parameters.push('$Arguments['+ $i +']');
		}

		$Object.__apply	= this;
		var $Result = eval('$Object.__apply(' + $Parameters.join(', ') + ')');
		$Object.__apply	= null;
		
		return $Result;
	}
}

Function.Empty 		= function() { };
Function.Identity	= function($Whatever) { return $Whatever; };


// -- String Extensions -------------------------------------------------------

Object.Extend(String.prototype, {
	StartsWith: function(s) {
		return this.substr(0, s.length) == s;
	},
	
	EndsWith: function(s) {
		return this.substr(this.length - s.length, s.length) == s;		
	},
	
	Trim: function($s) {
		return this.replace(/^\s+|\s+$/, '');
	},
	
	Camelize: function()
	{
		var $Parts 	= this.split('-');
		var $Result = $Parts[0];
		
		if($Parts.length > 1) 
		{
			if(this.indexOf('-') == 0) {
				$Result = $Parts[0].charAt(0).toUpperCase() + $Parts[0].substring(1)
			}
			
			for(var $i = 1; $i < $Parts.length; ++$i)
			{
				var $S 	= $Parts[$i];
				$Result += $S.charAt(0).toUpperCase() + $S.substring(1);
			}
		}
		
		return $Result;
	},
	
	MultiSplit: function($Tokens) 
	{
		$Words = [this];
		
		$Tokens.Each(function($Token)
		{
			$NewWords = [];
		
			$Words.Each(function($Word) {
				$Word.split($Token).Each(function($Part) { 
					if($Part.length > 0) { $NewWords.push($Part); } 
				});				
			});
				
			$Words = $NewWords;
		});	
	
		return $Words;
	},
	
	ParseColor: function() 
	{
		var $Color = '#';
		
		if(this.slice(0, 1) == '#') 
		{
			if(this.length == 4) // must be shorthand-hex
			{
				for(var $i = 1; $i < 4; ++$i) {
					$Color += this.CharAt($i) + this.CharAt($i);
				}
			} 
			else if(this.length == 7) // already in #xxxxxx format
			{
				$Color = this;
			}
		}
		else if(this.slice(0, 4) == 'rgb(') // rgb() format
		{
			var $ColorParts = this.slice(4, this.length - 1).split(' ');
			
			$ColorParts.Each(function($c) { 
				$Color += parseInt($c).ToHex(); 
			});
		}
		
		$Color = $Color.toLowerCase();
		
		return ($Color.length ==  7) ? $Color : (arguments[0] || this);
	}
});

Object.Extend(Number.prototype, 
{
	ToHex: function() 
	{
		var $Digits = this.toString(16);
		return (this < 16) ? '0'+$Digits : $Digits;
	}
});


// -- Enumerable --------------------------------------------------------------

// This is an interface which provides several
// methods to work on enumerable collections.
// The implementing object has to define the
// special __Each method by itself to provide
// correct iteration over it's values.
//

var $ContinueMessage	= {};
var $BreakMessage 		= {};

// TODO: Warum klappt das nicht, wenn $Continue/$BreakMessage
// Member von Enumerable sind? Da kommt immer 'undefined'
// bei dem catch($e) Block..!?
//

var Enumerable =
{
	// --------------------------------------------------------------------------
	// Each() tries to call an custom defined method __Each
	// of the implementing object providing a lambda function
	// which calls the user-specified iterator function.
	// --------------------------------------------------------------------------
	Each: function($IteratorFunc)
	{
		var $Index = 0;
		
		try 
		{
			// As long as no exception (except of our own ContinueMessage)
			// has been thrown it iterates over the collection.
			//
			this.__Each(function($Value) { 
										try {
											$IteratorFunc($Value, $Index++); 
										} catch($e) {
											if($e != $ContinueMessage) {
												throw $e;
											}
										}
									});
		}
		catch($e)
		{
			// If __Each throws an exception which is no 'normal'
			// BreakMessage -> something went wrong.
			//
			if($e != $BreakMessage) {
				throw $e;
			}
		}
	},
	
	
	// --------------------------------------------------------------------------
	// Checks if ALL elements satisfy the conditions
	// defined by the iterator function.
	// --------------------------------------------------------------------------
	All: function($IteratorFunc)
	{
		var $Result = true;
	
		this.Each(
			function($Value, $Index) 
			{
				if(!($Result &= ($IteratorFunc || Function.Identity)($Value, $Index))) {
					throw $BreakMessage;
				}
			});
	
		return $Result;
	},
	
	
	// --------------------------------------------------------------------------
	// Checks if ANY element satisfy the conditions
	// defined by the iterator function.
	// --------------------------------------------------------------------------
	Any: function($IteratorFunc)
	{
			var $Result = false;
			
			this.Each(
				function($Value, $Index) 
				{
					if($Result |= ($IteratorFunc || Function.Identity)($Value, $Index)) {
						throw $BreakMessage;
					}
				});

			return $Result;
	},
	

	// --------------------------------------------------------------------------
	// Returns an array with the results from applying the iterator function
	// to every element in the original array.
	// --------------------------------------------------------------------------
	Map: function($IteratorFunc)
	{
		var $Results = [];
		
		this.Each(
			function($Value, $Index) {
				$Results.push(($IteratorFunc || Function.Identity)($Value, $Index));
			}
		);
		
		return $Results;
	},
	
	
	// --------------------------------------------------------------------------
	// Returns true if an object is in this collection
	// or false otherwise.
	// --------------------------------------------------------------------------
	Contains: function($Object)
	{
		var $Found = false;
		this.Each(
			function($Value) 
			{
				if($Value == $Object) 
				{
					$Found = true;
					throw $BreakMessage;
				}
			}
		);
		
		return $Found;
	},

	
	// --------------------------------------------------------------------------
	// Returns an array containing all elements satisfying
	// the iterator function's conditions.
	// --------------------------------------------------------------------------
	FindAll: function($IteratorFunc)
	{
		var $Results = [];
		
		this.Each(
			function($Value, $Index) {
				if($IteratorFunc($Value, $Index)) {
					$Results.push($Value);
				}
			}
		);
		
		return $Results;
	},
	

	// --------------------------------------------------------------------------
	// Returns the first element which satifies
	// the iterator function's conditions.
	// --------------------------------------------------------------------------
	FindFirst: function($IteratorFunc)
	{
			var $Result;
			this.Each(
				function($Value, $Index) {
					if($IteratorFunc($Value, $Index)) {
						$Result = $Value;
						throw $BreakMessage;
					}
				}
			);
			
			return $Result;
	},
	
	
	// --------------------------------------------------------------------------
	// Returns one array containing two arrays -
	// the first one contains all elements satisfying
	// the conditions, the second one contains all
	// other elements - so it partitions the original
	// array into two sub-arrays.
	// --------------------------------------------------------------------------
	Partition: function($IteratorFunc)
	{
		var $True 	= [];
		var $False	= [];
		
		this.Each(
			function($Value, $Index) 
			{
				(($IteratorFunc || Function.Identity)($Value, $Index)
					? $True
					: $False
				).push($Value);
			}
		);
		
		return [$True, $False];
	}
}

Object.Extend(Object.Extend(Array.prototype, Enumerable), 
{
	__Each: function($IteratorFunc)
	{
		for(var $i = 0; $i < this.length; ++$i) {
			$IteratorFunc(this[$i]);
		}
	}
});

