/**
 * @fileoverview BWinProto JavaScript framework, version 4.25
 *
 * <pre>
 * Copyright(c) 2003-2007 by BWin Technologies Ltd.
 * http://www.bwintech.com
 * Quay House, South Esplanade, St. Peter Port
 * GY1 4EJ Guernsey, CI, UK
 * All rights reserved.
 * </pre>
 */

/*
** The BWin Prototype
** ------------------
**
** This version of the BWinPrototype will contain more documentation about how
** stuff is done. A better and improved version of the AJAX object and a whole
** brand spanking new debugger module.
**
*/
/*jsl:option explicit*/

var BWinProto = {
	version: "4.25",
	date: "26-09-2006",

	HTA: document.mimeType == "HTML Application" ? true : false,

	debug: false,
	debugConsole: false,
	extDebug: false
};

function $extDebug(){ BWinProto.extDebug ? function(){debugger;}() : null; }

/**	Change Log:
*	-----------
*   New: +, Improvements: -, BugFixes: !
*
*	26-09-2006. v2.25
*		+ String now knows a toUTF8() method. Wiht this method your string will be
*		  converted to a UTF8 string. toUTF8() Can take one argument if set to true it
*		  will also add a UTF8 header.
*		- The AJAX ombject will now look for the latest MSXML object available on the
*		  client machine.
*
*	02-08-2006. v4.24
*		! Fixed a bug in the Object.extend() method. If a property was null in the source
*		  object and did not excist in the target object it did not get copied either.
*		  This was easaly fixed by checking the targets obejct prototype and not allowing
*		  a type cast during the compare.
*
*	19-07-2006. v4.23
*		- Improved the Class.extend() method alot. Now when a class inherets everything
*		  from it's super classes it is also set in the prototype. So it can be accessed
*		  from the instance via this.constructor.prototype.
*
*	14-07-2006. v4.22
*		! Fixed a bug in the fromChars method. This will now simple replace the unicodes
*		  for qoute, d.qoute and backslash with there appropeate char.
*
*		- Created primitive files for every JS primitive, except undefined and NULL.
*		- toChars now will serialized sub objects and check for circular refrences
*		  and insert a empty object and a comment saying;
*		   / * Circular reference to <<Name of circular object>> * /
*
*	13-07-2006. v4.21
*		! Found a very very nast bug in WSH in wich the instance of a class
*		  does not inheret the prototype correctly. This came up in the toChar
*		  method and there is a work around now.
*
*		- Completed and got the tree.toChars done. Now you can build a tree and
*		  tear it down to a simple function wich in its turn build the same tree
*		  for you when evaluated.
*
*	12-07-2006. v4.20
*		! Fixed some minor bugs in the toChars method
*
*		- The toChars method does not char sub object since these can contain
*		  a nasty circular reference.
*
*	11-07-2006. v4.19
*		+ Built a true generic toChars method. This will convert simple objects
*		  to a JSON object/string.
*
*	08-07-2006. v4.18
*		- When BWproto is loaded in a frame it will check if BWproto was loaded
*		  in top. If so all debug info will go there. Just like the popup.
*
*	07-07-2006. v4.17
*		! Fixed the opener debug issue. All the Debug info is not routed to one
*		  debug window. Some better control over this should be written some time soon
*		  but for now it is ok.
*
*		- Now the AJAX.options.postDataType is now case insensetive.
*		- Added 'userName' and 'userPass' options to the AJAX.options object. If a
*		  request has to be done with a userName and password combination this now
*		  can be done in a nice and standard way.
*
*	08-06-2006. v4.16
*		! Fixed The popup debug output. Kind of hackish though... Still needs a better
*		  looking at. It doesn't work completly.
*
*	07-06-2006. v4.15
*		! Fixed a little URL construction bug in the AJAX object.
*		! Fixed document.getElemenetsByClassName() It now simply returns
*		  an arry with all the elements with a particular className. And doesn't
*		  throw any nasty errors.
*
*		- Incase the window were located in is a popup and debugging is enabled
*		  and there is a console. Forward all debugging info to the parent window.
*
*	06-06-2006. v4.14
*		! Fixed a big time memory leak with the b64 hack. Forgot to close the
*		  the file we were reading out. So this would fill up the memory gradually.
*
*	02-06-2006. v4.13
*		! Fixed the object argument with $v() and $s().
*
*	01-06-2006. v4.12
*		! Fixed the Crypto.encodeFile() issue with binary files. Sort of a
*		  hackish fix though.
*		! Fixed the bad output and the error when loading a new file or
*		  refreshing the page. This was related to the events.
*
*	29-05-2006. v4.11
*		- Some minor tweaks to the treenode class.
*		- The output console is now limited to 8192 charecters (8k text). If it
*		  overflows it will be cut to 4096 chars. (or the closes new line). This
*		  should speed up the console.
*
*	24-05-2006. v4.10
*		- Changed the encode algorytems for the base64 encoders. Now using the 6bit
*		  table instead of the 12bit tables. A 'patch' file can always be included
*		  to overwrite the slower functions with the faster one if there is any need
*		  for it. This shaves about 30kb off the complete file.
*
*	23-05-2006. v4.9
*		+ Added a replaceCharAt(index, char) method to the string object. This will
*		  a single char at the given location.
*
*		- Crypto.base64.decodeToASCII now chops off the EOT (End Of Text) char.
*
*	22-05-2006. v4.8
*		- Improved the b64 and added sevral functions for encoding and decoding
*		  base64 strings. Although the decoder is not fully optimised it will
*		  do for the time being.
*
*	21-05-2006. v4.7
*		+ New B64 module b64_encrypt() and b64_decrypt()
*
*	20-05-2006. v4.6
*		! Fixed a little bug for the events in Ajax.Updater.
*
*		+ New Ajax.FormPost. You will be able to "add" form elements with
*		  the addFormElement() function. And the form can be submittend
*		  with the submitForm() method.
*
*	19-05-2006. v4.5
*		! Nested Ajax object, fixed by creating a request manager
*		! Fixed the HTA onUnload
*
*		+ Support for HTA's
*
*		- Some minor fixes to the AJAX object
*		- The AJAX options object now contains all options. If any new options are
*		  during the creation the exciting ones will be overwritten. This prevents
*		  prototype contamination.
*		- Simplyfied and imporoved Event code for AJAX objects
*		- JSlint optimizations no more use of with(){}
*		- Added nifty little debug options BWinProto.extDebugger. When set on
*		  true you a debugger is called using the function $extDebug()
*
**/


var Class = {
	create: function(initFunc){
		return !initFunc ?
			function(){this.initialize.apply(this, arguments);} : initFunc;
	},

	extend2: function(initFunc){
		var returnFunction = !initFunc ?
			function(){this.initialize.apply(this, arguments);} : initFunc;

		for(var n = arguments.length - 1; n > 0 ; n--){
			//TRACE(0, "Class: extend :: arg[" + n + "] is of type " + arguments[n].isType());
			Object.extend(returnFunction.prototype, arguments[n].prototype); }

		return returnFunction;
	},

	// "extends" can not be used it is a reserved word.... BOOO!
	extend: function(){
		var returnFunction = function(){ this.initialize.apply(this, arguments); };

		for(var n = arguments.length - 1; n > -1 ; n--){
			//TRACE(0, "Class: extend :: arg[" + n + "] is of type " + arguments[n].isType());
			Object.extend(returnFunction.prototype, arguments[n].prototype); }

		return returnFunction;
	},

	privateProperty: function(val, exposeName, options){
		if(!exposeName || exposeName.isType() != "string"){
			TRACE(0, "Obj: Unable to add private propertie, exposure name was invaled.");
			return;
		}

		// Setting some vars
		var self = this;
		var fExposeName = exposeName.charAt(0).toUpperCase() + exposeName.substring(1, exposeName.length);

		// Checking if the exposed methods already exist.
		if( self["set" + fExposeName]			|| self["get" + fExposeName]			||
			self["set" + fExposeName + "Type"] 	|| self["get" + fExposeName + "Type"]	){
			TRACE(0, "Obj: Unable to add private propertie, exposed methods already excisted", "BWinProto");
			return;
		}

		// Creating the options object if it is not there yet.
		if(!options){ options = {}; }

		// Setting some stuff so we can set what we are actually exposing for the
		// private property.
		if(!options.exposeGet || options.exposeGet.isType() != "boolean"){ options.exposeGet = true; }
		if(!options.exposeSet || options.exposeSet.isType() != "boolean"){ options.exposeSet = false; }
		if(!options.exposeTypeSet || options.exposeTypeSet.isType() != "boolean"){ options.exposeTypeSet = false; }
		if(!options.exposeTypeGet || options.exposeTypeGet.isType() != "boolean"){ options.exposeTypeGet = true; }

		// Setting some behaviour for the private prop.
		if(!options.allowTypeCast || options.allowTypeCast.isType() != "boolean"){ options.allowTypeCast = true; }
		if(!options.allowUnsafeSet || options.allowUnsafeSet.isType() != "boolean"){ options.allowUnsafeSet = false; }

		return new (function(){
			var value = val;

			var allowTypeCast = options.allowTypeCast;
			var allowUnsafeSet = options.allowUnsafeSet;

			// privates
			function set(nVal){
				if(nVal.isType() != value.isType()){ return; }
				value = nVal;
			}

			function get(){
				return value; }

			function typeCast(newType){
				value = new newType(); }

			// setters and getters;
			if(options.exposeGet){
				self["get" + fExposeName] = function(){
					return get();
				};
			}
			if(options.exposeSet){
				self["set" + fExposeName] = function(nVal){
					if(	allowUnsafeSet === false			&&
						nVal.isType() != (get()).isType()	){ return; }

					set(nVal);
					return get();
				};
			}

			// type setters and getters
			if(allowTypeCast === true){
				self["set" + fExposeName + "Type"] = function(nType, args){
					set(new nType.apply(this, args));
					return get();
				};
			}
			if(options.exposeTypeGet === true){
				self["get" + fExposeName + "Type"] = function(){
					return (get()).isType();
				};
			}
		});
	}
};

Object.extend = function(destination, source) {
	for(var property in source){
		if(destination.constructor.prototype[property] === source[property]){ continue; }
		destination[property] = source[property];
	}
	return destination;
};

