/// <reference path="prototype.js" />
/// <reference path="prototype-ui.js" />
/// <reference path="jquery.js" />

/*
var $j = window.jQuery ? jQuery.noConflict() : undefined;
var $p = window.$;
var element = $(id);
var jElement = $j(element);
GUI.assert(element===jElement.get(0));
*/
/*
(function () {
	var test = {
		test1: function () {
			this.counter = 10;
		},
		test2: function () {
			this.counter++;
		}
	}
	test.test1();
	test.test2();
	var counter = test.counter;
})();
*/
/*
[Array, Hash, ObjectRange].each(function (classObj) {
	function eachSpeed(iterator, context) {
		iterator = iterator || Prototype.K;
		var index = 0;
		this._each(function (value) {
			iterator.call(context, value, index++);
		});
		return this;
	}
	classObj.prototype.eachSpeed = eachSpeed;
});
*/
(function () {
	/*
	var arrayProto = Array.prototype,
	slice = arrayProto.slice,
	_each = arrayProto.forEach; // use native browser JS 1.6 implementation if available

	function each(iterator, context) {
	for (var i = 0, length = this.length >>> 0; i < length; i++) {
	if (i in this) iterator.call(context, this[i], i, this);
	}
	}
	if (!_each) _each = each;
	*/
	function detectSpeed(iterator, context) { // 05.11.2011 Ossig Got rid of throw $break;
		iterator = iterator || Prototype.K;
		for (var i = 0, length = this.length >>> 0; i < length; i++) { // 05.11.2011 Ossig >>> 0: int ==> uint?
			if (i in this) {
				var value = this[i];
				if (iterator.call(context, value, i)) {
					return value;
				}
			}
		}
	}
	function anySpeed(iterator, context) { // 05.11.2011 Ossig Got rid of throw $break;
		iterator = iterator || Prototype.K;
		for (var i = 0, length = this.length >>> 0; i < length; i++) { // 05.11.2011 Ossig >>> 0: int ==> uint?
			if (i in this && iterator.call(context, this[i], i)) {
				return true;
			}
		}
		return false;
	}
	Object.extend(Array.prototype, {
		detectSpeed: detectSpeed,
		findSpeed: detectSpeed,
		anySpeed: anySpeed,
		someSpeed: anySpeed
	});
})();
(function () {
	/*
	function _each(iterator) {
		var value = this.start;
		while (this.include(value)) {
			iterator(value);
			value = value.succ();
		}
	}
	*/
	function detectSpeed(iterator, context) { // 05.11.2011 Ossig Got rid of throw $break;
		iterator = iterator || Prototype.K;
		var value = this.start;
		while (this.include(value)) {
			if (iterator.call(context, value)) return value;
			value = value.succ();
		}
	}
	function anySpeed(iterator, context) { // 05.11.2011 Ossig Got rid of throw $break;
		iterator = iterator || Prototype.K;
		var value = this.start;
		while (this.include(value)) {
			if (iterator.call(context, value)) return true;
			value = value.succ();
		}
		return false;
	}
	Object.extend(ObjectRange.prototype, {
		detectSpeed: detectSpeed,
		findSpeed: detectSpeed,
		anySpeed: anySpeed,
		someSpeed: anySpeed
	});
})();
if (window.UI) {
	UI.WindowManager.Stack.addMethods({ // 04.11.2009 Ossig Codefix
		destroy: function () {
		}
	});
}

var CountingObserver = Object.extend(Class.create(PeriodicalExecuter, {
	/// <summary>
	/// CountingObserver
	/// </summary> 
	/// <param name="prefix" type="String">Loop marker.</param> 
	/// <param name="callback" type="Function">Loop criteria: Returns true to quit looping.</param> 
	/// <param name="frequency" type="Number">Loop repetition rate [sec].</param> 
	/// <param name="opt" type="Object">{ pingIndex: 0, timeSpan: 0, baseCtor: true }</param> 
	initialize: function ($super, prefix, callback, frequency, opt) {
		this.opt = Object.extend({ pingIndex: 0, timeSpan: 0, skipBaseCtor: false }, opt || {});
		if (this.opt.timeSpan) this.log = this.logSpan;
		this.prefix = prefix;
		this.outerCallback = callback;
		this.pingIndex = this.opt.pingIndex;
		this.timeSpan = this.opt.pingIndex * frequency;
		if (this.opt.skipBaseCtor) {
			this.frequency = frequency;
			this.baseCtor = $super.bind(this, this.innerCallback, frequency);
		} else {
			$super(this.innerCallback, frequency);
		}
	},
	innerCallback: function (executer) {
		/// <summary>loopCheck</summary>
		/// <param name="executer">Provided by base class PeriodicalExecuter. May be undefined when called explicitly.</param>
		/// <returns>true when to break</returns>
		if (this.outerCallback() || !this.loopCheck()) {
			if (executer) executer.stop(); // 23.11.2010 Ossig executer valid when called from PeriodicalExecuter.
			this.pong();
			return true;
		}
		this.ping();
		return false;
	},
	loopCheck: function () {
		/// <summary>loopCheck</summary>
		/// <returns>false when to break</returns>

		// return this.opt.pingCount < 0 || this.pingIndex < this.opt.pingCount;
		return this.opt.timeSpan <= 0 || this.timeSpan < this.opt.timeSpan;
	},
	ping: function () {
		this.log("ping");
		this.pingIndex++;
		this.timeSpan += this.frequency;
	},
	pong: function () {
		this.log("pong");
	},
	log: function (state) {
		GUI.logTimeStamp(this.prefix, state, this.pingIndex, "(", this.timeSpan, "sec)");
	},
	logSpan: function (state) {
		GUI.logTimeStamp(this.prefix, state, this.pingIndex, "(", this.timeSpan, "of", this.opt.timeSpan, "sec)");
	}
}), { __class: true });     // intelligense

var CountingObserver0 = Object.extend(Class.create(CountingObserver, {
	initialize: function ($super, prefix, callback, frequency, opt) {
		$super(prefix, callback, frequency, Object.extend(opt || {}, { skipBaseCtor: true }));
		if (this.innerCallback()) return;
		this.baseCtor();
		this.baseCtor = undefined;
	}
}), { __class: true });    // intelligense

function _documentWhenReady(callback) {
	if (document.loaded)
		callback.call(document);
	else
		document.observe('dom:loaded', callback);
}

function tryGetMember(obj, memberName, defValue) {
	if (!obj) return defValue;
	try {
		return obj[memberName];
	} catch (ex) {
		return defValue; // 30.09.2010 Ossig cross scripting
	}
}

function documentWhenReady(documentArg, callback) {
	// 26.11.2009 Ossig _window.document changes instance: _document.defaultView.document != _document.
	var _window = documentArg.defaultView ? documentArg.defaultView : documentArg.parentWindow;
	var _document = documentArg;
	// var counter = 0;
	function execute() {
		try {
			var _document = _window.document;
		} catch (ex) {
			return true; // 28.09.2010 Ossig cross scripting
		}
		// if (!_document) return true; // 26.11.2009 Ossig Happens, but no idea why.
		var stop=!_document || _window._documentWhenReady;
		// GUI.log("documentWhenReady", (stop ? "pong" : "ping"), counter++, ": _document=", _document, ", _documentWhenReady=", _documentWhenReady, ", _document.loaded=", _document ? _document.loaded : undefined, ", callback=", callback);
		GUI.log("documentWhenReady: _document=", _document, ", _documentWhenReady=", _window._documentWhenReady, ", _document.loaded=", _document ? _document.loaded : undefined, ", callback=", callback);
		if (_document && _window._documentWhenReady) _window._documentWhenReady(callback);
		return stop;
	}
	/*
	if (execute()) return;
	new window.PeriodicalExecuter(function(_window, callback, executer) {
		if (execute()) executer.stop();
	} .bind(this, _window, callback), 1);
	*/
	new CountingObserver0("documentWhenReady", execute, 1, {timeSpan: 60});
}

/*
// if (!document.whenReady) { // 19.08.2009 prototype-ui
document.whenReady = (function () {
	var queue = [];
	document.observe('dom:loaded', function () {
		GUI.log("document.whenReady: dom:loaded");
		// document.whenReady = function (callback) { callback.bind(document).defer() };  // 04.08.2010 Ossig May be activated by queue.invoke.
		document.whenReady = function (callback) {
			GUI.log("whenReady: call direct");
			callback.call(document);
		}; // 28.11.2010 Ossig Behaviour in case of nested document.whenReady constructions.
		GUI.log("whenReady: queue.length=", queue.length, queue.inspect());
		// queue.invoke('call', document);
		queue.each(function (callback) {
			try {
				callback.call(document);
			} catch (ex) {
				geSystem.writeError(ex);
			}
		});
		queue.clear();
		// document.whenReady = function(callback) { callback.bind(document).defer() };
	});
	return function (callback) {
		queue.push(callback);
		GUI.log("whenReady: queue.length=", queue.length);
	};
})();
// }
*/

if (window.$j) {
	document.whenReady = (function () {
		var queue = [];
		$j(document).bind("ready", function () { // 07.04.2011 Ossig "ready" triggered after jQuery queue cleared: http://bugs.jquery.com/ticket/7279
			var domLoaded;
			function onDomLoaded() {
				if (domLoaded) domLoaded.stop();
				document.whenReady = function (callback) {
					/*
					try {
					callback.call(document);
					} catch (e) {
					GUI.error(e);
					throw e;
					}
					*/
					callback.call(document);
				}; // 28.11.2010 Ossig Behaviour in case of nested document.whenReady constructions.
				/*
				try {
				queue.invoke('call', document);
				} catch (e) {
				GUI.error(e);
				throw e;
				}
				*/
				queue.invoke('call', document);
				queue.clear();
			}
			if (document.loaded)
				onDomLoaded()
			else
				domLoaded = document.on('dom:loaded', onDomLoaded);
		});
		return function (callback) {
			queue.push(callback);
		};
	})();
} else {
	document.whenReady = (function () {
		var queue = [];

		document.observe('dom:loaded', function () {
			// document.whenReady = function (callback) { callback.bind(document).defer() };  // 04.08.2010 Ossig May be activated by queue.invoke.
			document.whenReady = function (callback) { callback.call(document) }; // 28.11.2010 Ossig Behaviour in case of nested document.whenReady constructions.
			queue.invoke('call', document);
			queue.clear();
			// document.whenReady = function(callback) { callback.bind(document).defer() };
		});

		return function (callback) { queue.push(callback) };
	})();
}

/* 04.04.2011 Ossig Dangerous! document.whenReady at calltime.
document.$whenReady = function (callback) {
	$j(document.whenReady.curry(callback));
}
*/
/*
document.$whenReady = function (callback) {
	$j(function (callback) {
		document.whenReady(callback);
	} .curry(callback));
}
document.$whenReady = function (callback) {
	return document.whenReady(callback);
}
*/

document.beforeUnload = (function () {
	function onUnload(event) {
		if (event) Event.stopObserving(window, "unload", onUnload);
		document.beforeUnload = function (callback) { callback.call(document) };
		queue.invoke('call', document);
		queue.clear();
		if (event) document.fire("ge:unload1");
	}
	var queue = [];
	document.whenReady(function () {
		if (window.closed)
			onUnload();
		else
			Event.observe(window, "unload", onUnload);
	});
	return function (callback) { queue.push(callback) };
})();
document.whenUnload = (function () {
	var handler;
	function onUnload() {
		if (handler) handler.stop();
		document.whenUnload = function (callback) { callback.call(document) };
		queue.invoke('call', document);
		queue.clear();
		document.fire("ge:unload2");
	}
	var queue = [];
	document.whenReady(function () {
		if (window.closed)
			onUnload();
		else
			handler = document.on("ge:unload1", onUnload);
	});
	return function (callback) { queue.push(callback) };
})();
document.afterUnload = (function () {
	var handler;
	function onUnload() {
		if (handler) handler.stop();
		document.afterUnload = function (callback) { callback.call(document) };
		queue.invoke('call', document);
		queue.clear();
	}
	var queue = [];
	document.whenReady(function () {
		if (window.closed)
			onUnload();
		else
			handler = document.on("ge:unload2", onUnload);
	});
	return function (callback) { queue.push(callback) };
})();

document.whenWindowReady = (function() {
	function onLoad() {
		Event.stopObserving(window, "load", onLoad);
		// if (document.windowLoaded) return;
		// document.windowLoaded = true;
		// document.fire('dom:windowloaded'); document.observe('dom:windowloaded', function () {});
		document.whenWindowReady = function (callback) { callback.call(document) }; // 28.11.2010 Ossig Behaviour in case of nested document.whenReady constructions.
		queue.invoke('call', document);
		queue.clear();
	}
	var queue = [];
	Event.observe(window, "load", onLoad);
	return function(callback) { queue.push(callback) };
})();