// Port all info to the parent winow debugging console.
if(window.opener && window.opener.BWinProto){
	var $DBG = {
		outConsoles:	window.opener.$DBG.outConsoles,
		windowCleaner:	window.opener.$DBG.windowCleaner,
		consoleClasses:	window.opener.$DBG.consoleClasses,

		message: function(dLvl, dMes, targetIds, targetTypes){
			return window.opener.$DBG.message(dLvl, dMes, targetIds, targetTypes); },

		addConsole: function(dLvl, id, type, options){
			return window.opener.$DBG.addConsole(dLvl, id, type, options); },

		removeConsole: function(targetIds, targetTypes){
			return window.opener.$DBG.removeConsole(targetIds, targetTypes); }
	};
}else if((top.location.href != window.location.href) && top.BWinProto){
		var $DBG = {
		outConsoles:	top.$DBG.outConsoles,
		windowCleaner:	top.$DBG.windowCleaner,
		consoleClasses:	top.$DBG.consoleClasses,

		message: function(dLvl, dMes, targetIds, targetTypes){
			return top.$DBG.message(dLvl, dMes, targetIds, targetTypes); },

		addConsole: function(dLvl, id, type, options){
			return top.$DBG.addConsole(dLvl, id, type, options); },

		removeConsole: function(targetIds, targetTypes){
			return top.$DBG.removeConsole(targetIds, targetTypes); }
		};
}else{
	var $DBG_console = Class.create();
	$DBG_console.prototype = {
		id: null,
		debugLevel: -1,

		out: function(dLvl, dMes){ return; },

		initialize: function(dLvl, id, type, options){
			if(	typeof(dLvl)	!= "number"	||
				typeof(id)		!= "string"	||
				typeof(type)	!= "string"	){
				return;
			}else if(!$DBG.consoleClasses[type]){
				return;
			}

			// making sure we won't get a out of range error when trying to reed options.height or similar.
			if(!options){ options = {}; }

			this.id = id;
			this.debugLevel = dLvl;

			Object.extend(this, $DBG.consoleClasses[type]);

			this.consoleInit(options);
		}
	};

	var $DBG = {
		outConsoles: [],
		windowCleaner: false,

		consoleClasses: {
			// Array output. Will always be done no matter what happens.
			arr: {
				outputArray: [],

				getLog: function(){
					return this.outputArray.join("\n");
				},

				out: function(dLvl, dMes){
					// if(dLvl < this.debugLevel){ return; }
					this.outputArray.push([dLvl, dMes]);
				},

				consoleInit: function(){ return; }
			},

			// Log files. Write the debug information to a file.
			logFile: {
				fSys: null,

				out: function(dLvl, dMes){
					if(dLvl < this.debugLevel){ return; }
					var fileOut = this.file.OpenAsTextStream(8, -2);
					fileOut.write("\n" + dMes);
					fileOut.close();
				},

				consoleInit: function(options){
					this.fSys = new ActiveXObject("Scripting.FileSystemObject");

					// Check if we got some options on where write the output to.
					if(options.file){
						if(!this.fSys.GetFile(options.file)){
							if(!options.createFile){ return; }
							// We can create a file
							this.fSys.CreateTextFile(options.file);
						}

						this.file = this.fSys.GetFile(options.file);

					}else{
						if(!options.createFile){ return; }

						var filename = "./" + (new Date()).getTime() + "." + this.id + ".bwin.log";
						this.fSys.CreateTextFile(filename);
						this.file = this.fSys.GetFile(filename);
					}
				}
			},

			// Output is passed on to a custom function...
			func: {
				outFunc: function(){ return; },

				out: function(dLvl, dMes){
					if(dLvl < this.debugLevel){ return; }
					this.outFunc.apply(this, arguments);
				},

				consoleInit: function(options){
					this.outFunc = options;
				}
			},

			// A popup window... Looking like a putty console or something in that direction.
			popup: {
				dWindow: null,
				dConsole: null,

				//winOptions: null,

				getFocus: true,

				out: function(dLvl, dMes){
					if(dLvl < this.debugLevel){ return; }
					if(!this.dWindow || !this.dConsole){
						// Don't trace this could cause a deadlock...
						// TRACE(0, "$DBG: unable to write to debug popup console. Console or window does not excist");
						return; }

					if(this.getFocus === true){ this.dWindow.focus(); }

					if(this.dConsole.innerText.length > 8192){
					this.dConsole.innerText = this.dConsole.innerText.substr(this.dConsole.innerText.length - 4096); }

					this.dConsole.innerText += "\n" + dMes;
					if(this.dConsole.scrollHeight - this.dConsole.clientHeight > 0){
						this.dConsole.scrollTop = this.dConsole.scrollHeight - this.dConsole.clientHeight; }
				},

				consoleInit: function(options){
					/* this.winOptions == null ?
						this.winOptions = options	:
						var options = this.winOptions; */

					this.getFocus = options.getFocus ? options.getFocus : false;
					this.dWindow = window.open("", (options.wName ? options.wName : "_" + (new Date()).getTime()), "width=" + (options.windowWidth ? options.windowWidth : 1100) + ",height=" + (options.windowHeight ? options.windowHeight : 700) + ",toolbar=0,resize=0,location=0,scrollbars=0,status=0");

					this.dWindow.document.title = options.title ? options.title : "debug window";

					// document.body style properties.
					this.dWindow.document.body.style.background	= options.background ? options.background : "black";
					this.dWindow.document.body.style.margin		= "0px";
					this.dWindow.document.body.style.padding	= "0px";

					// Setting the console properties
					this.dConsole = this.dWindow.document.createElement("textarea");
					this.dConsole.id = "dConsole";

					this.dConsole.style.background	= options.background ? options.background : "black";

					this.dConsole.style.position	= "absolute";
					this.dConsole.style.top			= "0px";	this.dConsole.style.left	= "0px";
					this.dConsole.style.height		= "100%";	this.dConsole.style.width	= "100%";

					this.dConsole.style.padding		= "0px";
					this.dConsole.style.border 		= "0px";
					this.dConsole.style.color 		= options.color			? options.color			: "#00FF00";
					this.dConsole.style.fontFamily	= options.fontFamily	? options.fontFamily	: "\"Courier New\", Courier, monospace";
					this.dConsole.style.fontSize	= options.fontSize		? options.fontSize		: "11px";
					this.dConsole.style.fontStyle	= "normal";
					this.dConsole.style.fontVariant	= "normal";
					this.dConsole.style.fontWeight	= "normal";

					this.dWindow.document.body.appendChild(this.dConsole);
				} // consoleInit();
			},

			// Post debug info to someplace exotic.
			post: {
				/*** Old Post stuff
					if(!options.url){ return; }
					this.url = options.url;
					this.prefix = (options.prefix ? options.prefix : "");

					this.out = function(dLvl, dMes){
						if(dLvl < this.debugLevel){ return; }
						var xHTTP = Ajax.Request(
							this.url,
							{	method: "post",
								postBody: this.prefix + dMes
							});
					};
				***/
				consoleInit: function(){ return; }
			}
		},

		message: function(dLvl, dMes, targetIds, targetTypes){
			if(this.outConsoles.length === 0){ return; }

			if(!targetIds){ var targetIds = "all"; }
			if(!targetTypes){ var targetTypes = "all"; }

			targetIds = (targetIds.replace(" ", "")).split(",");
			targetTypes = (targetTypes.replace(" ", "")).split(",");

			//debugger;
			if(targetIds[0] == "all" && targetTypes[0] == "all"){
				// Output on all the output consoles
				for(var n = 0; n < this.outConsoles.length; n++){
					this.outConsoles[n].out(dLvl, dMes); }
				return;

			}else if(targetIds[0] == "all"){
				// For all ID's
				for(var n = 0; n < this.outConsoles.length; n++){			// For all the out consoles
					for(var n1 = 0; n1 < targetTypes.length; n1++){			// For all the targetIds
						if(this.outConsoles[n].type == targetTypes[n1]){	// If id is the same
							this.outConsoles[n].out(dLvl, dMes);
				}	}	}

				return;

			}else if(targetTypes[0] == "all"){
				// For all types
				for(var n = 0; n < this.outConsoles.length; n++){			// For all the out consoles
					for(var n1 = 0; n1 < targetIds.length; n1++){			// For all the targetTypes
						if(this.outConsoles[n].id == targetIds[n1]){		// If type is the same
							this.outConsoles[n].out(dLvl, dMes);
				}	}	}

				return;

			}else{
				// depening on the given id(s) and type(s)
				for(var n = 0; n < this.outConsoles.length; n++){			// For all the out consoles
					var matchType = false;
					var matchId = false;

					// Look for matching type
					for(var n1 = 0; n1 < targetTypes.length; n1++){
						if(this.outConsoles[n].type == targetTypes[n1]){ matchType = true; } }

					// Look for matching IDs
					for(n1 = 0; n1 < targetIds.length; n1++){
						if(this.outConsoles[n].id == targetIds[n1]){ matchId = true; } }

					if(matchType === true && matchId === true){
							this.outConsoles[n].out(dLvl, dMes); }
				}

				return;
			}
		}, // message()

		addConsole: function(dLvl, id, type, options){
			/*** a close for open debug windows...
			if(this.windowCleaner === false && this.type == "popup"){
				Event.init(window, "unload",
					function(){
						for(var n = 0; n < $DBG.outConsoles.length; n++){
							try{ $DBG.outConsoles[n].close(); }
							catch(e){ continue; }
						}
					}, true);
			}
			***/
			this.outConsoles.push(new $DBG_console(dLvl, id, type, options));
		},

		removeConsole: function(targetIds, targetTypes){
			if(this.outConsoles.length === 0){ return; }

			if(!targetIds){ var targetIds = "all"; }
			if(!targetTypes){ var targetTypes = "all"; }

			targetIds = ((targetIds.toLowerCase()).replace(" ", "")).split(",");
			targetTypes = ((targetTypes.toLowerCase()).replace(" ", "")).split(",");

			if(targetIds[0] == "all" && targetTypes[0] == "all"){
				this.outConsoles = [];
				return;

			}else if(targetIds[0] == "all"){
				// For all ID's
				this.outConsoles.each( function(obj, index){
					targetTypes.each( function(_obj, _index){
						if(obj.type.toLowerCase() == _obj){ $DBG.outConsoles.slice(index, index + 1); }
					});
				});

				return;

			}else if(targetTypes[0] == "all"){
				// For all types
				this.outConsoles.each( function(obj, index){
					targetIds.each( function(_obj, _index){
						if(obj.id.toLowerCase() == _obj){ $DBG.outConsoles.slice(index, index + 1); }
					});
				});

				return;

			}else{
				// depening on the given id(s) and type(s)
				this.outConsoles.each( function(obj, index){
					targetIds.each( function(_obj, _index){
						targetTypes.each( function(__obj, __index){
							if(	obj.id.toLowerCase() == _obj 	&&
								obj.type.toLowerCase() == __obj	){

								$DBG.outConsoles.slice(index, index + 1);
							}
						});
					});
				});

				return;
			}
		}
	};

	// creating a debugger
	if(BWinProto.debug){
		$DBG.addConsole(0, "BWinProto", "arr", {});
		if(BWinProto.debugConsole){
			$DBG.addConsole(0, "BWinProto", "popup", { windowHeight: "500px", wName: "BWinProto" });
	}	}
} // We have no parent window so we will create our own debug console.