function $_id(id) { // nearly the same as $(object).id
	// return Object.isString(id) || !id ? id : id.id;
	// return Object.isString(id) || !id ? id : id.id ? id.id : id.name; // 05.09.2009 name technique tolerance.
	return !id || Object.isString(id) ? id : id.id || id.name;
}

if (!window.UI) { // 18.04.2010 Ossig proptype-ui
	document.whenReady(function () {
		window.$head = $(document.getElementsByTagName('head')[0]);
		window.$body = $(document.body);
	});
}

function getDTFrame() { // 16.08.2009 Ossig DT ... DeskTop
	/// <summary>getDTFrame summary</summary>
	/// <param name="frame">frame Window</param>
	/// <returns>main browser window</returns>
	/*
	function checkFrame(frame) { // StopGlobalEagle Popup outside GUI frame.
		return frame && frame.remoteGUI ? frame : undefined;
	}
	*/
	// if (!frame) frame = window;
	/*
	while (frame.parent != frame)
	frame = frame.parent;
	return frame;
	*/
	// return frame.top;
	// return frame ? frame.top : window.top;
	return window.top;
}

function reposToScreen(w, opt) {
	w.resizeTo(opt.width, opt.height); // 23.05.2006 Ossig Make window small, then it's easier to pack into the edge of the screen (Firefox).
	w.moveTo(opt.left, opt.top);
	w.resizeTo(opt.width, opt.height); // 23.05.2006 Ossig In case the window could not be resized on old position (IE).
}

/*
function _getGUIInstance(remoteGUIFlag) {
	/+
	function getOrigFrame(frame) {
		function checkFrame(frame) { // 04.09.2009 Ossig Menu FrameSet access to OrigFrame still no ready.
			return frame && !frame.closed && frame.nativeGUI ? frame : undefined;
		}
		if (!frame) frame = window;
		if (checkFrame(frame.OrigFrame)) return frame.OrigFrame;
		if (checkFrame(frame.top.opener)) return frame.top.opener;
		return frame;
	}
	+/
	if (window.console) window.console.log("_getGUIInstance: window.OrigFrame=", window.OrigFrame);
	if (!remoteGUIFlag) return window.OrigFrame.nativeGUI;
	var dtFrame = getDTFrame();
	return dtFrame.remoteGUI ? dtFrame.remoteGUI : dtFrame.nativeGUI; // Popup outside GUI frame", "return getOrigFrame().nativeGUI;
}
*/

function initGUI(remoteGUIOpt) {
	var dtFrame = getDTFrame();
	/*
	if (!dtFrame.remoteGUI && dtFrame.RemoteGUI) dtFrame.remoteGUI = new dtFrame.RemoteGUI();
	if (!window.nativeGUI && window.NativeGUI) window.nativeGUI = new window.NativeGUI();
	return remoteGUIOpt && dtFrame.remoteGUI ? dtFrame.remoteGUI.init.bind(dtFrame.remoteGUI) : window.nativeGUI.init.bind(window.nativeGUI);
	*/
	function activateGUI(activeFrame, className, instanceName, opt) {
		// GUI.log("A1");
		var activeGUI = activeFrame[instanceName];
		if (!activeGUI) { // && classInstance // 17.09.2009 Ossig Has to exist.
			// GUI.log("A2");
			var classInstance = activeFrame[className];
			if (!classInstance) return Prototype.emptyFunction; // 30.09.2011 Ossig Remove.aspx
			var activeGUI = new classInstance(opt);
			// GUI.log("A3");
			activeFrame[instanceName] = activeGUI;
			// GUI.log("A4");
		}
		return activeGUI.init.bind(activeGUI);
	}
	// return remoteGUIOpt && dtFrame.RemoteGUI ? activateGUI(dtFrame, "RemoteGUI", "remoteGUI", remoteGUIOpt) : activateGUI(window, "NativeGUI", "nativeGUI");
	return activateGUI(dtFrame, "RemoteGUI", "remoteGUI", remoteGUIOpt);
}

// 04.02.2010 Ossig Available in EVERY window.
var GUI = (function () {
	var GUI = {
		getConsole: function () { // Could change while page open.
			var console = window.console;
			if (!console) return undefined;
			if (!Object.isFunction(console.log)) return undefined;
			if (!Object.isFunction(console.error)) console.error = console.log;
			return console;
		},
		assert: function (condition) {
			if (condition) return;
			var console = this.getConsole();
			if (!console) return;
			var argArr = $A(arguments);
			argArr.shift(); // take off condition
			argArr.unshift("GE assertion\r\n");
			console.error.apply(console, argArr);
		},
		error: function () {
			var console = this.getConsole();
			if (!console) return;
			var argArr = $A(arguments);
			argArr.unshift("GE error\r\n");
			console.error.apply(console, argArr);
		},
		log: function () { // 10.09.2009 Ossig Notfalls auf empty function legen.
			var console = this.getConsole();
			if (!console) return;
			console.log.apply(console, arguments);
		},
		logTimeStamp: function () {
			var console = this.getConsole();
			if (!console) return;
			var argList = [this.timeStamp(), ": "].concat($A(arguments));
			console.log.apply(console, argList);
		},
		timeStamp: Object.extend(function (date) { // dataValue=undefined, date.getTime();
			return this.timeStamp.format({ date: date, dot: ".", blank: " ", colon: ":" });
		}, {
			format: function (opt) {
				opt = Object.extend({ dot: ".", blank: " ", colon: ":" }, opt || {});
				var date = opt.date ? new Date(opt.date) : new Date();
				var sArr = [];
				sArr.push(date.getDate().toPaddedString(2));
				sArr.push(opt.dot);
				sArr.push((date.getMonth() + 1).toPaddedString(2));
				sArr.push(opt.dot);
				sArr.push(date.getFullYear().toPaddedString(4));
				sArr.push(opt.blank);
				sArr.push(date.getHours().toPaddedString(2));
				sArr.push(opt.colon);
				sArr.push(date.getMinutes().toPaddedString(2));
				sArr.push(opt.colon);
				sArr.push(date.getSeconds().toPaddedString(2));
				sArr.push(opt.dot);
				sArr.push(date.getMilliseconds().toPaddedString(3));
				return sArr.join("");
			},
			formatSortable: function (opt) {
				function pad(number, length) {
					var str = '' + number;
					while (str.length < length) {
						str = '0' + str;
					}
					return str;
				}
				function pad2(number) {
					return pad(number, 2);
				}
				opt = Object.extend({ blank: "_", colon: "_" }, opt || {});
				var date = opt.date ? new Date(opt.date) : new Date();
				var sArr = [];
				if (opt.prefix) sArr.push(opt.prefix);
				sArr.push(date.getFullYear());
				sArr.push(pad2(date.getDate()));
				sArr.push(pad2(date.getMonth() + 1));
				sArr.push(opt.blank);
				sArr.push(pad2(date.getHours()));
				sArr.push(pad2(date.getMinutes()));
				sArr.push(pad2(date.getSeconds()));
				sArr.push(opt.colon);
				sArr.push(pad(date.getMilliseconds(), 3));
				return sArr.join("");
			}
		}),
		GESymbolLink: Class.create({
			initialize: function (opt) {
				opt = Object.extend({ bgSymbol: true }, opt || {}); // symbolClassName, value, classArr, bgSymbol
				var classArrTemplate = ["ge-button-symbol", "ge-button-symbol-left"];
				var classArr = opt.classArr ? classArrTemplate.concat(opt.classArr).uniq().compact() : classArrTemplate; // 16.12.2009 Ossig Original class array may be Scalar or Array.
				var element = new Element("a", { href: "#", "class": classArr.join(" ") });
				if (opt.symbolClassName) {
					if (opt.bgSymbol) element.insert(new Element("span", { "class": "ge-symbol" }));
					element.insert(new Element("span", { "class": "ge-symbol " + opt.symbolClassName }));
				}
				element.insert(new Element("span", { "class": "ge-text" }).insert(opt.value));
				this.element = element;
				document.whenUnload(this.dispose.bind(this));
			},
			dispose: function () {
				this.element = undefined;
			}
		}),
		sumSize: function (sizeArr) {
			function add(sum, value) {
				return sum + parseInt(value);
			}
			return {
				width: sizeArr.pluck("width").inject(0, add),
				height: sizeArr.pluck("height").inject(0, add)
			};
		},
		bodyVisible: function () {
			// return true;
			if (window.closed || !window.$body) return false;
			/*
			var innerSize = window.$body.getInnerSize();
			return innerSize.width != 0 || innerSize.height != 0;
			*/
			return window.$body.hasVisibleSize();
		},
		BusySpinner: Class.create({
			initialize: function (opt) {
				this._showMethod = this._show.bind(this);
				opt = Object.extend({
					// show: true,
					lock: false,
					hideElement: undefined
				}, opt || {});
				this._opt = opt;
				var element = new Element("div", { "class": "ge-busy-container" });
				this.element = element;
				// this.show(opt.show);
				this._lockCount = 0;
				this._show(this._lockCount);
				if (opt.lock) this.lock();
				document.whenUnload(this.dispose.bind(this));
			},
			dispose: function () {
				this.element = undefined;
			},
			lock: function () {
				var lock1 = this._lockCount;
				var lock2 = ++this._lockCount;
				if (!lock1 == !lock2) return;
				this._show(lock2);
			},
			unlock: function () {
				var lock1 = this._lockCount;
				var lock2 = --this._lockCount;
				if (!lock1 == !lock2) return;
				this._showMethod.defer(lock2);
			},
			_show: function (value) {
				if (arguments.length <= 0) value = true;
				var element = this.element;

				// element.style.display = value ? "inline" : "none";
				element.removeClassName("ge-busy-active");
				element.removeClassName("ge-busy-hidden");
				element.addClassName(value ? "ge-busy-active" : "ge-busy-hidden");

				var hideElement = this._opt.hideElement;
				if (hideElement) hideElement.style.display = !value ? "inline" : "none";
			}
		})
	};
	document.whenReady(function () {
		var console = GUI.getConsole();
		GUI.getConsole = function () {
			return console;
		};
	});
	return GUI;
})();

/* 28.03.2010 Ossig prototype-ui CSS.addRule */
if (!window.CSS) window.CSS = {};
if (!Object.isFunction(Element.appendText))
	Element.addMethods({
		appendText: function(element, text) {
			element = $(element);
			element.appendChild(document.createTextNode(String.interpret(text)));
			return element;
		}
	});
if (!CSS.addRule) {
	/*
	document.$whenReady(function() {
		window.$head = $(document.getElementsByTagName('head')[0]);
	});
	*/
	Object.extend(CSS, { // 
		addRule: function(css) {
			var style = new Element('style', { type: 'text/css', media: 'screen' });
			$head.insert(style);
			if (style.styleSheet) style.styleSheet.cssText = css;
			else style.appendText(css);
			return style;
		}
	});
}
Element.addMethods({
	_getFrameSize: function (element) {
		return (
			[element.getMarginDimensions()
			, element.getBorderDimensions()
			, element.getPaddingDimensions()
			]);
	},
	// setBounds
	setInnerWidth: function (element, innerWidth) {
		element.style.width = parseInt(innerWidth) + "px"; // element.clientSize;
	},
	setInnerHeight: function (element, innerHeight) {
		element.style.height = parseInt(innerHeight) + "px"; // element.clientHeight;
	},
	/*
	removeInnerSize: function(element) {
	element.style.width = undefined;
	element.style.height = undefined;
	element.style.removeAttribute("width",false);
	},
	*/
	setInnerSize: function (element, innerSize) {
		element.setInnerWidth(innerSize.width);
		element.setInnerHeight(innerSize.height);
		// GUI.log(element.id, "setInnerSize", innerWidth, innerHeight);
	},
	// setBounds
	setOuterSize: function (element, outerSize) {
		function setOuterSizeWebKit(element, outerSize) {
			var frameSize = element._getFrameSize();
			frameSize[0].width = 0; // 16.09.2010 Ossig http://code.google.com/p/chromium/issues/detail?id=23816 document.defaultView.getComputedStyle(element,null)["margin-right"] gap to outer div.
			var sizeInv = GUI.sumSize([{ width: -outerSize.width, height: -outerSize.height}].concat(frameSize));
			element.setInnerSize({ width: -sizeInv.width, height: -sizeInv.height });
		}
		if (Prototype.Browser.WebKit) {
			var outerElement = element.up();
			if (outerElement && outerElement.style["width"]) {
				setOuterSizeWebKit(element, outerSize);
				return;
			}
		}
		var sizeInv = GUI.sumSize([{ width: -outerSize.width, height: -outerSize.height}].concat(element._getFrameSize()));
		element.setInnerSize({ width: -sizeInv.width, height: -sizeInv.height });
	},
	// getBounds
	getInnerSize: function (element) {
		return { width: element.clientWidth, height: element.clientHeight };
	},
	hasVisibleSize: function (element) {
		var size = element.getInnerSize();
		return size.width || size.height;
	},
	getOuterSize: function (element) {
		return element.getDimensions();
	},
	removeStyle: function (element, style) {
		styleHash = $H();
		styleHash.set(style, "");
		return element.setStyle(styleHash.toObject());
	}
});