function TRACE(dLvl, dMes){
	try{ $DBG.message(dLvl, dMes, "BWinProto"); }catch(err){ } }

function cTRACE(dLvl, dMes, cId, cType){
	try{ $DBG.message(dLvl, dMes, cId, cType); }catch(err){ } }

var Try = {
	these: function() {
		var returnValue;

		for(var i = 0; i < arguments.length; i++){
			var lambda = arguments[i];
			try {
				returnValue = lambda();
				break;
			} catch (e) {}
		}
		return returnValue;
	}
};

var PeriodicalExecuter = Class.create();
PeriodicalExecuter.prototype = {
	callback: function(){ return; },
	frequency: 1,
	currentlyEcecuting: false,


	registerCallback: function() {
		setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
	},

	onTimerEvent: function() {
		if (!this.currentlyExecuting) {
			try {
				this.currentlyExecuting = true;
				this.callback();
			} finally {
				this.currentlyExecuting = false;
			}
		}
	},

	initialize: function(callback, frequency) {
		this.callback = callback;
		this.frequency = frequency;
		this.currentlyExecuting = false;

		this.registerCallback();
	}
};

/*--------------------------------------------------------------------------*/

document.getElementsByClassName = function(className, parentElement) {
	var results = [];
	var children = ($(parentElement) || document.body).getElementsByTagName('*');

	// Because the getElementsByTagName returns a DOM kind of arry it doesn't have
	// the fancy methods written for the array prototype.
	for(var n = 0; n < children.length; n++){
		if(children[n].className.match(new RegExp("(^|\\s)" + className + "(\\s|$)"))){
			results.push(children[n]);
	}	}

	return results;
};

function $() {
	var elements = new Array();

	for (var i = 0; i < arguments.length; i++) {
		var element = arguments[i];
		if (typeof element == 'string') { element = document.getElementById(element); }
		if (arguments.length == 1) { return element; }
		elements.push(element);
	}
	return elements;
}

function $v(element){
	if(!element.value){
		try{
			if(element.isType() == "string"){
				element = $(element); }
		}catch(err){
			TRACE(0, "$VAL: Invalid argument");
			return;
		}
	}
	return element.value;
}

function $s(element){
	if(!element.style){
		try{
			if(element.isType() == "string"){
				element = $(element); }
		}catch(err){
			TRACE(0, "$STYL: Invalid argument");
			return;
		}
	}
	return element.style;
}
// $$__objectArray: This array has to be global and is not meant for users to fool around
// with. When a Object.toChars() method is invoked and it encounters a subObject it is
// pushed on the array aswell. If that subObject is encouterd later on we know there
// are more then one reference there. And because we don't want to run the risk in getting
// stuck in a circular reference loop we need this array.
//
// recursionLevels: It keeps track of how often we have invoked the toChars() method. When
// it reaches 0 again the whole array will be flushed.
var $$__objectArray = [];
$$__objectArray.recursionLevels = 0;

Object.extend(Object.prototype, {
	/*
	** Return an object type by checking what the consturucter is. Works for everything except the
	** primitives. There will return "function" and not the real type they actually are wich is logical
	** it are the "primitives".
	** -- Dre
	*/
	isType: function(obj){
		if(!obj){ obj = this; }
		if(obj.constructor === Array){ return "array"; }
		if(obj.constructor === Boolean){ return "boolean"; }
		if(obj.constructor === Date){ return "date"; }
		if(obj.constructor === Function){ return "function"; }
		if(obj.constructor === Number){ return "number"; }
		if(obj.constructor === Object){ return "object"; }
		if(obj.constructor === RegExp){ return "regexp"; }
		if(obj.constructor === String){ return "string"; }
		if(obj.constructor === undefined){ return "undefined"; }
		if(!obj.constructor || obj.constructor === null){ return "null"; }
		return "unknown type";
	},

	toChars: function(){
		// This is the dirty fix for the prototype pointer bug...
		if(this.toChars != this.constructor.prototype.toChars){
			//TRACE(0, "OBJ: Breaking default toChars method for the defined method in the objexts prototype");
			return this.constructor.prototype.toChars.call(this); }

		try{
			// Adding one to the recusrion level.
			$$__objectArray.recursionLevels++;

			var serialized = "{";
			for(var obj in this){
				if(	this[obj] == this.constructor[obj]				||
					this[obj] == this.constructor.prototype[obj]	){
					//TRACE(0, "Skipped prototype object :: " + obj);
					continue;
				}else if(this[obj] === undefined){
					serialized += obj + ":undefined,";
					continue;
				}else if(this[obj] === null){
					serialized += obj + ":null,";
					continue;
				}else if(this[obj].isType() == "object"){
					var self = this;		// Scope issue
					var multiRef = null;	// If we found a reference to an exciting object this is what
											// going to point it out for us.

					// Check if we found an earlyer reference of this object.
					$$__objectArray.each(function(obj, indx, arr){
						if(arr[indx].obj == self){
							multiRef = arr[indx];
							return true;
					}	});

					// So were checking this object out. Put it on the object stack so we dont encounter it again...
					$$__objectArray.push({obj: this[obj], objName: obj});

					// generate the serialized string.
					serialized += obj + ":" + (multiRef === null ? this[obj].toChars() : "{} /* Circular reference to <<" + multiRef.objName + ">> */") + ",";

					continue;
				}else{
					serialized += obj + ":" + this[obj].toChars() + ",";
					continue;
				}
			}
		}catch(err){
			TRACE(0, err.message);
			serialized = "{/*Something went wrong serializing this object :: " + err.message + "*/}";
		}finally{
			$$__objectArray.recursionLevels--;
			if($$__objectArray.recursionLevels === 0){
				$$__objectArray = [];
				$$__objectArray.recursionLevels = 0;
			}
			return serialized.length > 1 ? serialized.substring(0, serialized.lastIndexOf(",")) + "}" : "{}";
		}
	}
}); // Extend(Object.prototype)

Object.extend(Object, {
	fromChars: function(charedObject){
		var outObj = (new Function("return " + charedObject))();
		for(var obj in outObj){
			if(!outObj[obj]){ continue; }
			if(outObj[obj].isType() == "string"){
				outObj[obj] = outObj[obj].replace(/\\u0027/g, "'").replace(/\\u0022/g, '"').replace(/\\005C/g, "\\");
		}	}
		return outObj;
	},

	protectedExtend: function(destination, source){
		for(var property in source){
			if(destination[property]){ continue; }
			destination[property] = source[property];
		}
		return destination;
	},

	inspect: function(object){
		try {
			if(object == undefined){ return 'undefined'; }
			if(object === null){ return 'null'; }
			return object.inspect ? object.inspect() : object.toString();
		} catch (e) {
			if (e instanceof RangeError){ return '...'; }
			throw e;
		}
	}
}); // Extend(Object)

// function string.inspect(){ return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'"; }
// function array.insepect(){ return '[' + this.map(Object.inspect).join(', ') + ']'; }
/*
	Array.collect: function(iterator) {
		var results = [];
		this.each(function(value, index) {
			results.push(iterator(value, index));
		});
		return results;
	}
**/


Object.extend(Function.prototype, {
	bind: function(object) {
		var __method = this;
		return function() {
			return __method.apply(object, arguments);
		};
	}, // bind()

	bindAsEventListener: function(object) {
		var __method = this;
		return function(event) {
			return __method.call(object, event || window.event);
		};
	},

	toChars: function(){ return this.toString(); }
});
Object.extend(Number.prototype, {
	toColorPart: function() {
		var digits = this.toString(16);
		if(this < 16){ return '0' + digits; }
		return digits;
	},
	toChars: function(){ return this.toString(); }
});