Element.Rectangle = Class.create(Element.Offset, {
	initialize: function (offset, size) {
		switch (arguments.length) {
			case 0:
				offset = new Element.Offset(0, 0);
				size = { width: 0, height: 0 };
				break;
			case 1:
				size = offset; // arguments(0);
				offset = new Element.Offset(0, 0);
		}
		this.left = offset.left.round();
		this.top = offset.top.round();
		var width = size.width.round();
		var height = size.height.round();
		this.right = this.left + width;
		this.bottom = this.top + height;

		this[0] = this.left;
		this[1] = this.top;
		this[2] = this.right;
		this[3] = this.bottom;
	},
	insideRect: function (rect) {
		return this.left >= rect.left && this.top >= rect.top && this.right <= rect.right && this.bottom <= rect.bottom;
	},
	intersectRect: function (rect) {
		// http://stackoverflow.com/questions/2752349/fast-rectangle-to-rectangle-intersection
		return this.left <= rect.right && rect.left <= this.right && this.top <= rect.bottom && rect.top <= this.bottom;
	},
	inspect: function () {
		return "#<Element.Rectangle left: #{left} top: #{top} right: #{right} bottom: #{bottom}>".interpolate(this);
	},
	toString: function () {
		return "[#{left}, #{top}, #{right}, #{bottom}]".interpolate(this);
	},
	toArray: function () {
		return [this.left, this.top, this.right, this.bottom];
	},
	getDimensions: function () {
		return { width: this.right - this.left, height: this.bottom - this.top };
	}
});

var GEHash = Class.create(Hash, {
	diff: function(compareHash) {
		compareHash = $H(compareHash);
		var diffHash = this.inject($H(), function(diffHash, pair) {
			var compareValue = compareHash.get(pair.key);
			if (pair.value != compareValue)
				diffHash.set(pair.key, compareValue);
			return diffHash;
		});
		/*
		if (diffHash.size() > 0) {
			GUI.log(diffHash.inspect())
		}
		*/
		return diffHash.size() > 0 ? diffHash.toObject() : undefined;
	}
});

Object.extend(GUI, {
	/*
	Shadow_JS: Class.create(UI.Shadow, { // 11.04.2010 Ossig Assumes prototype-ui to be included.
		initialize: function($super, element, tableElement) {
			var opt = { theme: "mac_shadow", withIFrameShim: false };
			element.style.left = 0;
			element.style.top = 0;
			$super(element, opt);
			this.init(element, tableElement);
		},
		setShadowSize: function() {
			var dimensions = this.element.getDimensions();
			this.setSize(dimensions.width, dimensions.height);
		}
	}),
	Shadow_CSS: Class.create({ // 11.04.2010 Ossig Assumes FF 3.6
		initialize: function(element, opt) {
			this.opt = Object.extend({ displayStyle: "block" }, opt || {});
			this.element = element;
			element.style.display = "none";
			return;
			this.init(element, tableElement);
			new window.PeriodicalExecuter(function(executer) {
				if (window.closed) executer.stop();
				if (GUI.bodyVisible()) this.onResizeElement();
			} .bind(this), 2);
		},
		setShadowSize: function() {
		},
		show: function() {
			var element = this.element;
			if (!element) return;
			// element.style.display = "table-cell";
			// element.style.display = "table";
			element.style.display = this.opt.displayStyle;
		},
		hide: function() {
			var element = this.element;
			if (!element) return;
			element.style.display = "none";
		}
	}),
	*/
	FrameSize: Class.create({
		/*
		opt={frameClass, frameMinSize, frameMaxSize, contentWindow, contentOffsetSize}
		*/
		initialize: function(frameElementArr, opt) {
			this.opt = Object.extend({ contentWindow: window, contentOffsetSize: {}, contentMinSize: {} }, opt || {});
			this.frameElementArr = frameElementArr ? frameElementArr.compact() : []; // frameWindow=frameElement.ownerDocument.InitDOM;
		},
		getDeltaSize: function(minSize) {
			var actualSize = this.getActualContentSize();
			var targetSize = this.getTargetContentSize();
			if (minSize) {
				if (targetSize.width < minSize.width) targetSize.width = minSize.width;
				if (targetSize.height < minSize.height) targetSize.height = minSize.height;
			}
			var deltaSize = GUI.sumSize([targetSize, { width: -actualSize.width, height: -actualSize.height}]);
			return deltaSize;
		},
		getActualContentSize: function() {
			// return this.opt.contentWindow.$body.getInnerSize();
			var documentElement = this.opt.contentWindow.document.documentElement;
			return { width: documentElement.clientWidth, height: documentElement.clientHeight };
		},
		getTargetContentSize: function() {
			// return GUI.sumSize([this.opt.contentOffsetSize, this.opt.contentWindow.$body.getOuterSize()]);
			var documentElement = this.opt.contentWindow.document.documentElement;
			return GUI.sumSize([this.opt.contentOffsetSize, { width: documentElement.offsetWidth, height: documentElement.offsetHeight}]);
		},
		adjustHeight: function() {
			var deltaHeight = this.getDeltaSize(this.opt.contentMinSize).height;
			GUI.log("FrameSize: deltaHeight=", deltaHeight);
			var frameElementArr = this.frameElementArr;
			if (!deltaHeight || !frameElementArr) return;
			frameElementArr.each(function(frameElement) {
				if (!frameElement) return;
				var innerHeight = frameElement.getInnerSize().height;
				var innerHeight2 = innerHeight + deltaHeight;
				GUI.log("FrameSize:", frameElement, " height:", innerHeight, " ==>", innerHeight2);
				frameElement.setInnerHeight(innerHeight2);
			});
		}
	}),
	adjustCMSFrameSize: function(contentWindow, frameElement) {
		var dtFrame = getDTFrame();
		if (dtFrame.GUI.options.cmsMode)
			dtFrame.CMS.adjustFrameSize(contentWindow, frameElement);
	}
});
/*
[GUI.Shadow_JS, GUI.Shadow_CSS].invoke("addMethods", {
	init: function(element, tableElement) {
		this.windowInnerSize = new GEHash(window.$body.getInnerSize());
		this.viewportSize = new GEHash(document.viewport.getDimensions());
		this.windowPageSize = new GEHash({ width: document.width, height: document.height });

		var onResizeElementEvent = this.onResizeElement.bindAsEventListener(this);
		Element.observe(window, "resize", onResizeElementEvent);
		/+
		var elementArr = element.select("table");
		elementArr.concat(window, element).each(function(element) {
		Element.observe(element, "resize", onResizeElementEvent);
		});
		+/
		this._onResizeElementEvent = this._onResizeElement.bind(this);
		Element.observe(window, "scroll", this.log.bind(this, "scroll"));
		Element.observe(window, "click", this.log.bind(this, "click"));
		this.tableElementArr = element.select("table");
		this.tableWidth = this.computeTableWidth();
		this.tableElement = tableElement;
	},
	onResizeElement: function(event, unblock) {
		this._onResizeElementEvent.defer(event, unblock);
	},
	_onResizeElement_Version1: function(event, unblock) {
		if (!unblock) {
			this.log("blocked", this.blocked);
			if (this.blocked) return;
			if (!GUI.bodyVisible()) return;

			this.log({ scrollWidth: $body.scrollWidth, scrollHeight: $body.scrollHeight });

			var windowInnerSize = window.$body.getInnerSize();
			this.log("window size diff:", this.windowInnerSize.toObject(), windowInnerSize);
			// if (!this.windowInnerSize.diff(windowInnerSize)) return;
			// this.log("window size diff acknowledged");
			this.windowInnerSize = new GEHash(windowInnerSize);

			var viewportSize = document.viewport.getDimensions();
			this.log("viewport size diff:", this.viewportSize.toObject(), viewportSize);
			// if (!this.viewportSize.diff(viewportSize)) return;
			// this.log("viewport size diff acknowledged");
			this.viewportSize = new GEHash(viewportSize);

			var windowPageSize = { width: document.width, height: document.height };
			// var windowPageSize = frameElement.getDimensions();
			this.log("page size diff:", this.windowPageSize.toObject(), windowPageSize);
			if (!this.windowPageSize.diff(windowPageSize)) return;
			this.log("page size diff acknowledged");
			this.windowPageSize = new GEHash(windowPageSize);
		} else
			this.blocked = false;
		var element = this.element;
		if (element.style.width && !unblock) {
			this.blocked = true;
			// element.style.width = nothing;
			// element.style.removeAttribute("width");
			element.style.width = "";
			this._onResizeElementEvent.delay(1, undefined, true);
		}
		/+
		var dimensions = element.getDimensions();
		this.setSize(dimensions.width, dimensions.height);
		+/

		var maxWidth = this.computeTableWidth();
		this.tableWidth = maxWidth;
		var innerWidth = element.getInnerSize().width;
		this.log("width=", innerWidth, ", maxWidth=", maxWidth);
		if (innerWidth < maxWidth) element.setInnerWidth(maxWidth);
		this.setShadowSize();
	},
	_onResizeElement: function(event, unblock) {
		if (!unblock) {
			this.log("blocked", this.blocked);
			if (this.blocked) return;
			if (!GUI.bodyVisible()) return;

			this.log({ scrollWidth: $body.scrollWidth, scrollHeight: $body.scrollHeight });

			var windowInnerSize = window.$body.getInnerSize();
			this.log("window size diff:", this.windowInnerSize.toObject(), windowInnerSize);
			// if (!this.windowInnerSize.diff(windowInnerSize)) return;
			// this.log("window size diff acknowledged");
			this.windowInnerSize = new GEHash(windowInnerSize);

			var viewportSize = document.viewport.getDimensions();
			this.log("viewport size diff:", this.viewportSize.toObject(), viewportSize);
			// if (!this.viewportSize.diff(viewportSize)) return;
			// this.log("viewport size diff acknowledged");
			this.viewportSize = new GEHash(viewportSize);

			var windowPageSize = { width: document.width, height: document.height };
			// var windowPageSize = frameElement.getDimensions();
			this.log("page size diff:", this.windowPageSize.toObject(), windowPageSize);
			// if (!this.windowPageSize.diff(windowPageSize)) return;
			// this.log("page size diff acknowledged");
			this.windowPageSize = new GEHash(windowPageSize);
		} else
			this.blocked = false;
		var element = this.element;
		if (element.style.width && !unblock) {
			this.blocked = true;
			// element.style.width = nothing;
			// element.style.removeAttribute("width");
			element.style.width = "";
			this._onResizeElementEvent.delay(1, undefined, true);
		}
		/+
		var dimensions = element.getDimensions();
		this.setSize(dimensions.width, dimensions.height);
		+/

		var maxWidth = this.computeTableWidth();
		this.tableWidth = maxWidth;
		var innerWidth = element.getInnerSize().width;
		this.log("width=", innerWidth, ", maxWidth=", maxWidth);
		if (innerWidth < maxWidth) element.setInnerWidth(maxWidth);
		this.setShadowSize();
	}, /+
	_onResizeElement: function(event, unblock) {
	}, +/
	computeTableWidth: function() {
		var maxWidth = this.tableElementArr.inject(0, function(maxWidth, element) {
			var dimensions = element.getDimensions();
			var width = dimensions.width;
			if (width > maxWidth) maxWidth = width;
			return maxWidth;
		});
		return maxWidth;
	},
	log: function(arg) {
		GUI.log.apply(GUI, ["_Shadow"].concat($A(arguments))); // 20.09.2009 Ossig Übergibt Argumente.
	}
});
*/

function xOr(x, y) {
	return !x != !y;
}

document.whenReady(function () {
	var onHandlerArr = [];
	onHandlerArr.push(document.on("dragover", function (event) {
		// var targetElement = event.findElement("html");
		// if (window.console) console.log(targetElement, " dragover");
		event.stop();
		event.dataTransfer.dropEffect = "none";
	}));
	onHandlerArr.push(document.on("unload", function (event) {
		onHandlerArr.invoke("stop");
		onHandlerArr = undefined;
		GUI.log("guibase.js unloaded.")
	}));
});