// Some string methods
Object.extend(String.prototype, {
	splice: function(start, end, spliceString){
		if(	!start	|| start.isType() != "number"	||
			!end	|| end.isType() != "number"		){
			return null;
		}

		if(!spliceString){ spliceString = ""; }

		var preSplice = this.substr(0, start);
		var postSplice = this.substr(end);

		return preSplice + spliceString + postSplice;
	},

	replaceCharAt: function(index, replacementChar){
		if(	index > this.length		||
			index < 0				){
			TRACE(0, "String: Invalid index unable to to replace char at " + index);
		}else if(!replacementChar){
			TRACE(0, "String: No replacement char was given.");
		}

		return this.substring(0, index - 1) + replacementChar + this.substring(index + 1);
	},

	toUTF8: function(addHeader){
		// Because we are creating a UTF-8 string we need to add the UTF-8 header aswell
		var outputString = addHeader === true ? (String.fromCharCode(0xEF) + String.fromCharCode(0xBB) + String.fromCharCode(0xBF)) : "";
		var charCode = 0x00;

		for(var n = 0; n < this.length; n++){
			charCode = this.charCodeAt(n);

			if(charCode < 0x00000080){
				outputString += String.fromCharCode(charCode);
				continue;
			}else if(charCode >= 0x00000080 && charCode <= 0x000007FF){
				outputString += String.fromCharCode(192 + (charCode / 64));
				outputString += String.fromCharCode(128 + (charCode % 64));
				continue;
			}else if(charCode >= 0x000007FF && charCode <= 0x0000FFFF){
				outputString += String.fromCharCode(224 + (charCode / 4096));
				outputString += String.fromCharCode(128 + ((charCode / 64) % 64));
				outputString += String.fromCharCode(128 + (charCode % 64));
				continue;
			}else if(charCode >= 0x0000FFFF && charCode <= 0x001FFFFF){
				outputString += String.fromCharCode(240 + (charCode / 262144));
				outputString += String.fromCharCode(128 + ((charCode / 4096) % 64));
				outputString += String.fromCharCode(128 + ((charCode / 64) % 64));
				outputString += String.fromCharCode(128 + (charCode % 64));
				continue;
			}else if(charCode >= 0x001FFFFF && charCode <= 0x03FFFFFF){
				outputString += String.fromCharCode(248 + (charCode / 16777216));
				outputString += String.fromCharCode(128 + ((charCode / 262144) % 64));
				outputString += String.fromCharCode(128 + ((charCode / 4096) % 64));
				outputString += String.fromCharCode(128 + ((charCode % 64) % 64));
				outputString += String.fromCharCode(128 + (charCode % 64));
				continue;
			}else if(charCode >= 0x03FFFFFF && charCode <= 0x7FFFFFFF){
				outputString += String.fromCharCode(252 + (charCode / 1073741824));
				outputString += String.fromCharCode(128 + ((charCode / 16777216) % 64));
				outputString += String.fromCharCode(128 + ((charCode / 262144) % 64));
				outputString += String.fromCharCode(128 + ((charCode / 4096) % 64));
				outputString += String.fromCharCode(128 + ((charCode / 64) % 64));
				outputString += String.fromCharCode(128 + (charCode % 64));
				continue;
			}else{
				TRACE(0, "toUTF8: The char at [" + n + "] can not be presented as a valid unicode character.");
				continue;
			}
		}

		return outputString;
	},

	toChars: function(){
		return "'" + this.replace(/\\/g, "\\u005C").replace(/'/g, "\\u0027").replace(/"/g, "\\u0022") + "'"; }

});
Object.extend(Boolean.prototype, {
	toChars: function(){
		return this.toString(); }
});Object.extend(Date.prototype, {
	toChars: function(){
		return this.toString(); }
});
Object.extend(RegExp.prototype, {
	toChars: function(){
		return this.toString(); }
});

var Event = {
	keyHandle: null,

	//this array will hold all the event observers.
	observers: [],

/**************************************************************************************************
	Hook events
**************************************************************************************************/
	init: function(element, name, observer, startHandle){
		TRACE(0, "BWinEvent: on" + name + " intialized to element, " + (element.id ? element.id : element.tagName), "BWinProto");

		/* Check if the observer 'funtion' is a string or actually a real function */
		switch(observer.isType()){
			case "function":
				this.observers.push([element, name, observer]);
				break;
			case "string":
				this.observers.push([element, name, new Function(observer)]);
				break;
			default:
				return null;
				break;
		}

		/* Are we going to start this event imediatly or just wait for a start(); */
		if(startHandle === true){ this.startHandle(element, name); }
	},

	hookOnId: function(hookID, name, observer, startHandle){
		this.init($(hookID), name, observer, startHandle);
		return;
	},

	hookOnTag: function(hookTag, name, observer, startHandle){
		var hookTag = document.getElementsByTagName(hookTag);
		for(var n = 0; n < hookTag.length; n++){
			this.init(hookTag[n], name, observer, startHandle);
		}
		return;
	},

	hookOnClass: function(hookClass, name, observer, startHandle){
		var hookClass = document.geElementsByClassName(hookClass);
		for(var n = 0; n < hookClass.length; n++){
			this.init(hookClass[n], name, observer, startHandle);
		}
		return;
	},

/**************************************************************************************************
	start events
**************************************************************************************************/
	startHandle: function(element, name){
		for(var n =0; n < this.observers.length; n++){
			if (this.observers[n][0] === element && this.observers[n][1] === name) {
				TRACE(0, "BWinEvent: on" + name + " started on element, " + (element.id ? element.id : element.tagName), "BWinProto");
				this.observers[n][0].attachEvent('on' + name, this.observers[n][2]);
		}	}
	},

	startHandleOnId: function(elementID, name){
		this.startHandle($(elementID), name);
	},

	startHandleOnTag: function(elementTagName, name){
		for(var n =0; n < this.observers.length; n++){
			if (this.observers[n][0].tagName === elementTagName && this.observers[n][1] === name) {
				this.observers[n][0].attachEvent('on' + name, this.observers[n][2]);
		}	}
	},

	startHandleOnClass: function(elementClassName, name){
		for(var n =0; n < this.observers.length; n++){
			if (this.observers[n][0].className === elementClassName && this.observers[n][1] === name) {
				this.observers[n][0].attachEvent('on' + name, this.observers[n][2]);
		}	}
	},

/**************************************************************************************************
	stop events
**************************************************************************************************/
	stopHandle: function(element, name, observer){
		TRACE(0, "BWinEvent: stopping event on" + name + " on element " +  + (element.id ? element.id : element.tagName), "BWinProto");
		element.detachEvent('on' + name, observer);
	},

	stopHandleOnId: function(elementID, name, observer){
		this.stopHandle($(elementID), name, observer);
	},

	stopHandleOnTag: function(elementTagName, name, observer){
		for(var n = 0; n < this.observers.length; n++){
			if(this.observers[n][0].tagName === elementTagName){
				this.stopHandle(this.observers, name, observer);
		}	}
	},

	stopHandleOnClass: function(elementClassName, name, observer){
		for(var n = 0; n < this.observers.length; n++){
			if(this.observers[n][0].className === elementClassName){
				this.stopHandle(this.observers, name, observer);
		}	}
	},

/**************************************************************************************************
	destroy events
**************************************************************************************************/
	destroy: function(element, name) {
		for(var n = 0; n < this.observers.length; n++){
			if (this.observers[n][0] === element && this.observers[n][1] === name.toLowerCase()) {
				this._destroy(n);
		}	}
	},

	destroyHandleOnId: function(elementID, name){
		for(var n = 0; n < this.observers.length; n++){
			if(this.observers[n][0].id === elementID && this.observers[n][1] === name.toLowerCase()) {
				this._destroy(n);
		}	}
	},

	destroyHandleOnTag: function(elementTag, name){
		for(var n = 0; n < this.observers.length; n++){
			if(this.observers[n][0].tagName === elementTag && this.observers[n][1] === name.toLowerCase()) {
				this._destroy(n);
		}	}
	},

	destroyHandleOnClass: function(elementClass, name){
		for(var n = 0; n < this.observers.length; n++){
			if(this.observers[n][0].className === elementClass && this.observers[n][1] === name.toLowerCase()) {
				this._destroy(n);
		}	}
	},

	_destroy: function(n){
		try{
			this.stopHandle(this.observers[n][0], this.observers[n][1], this.observers[n][2]);
			TRACE(0, "BWinEvent: on" + this.observers[n][1] + " destroyed on element, " + (this.observers[n][0].id ? this.observers[n][0].id : this.observers[n][0].tagName), "BWinProto");
			this.observers.splice(n, 1);
		}catch(err){ }
	},

/**************************************************************************************************
	Misc functions
**************************************************************************************************/
	pointerX: function(event){
		return (event.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft));
	},

	pointerY: function(event){
		return (event.clientY + (document.documentElement.scrollTop || document.body.scrollTop));
	},

	stopBubble: function(){
		(function(){
			event.returnValue = false;
			event.cancelBubble = true;
		}).call(arguments.caller);
	}
};

// Prevent the memory leaks in IE. And since IE is the only browser were coding for... here it is.
if(!BWinProto.HTA){
	Event.init(window, "unload",
		function(){
			Event.observers.each( function(entry, index){
				Event._destroy(index); });
		}, true);
}
Object.extend(Array.prototype, {
	first: function(){ return this[0]; },
	last: function(){ return this[this.length - 1]; },

	each: function(iterator, begin, end, direction){
		if(!begin	|| begin.isType()	!= "number" || begin < 0	){ begin = 0; }
		if(!end		|| end.isType()		!= "number"					){ end = this.length; }
		if(!direction || direction.isType() != "boolean"			){ direction = true; }

		/*jsl:ignore*/
		for(var n = begin; n < end; direction ? n++ : n--){
			if(iterator(this[n], n, this) === true){ break; } }
		/*jsl:end*/
	},

	findAll: function(searchObj){
		var results = [];
		this.each( function(obj, index){
			if(obj === searchObj){ results.push(obj); } });

		return (results.length === 0 ? undefined : results);
	},

	firstIndexOf: function(searchObj){
		var result = null;
		this.each( function(obj, index){
			if(obj === searchObj){ result = index; return true; } });
		return result;
	},

	lastIndexOf:  function(searchObj){
		var result = null;
		this.each( function(obj, index){
			if(obj === searchObj){ result = index; return true; }
		}, (this.length - 1), 0, false);
		return result;
	},

	grepAndExec: function(searchObj, iterator){
		this.each( function(obj, index){
			return obj === searchObj ? iterator(obj) : null; } );
	},

	toChars: function(){
		var serializedArray = "[";
		this.each(function(obj, indx, arr){
			Try.these(
				function(){ serializedArray += arr[indx].toChars(); },
				function(){ serializedArray += arr[indx].toString(); }
			);
			if(indx < arr.length - 1){ serializedArray += ","; }
		});
		return serializedArray + "]";
	}
}); // Array.prototype
var Ajax = {
	// private classes
	__base: Class.create(),
	__formElement: Class.create(),

	// The AJAX main classes
	Request:			null,
	Updater:			null,
	FormPost:			null,
//	PeriodicalUpdater:	null,

	// The "events" that take place...
	Events: ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'],

	// By default IE is limited to 2 similtainus connections so we need a que to manage the
	// connections if we have more then 2 Requests.
	activeRequests: [],
	quedRequests: [],
	maxSimultaneousRequests: 2,

	sendRequest: function(scope){
		var sendFunc = function(){
			this.transport.send(
				(this.options.method.toLowerCase() == "post") ?
					(this.options.postBody ? this.options.postBody : this.options.parameters) : null);
		};

		sendFunc.scope = scope;

		if(this.activeRequests.length < 2){
			this.activeRequests.unshift(sendFunc);
			this.activeRequests.first().call(this.activeRequests.first().scope);
		}else{
			this.quedRequests.unshift(sendFunc);
		}
	},

	// Depending on wich browser or version of a browser were using return a XMLHTTP object.
	getTransport: function(){
		return Try.these(
			function() {TRACE(0, "AJAX: Msxml2.XMLHTTP.5.0"); return new ActiveXObject("Msxml2.XMLHTTP.5.0"); },
			function() {TRACE(0, "AJAX: Msxml12.XMLHTTP"); return new ActiveXObject('Msxml2.XMLHTTP');},
			function() {TRACE(0, "AJAX: Microsoft.XMLHTTP"); return new ActiveXObject('Microsoft.XMLHTTP');},
			function() {TRACE(0, "AJAX: XMLHttpRequest()");  return new XMLHttpRequest();}
		) || false;
	}
};

// The classes the AJAX objet contains have to be inited outside the object because they depend
// on one and other. If done using the "this" keyword the object won't be intialized yet and
// the classes won't be build. The propertie that are set to null in the AJAX object are only
// indicaters that something will come in there place.
Object.extend(Ajax.__base.prototype, {
	requestSuccess: null,

	options: null,

	responseIsSuccess: function(){
		//return	(this.transport.status >= 200 && this.transport.status < 300);
		return this.requestSuccess;
	},

	setOptions: function(options){
		this.options = Object.extend({
			// Properties
			method:			"get",
			asynchronus:	false,
			parameters:		"",

			postBody:		"",
			postDataType:	"TEXT",

			userName:		"",
			userPass:		"",

			requestHeaders:	[],

			// Events
			onUninitialized:	function(){ return; },
			onLoading:			function(){ return; },
			onLoaded:			function(){ return; },
			onInteractive:		function(){ return; },
			onComplete:			function(){ return; },

			onSuccess:			function(){ return; },
			onFailure:			function(){ return; },
			onIllegalStatus:	function(){ return; },

			// Status Events
			on100: function(){ return; },	// Continue
			on101: function(){ return; },	// Switching Protocols

			on200: function(){ return; },	// OK
			on201: function(){ return; },	// Created
			on202: function(){ return; },	// Accepted
			on203: function(){ return; },	// Non-Authoritative Information
			on204: function(){ return; },	// No Content
			on205: function(){ return; },	// Reset Content
			on206: function(){ return; },	// Partial Content

			on300: function(){ return; },	// Multiple Choices
			on301: function(){ return; },	// Moved Permanently
			on302: function(){ return; },	// Found
			on303: function(){ return; },	// See Other
			on304: function(){ return; },	// Not Modified
			on305: function(){ return; },	// Use Proxy
			on307: function(){ return; },	// Temporary Redirect

			on400: function(){ return; },	// Bad Request
			on401: function(){ return; },	// Unauthorized
			on402: function(){ return; },	// Payment Required
			on403: function(){ return; },	// Forbidden
			on404: function(){ return; },	// Not Found
			on405: function(){ return; },	// Method Not Allowed
			on406: function(){ return; },	// Not Acceptable
			on407: function(){ return; },	// Proxy Authentication Required
			on408: function(){ return; },	// Request Time-out
			on409: function(){ return; },	// Conflict
			on410: function(){ return; },	// Gone
			on411: function(){ return; },	// Length Required
			on412: function(){ return; },	// Precondition Failed
			on413: function(){ return; },	// Request Entity Too Large
			on414: function(){ return; },	// Request-URI Too Large
			on415: function(){ return; },	// Unsupported Media Type
			on416: function(){ return; },	// Requested range not satisfiable
			on417: function(){ return; },	// Expectation Failed

			on500: function(){ return; },	// Internal Server Error
			on501: function(){ return; },	// Not Implemented
			on502: function(){ return; },	// Bad Gateway
			on503: function(){ return; },	// Service Unavailable
			on504: function(){ return; },	// Gateway Time-out
			on505: function(){ return; }	// HTTP Version not supported
		}, options);
	}
});

// The AJAX main classes - Request
Ajax.Request = Class.extend(Ajax.__base);
Object.extend(Ajax.Request.prototype, {
	// Some random protperties
	url: null,

	// The transport object
	transport: null,

	respondToReadyState: function(){
		var readyState = null;
		// Set the readyState number.
		if(!this.transport){
			readyState = 0;
		}else{
			readyState = this.transport.readyState;
		}

		// Setting the event name.
		var event = Ajax.Events[readyState];

		switch(event){
			case "Uninitialized":
			case "Loading":
			case "Loaded":
			case "Interactive":
				this.options["on" + event].call(this);
				break;
			case "Complete":
				switch(this.transport.status){
					case 100:	// Continue
					case 101:	// Switching Protocols

					case 200:	// OK
					case 201:	// Created
					case 202:	// Accepted
					case 203:	// Non-Authoritative Information
					case 204:	// No Content
					case 205:	// Reset Content
					case 206:	// Partial Content

					case 300:	// Multiple Choices
					case 301:	// Moved Permanently
					case 302:	// Found
					case 303:	// See Other
					case 304:	// Not Modified
					case 305:	// Use Proxy
					case 307:	// Temporary Redirect

					case 400:	// Bad Request
					case 401:	// Unauthorized
					case 402:	// Payment Required
					case 403:	// Forbidden
					case 404:	// Not Found
					case 405:	// Method Not Allowed
					case 406:	// Not Acceptable
					case 407:	// Proxy Authentication Required
					case 408:	// Request Time-out
					case 409:	// Conflict
					case 410:	// Gone
					case 411:	// Length Required
					case 412:	// Precondition Failed
					case 413:	// Request Entity Too Large
					case 414:	// Request-URI Too Large
					case 415:	// Unsupported Media Type
					case 416:	// Requested range not satisfiable
					case 417:	// Expectation Failed

					case 500:	// Internal Server Error
					case 501:	// Not Implemented
					case 502:	// Bad Gateway
					case 503:	// Service Unavailable
					case 504:	// Gateway Time-out
					case 505:	// HTTP Version not supported
						this.requestSuccess = (this.transport.status >= 200 && this.transport.status < 300);
						this.options["on" + this.transport.status].call(this);
						break;
					default:
						// No status was matched. It's an invalid status.
						this.options.onIllegalStatus.call(this);
						break;
				} // switch(status)


				if(this.responseIsSuccess()){
					// If the response was a success...
					this.options.onSuccess.call(this);
				}else{
					// If the response failed for some reason...
					this.options.onFailure.call(this);
				}

				this.options.onComplete.call(this);

				if(Ajax.activeRequests.length > Ajax.maxSimultaneousRequests){
					TRACE(0, "AJAX: More then " + Ajax.maxSimultaneousRequests + " are active!");
				}

				// Removing the request and adding a new one...
				var self = this;
				Ajax.activeRequests.each( function(obj, index, arr){
					if(obj.scope == self){
						if(Ajax.quedRequests.length > 0){
							arr.splice(index, index + 1, Ajax.quedRequests.pop());
							arr[index].call(arr[index].scope);
							return true;
						}else{
							arr.splice(index, index + 1);
							return true;
						}
					}
				});

				/* Avoid memory leak in MSIE: clean up the oncomplete event handler */
				this.transport.onreadystatechange = function(){ return; };

				break;	// case "complete":
			default: break;
		}
	},

	setRequestHeaders: function(){
		// Is this a post?
		if(this.options.method.toLowerCase() == "post"){
			if(this.options.postDataType.toLowerCase() == "text"){
				// sending text
				this.transport.setRequestHeader("Method", "POST " + this.url + " HTTP/1.1");
				this.transport.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
				//this.tranpsort.setRequestHeaders("Content-Type", "text/plain");
			}else if(	this.options.postDataType.toLowerCase() == "base64"	||
						this.options.postDataType.toLowerCase() == "b64"		){
				// We are not sending text were sending b64
				this.transport.setRequestHeader("Method", "POST " + this.url + " HTTP/1.1");
				this.transport.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
				this.transport.setRequestHeader("Content-transfer-encoding", "base64");
			}else if(this.options.postDataType.toLowerCase() == "form"){
				// Submitting a form
				this.transport.setRequestHeader("Accept", "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
				this.transport.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + this.boundary);
			}else{
				// No type was given....
				TRACE(0, "AJAX: Invalid data type no header has been set.");
			}
		}

		// Add all the headers.
		if(this.options.requestHeaders && this.options.requestHeaders.length > 1){
			//debugger;
			/*jsl:ignore*/
			for(var n = 0; n < this.options.requestHeaders.length; n++){
				this.transport.setRequestHeader(this.options.requestHeaders[n], this.options.requestHeaders[++n]); }
			/*jsl:end*/
		}
	},

	request: function(url){
		// fire the onUninitialized event.
		this.respondToReadyState();

		// open the "socket"
		this.transport.open(this.options.method, this.url, this.options.asynchronus, this.options.userName, this.options.userPass);

		// bind the onReadyStateChange event
		this.transport.onreadystatechange = this.respondToReadyState.bind(this);

		// set the headers
		this.setRequestHeaders();

		Ajax.sendRequest(this);
	},

	initialize: function(url, options){
		this.transport = Ajax.getTransport();
		// Check if there is any transport?
		if(!this.transport){ alert("Unable to create XMLHTTP object."); return; }

		// set the options
		this.setOptions(options);

		// now the settings are set. Build the URL if needed
		if(this.options.method == "get" && this.options.parameters.length > 0){
			this.url = url + (url.match(/\?/) ? '&' : '?') + this.options.parameters;
		}else{
			this.url = url;
		}

		// Do the request
		this.request();
	}
});

Object.extend(Ajax.__formElement.prototype, {
	fieldName:		null,
	fieldHeaders:	null,
	fieldContent:	null,

	getHeaders: function(){
		var headerString = "";
		this.fieldHeaders.each( function(obj, index, arr){
			headerString += obj[0] + ": " + obj[1] + String.fromCharCode(13) + String.fromCharCode(10); });
		return headerString;
	},

	initialize: function(fieldName, fieldHeaders, fieldContent){
		this.fieldName = fieldName;
		this.fieldContent = fieldContent;
		this.fieldHeaders = fieldHeaders;
	}
});

// Form Post
Ajax.FormPost = Class.extend(Ajax.__base, Ajax.Request);
Object.extend(Ajax.FormPost.prototype, {
	boundary: null,
	formFields: null,

	addFormField: function(fieldName, fieldHeaders, fieldContent){
		var nameUnique = true;
		this.formFields.each( function(obj, indx, arr){
			if(obj.fieldName.toLowerCase() == fieldName.toLowerCase){
				nameUnique = false;
				return true;
			}
		});

		if(nameUnique){
			this.formFields.push(new Ajax.__formElement(fieldName, fieldHeaders, fieldContent)); }

		return;
	},

	removeFormField: function(fieldName){
		this.formFields.each( function(obj, index, arr){
			if(fieldName == obj.fieldName){
				arr.splice(index, index + 1);
				return true;
			}
		});
	},

	createBody: function(){
		var formBody = this.boundary;
		var self = this;
		this.formFields.each( function(obj, indx, arr){
			formBody += String.fromCharCode(13) + String.fromCharCode(10) + obj.getHeaders() + String.fromCharCode(13) + String.fromCharCode(10);
			formBody += obj.fieldContent + String.fromCharCode(13) + String.fromCharCode(10);
			formBody += self.boundary;
		});

		this.options.postBody = formBody + "--" + String.fromCharCode(13) + String.fromCharCode(10);
	},

	submitForm: function(){
		// create the post body
		this.createBody();
		this.request();
		return;
	},

	initialize: function(url, options){
		this.transport = Ajax.getTransport();
		// Check if there is any transport?
		if(!this.transport){ alert("Unable to create XMLHTTP object."); return; }

		this.url = url;
		this.boundary = "-----------------------------7d62b12209d2";
		this.formFields = new Array();

		// set the options
		options.postDataType = "FORM";
		options.method = "POST";
		this.setOptions(options);

	}
});

// Updater
Ajax.Updater = Class.extend(Ajax.__base, Ajax.Request);
Object.extend(Ajax.Updater.prototype, {

	respondToReadyState: function(){
		var readyState = null;
		// Set the readyState number.
		if(!this.transport){
			readyState = 0;
		}else{
			readyState = this.transport.readyState;
		}

		// Setting the event name.
		var event = Ajax.Events[readyState];

		switch(event){
			case "Uninitialized":
			case "Loading":
			case "Loaded":
			case "Interactive":
				this.options["on" + event].call(this);
				break;
			case "Complete":
				switch(this.transport.status){
					case 100:	// Continue
					case 101:	// Switching Protocols

					case 200:	// OK
					case 201:	// Created
					case 202:	// Accepted
					case 203:	// Non-Authoritative Information
					case 204:	// No Content
					case 205:	// Reset Content
					case 206:	// Partial Content

					case 300:	// Multiple Choices
					case 301:	// Moved Permanently
					case 302:	// Found
					case 303:	// See Other
					case 304:	// Not Modified
					case 305:	// Use Proxy
					case 307:	// Temporary Redirect

					case 400:	// Bad Request
					case 401:	// Unauthorized
					case 402:	// Payment Required
					case 403:	// Forbidden
					case 404:	// Not Found
					case 405:	// Method Not Allowed
					case 406:	// Not Acceptable
					case 407:	// Proxy Authentication Required
					case 408:	// Request Time-out
					case 409:	// Conflict
					case 410:	// Gone
					case 411:	// Length Required
					case 412:	// Precondition Failed
					case 413:	// Request Entity Too Large
					case 414:	// Request-URI Too Large
					case 415:	// Unsupported Media Type
					case 416:	// Requested range not satisfiable
					case 417:	// Expectation Failed

					case 500:	// Internal Server Error
					case 501:	// Not Implemented
					case 502:	// Bad Gateway
					case 503:	// Service Unavailable
					case 504:	// Gateway Time-out
					case 505:	// HTTP Version not supported
						this.options["on" + this.transport.status].call(this);
						break;
					default:
						// No status was matched. It's an invalid status.
						this.options.onIllegalStatus.call(this);
						break;
				} // switch(status)

				// If the response was a success...
				if(this.responseIsSuccess()){
					this.options.onSuccess.call(this);
					this.element.innerHTML = this.transport.responseText;
				}else{
					// If the response failed for some reason...
					this.options.onFailure.call(this);
				}

				/* Avoid memory leak in MSIE: clean up the oncomplete event handler */
				this.transport.onreadystatechange = function(){ return; };

				break;	// case "complete":
			default: break;
		}
	},

	initialize: function(url, element, options){
		// Check if there is any transport?
		if(!this.transport){ alert("Unable to create XMLHTTP object."); return; }

		// The element were going to update.
		this.element = element;

		// set the options
		this.setOptions(options);

		// now the settings are set. Build the URL if needed
		if(this.options.method == "get" && this.options.parameters.length > 0){
			this.url += (url.match(/\?/) ? '&' : '?') + this.options.parameters;
		}else{
			this.url = url;
		}

		// Do the request
		this.request();
	}
});

if(!Element){ var Element = {}; }

Object.extend(Element, {
	// Classes
	ClassNames: function(element){
		// This should return a normal array with some added properties to handle all the className
		// stuff. Like adding and removing one. Use the set() method to apply the new classNames to
		// the element.
		return Object.extend(element.className.split(" "), {
			// Props
			element: element,

			// Methods
			remove: function(classNameToRemove) {
				// Do we actually have that classname?
				if(	classNameToRemove.isType() != "string"		||
					this.findAll(classNameToRemove) === null	){ return; }

				// If so...
				var self = this;
				this.each( function(obj, indx){
					if(obj.match(classNameToRemove)){ self.splice(indx, indx + 1); return true; }
				});
			},

			add: function(classNameToAdd) {
				// Do we actually have that classname?
				if(	classNameToAdd.isType() != "string"		||
					this.findAll(classNameToAdd) !== null	){ return; }

				this.push(classNameToAdd);
			},

			set: function() {
				this.element.className = this.join(" "); },

			hasClassName: function(matchPattern){
				var result = undefined;
				this.each( function(obj, indx, arr){
					if(arr[indx].match(matchPattern)){
						result = arr[indx];
						return true;
					}
				});

				return result;
			}

		});
	},

	// Methods
	visible: function(el){
		return 	$(el).currentStyle.display != "none"		&&
				($(el).currentStyle.visibility != "hidden"	||
				 $(el).currentStyle.visibility != "collapse");
	},

	hide: function(){
		for(var n = 0; n < arguments.length; n++){
			try{
				$(arguments[n]).style.display = "none";
			}catch(e){
				TRACE(0, "ELMT: Unable to hide object (" + arguments[n] +")"); }
		}
	},

	show: function(){
		for(var n = 0; n < arguments.length; n++){
			try{
				$(arguments[n]).style.display = "block";
			}catch(e){
				TRACE(0, "ELMT: Unable to show object (" + arguments[n] +")"); }
		}
	},

	classNames: function(element) {
 		return new Element.ClassNames(element);
	},

	hasClassName: function(element, className) {
		if(!(element = $(element))){ return; }
		if(!element.className){ return; }
		return Element.classNames(element).hasClassName(className);
	},

	addClassName: function(element, className) {
		if (!(element = $(element))){ return; }

		var tEl = this.classNames(element);
		tEl.add(className);
		tEl.set();

		return 1;
	},

	removeClassName: function(element, className) {
		if (!(element = $(element))){ return; }

		var tEl = this.classNames(element);
		tEl.remove(className);
		tEl.set();

		return 1;
	},

	replaceClassName: function(element, match, replace){
		if (!(element = $(element))){ return; }

		var tEl = this.classNames(element);

		if(match.isType() == "regexp"){
			tEl.each( function(obj, indx, arr){
				if(arr[indx].match(match)){ arr[indx].replace(match, replace); } });
		}else{
			tEl.each( function(obj, indx, arr){
				if(arr[indx] == match){ arr[indx] = replace; } });
		}
		tEl.set();

		return 1;
	}

});
var UnsortedNodeConstruct = Class.create();
Object.extend(UnsortedNodeConstruct.prototype, {
//UnsortedNodeConstruct.prototype = {
	nodeId:			null,
	nodeName:		null,
	nodePath:		null,
	nodeFullPath:	null,
	parentNode:		null,

	top:	null,
	all:	null,	// Should be an array
	level:	0,

	childNodes:	null,	// Like all this should be an array. But if defined in the prototype it will
						// be shared over all instances of the this class.

	getNodeContent: null, // The magic function that will return the private content object of every node

	sink: function(funcObj){
		//if(!funcObj.beforeSink || funcObj.beforeSink.isType() != "function"){ funcObj.beforeSink = function(){ return; }; }
		//if(!funcObj.afterSink || funcObj.afterSink.isType() != "function"){ funcObj.afterSink = function(){ return; }; }

		var stopSink = funcObj.beforeSink.call(this, funcObj.sinkPath);
		if(!stopSink){
			funcObj.sinkPath.unshift(this);
			this.childNodes.each( function(obj, indx, arr){
				arr[indx].sink.call(arr[indx], funcObj); });
			try{ funcObj.afterSink.call(this, funcObj.sinkPath); }catch(err){ }
		}

		return;
	},

	bubble: function(funcObj){
		if(!funcObj.beforeBubble || funcObj.beforeBubble.isType() != "function"){ funcObj.beforeBubble = function(){ return; }; }
		if(!funcObj.afterBubble || funcObj.afterBubble.isType() != "function"){ funcObj.afterBubble = function(){ return; }; }

		var stopBubble = funcObj.beforeBubble.call(this, funcObj.bubblePath);
		if(!stopBubble && this.parentNode !== null){
			funcObj.bubblePath.unshift(this);
			this.parentNode.bubble.call(this.parentNode, funcObj);
			funcObj.afterBubble.call(this, funcObj.bubblePath);
		}

		return;
	},

	appendChild: function(content, nodeName){
		// Push a new node on the collection with the following arguments:

		if(!nodeName){
			if(this.top){
				nodeName = this.nodeName + this.top.generateId();
			}else{
				nodeName = this.nodeName + this.generateId();
			}
		}

		return this.childNodes.push( new UnsortedNodeConstruct( nodeName, {}, (!content ? {} : content), this) );
	},

	removeChild: function(){ return; },

	toChars:function(treePath){
		if(!treePath){
			treePath = "";
			// If this node happens to be the top node it is not going to recive any arguments and
			// a function should be returnd to genereate the tree as is.
			var serializedTree = "function(){var treeObject = new UnsortedNodeConstruct(\"" + this.nodeName + "\", " + (this.settings ? this.settings.toChars() : this.top.settings.toChars()) + ", " + this.getNodeContent().toChars() + ");";
			this.childNodes.each(function(obj, indx, arr){
				serializedTree += "treeObject.appendChild(" + arr[indx].getNodeContent().toChars() + ", \"" + arr[indx].nodeName + "\");";
				serializedTree += arr[indx].toChars("treeObject.childNodes[" + indx.toString() + "]");
			});
			return serializedTree + "return treeObject;}()";
		}else{
			// This is not the top node of the tree we are serializing. so we don't have to return
			// a complete function.
			var serializedTree = "";
			this.childNodes.each(function(obj, indx, arr){
				serializedTree += treePath + ".appendChild(" + arr[indx].getNodeContent().toChars() + ", \"" + arr[indx].nodeName + "\");";
				serializedTree += arr[indx].toChars(treePath + ".childNodes[" + indx.toString() + "]");
			});
			return serializedTree;
		}
	},

	initialize: function(nodeName, settings, content, parentNode){
		if(!nodeName){
			TRACE(0, "TREE: Creation failed. No name was given.");
			return; }

		// Createing a private (closure) instance of content so it can easly be returned
		// withoud distilling the object.
		var prv_content = content ? content : {};
		this.getNodeContent = function(){ return prv_content; };

		// Some properties that apply to every node.
		this.nodeName = nodeName;
		//this.nodeContent = content;
		Object.protectedExtend(this, content);

		this.all = new Array();
		this.childNodes = new Array();

		if(parentNode){	// Child node
			this.parentNode = parentNode;

			// If top == null && parentNode -> parentNode == top.
			this.top = (this.parentNode.top ? this.parentNode.top : this.parentNode);

			// A unuiqe ID and other node properties
			this.nodeId = this.top.generateId();
			this.nodePath = this.parentNode.nodeFullPath + "/";
			this.nodeFullPath = this.nodePath + this.nodeName;
			this.level = this.parentNode.level + 1;

			if(this.top.settings.directChild === true){
				if(this.parentNode[this.nodeName]){
					TRACE(0, "TREE: Cannot create direct link from parent. Propertie already excists. [" + this.nodeName + "]");
				}else{
					this.parentNode[this.nodeName] = this;
				}
			}

			this.bubble({
				beforeBubble: function(bPath){ return; },
				afterBubble: function(bPath){
					if(this.parentNode){
						this.parentNode.all.push(bPath.last()); } },
				bubblePath: []
			});
		}else{			// Root node
			// Root node extentions.
			Object.extend(this, {
				settings: {
					enforeceChildNames:	false,
					directChild:		false
				},

				getNodeById: function(nodeId){ return; },

				lastGeneratedId: "_" + (new Date()).getTime(),
				generateId: function(){
					var self = this;

					function genId(tId){
						if(tId != self.lastGeneratedId){
							return tId; }
						return genId("_" + (new Date()).getTime());
					}

					this.lastGeneratedId = genId("_" + (new Date()).getTime());
					return this.lastGeneratedId;
				}
			});

			Object.extend(this.settings, settings);

			//General tree properties
			this.nodeId = this.generateId();
			this.nodePath = "./";
			this.nodeFullPath = this.nodePath + this.nodeName;
		}

		TRACE(0, "TREE: New node created " + this.nodeFullPath);
	}
});

var SortedNodeConstruct = Class.extend(UnsortedNodeConstruct);
Object.extend(SortedNodeConstruct.prototype, {
	children: null,

	firstChild:	null,
	lastChild:	null,

	previousSibling:	null,
	nextSibling:		null,

	appendChild: function(){ return; },
	removeChild: function(){ return; },
	insertBefore: function(){ return; },

	initialize: function(){ return; }
});


var MenuCollection = [];
Object.extend(MenuCollection, {
	getMenuById: function(menuId){
		var menuResult = undefined;

		this.each( function(obj, indx, arr){
			if(obj.menuId == menuId){
				menuResult = arr[indx];
				return true;
			}
		});

		return menuResult;
	},

	__MenuConstruct: Class.create(),
	__ItemConstruct: Class.create(),

	newMenu: function(menuId, menuEvents, itemEvents, menuStyle, itemStyle, renderOptions){
		return this.push(new this.__MenuConstruct(menuId, menuEvents, itemEvents, menuStyle, itemStyle, renderOptions));
	}
}); // Object.extend(MenuCollection)

MenuCollection.__MenuConstruct.prototype = {
	menuEevents: null,
	itemEvents: null,

	menuStyle: null,
	itemStyle: null,

	renderOptions: null,

	menuId: null,
	menuItems: [],

	addItem: function(events, style, content, renderOptions, itemIndex){
		if(itemIndex <= 0){ itemIndex += 1; }
		this.menuItems.splice(
			itemIndex,
			(itemIndex - 1),
			new MenuCollection.__ItemConstruct(events, style, content, renderOptions) );
	},
	removeItem: function(itemIndex){
		this.menuItems.splice(itemIndex, (itemIndex + 1));
	},

	renderMenu: function(){ return; },

	initialize: function(menuId, menuEvents, itemEvents, menuStyle, itemStyle, renderOptions){
		if(	!menuId						||
			menuId.isType() != "string"	){
			// need to add a check if there is a menu with the same ID here...
			this.menuId = menuId;
		}else{
			this.menuId = "_" + (new Date()).getTime();
		}

		// Extend this menu abstract object with the given events, style and rendering options....
		try{
			menuEvents.isType() != "object"	?
				this.menuEvents = {}				:
				this.menuEvents = menuEvents		;
		}catch(err){ this.menuEvents = {}; }

		try{
			itemEvents.isType() != "object"	?
				this.itemEvents = {}				:
				this.itemEvents = itemEvents		;
		}catch(err){ this.itemEvents = {}; }

		try{
			menuStyle.isType() != "object"	?
				this.menuStyle = {}				:
				this.menuStyle = menuStyle		;
		}catch(err){ this.menuStyle = {}; }

		try{
			itemStyle.isType() != "object"	?
				this.itemStyle = {}				:
				this.itemStyle = itemStyle		;
		}catch(err){ this.itemStyle = {}; }

		try{
			renderOptions.isType() != "object"	?
				this.renderOptions = {}				:
				this.renderOptions = renderOptions	;
		}catch(err){ this.renderOptions = {}; }

	}
};

MenuCollection.__ItemConstruct.prototype = {
	events: null,
	style: null,

	renderOptions: null,

	content: null,

	initialize: function(events, style, content, renderOptions){
		try{
			events.isType() != "object"	?
				this.events = {}			:
				this.events = events		;
		}catch(err){ this.events = {}; }

		try{
			style.isType() != "object"	?
				this.style = {}				:
				this.style = style			;
		}catch(err){ this.style = {}; }

		try{
			content.isType() != "string"	?
				this.content = ""				:
				this.content = content			;
		}catch(err){ this.content = ""; }

		try{
			renderOptions.isType() != "object"	?
				this.renderOptions = {}				:
				this.renderOptions = renderOptions	;
		}catch(err){ this.renderOptions = {}; }
	}
};


var MenuCollectionPrototype = Class.create();
MenuCollectionPrototype.prototype = {
	slidingMenu_Abstract: Class.create(),
	contextMenu_Abstract: Class.create(),

	initialize: function(){
		this.slidingMenu_Abstract.prototype = {
			moving:	setTimeout("null", 1),
			Items:	[],
			Menu:	{},
			Hdr:	{},
			Link:	{},
			Bar:	{},
			initialize: function(){
				Object.extend(this, new SlidingMenuConstruct());
				return;
			}
		};

		this.contextMenu_Abstract.prototype = {
			menuTimer:	setTimeout('null', 1),
			item: 		new Array(),
			initialize: function(){
				Object.extend(this, new ContextMenuConstruct());
				return;
			}
		};

		return;
	},

	getMenuById: function(menuID){
		var result;
		this.each(
			function(){	if(arguments[0].id == menuID){ result = arguments[1]; } }
		);
		return this[result];
	},

	newMenu: function(ID, menuType){
		/* All the raw Menu data from the server */
		var HttpReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");
		HttpReq.open("GET", "http://10.10.1.25/Kashy/BWinOS/examples/php/?id=" + ID, false);
		HttpReq.send();

		switch(menuType.toLowerCase()){
			case "slidingmenu":
				this.push(new this.slidingMenu_Abstract());
				break;
			case "contextmenu":
				this.push(new this.contextMenu_Abstract());
				break;
			default:
				alert("Illegal argument(" + menuType + ")\nThis menu type does not excist");
				return null;
		}

		new Function("obj", HttpReq.responseText)(this[this.length - 1]); // Create and run a function.
		return this[this.length - 1];
	}
};

function MenuCollectionConstruct(){
	var tMenuCollection = [];
	Object.extend(tMenuCollection, new MenuCollectionPrototype());
	tMenuCollection.constructor = "Collection";
	return tMenuCollection;
}
var Crypto = {
	base64: {
		defaultBase64: [
			"A", "B", "C", "D", "E", "F", "G", "H",
			"I", "J", "K", "L", "M", "N", "O", "P",
			"Q", "R", "S", "T", "U", "V", "W", "X",
			"Y", "Z", "a", "b", "c", "d", "e", "f",
			"g", "h", "i", "j", "k", "l", "m", "n",
			"o", "p", "q", "r", "s", "t", "u", "v",
			"w", "x", "y", "z", "0", "1", "2", "3",
			"4", "5", "6", "7", "8", "9", "+", "/"
		],

		urlSafeBase64: [
			"A", "B", "C", "D", "E", "F", "G", "H",
			"I", "J", "K", "L", "M", "N", "O", "P",
			"Q", "R", "S", "T", "U", "V", "W", "X",
			"Y", "Z", "a", "b", "c", "d", "e", "f",
			"g", "h", "i", "j", "k", "l", "m", "n",
			"o", "p", "q", "r", "s", "t", "u", "v",
			"w", "x", "y", "z", "0", "1", "2", "3",
			"4", "5", "6", "7", "8", "9", "-", "_"
		],

		base64Decode: [
/*			 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F   */
/*  0  */	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/*  1  */	-1, -1, -1, -1,	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/*  2  */	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
/*  3  */	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
/*  4  */	-1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
/*  5  */	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
/*  6  */	-1, 26, 27, 28,	29, 30, 31, 32, 33, 34, 35, 36, 37, 38,	39, 40,
/*  7  */	41, 42, 43, 44, 45, 46, 47, 48,	49, 50, 51, -1, -1, -1, -1, -1,
/*  8  */	-1, -1,	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,	-1, -1, -1, -1,
/*  9  */	-1, -1, -1, -1, -1, -1,	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/*  A  */	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,	-1, -1, -1, -1, -1, -1,
/*  B  */	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/*  C  */	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/*  D  */	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/*  E  */	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/*  F  */	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
/*			 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F   */
		],

		/* Note: bytesPerChar has to be 1 || 2 (ascii || unicode) */
		encodeString: function(uString, encodingTable, bytesPerChar){
			if(uString.length <= 0){
				TRACE(0, "B64: Unable to encode, no data given.");
				return;
			}

			var sTime = (new Date()).getTime();

			var strIndex = 0;
			var base64String = "";

			// Just for some optimalisation we are going to do the same twice here.
			// Depending on how many bytes per char the string is encoded.
			switch(bytesPerChar){
/**************************************************************************************************/
				case 1:
					// The hackish getByte function. This function checks if the one byte char
					// is really one byte and not some 2 byte char that got mixed up in the
					// ASCII table somewhere.
					function getByte(inByte){ return inByte; }

					// With a 1 byte per char we will chop the top byte (basically everything above 255)
					var byte1 = 0x00;
					var byte2 = 0x00;
					var byte3 = 0x00;
					do{
						byte1 = getByte(uString.charCodeAt(strIndex));
							if(++strIndex < uString.length){
								byte2 = getByte(uString.charCodeAt(strIndex));
								if(++strIndex < uString.length){
									byte3 = getByte(uString.charCodeAt(strIndex));
									base64String += "" +
										encodingTable[ byte1 >> 2] +
										encodingTable[(byte1 & 0x03) << 4 | byte2 >> 4] +
										encodingTable[(byte2 & 0x0F) << 2 | byte3 >> 6] +
										encodingTable[ byte3 & 0x3F];
									continue;
								}
								base64String += "" +
									encodingTable[ byte1 >> 2] +
									encodingTable[(byte1 & 0x03) << 4 | byte2 >> 4] +
									encodingTable[(byte2 & 0x0F) << 2] +
									"=";
								break;
							}
						base64String += "" +
							encodingTable[ byte1 >> 2] +
							encodingTable[(byte1 & 0x03) << 4] +
							"==";
						break;
					}while(++strIndex < uString.length)
					break;
/**************************************************************************************************/
				case 2:
					var byte1 = 0x00;	var byte2 = 0x00;
					var byte3 = 0x00;	var byte4 = 0x00;
					var byte5 = 0x00;	var byte6 = 0x00;
					do{
						byte1 = (uString.charCodeAt(strIndex) & 0xFF00) >> 8;
						byte2 =  uString.charCodeAt(strIndex) & 0x00FF;
							if(++strIndex < uString.length){
								byte3 = (uString.charCodeAt(strIndex) & 0xFF00) >> 8;
								byte4 =  uString.charCodeAt(strIndex) & 0x00FF;
								if(++strIndex < uString.length){
									byte5 = (uString.charCodeAt(strIndex) & 0xFF00) >> 8;
									byte6 =  uString.charCodeAt(strIndex) & 0x00FF;

									base64String += "" +
										encodingTable[ byte1 >> 2] +
										encodingTable[(byte1 & 0x03) << 4 | byte2 >> 4] +
										encodingTable[(byte2 & 0x0F) << 2 | byte3 >> 6] +
										encodingTable[ byte3 & 0x3F];

									base64String += "" +
										encodingTable[ byte4 >> 2] +
										encodingTable[(byte4 & 0x03) << 4 | byte5 >> 4] +
										encodingTable[(byte5 & 0x0F) << 2 | byte6 >> 6] +
										encodingTable[ byte6 & 0x3F];
									continue;
								}

								base64String += "" +
									encodingTable[ byte1 >> 2] +
									encodingTable[(byte1 & 0x03) << 4 | byte2 >> 4] +
									encodingTable[(byte2 & 0x0F) << 2 | byte3 >> 6] +
									encodingTable[ byte3 & 0x3F];

								base64String += "" +
									encodingTable[ byte4 >> 2] +
									encodingTable[(byte4 & 0x03) << 4] +
									"==";
								break;
							}

						base64String += "" +
							encodingTable[ byte1 >> 2] +
							encodingTable[(byte1 & 0x03) << 4 | byte2 >> 4] +
							encodingTable[(byte2 & 0x0F) << 2] +
							"=";
						break;
					}while(++strIndex < uString.length)
					break;
/**************************************************************************************************/
				default:
					TRACE(0, "B64: Unable to encode data, no valid amount of bytes per char was given.");
					return;
			}

			TRACE(0, "B64: Encoded " + (bytesPerChar == 1 ? uString.length : uString.length * 2) + " chars with " + bytesPerChar + " bytes per char. In " + ((new Date()).getTime() - sTime) + "ms");

			return base64String;
		},

		encodeFile: function(filePath, encodeTable){
			TRACE(0, "B64: Encoding file [ " + filePath + " ]");
			var fSys = new ActiveXObject("Scripting.FileSystemObject");
			var openFile = fSys.GetFile(filePath).OpenAsTextStream(1, 0);
			var b64String = this.encodeString(openFile.ReadAll(), encodeTable, 1);
			openFile.close();
			return b64String;
		},

		decodeToRaw: function(eString){
			var sTime = (new Date()).getTime();
			var strIndex = 0;
			var binString = "";
			var buffer = [
				"",	"",	"",	"",
				"",	"",	"",	""
			];

			do{
				/*jsl:ignore*/
				for(var n = 0; n < 8; n++){
					buffer[n] = this.base64Decode[eString.charCodeAt(strIndex++)]; }
				/*jsl:end*/
				binString += this.__decodeSet(buffer);
			}while(strIndex < eString.length)

			//if(arguments.caller.toString() == this.decodeToHex.toString()){ debugger; }
			// Check if a padding char was added
			// 0x00000036 & 0x000000FF == 0x00000003
			// 0x00000036 & 0x000000FF != 0x00000003
			// Both produce false (0x00). The engien takes the equasion first and does a bitwise
			// operation on the result.
			if(	(binString.charCodeAt(binString.length - 1) & 0x000000FF) != 0x00000003 &&
				(binString.charCodeAt(binString.length - 1) & 0x0000FFFF) != 0x00000300	){
				binString += String.fromCharCode(0x00000300);
			}

			TRACE(0, "B64: Decoded " + eString.length + " base64Chars in to " + 0x00 + " bytes. In " + ((new Date()).getTime() - sTime) + "ms");
			return binString;
		},

		decodeToHex: function(eString){
			var rawOutput = this.decodeToRaw(eString);
			var outputString = "";
			for(var n = 0; n < rawOutput.length; n++){
				outputString += (( 0x0000FF00 & rawOutput.charCodeAt(n)) >> 8).toString(16) + " ";
				outputString += (  0x000000FF & rawOutput.charCodeAt(n)).toString(16) + " ";
			}
			return outputString;
		},

		decodeASCIIString: function(eString){
			var rawOutput = this.decodeToRaw(eString);
			var outputString = "";

			for(var n = 0; n < rawOutput.length; n++){
				outputString += String.fromCharCode((0x0000FF00 & rawOutput.charCodeAt(n)) >> 8);
				outputString += String.fromCharCode( 0x000000FF & rawOutput.charCodeAt(n));
			}

			return outputString.charCodeAt(outputString.length - 1) === 0x00000000 ?
				outputString.substring(0, outputString.length - 2)	:
				outputString.substring(0, outputString.length - 1)	;
		},

		decodeUNI16String: function(eString){
			var rawOutput = this.decodeToRaw(eString);

			return rawOutput.charCodeAt(rawOutput.length - 1) === 0x00000000 ?
				rawOutput.substring(0, rawOutput.length - 2)	:
				rawOutput.substring(0, rawOutput.length - 1)	;
		},

		__decodeSet: function(set){
			var bString = "";
			var tInt0 = 0x00000000;
			var tInt1 = 0x00000000;
			if(set[5] == undefined){
				if(set[2] == -1){
					// 1  <-->  MQ==
					tInt0 |= (set[0] << 26);
					tInt0 |= (set[1] << 20);
					bString += String.fromCharCode(((0xFF000000 & tInt0) | 0x00030000) >> 16);
					return bString;
				}

				if(set[3] == -1){
					// 12  <-->  MTI=
					tInt0 |= (set[0] << 26);
					tInt0 |= (set[1] << 20);
					tInt0 |= (set[2] << 14);
					bString += String.fromCharCode((0xFFFF0000 & tInt0) >> 16);
					bString += String.fromCharCode( 0x00000300);
					return bString;
				}

				// 123  <-->  MTIz
				tInt0 |= (set[0] << 26);
				tInt0 |= (set[1] << 20);
				tInt0 |= (set[2] << 14);
				tInt0 |= (set[3] << 8);
				bString += String.fromCharCode((0xFFFF0000 & tInt0) >> 16);
				bString += String.fromCharCode((0x0000FF00 & tInt0) | 0x00000003);
				return bString;
			}

			// 1234  <-->  MTIzNA==
			if(set[6] == -1){
				tInt0 |= (set[0] << 26);
				tInt0 |= (set[1] << 20);
				tInt0 |= (set[2] << 14);
				tInt0 |= (set[3] << 8);

				tInt1 |= (set[4] << 26);
				tInt1 |= (set[5] << 20);

				bString += String.fromCharCode((0xFFFF0000 & tInt0) >> 16);
				bString += String.fromCharCode((0x0000FF00 & tInt0) | (0xFF000000 & tInt1) >> 24);
				bString += String.fromCharCode(0x00000300);
				return bString;
			}

			// 12345  <-->  MTIzNDU=
			if(set[7] == -1){
				tInt0 |= (set[0] << 26);
				tInt0 |= (set[1] << 20);
				tInt0 |= (set[2] << 14);
				tInt0 |= (set[3] << 8);

				tInt1 |= (set[4] << 26);
				tInt1 |= (set[5] << 20);
				tInt1 |= (set[6] << 14);

				bString += String.fromCharCode((0xFFFF0000 & tInt0) >> 16);
				bString += String.fromCharCode((0x0000FF00 & tInt0) | (0xFF000000 & tInt1) >> 24);

				bString += String.fromCharCode(((0x00FF0000 & tInt1) | 0x00000300) >> 8);
				return bString;
			}

			// 123456  <-->  MTIzNDU2
			tInt0 |= (set[0] << 26);
			tInt0 |= (set[1] << 20);
			tInt0 |= (set[2] << 14);
			tInt0 |= (set[3] << 8);

			tInt1 |= (set[4] << 26);
			tInt1 |= (set[5] << 20);
			tInt1 |= (set[6] << 14);
			tInt1 |= (set[7] << 8);

			bString += String.fromCharCode((0xFFFF0000 & tInt0) >> 16);
			bString += String.fromCharCode((0x0000FF00 & tInt0) | (0xFF000000 & tInt1) >> 24);

			bString += String.fromCharCode((0x00FFFF00 & tInt1) >> 8);
			return bString;
		}
	}
};