/*
$j(function () {
	// PLUpload 1.4.2 Chrome 11 Workaround: http://dev.sourcefabric.org/browse/CC-2240, https://github.com/moxiecode/plupload/issues/275.
	if (!window.BlobBuilder) {
		if (window.WebKitBlobBuilder)
			window.BlobBuilder = window.WebKitBlobBuilder;
	}
	// http://blog.filesender.org/2011/05/05/status-filesender-1-5/
	if (window.File && !File.prototype.slice) {
		if (File.prototype.webkitSlice)
			File.prototype.slice = File.prototype.webkitSlice;
		else if (File.prototype.mozSlice)
			File.prototype.slice = File.prototype.mozSlice;
	}
});
*/

/*
var geAjaxJSON = {
	execute: function (url, param) {
		param = param ? param + "&" : "";
		param += "GEAjax=1";
		var absUrl = window.geSystem ? geSystem.getAbsSystemURL(url) : url;
		GUI.log("geAjaxJSON: ", { url: absUrl, param: param });

		var resultSync;
		new Ajax.Request(absUrl, {
			method: 'post', // 19.10.2010 Ossig param will be posted.
			// contentType: "application/json",
			contentType: "application/x-www-form-urlencoded",
			evalJSON: "force",
			// requestHeaders: { "Content-type": "application/x-www-form-urlencoded", "Content-length": param ? param.length : 0 },
			requestHeaders: { "Content-type": "application/x-www-form-urlencoded" },
			// requestHeaders: { "Content-type": "application/json" },
			asynchronous: false,
			postBody: param,
			onException: function (request, ex) {
				GUI.error("geAjaxJSON: ", ex, " | ", request.transport.responseText);

			},
			onSuccess: function (response) {
				// resultSync = response.responseText;
				resultSync = response.responseJSON;
				if (resultSync) return resultSync;
				resultSync = { errorMessage: "geAjaxJSSON data missing"};
			},
			onFailure: function (response) {
				resultSync = { errorMessage: "geAjaxJSON" };
			}
		});
		return resultSync;
	}
};
*/

var geAjax = function (urlParam, postParam) {
	var unloadMode = false;
	document.whenUnload(function () {
		unloadMode = true;
	});
	function _execute(urlParamString, postParam, ajax) {
		var ajax = Object.extend({ evalJSON: "force", asynchronous: false, onSuccess: undefined, onFailure: undefined }, ajax || {});
		// var urlParam = $H(Object.isString(urlParam) ? urlParam.toQueryParams() : urlParam || {});
		// var urlParamString = urlParam.toQueryString();
		var postParam = $H(Object.isString(postParam) ? postParam.toQueryParams() : postParam || {});
		postParam.set("GEAjax", 1);
		var postParamString = postParam.toQueryString();
		// var postParamString = (postParamString ? "&" : "") + "GEAjax=1";
		var absUrl = window.geSystem ? geSystem.getAbsSystemURL(urlParamString) : urlParamString;
		// var absUrl = "AjaxGate.aspx?" + urlParamString
		GUI.log("geAjax: ", { url: absUrl, postParam: postParamString });
		var resultSync;
		new Ajax.Request(absUrl, {
			method: 'post', // 19.10.2010 Ossig param will be posted.
			// contentType: "application/json",
			contentType: "application/x-www-form-urlencoded",
			evalJSON: ajax.evalJSON, // true, // evalJSON: "force",
			evalJS: false,
			requestHeaders: { "Content-type": "application/x-www-form-urlencoded" },
			// requestHeaders: { "Content-type": "application/json" }, // 22.06.2011 Ossig postBody will not arrive.
			// asynchronous: false,
			asynchronous: ajax.asynchronous && !unloadMode,
			postBody: postParamString,
			onException: function (request, ex) {
				// GUI.error("geAjax: ", ex, " | ", request.transport.responseText);
				resultSync = { errorMessage: ["geAjax", ex, "|", request.transport.responseText].join(" ") };
				// this.onFailure(resultSync);
				/*
				try {
					request.options.onFailure(resultSync);
				} catch (e) {
					GUI.error(resultSync.errorMessage);
				}
				*/
				if (Object.isFunction(ajax.onFailure))
					ajax.onFailure(resultSync);
			},
			onSuccess: function (response) {
				resultSync = (function () {
					var result = response.responseJSON;
					if (result) return result;
					var responseText = response.responseText;
					if (!responseText) return { errorMessage: "geAjax data missing" };
					try {
						result = eval('(' + responseText + ')');
					} catch (e) {
						result = { errorMessage: "geAjax " + e };
						// result = { errorMessage: ["geAjax " + e, responseText].join("\n") };
					}
					return result;
				})();
				if (resultSync.errorMessage && Object.isFunction(ajax.onFailure))
					ajax.onFailure(resultSync);
				else if (Object.isFunction(ajax.onSuccess))
					ajax.onSuccess(resultSync);
			},
			onFailure: function (response) {
				resultSync = { errorMessage: response.errorMessage || "geAjax failure" };
				if (Object.isFunction(ajax.onFailure))
					ajax.onFailure(resultSync);
			}
		});
		return resultSync;
	}
	function _executeAjaxGate(ajax, opt) {
		var opt = Object.extend({ cmd: "Unknown", asynchronous: false }, opt || {});
		return _execute(
			"AjaxGate.aspx?" + Object.toQueryString(Object.extend({ cmd: opt.cmd }, urlParam || {})),
			postParam,
			Object.extend({ evalJSON: true, asynchronous: opt.asynchronous }, ajax || {}));
	}
	return {
		execute: function (ajax) {
			return _execute(urlParam, postParam, ajax);
		},
		executeMenuPoint: function (ajax) {
			// ajax: {onSuccess, onFailure}, param: {menuPointID}
			/*
			return _execute(
			"AjaxGate.aspx?" + Object.toQueryString(Object.extend({ cmd: "MenuPoint" }, urlParam || {})),
			postParam,
			Object.extend({ evalJSON: true, asynchronous: true }, ajax || {}));
			*/
			return _executeAjaxGate(ajax, { cmd: "MenuPoint", asynchronous: true });
		},
		executeExcelImport: function (ajax) {
			// ajax: {onSuccess, onFailure}
			/*
			return _execute(
			"AjaxGate.aspx?" + Object.toQueryString(Object.extend({ cmd: "ExcelImport" }, urlParam || {})),
			postParam,
			Object.extend({ evalJSON: true, asynchronous: false }, ajax || {}));
			*/
			return _executeAjaxGate(ajax, { cmd: "ExcelImport" });
		},
		executeExcelExport: function (ajax) {
			return _executeAjaxGate(ajax, { cmd: "ExcelExport" });
		},
		executeUnloadGEWindow: function (ajax) {
			// param: {geWindowID}
			/*
			return _execute(
			"AjaxGate.aspx?" + Object.toQueryString(Object.extend({ cmd: "UnloadGEWindow" }, urlParam || {})),
			postParam,
			Object.extend({ evalJSON: true, asynchronous: true }, ajax || {}));
			*/
			return _executeAjaxGate(ajax, { cmd: "UnloadGEWindow", asynchronous: true });
		},
		executeCallWeb: function (ajax) {
			return _executeAjaxGate(ajax, { cmd: "CallWeb" });
		}
	}
};

var GE = {
	splitNull: function (value, delimiter) { // 19.11.2011 Ossig Speed: value resolved only 1 times by caller.
		return value ? value.split(delimiter) : [];
	},
	splitTerm: function (term, delimiter) {
		if (!term) return [];
		// var ParLiOrg = smSpec.ParameterListOriginal.split(sep);
		// 19.12.2011 Ossig http://stackoverflow.com/questions/6462578/alternative-to-regex-match-all-instances-not-inside-quotes
		// var ParLiOrg = smSpec.ParameterListOriginal.split(/\,(?=(?:[^"\\]*(?:\\.|"(?:[^"\\]*\\.)*[^"\\]*"))*[^"]*$)/g);
		GUI.assert(delimiter.length == 1, delimiter);
		var pattern = ['\\', delimiter, '(?=(?:[^"\\\\]*(?:\\\\.|"(?:[^"\\\\]*\\\\.)*[^"\\\\]*"))*[^"]*$)'].join("");
		var regExp = new RegExp(pattern, "g");
		return term.split(delimiter);
	},
	isNullOrEmpty: function (value) {
		// return value == undefined || (Object.isString(value) && value.length == 0);
		if (value == undefined) return true;
		var empty = value.empty;
		return Object.isFunction(empty) && empty();
	},
	// GE.$Range(count).each(function(index) { });
	$Range: function (start, count) {
		switch (arguments.length) {
			case 0:
				start = 0;
				count = 0;
				break;
			case 1:
				count = start;
				start = 0;
		}
		return $R(start, start + count, true);
	},
	// 16.07.2011 Ossig Alterative: Object.toJSON
	// var pattern = new GE.EscapePattern(["\\", "\t", "\n"], ["\\\\", "\\t", "\\n"]);
	EscapePattern: Class.create({
		initialize: function (fromArr, toArr) {
			// toArr.first: "\\\\" when escape character is "\\"
			this.fromArr = $A(fromArr);
			this.toArr = $A(toArr);
			this.from = this.fromArr.splice(0, 1).first();
			this.to = this.toArr.splice(0, 1).first();
			GUI.assert(fromArr && toArr && this.fromArr.size() == this.toArr.size(), "GE.StringFilter");
		},
		encode: function (value) {
			if (value === undefined || !this.toArr.size()) return value;
			if (!Object.isString(value)) value = value.toString();
			var valueList = value.split(this.from);
			var fromRegExpArr = (function () {
				if (!this.fromRegExpArr) {
					this.fromRegExpArr = this.fromArr.collect(function (fromValue) {
						return new RegExp(fromValue, "g");
					});
				}
				return this.fromRegExpArr;
			})();
			this.toArr.each(function (toValue, index) {
				var fromRegExp = fromRegExpArr[index];
				valueList = valueList.collect(function (value) {
					return value.replace(fromRegExp, toValue);
				});
			});
			return valueList.join(this.to);
		},
		decode: function (value) {
			if (value === undefined || !this.toArr.size()) return value;
			if (!Object.isString(value)) value = value.toString();
			var valueList = value.split(this.to);
			var toRegExpArr = (function () {
				if (!this.toRegExpArr) {
					this.toRegExpArr = this.toArr.collect(function (toValue) {
						return new RegExp(toValue, "g");
					});
				}
				return this.toRegExpArr;
			})();
			this.fromArr.each(function (fromValue, index) {
				var toRegExp = toRegExpArr[index];
				valueList = valueList.collect(function (value) {
					return value.replace(toRegExp, fromValue);
				});
			});
			return valueList.join(this.from);
		}
	}),
	FileInfo: Class.create({
		initialize: function (fileURL) {
			function splitPath3(Path) {
				if (Path == undefined) return [];
				var DotIndex = Path.lastIndexOf(".");
				var BackSlashIndex = Path.lastIndexOf("/");
				if (BackSlashIndex < 0) BackSlashIndex = Path.lastIndexOf("\\");
				if (DotIndex <= BackSlashIndex) DotIndex = -1;
				if (BackSlashIndex <= 0) BackSlashIndex = -1;
				var Dir = Path.substring(0, BackSlashIndex + 1);
				var FileName = DotIndex < 0 ? Path.substr(BackSlashIndex + 1) : Path.substring(BackSlashIndex + 1, DotIndex);
				var FileExt = DotIndex < 0 ? '' : Path.substr(DotIndex);
				return [Dir, FileName, FileExt];
			}
			this.infoArr = splitPath3(fileURL);
		},
		getFileName: function () {
			return this.infoArr[1];
		},
		getDotExt: function () {
			return this.infoArr[2];
		},
		getDirPath: function () {
			return this.infoArr[0];
		}
	}),
	IDGenerator: Class.create({
		_index: 0, // global for all generator instances.
		initialize: function (prefix) {
			this._prefix = prefix ? prefix.toString() : "";
		},
		createID: function () {
			return this._prefix + (++this._index);
		}
	}),
	Dict: Class.create(Hash, {
		// Compatible to VB Dictionary.
		initialize: function ($super, _KeyValueArray) {
			if (!Object.isArray(_KeyValueArray)) {
				$super(_KeyValueArray);
				return;
			}
			_KeyValueArray.each(function (pair) {
				this.set(pair.Key, pair.Value);
			}, this);
		},
		toJSON: function () {
			return this.collect(function (pair) {
				return { Key: pair.key, Value: pair.value };
			});
		}
	})
};

Object.extend(Math, {
	sign: function (number) {
		return number > 0 ? 1 : number < 0 ? -1 : 0
	}
});

