(function(){
	var
		global = this,
		undefined,
		$,
		sj = global.sj = $ = function(t) {
			if (!t) return t;
			if (t.sjCompatablized !== true) {
				switch (typeof t) {
					case "string":
						if (arguments.length == 2) t;
						break;
					default:
						if ((t.nodeName.toLowerCase() != "select" && $.js.isArray(t)) || Object.prototype.toString.call(t) == "[object NodeList]" || Object.prototype.toString.call(t) == "[object HTMLCollection]") {
							/*var l = t.length;
							for (var i = 0; i < l; i++) {
								$(t[i]);
							}*/
						} else {
							if (t.nodeType && t.nodeName.toLowerCase() != "script") $.compat.node(t);
							if (typeof t.innerHTML !== undefined && t.nodeName.toLowerCase() != "script") $.compat.element(t);
						}
						break;
				}
				t.sjCompatablized = true;
			}
			return t;
		};
	
	$.version = 1.00;
	$.env = {
		SITE_ROOT: "/",
		JSON_URL: "/static/js/json2.js",
		PROGRAM_ROOT: "/static/apps/",
		MODULE_ROOT: "/static/modules/",
		JSON_REQUIRED: true
	};
	
	$.browser = {
		init: function() {
			this.name = this.searchString(this.dataBrowser) || "An unknown browser";
			this.version = this.searchVersion(navigator.userAgent)
				|| this.searchVersion(navigator.appVersion)
				|| "an unknown version";
			this.OS = this.searchString(this.dataOS) || "an unknown OS";
			this.engine = {
				name: this.searchString(this.dataEngine),
				version: this.searchVersion(navigator.userAgent)
			}
		},
		searchString: function(data) {
			for (var i = 0, l = data.length; i < l; i++) {
				var dataString = data[i].string;
				var dataProp = data[i].prop;
				this.versionSearchString = data[i].versionSearch || new RegExp(data[i].identity);
				if (dataString) {
					if (dataString.indexOf(data[i].subString) != -1) return data[i].identity;
				} else if (dataProp) return data[i].identity;
			}
		},
		searchVersion: function(dataString) {
			var m = dataString.match(this.versionSearchString);
			if (!m) return;
			return m[1];
		},
		dataBrowser: [
			{
				string: navigator.userAgent,
				subString: "Chrome",
				identity: "Chrome",
				versionSearch: /Chrome\/([0-9.]+)/
			},
			{
				string: navigator.userAgent,
				subString: "OmniWeb",
				versionSearch: "OmniWeb/",
				identity: /OmniWeb.([0-9.]+)/
			},
			{
				string: navigator.vendor,
				subString: "Apple",
				identity: "Safari",
				versionSearch: /Version.([0-9.]+)/
			},
			{
				string: navigator.userAgent,
				subString: "Android",
				identity: "Android",
				versionSearch: /Version.([0-9.]+)/
			},
			{
				prop: window.opera,
				identity: "Opera"
			},
			{
				string: navigator.vendor,
				subString: "iCab",
				identity: "iCab"
			},
			{
				string: navigator.vendor,
				subString: "KDE",
				identity: "Konqueror"
			},
			{
				string: navigator.userAgent,
				subString: "Firefox",
				identity: "Firefox",
				versionSearch: /Firefox\/([0-9.]+)/
			},
			{
				string: navigator.vendor,
				subString: "Camino",
				identity: "Camino"
			},
			{		// for newer Netscapes (6+)
				string: navigator.userAgent,
				subString: "Netscape",
				identity: "Netscape"
			},
			{
				string: navigator.userAgent,
				subString: "MSIE",
				identity: "Explorer",
				versionSearch: /MSIE.([0-9.]+)/
			},
			{
				string: navigator.userAgent,
				subString: "Gecko",
				identity: "Mozilla",
				versionSearch: /rv:([0-9.]+)/
			},
			{ 		// for older Netscapes (4-)
				string: navigator.userAgent,
				subString: "Mozilla",
				identity: "Netscape",
				versionSearch: /Mozilla.([0-9.]+)/
			}
		],
		dataOS: [
			{
				string: navigator.platform,
				subString: "Win",
				identity: "Windows"
			},
			{
				string: navigator.platform,
				subString: "Mac",
				identity: "Mac"
			},
			{
				string: navigator.userAgent,
				subString: "iPhone",
				identity: "iPhone/iPod"
			},
			{
				string: navigator.userAgent,
				subString: "iPad",
				identity: "iPad"
			},
			{
				string: navigator.platform,
				subString: "Linux",
				identity: "Linux"
			}
		],
		dataEngine: [
			{
				string: navigator.userAgent,
				subString: "WebKit",
				identity: "WebKit",
				versionSearch: /AppleWebKit\/([0-9.]+)/
			},
			{
				string: navigator.userAgent,
				subString: "MSIE",
				identity: "Trident",
				versionSearch: /MSIE([0-9.]+)/
			},
			{
				string: navigator.userAgent,
				subString: "Opera",
				identity: "Presto",
				versionSearch: /Presto\/([0-9.]+)/
			},
			{
				string: navigator.userAgent,
				subString: "Gecko",
				identity: "Gecko",
				versionSearch: /rv:([0-9.]+)/
			}
		]
	};
	$.browser.init();
	
	var updatePointerInfo = function(e) {
		$.env.POINTER_DATA = {
			target: e.target,
			altKey: e.altKey,
			ctrlKey: e.ctrlKey,
			metaKey: e.metaKey,
			shiftKey: e.shiftKey,
			button: e.button,
			which: e.which,
			clientX: e.clientX,
			clientY: e.clientY,
			layerX: e.layerX,
			layerY: e.layerY,
			pageX: e.pageX,
			pageY: e.pageY,
			screenX: e.screenX,
			screenY: e.screenY
		};
	};
	
	var initCallbacks = [];
	$.isReady = false;
	$.init = function() {
		if ($.isReady) return;
		if (!document.body) document.body = document.getElementsByTagName("body")[0];
		if ($.env.JSON_REQUIRED && typeof JSON === "undefined") {
			$.html.addScript($.env.JSON_URL, $.init);
			return;
		}
		$.env.startup = new Date();
		if (!$.ui) $.ui = {};
		if (!$.ui.loader) $.ui.loader = new Image();
		$.ui.loader.src = [$.env.SITE_ROOT, "static/img/ajax/ajax-loader.gif"].join("");
		
		var i = $.dom.create("p", {style:"width:100%;height:200px"});
		var o = $.dom.create("div", {style:"position:absolute;top:0px;left:0px;visibility:hidden;width:200px;height:150px;overflow:hidden"});
		o.appendChild(i);
		document.body.appendChild(o);
		var w1 = i.offsetWidth;
		var h1 = i.offsetHeight;
		o.style.overflow = "scroll";
		var w2 = i.offsetWidth;
		var h2 = i.offsetHeight;
		if (w1 == w2) w2 = o.clientWidth;
		if (h1 == h2) h2 = o.clientWidth;
		document.body.removeChild(o);
		
		$.env.SCROLL_BAR_WIDTH = w1 - w2;
		$.env.SCROLL_BAR_HEIGHT = h1 - h2;
		
		$.isReady = true;
		document.addEventListener("mousemove", updatePointerInfo, false);
		updaterTimer.start();

		for (var i = 0, l = initCallbacks.length; i < l; i++) {
			initCallbacks[i].call(global);
		}
	};
	$.init.addCallback = function(cb) {
		initCallbacks.push(cb);
	}
	
	$.q = {
		c: function(t, s, u) {
			if (typeof t === "string") {
				u = s;
				s = t;
				t = document;
			}
			else if (!t.getElementsByClassName) t.getElementsByClassName = $.compat.element.getElementsByClassName;
			var r = t.getElementsByClassName(s);
			if (u) {
				if (r.length == 0) return null;
				else return $(r[0]);
			}
			return r;
		},
		i: function(s) {
			return $(document.getElementById(s));
		},
		n: function(s, u) {
			var r = document.getElementsByName(s);
			if (u) {
				if (r.length == 0) return null;
				else return $(r[0]);
			}
			return r;
		},
		t: function(t, s, u) {
			if (typeof t === "string") {
				u = s;
				s = t;
				t = document;
			}
			if (!t.getElementsByTagName) return [];
			var r = t.getElementsByTagName(s);
			if (u) {
				if (r.length == 0) return null;
				else return $(r[0]);
			}
			return r;
		}
	};
	
	$.compat = {
		fixPrototypes: function() {
			/*if (!Function.prototype.mixin) Function.prototype.mixin = function(parent, opts, wanted) {
				if (!parent) return;
				var p = new parent(opts);
				for (var i in p) {
					if ((!wanted || (wanted.length > 0 && wanted.indexOf(i) > -1)) && !(i in this.prototype) && p.hasOwnProperty(i)) this.prototype[i] = p[i];
				}
			};*/
			if (!Array.prototype.indexOf) {
				Array.prototype.indexOf = function(elt) {
					var len = this.length >>> 0;

					var from = Number(arguments[1]) || 0;
					from = (from < 0) ? Math.ceil(from) : Math.floor(from);
					if (from < 0) from += len;
					for (; from < len; from++) {
						if (from in this && this[from] === elt) return from;
					}
					return -1;
				};
			}
			if (!Array.prototype.contains) {
				Array.prototype.contains = function(obj) {
					if (this.indexOf(obj) != -1) return true;
					return false;
				};
			}
			if (!String.prototype.trim) {
				String.prototype.trim = function(t) {
					return (this || t || "").replace(/^\s+|\s+$/g, "");
				}
			}
		},
		window: function(t) {
			if (!t.addEventListener) $.js.objectMixin(t, $.compat.event.EventTarget);
		},
		document: function(t) {
			if (!t.addEventListener) $.js.objectMixin(t, $.compat.event.EventTarget);
			if (!t.createEvent) $.js.objectMixin(t, $.compat.event.DocumentEvent);
			if (!t.setUserData) t.setUserData = $.compat.node.setUserData;
			if (!t.getUserData) t.getUserData = $.compat.node.getUserData;
			//if (!t.querySelector) t.querySelector = $.compat.node.querySelector;
			//if (!t.querySelectorAll) t.querySelectorAll = $.compat.node.querySelectorAll;
			if (!t.getElementsByClassName) t.getElementsByClassName = $.compat.element.getElementsByClassName;
		},
		node: function(t) {
			if (!t.addEventListener) $.js.objectMixin(t, $.compat.event.EventTarget);
			if (!t.setUserData) t.setUserData = $.compat.node.setUserData;
			if (!t.getUserData) t.getUserData = $.compat.node.getUserData;
			//if (!t.querySelector) t.querySelector = $.compat.node.querySelector;
			//if (!t.querySelectorAll) t.querySelectorAll = $.compat.node.querySelectorAll;
			if (!t.hasAttribute) t.hasAttribute = $.compat.node.hasAttribute;
			//if (!t.hasAttributeNS) t.hasAttributeNS = $.compat.node.hasAttributeNS;
			//if (!t.getAttributeNS) t.getAttributeNS = $.compat.node.getAttributeNS;
			//if (!t.setAttributeNS) t.setAttributeNS = $.compat.node.setAttributeNS;
		},
		element: function(t) {
			if (!t.classList) t.classList = new $.compat.element.classList(t);
			if (!t.getElementsByClassName) t.getElementsByClassName = $.compat.element.getElementsByClassName;
		}
	};
	$.compat.fixPrototypes();
	$.compat.event = {};
	$.compat.event.eventList = {
		"": ["activate", "abort", "click", "dblclick", "focusin", "focusout", "keydown", "keypress", "keyup", "mousedown", "mouseenter", "mouseover", "mouseup", "mouseout", "mousemove", "mousewheel"],
		a: ["blur", "focus"],
		body: ["load", "unload"],
		button: ["blur", "focus"],
		form: ["reset", "submit"],
		input: ["blur", "focus", "change", "select"],
		label: ["blur", "focus"],
		select: ["blur", "focus", "change"],
		textarea: ["blur", "focus", "change", "select"]
	};
	$.compat.event.Event = function() {};
	$.compat.event.Event.prototype = {
		CAPTURING_PHASE: 1,
		AT_TARGET: 2,
		BUBBLING_PHASE: 3,
		type: "",
		target: null,
		currentTarget: null,
		eventPhase: 2,
		bubbles: true,
		cancelable: true,
		timeStamp: null,
		_propagationStopped: false,
		stopPropagation: function() {
			global.event.cancelBubble = true;
			this._propagationStopped = true;
		},
		preventDefault: function() {
			if (this.cancelable) {
				global.event.returnValue = false;
				this.defaultPrevented = true;
			}
		},
		initEvent: function(type, canBubble, cancelable) {
			this.type = type;
			this.bubbles = canBubble;
			this.cancelable = cancelable;
			this.timeStamp = new Date().getTime();
		},
		namespaceURI: null,
		_immediatePropagationStopped: false,
		stopImmediatePropagation: function() {
			global.event.cancelBubble = true;
			this._propagationStopped = true;
			this._immediatePropagationStopped = true;
		},
		defaultPrevented: false,
		initEventNS: function(namespace, type, canBubble, cancelable) {
			this.namespaceURI = namespace;
			this.initEvent(type, canBubble, cancelable);
		}
	};
	$.compat.event.CustomEvent = function() {};
	$.compat.event.CustomEvent.prototype = new $.compat.event.Event();
	$.compat.event.CustomEvent.prototype.detail = null;
	$.compat.event.CustomEvent.prototype.initCustomEvent = function(type, canBubble, cancelable, detail) {
		this.initEvent(type, canBubble, cancelable);
		this.detail = detail;
	};
	$.compat.event.CustomEvent.prototype.initCustomEventNS = function(namespace, type, canBubble, cancelable, detail) {
		this.initEventNS(namespace, type, canBubble, cancelable);
		this.detail = detail;
	};
	
	$.compat.event.UIEvent = function(){};
	$.compat.event.UIEvent.prototype = new $.compat.event.Event();
	$.compat.event.UIEvent.prototype.view = window;
	$.compat.event.UIEvent.prototype.detail = 0;
	$.compat.event.UIEvent.prototype.initUIEvent = function(type, canBubble, cancelable, view, detail) {
		this.initUIEventNS(null, type, canBubble, cancelable, view, detail);
	};
	$.compat.event.UIEvent.prototype.initUIEventNS = function(namespace, type, canBubble, cancelable, view, detail) {
		this.initEventNS(namespace, type, canBubble, cancelable);
		if (view) this.view = view;
		this.detail = detail;
	};
	
	$.compat.event.MouseEvent = function(){};
	$.compat.event.MouseEvent.prototype = new $.compat.event.UIEvent();
	$.compat.event.MouseEvent.prototype.screenX = 0;
	$.compat.event.MouseEvent.prototype.screenY = 0;
	$.compat.event.MouseEvent.prototype.clientX = 0;
	$.compat.event.MouseEvent.prototype.clientY = 0;
	$.compat.event.MouseEvent.prototype.ctrlKey = false;
	$.compat.event.MouseEvent.prototype.shiftKey = false;
	$.compat.event.MouseEvent.prototype.altKey = false;
	$.compat.event.MouseEvent.prototype.metaKey = false;
	$.compat.event.MouseEvent.prototype.button = 0;
	$.compat.event.MouseEvent.prototype.relatedTarget = null;
	$.compat.event.MouseEvent.prototype._modifiersList = null;
	$.compat.event.MouseEvent.prototype.initMouseEvent = function(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget) {
		this.initMouseEventNS(null, type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);
	};
	$.compat.event.MouseEvent.prototype.getModifierState = function(keyIdentifier) {
		if (this._modifiersList.split(/\s+/).indexOf(keyIdentifier) > -1) return true;
		return false;
	};
	$.compat.event.MouseEvent.prototype.initMouseEventNS = function(namespace, type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, button, relatedTarget, modifiersList) {
		this.initUIEventNS(namespace, type, canBubble, cancelable, view, detail);
		this.screenX = screenX;
		this.screenY = screenY;
		this.clientX = clientX;
		this.clientY = clientY;
		this._modifiersList = modifiersList;
		var mods = modifiersList.split(/\s+/);
		if (mods.indexOf("Control") > -1) this.ctrlKey = true;
		if (mods.indexOf("Shift") > -1) this.shiftKey = true;
		if (mods.indexOf("Alt") > -1) this.altKey = true;
		if (mods.indexOf("Meta") > -1) this.metaKey = true;
		this.button = button;
		this.relatedTarget = relatedTarget;
	};
	
	$.compat.event.MouseWheelEvent = function(){};
	$.compat.event.MouseWheelEvent.prototype = new $.compat.event.MouseEvent();
	$.compat.event.MouseWheelEvent.prototype.wheelDelta = 0;
	$.compat.event.MouseWheelEvent.prototype.initMouseWheelEvent = function(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, button, relatedTarget, modifiersList, wheelDelta) {
		this.initMouseWheelEventNS(null, type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, button, relatedTarget, modifiersList, wheelDelta);
	};
	$.compat.event.MouseWheelEvent.prototype.initMouseWheelEventNS = function(namespace, type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, button, relatedTarget, modifiersList, wheelDelta) {
		this.initMouseEventNS(namespace, type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, button, relatedTarget, modifiersList);
		this.wheelDelta = wheelDelta;
	};
	
	$.compat.event.WheelEvent = function(){};
	$.compat.event.WheelEvent.prototype = new $.compat.event.MouseEvent();
	$.compat.event.WheelEvent.prototype.DOM_DELTA_PIXEL = 0x00;
	$.compat.event.WheelEvent.prototype.DOM_DELTA_LINE = 0x01;
	$.compat.event.WheelEvent.prototype.DOM_DELTA_PAGE = 0x02;
	$.compat.event.WheelEvent.prototype.deltaX = 0;
	$.compat.event.WheelEvent.prototype.deltaY = 0;
	$.compat.event.WheelEvent.prototype.deltaZ = 0;
	$.compat.event.WheelEvent.prototype.deltaMode = 0x00;
	$.compat.event.WheelEvent.prototype.initWheelEvent = function(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, button, relatedTarget, modifiersList, deltaX, deltaY, deltaZ, deltaMode) {
		this.initWheelEventNS(null, type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, button, relatedTarget, modifiersList, deltaX, deltaY, deltaZ, deltaMode);
	};
	$.compat.event.WheelEvent.prototype.initWheelEventNS = function(namespace, type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, button, relatedTarget, modifiersList, deltaX, deltaY, deltaZ, deltaMode) {
		this.initMouseEventNS(namespace, type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, button, relatedTarget, modifiersList);
		if (typeof deltaX === "number") this.deltaX = deltaX;
		if (typeof deltaY === "number") this.deltaY = deltaY;
		if (typeof deltaZ === "number") this.deltaZ = deltaZ;
		if (typeof deltaMode === "number") this.deltaMode = deltaMode;
	};
	
	$.compat.event.TextEvent = function(){};
	$.compat.event.TextEvent.prototype = new $.compat.event.UIEvent();
	$.compat.event.TextEvent.prototype.DOM_INPUT_METHOD_UNKNOWN = 0x00;
	$.compat.event.TextEvent.prototype.DOM_INPUT_METHOD_KEYBOARD = 0x01;
	$.compat.event.TextEvent.prototype.DOM_INPUT_METHOD_PASTE = 0x02;
	$.compat.event.TextEvent.prototype.DOM_INPUT_METHOD_DROP = 0x03;
	$.compat.event.TextEvent.prototype.DOM_INPUT_METHOD_IME = 0x04;
	$.compat.event.TextEvent.prototype.DOM_INPUT_METHOD_OPTION = 0x05;
	$.compat.event.TextEvent.prototype.DOM_INPUT_METHOD_HANDWRITING = 0x06;
	$.compat.event.TextEvent.prototype.DOM_INPUT_METHOD_VOICE = 0x07;
	$.compat.event.TextEvent.prototype.DOM_INPUT_METHOD_MULTIMODAL = 0x08;
	$.compat.event.TextEvent.prototype.DOM_INPUT_METHOD_SCRIPT = 0x09;
	$.compat.event.TextEvent.prototype.data = null;
	$.compat.event.TextEvent.prototype.inputMode = 0x00;
	$.compat.event.TextEvent.prototype.initTextEvent = function(type, canBubble, cancelable, view, data, inputMode) {
		this.initTextEventNS(null, type, canBubble, cancelable, view, data, inputMode);
	};
	$.compat.event.TextEvent.prototype.initTextEventNS = function(namespace, type, canBubble, cancelable, view, data, inputMode) {
		this.initUIEventNS(namespace, type, canBubble, cancelable, view);
		this.detail = undefined;
		this.data = data;
		this.inputMode = inputMode;
	};
	
	$.compat.event.KeyboardEvent = function(){};
	$.compat.event.KeyboardEvent.prototype = new $.compat.event.UIEvent();
	$.compat.event.KeyboardEvent.prototype.DOM_KEY_LOCATION_STANDARD = 0x00;
	$.compat.event.KeyboardEvent.prototype.DOM_KEY_LOCATION_LEFT = 0x01;
	$.compat.event.KeyboardEvent.prototype.DOM_KEY_LOCATION_RIGHT = 0x02;
	$.compat.event.KeyboardEvent.prototype.DOM_KEY_LOCATION_NUMPAD = 0x03;
	$.compat.event.KeyboardEvent.prototype.DOM_KEY_LOCATION_MOBILE = 0x04;
	$.compat.event.KeyboardEvent.prototype.DOM_KEY_LOCATION_JOYSTICK = 0x05;
	$.compat.event.KeyboardEvent.prototype.keyIdentifier = null;
	$.compat.event.KeyboardEvent.prototype.keyLocation = 0x00;
	$.compat.event.KeyboardEvent.prototype.ctrlKey = false;
	$.compat.event.KeyboardEvent.prototype.shiftKey = false;
	$.compat.event.KeyboardEvent.prototype.altKey = false;
	$.compat.event.KeyboardEvent.prototype.metaKey = false;
	$.compat.event.KeyboardEvent.prototype.repeat = false;
	$.compat.event.KeyboardEvent.prototype.getModifierState = function(keyIdentifier) {
		if (this._modifiersList.split(/\s+/).indexOf(keyIdentifier) > -1) return true;
		return false;
	};
	$.compat.event.KeyboardEvent.prototype.initKeyboardEvent = function(type, canBubble, cancelable, view, keyIdentifier, keyLocation, modifiersList, repeat) {
		this.initKeyboardEventNS(null, type, canBubble, cancelable, view, keyIdentifier, keyLocation, modifiersList, repeat);
	};
	$.compat.event.KeyboardEvent.prototype.initKeyboardEventNS = function(namespace, type, canBubble, cancelable, view, keyIdentifier, keyLocation, modifiersList, repeat) {
		this.initUIEventNS(namespace, type, canBubble, cancelable, view);
		this.keyIdentifier = keyIdentifier;
		this.keyLocation = keyLocation;
		this.repeat = repeat;
		this._modifiersList = modifiersList;
		var mods = modifiersList.split(/\s+/);
		if (mods.indexOf("Control") > -1) this.ctrlKey = true;
		if (mods.indexOf("Shift") > -1) this.shiftKey = true;
		if (mods.indexOf("Alt") > -1) this.altKey = true;
		if (mods.indexOf("Meta") > -1) this.metaKey = true;
	};
	
	$.compat.event.CompositionEvent = function(){};
	$.compat.event.CompositionEvent.prototype = new $.compat.event.UIEvent();
	$.compat.event.CompositionEvent.prototype.data = null;
	$.compat.event.CompositionEvent.prototype.initCompositionEvent = function(type, canBubble, cancelable, view, data) {
		this.initCompositionEventNS(null, type, canBubble, cancelable, view, data);
	};
	$.compat.event.CompositionEvent.prototype.initCompositionEventNS = function(namespace, type, canBubble, cancelable, view, data) {
		this.initUIEventNS(namespace, type, canBubble, cancelable, view);
		this.data = data;
	};
	
	$.compat.event.MutationEvent = function(){};
	$.compat.event.MutationEvent.prototype = new $.compat.event.Event();
	$.compat.event.MutationEvent.prototype.MODIFICATION = 1;
	$.compat.event.MutationEvent.prototype.ADDITION = 2;
	$.compat.event.MutationEvent.prototype.REMOVAL = 3;
	$.compat.event.MutationEvent.prototype.relatedNode = null;
	$.compat.event.MutationEvent.prototype.prevValue = null;
	$.compat.event.MutationEvent.prototype.newValue = null;
	$.compat.event.MutationEvent.prototype.attrName = null;
	$.compat.event.MutationEvent.prototype.attrChange = 1;
	$.compat.event.MutationEvent.prototype.initMutationEvent = function(type, canBubble, cancelable, relatedNode, prevValue, newValue, attrName, attrChange) {
		this.initMutationEventNS(null, type, canBubble, cancelable, relatedNode, prevValue, newValue, attrName, attrChange);
	};
	$.compat.event.MutationEvent.prototype.initMutationEventNS = function(namespace, type, canBubble, cancelable, relatedNode, prevValue, newValue, attrName, attrChange) {
		this.initEventNS(namespace, type, canBubble, cancelable);
		this.relatedNode = relatedNode;
		this.prevValue = prevValue;
		this.newValue = newValue;
		this.attrName = attrName;
		this.attrChange = attrChange;
	};
	
	$.compat.event.MutationNameEvent = function(){};
	$.compat.event.MutationNameEvent.prototype = new $.compat.event.MutationEvent();
	$.compat.event.MutationNameEvent.prototype.prevNamespaceURI = null;
	$.compat.event.MutationNameEvent.prototype.prevNodeName = null;
	$.compat.event.MutationNameEvent.prototype.initMutationNameEvent = function(type, canBubble, cancelable, relatedNode, prevNamespaceURI, prevNodeName) {
		this.initMutationNameEventNS(null, type, canBubble, cancelable, relatedNode, prevNamespaceURI, prevNodeName);
	};
	$.compat.event.MutationNameEvent.prototype.initMutationNameEventNS = function(namespace, type, canBubble, cancelable, relatedNode, prevNamespaceURI, prevNodeName) {
		this.initMutationEventNS(namespace, type, canBubble, cancelable, relatedNode, null, null, null, null);
		this.prevNamespaceURI = prevNamespaceURI;
		this.prevNodeName = prevNodeName;
	};
	
	$.compat.event.EventException = function(code) {
		this.code = code;
	};
	$.compat.event.EventException.prototype = {
		UNSPECIFIED_EVENT_TYPE_ERR: 0,
		DISPATCH_REQUEST_ERR: 1,
		code: null
	};
	
	$.compat.event.EventTarget = function() {};
	$.compat.event.EventTarget.prototype = {
		addEventListener: function(type, listener, useCapture) {
			this.addEventListenerNS(null, type, listener, useCapture);
		},
		removeEventListener: function(type, listener, useCapture) {
			this.removeEventListenerNS(null, type, listener, useCapture);
		},
		dispatchEvent: function(evt) {
			if (!evt.type) throw new $.compat.event.EventException(0);
			if (evt._beingDispatched) throw new $.compat.event.EventException(1);
			evt._beingDispatched = true;
			var t = this;
			var isNatural = true;
			if (evt.namespace != null || !$.compat.event.isEventNatural(t.nodeName.toLowerCase(), evt.type)) isNatural = false;
			
			if (isNatural) {
				evt = sj.js.extend(document.createEventObject(), evt);
				this.fireEvent(['on', evt.type].join(""), evt);
			} else $.compat.event.handleEvent.call(t, evt);
			
			evt._beingDispatched = false;
		},
		addEventListenerNS: function(namespace, type, listener, useCapture) {
			if (useCapture === undefined) throw "Not Enough Arguments";
			var t = this;
			var isNatural = true;
			if (namespace != null || (typeof t.nodeName !== "undefined" && !$.compat.event.isEventNatural(t.nodeName.toLowerCase(), type))) isNatural = false;
			var re = $.compat.event.registeredEvents;
			if (!(type in re)) re[type] = [[], []];//first is captures, second is bubbles.
			var ret = re[type][useCapture ? 0 : 1];
			var l = ret.length;
			for (var i = 0; i < l; i++) {
				if (ret[i].namespace == namespace && ret[i].type == type && ret[i].target == t && ret[i].listener == listener) return;
			}
			ret.push({
				namespace: namespace,
				target: t,
				type: type,
				listener: listener
			});
			if (isNatural) t.attachEvent(["on", type].join(""), $.compat.event.handleEvent);
		},
		removeEventListenerNS: function(namespace, type, listener, useCapture) {
			if (useCapture === undefined) throw "Not Enough Arguments";
			var t = this;
			var isNatural = true;
			if (namespace != null || !$.compat.event.isEventNatural(t.nodeName.toLowerCase(), type)) isNatural = false;
			var re = $.compat.event.registeredEvents;
			if (!(type in re)) return;
			var ret = re[type][useCapture ? 0 : 1];
			var l = ret.length;
			var toRemove = -1;
			var numOfType = 0;
			for (var i = 0; i < l; i++) {
				if (ret[i].namespace == namespace && ret[i].type == type && ret[i].target == t && ret[i].listener == listener) toRemove = i;
				else if (ret[i].namespace == namespace && ret[i].type == type && ret[i].target == t) numOfType++;
			}
			if (toRemove > -1) ret.splice(toRemove, 1);
			if (numOfType == 0 && isNatural) t.detachEvent(["on", type].join(""), $.compat.event.handleEvent);
		}
	};
	
	$.compat.event.DocumentEvent = function() {};
	$.compat.event.DocumentEventMap = {
		Event: "Event",
		HTMLEvents: "Event",
		UIEvent: "UIEvent",
		UIEvents: "UIEvent",
		MouseEvent: "MouseEvent",
		MouseEvents: "MouseEvent",
		MutationEvent: "MutationEvent",
		MutationEvents: "MutationEvent",
		CompositionEvent: "CompositionEvent",
		MutationNameEvent: "MutationNameEvent",
		KeyboardEvent: "KeyboardEvent",
		MouseWheelEvent: "MouseWheelEvent",
		TextEvent: "TextEvent",
		WheelEvent: "WheelEvent",
		CustomEvent: "CustomEvent"
	};
	$.compat.event.DocumentEvent.prototype = {
		createEvent: function(type) {
			if (!(type in $.compat.event.DocumentEventMap)) throw new DOMException();
			return new $.compat.event[$.compat.event.DocumentEventMap[type]]();
		},
		canDispatch: function(namespace, type) {
			return true;
		}
	};
	
	$.compat.event.registeredEvents = {};
	$.compat.event.handleEvent = function(e) {
		if (global.event) {
			global.event.cancelBubble = true;
		}
		if (!e) e = global.event;
		var type = e.type;
		var target = e.srcElement || this;
		
		var ret = $.compat.event.registeredEvents;
		
		if (!("preventDefault" in e)) {
			var oe = e;
			if (type in $.compat.event.eventMap) type = $.compat.event.eventMap[type];
			var et = $.compat.event.eventTypes[type];
			var et3 = et[3];
			e = new et3();
			var ml = [];
			var to = target.ownerDocument;
			if (oe.ctrlKey) ml.push("Control");
			if (oe.altKey) ml.push("Alt");
			if (oe.shiftKey) ml.push("Shift");
			if (oe.metaKey) ml.push("Meta");
			if (et3 == $.compat.event.Event) {
				e.initEventNS(null, type, et[0], et[1]);
			} else if (et3 == $.compat.event.UIEvent) {
				e.initUIEventNS(null, type, et[0], et[1], (to ? to.parentWindow : null), oe.data);
			} else if (et3 == $.compat.event.MouseEvent) {
				e.initMouseEventNS(null, type, et[0], et[1], (to ? to.parentWindow : null), oe.data, oe.screenX, oe.screenY, oe.clientX, oe.clientY, oe.button, oe.fromElement, ml.join(" "));
			} else if (et3 == $.compat.event.CompositionEvent) {
				e.initCompositionEventNS(null, type, et[0], et[1], (to ? to.parentWindow : null), oe.data);
			} else if (et3 == $.compat.event.MutationNameEvent) {
				e.initMutationNameEventNS(null, type, et[0], et[1]);
			} else if (et3 == $.compat.event.MutationEvent) {
				e.initMutationEventNS(null, type, et[0], et[1], target.getAttributeNode(oe.propertyName), null, target[oe.propertyName], oe.propertyName, 1);
			} else if (et3 == $.compat.event.KeyboardEvent) {
				e.initKeyboardEventNS(null, type, et[0], et[1], (to ? to.parentWindow : null), oe.keyCode, 0x00, ml.join(" "), oe.repeat);
			} else if (et3 == $.compat.event.MouseWheelEvent) {
				e.initMouseWheelEventNS(null, type, et[0], et[1], (to ? to.parentWindow : null), oe.data, oe.screenX, oe.screenY, oe.clientX, oe.clientY, oe.button, oe.fromElement, ml.join(" "), oe.wheelDelta);
			} else if (et3 == $.compat.event.TextEvent) {
				e.initTextEventNS(null, type, et[0], et[1], (to ? to.parentWindow : null), oe.data, 0x00);
			} else if (et3 == $.compat.event.WheelEvent) {
				e.initWheelEventNS(null, type, et[0], et[1], (to ? to.parentWindow : null), oe.data, oe.screenX, oe.screenY, oe.clientX, oe.clientY, oe.button, oe.fromElement, ml.join(" "), 0, oe.wheelDelta, 0, 0);
			}
		}
		
		if (!(type in ret)) return true;
		
		e.target = target;
		var propagationPath = [];
		var p = target;
		while (p.parentNode && p.parentNode != p) {
			propagationPath.push(p.parentNode);
			p = p.parentNode;
		}
		var l = propagationPath.length;
		
		if (e._propagationStopped == false) {
			e.eventPhase = 1;
			for (var i = l - 1; i >= 0; i--) {
				var rett = ret[type][0];
				e.currentTarget = propagationPath[i];
				var listeners = [];
				var ll = rett.length;
				for (var j = 0; j < ll; j++) {
					if (rett[j].namespace == e.namespaceURI && rett[j].target == propagationPath[i]) {
						listeners.push(rett[j].listener);
					}
				}
				ll = listeners.length;
				for (var j = 0; j < ll; j++) {
					listeners[j].call(propagationPath[i], e);
					if (e._immediatePropagationStopped == true) break;
				}
				if (e._propagationStopped == true) break;
			}
			
			if (e._propagationStopped == false) {
				e.eventPhase = 2;
				e._immediatePropagationStopped = false;
				var rett = ret[type][0];
				e.currentTarget = target;
				var listeners = [];
				var ll = rett.length;
				for (var j = 0; j < ll; j++) {
					if (rett[j].namespace == e.namespaceURI && rett[j].target == target) {
						listeners.push(rett[j].listener);
					}
				}
				rett = ret[type][1];
				ll = rett.length;
				for (var j = 0; j < ll; j++) {
					if (rett[j].namespace == e.namespaceURI && rett[j].target == target) {
						listeners.push(rett[j].listener);
					}
				}
				ll = listeners.length;
				for (var j = 0; j < ll; j++) {
					listeners[j].call(target, e);
					if (e._immediatePropagationStopped == true) break;
				}
				
				if (e.bubbles == true && e._propagationStopped == false) {
					e.eventPhase = 3;
					e._immediatePropagationStopped = false;
					for (var i = 0; i < l; i++) {
						var rett = ret[type][1];
						e.currentTarget = propagationPath[i];
						var listeners = [];
						var ll = rett.length;
						for (var j = 0; j < ll; j++) {
							if (rett[j].namespace == e.namespaceURI && rett[j].target == propagationPath[i]) {
								listeners.push(rett[j].listener);
							}
						}
						ll = listeners.length;
						for (var j = 0; j < ll; j++) {
							listeners[j].call(propagationPath[i], e);
							if (e._immediatePropagationStopped == true) break;
						}
						if (e._propagationStopped == true) break;
					}
				}
			}
		}
		
		e._immediatePropagationStopped = false;
		e._propagationStopped = false;
		
		if (e.cancelable && e.defaultPrevented) {
			global.event.returnValue = false;
			e.defaultPrevented = false;
			return false;
		}
		
		e.defaultPrevented = false;
		
		return true;
	};
	
	$.compat.event.eventTypes = {
		abort: [true, false, [], $.compat.event.Event],
		blur: [false, false, [], $.compat.event.UIEvent],
		change: [true, false, [], $.compat.event.Event],
		click: [true, true, [], $.compat.event.MouseEvent],
		compositionstart: [true, true, [], $.compat.event.CompositionEvent],
		compositionupdate: [true, true, [], $.compat.event.CompositionEvent],
		compositionend: [true, true, [], $.compat.event.CompositionEvent],
		dblclick: [true, true, [], $.compat.event.MouseEvent],
		DOMActivate: [true, true, [], $.compat.event.UIEvent],
		DOMAttributeNameChanged: [true, false, [], $.compat.event.MutationNameEvent],
		DOMAttrModified: [true, false, [], $.compat.event.MutationEvent],
		DOMCharacterDataModified: [true, false, [], $.compat.event.MutationEvent],
		DOMElementNameChanged: [true, false, [], $.compat.event.MutationNameEvent],
		DOMFocusIn: [true, false, [], $.compat.event.UIEvent],
		DOMFocusOut: [true, false, [], $.compat.event.UIEvent],
		DOMNodeInserted: [true, false, [], $.compat.event.MutationEvent],
		DOMNodeInsertedIntoDocument: [false, false, [], $.compat.event.MutationEvent],
		DOMNodeRemoved: [true, false, [], $.compat.event.MutationEvent],
		DOMNodeRemovedFromDocument: [false, false, [], $.compat.event.MutationEvent],
		DOMSubtreeModified: [true, false, [], $.compat.event.MutationEvent],
		error: [true, false, [], $.compat.event.Event],
		focus: [false, false, [], $.compat.event.UIEvent],
		focusin: [true, false, [], $.compat.event.UIEvent],
		focusout: [true, false, [], $.compat.event.UIEvent],
		keydown: [true, true, [], $.compat.event.KeyboardEvent],
		keypress: [true, true, [], $.compat.event.KeyboardEvent],
		keyup: [true, true, [], $.compat.event.KeyboardEvent],
		load: [false, false, [], $.compat.event.Event],
		mousedown: [true, true, [], $.compat.event.MouseEvent],
		mouseenter: [false, true, [], $.compat.event.MouseEvent],
		mouseleave: [false, true, [], $.compat.event.MouseEvent],
		mousemove: [true, true, [], $.compat.event.MouseEvent],
		mouseout: [true, true, [], $.compat.event.MouseEvent],
		mouseover: [true, true, [], $.compat.event.MouseEvent],
		mouseup: [true, true, [], $.compat.event.MouseEvent],
		mousewheel: [true, true, [], $.compat.event.MouseWheelEvent],
		reset: [true, true, [], $.compat.event.Event],
		resize: [true, false, [], $.compat.event.UIEvent],
		scroll: [false, false, [], $.compat.event.UIEvent],
		select: [true, false, [], $.compat.event.Event],
		submit: [true, true, [], $.compat.event.Event],
		textInput: [true, true, [], $.compat.event.TextEvent],
		unload: [false, false, [], $.compat.event.Event],
		wheel: [true, true, [], $.compat.event.WheelEvent]
	};
	$.compat.event.eventMap = {
		activate: "DOMActivate",
		propertychange: "DOMAttrModified"
	};
	
	$.compat.event.isEventNatural = function(nodeName, type) {
		if (!nodeName) nodeName = "";
		nodeName = nodeName.toLowerCase();
		type = type.toLowerCase();
		if (nodeName in $.compat.event.eventList && $.compat.event.eventList[nodeName].indexOf(type) > -1) return true;
		if ($.compat.event.eventList[""].indexOf(type) > -1) return true;
		return false;
	};
	
	var nodeUserData = [];
	var cntSUD = 0;
	$.compat.node.setUserData = function(k, v) {
		var data = this.__userdata__;
		if (data === undefined) {
			var obj = {};
			obj[k] = v;
			this.__userdata__ = nodeUserData.push(obj) - 1;
			return null;
		} else {
			var datad = nodeUserData[data];
			var r = datad[k] || null;
			datad[k] = v;
			return r;
		}
	};
	$.compat.node.getUserData = function(k) {
		var data = this.__userdata__;
		if (data === undefined) return null;
		var nudd = nodeUserData[data];
		return (k in nudd ? nudd[k] : null);
	};
	$.compat.node.hasAttribute = function(attr) {
		//attribute = Element._attributeTranslations.has[attribute] || attribute;
		var node = this.getAttributeNode(attr);
		return !!(node && node.specified);
	};
	// For IE and its silly memory leaks. We're saving a recursive leak.
	var classListElements = [];
	$.compat.element.classList = function(e) {
		this._element = classListElements.push(e) - 1;
		this._arr = [];
	};
	$.compat.element.classListPrototype = function() {
		this._fake = true;
		this._dirty = true;
		this._refresh = function() {
			this._dirty = false;
			var clss = classListElements[this._element].className;
			if (!clss) return this;
			var classes = clss.split(/\s+/);
			var cl = classes.length;
			if (cl && classes[0] == "") classes.shift();
			if (cl && classes[cl - 1] == "") classes.pop();
			var arr = this._arr;
			arr.length = cl;
			this.length = cl;
			if (cl == 0) return this;
			for (var i = 0; i < cl; ++i) {
				arr[i] = classes[i];
			}
			return this;
		};
		this.item = function(i) {
			return this._arr[i] || null;
		};
		this.contains = function(token) {
			check(token);
			if (this._dirty) this._refresh();
			var arr = this._arr;
			for (var i = 0, l = this.length; i < l; ++i) {
				if (arr[i] == token) return true;
			}
			return false;
		};
		this.add = function(token) {
			check(token);
			if (this._dirty) this._refresh();
			var arr = this._arr;
			for (var i = 0, l = this.length; i < l; ++i) {
				if (arr[i] == token) return;
			}
			arr.push(token);
			this.length = arr.length;
			classListElements[this._element].className = arr.join(" ");
		};
		this.remove = function(token) {
			check(token);
			if (this._dirty) this._refresh();
			var arr = this._arr;
			for (var i = 0, l = this.length; i < l; ++i) {
				if (arr[i] == token) {
					arr.splice(i, 1);
					this.length = arr.length;
					classListElements[this._element].className = arr.join(" ");
				}
			}
		};
		this.toggle = function(token) {
			check(token);
			if (this._dirty) this._refresh();
			var arr = this._arr;
			for (var i = 0, l = this.length; i < l; ++i) {
				if (arr[i] == token) {
					this.remove(token);
					return;
				}
			}
			this.add(token);
		};
		function check(token) {
			if (token == "") {
				throw "SYNTAX_ERR";
			}
			if (token.indexOf(/\s/) != -1) {
				throw "INVALID_CHARACTER_ERR";
			}
		}
	};
	$.compat.element.classList.prototype = new $.compat.element.classListPrototype();
	$.compat.element.getElementsByClassName = function(s) {
		var sp = $.compat.element.getElementsByClassName.space;
		var r = [];
		var ci = this.childNodes.length > 0 ? this.childNodes[0] : null;
		while (ci != null) {
			if (ci.nodeType == 1) {
				if (ci.scopeName && ci.scopeName == "g_vml_") continue;// Stop from searching excanvas elements
				if (ci.className.split(sp).indexOf(s) > -1) r.push(ci);
				if (ci.childNodes.length > 0) ci = ci.childNodes[0];
				else if (ci.nextSibling) ci = ci.nextSibling;
				else {
					var onnext = false;
					while (ci.parentNode != this) {
						if (ci.parentNode.nextSibling) {
							ci = ci.parentNode.nextSibling;
							onnext = true;
							break;
						} else ci = ci.parentNode;
					}
					if (ci.parentNode == this && !onnext) break;
				}
			} else {
				if (ci.nextSibling) ci = ci.nextSibling;
				else {
					var onnext = false;
					while (ci.parentNode != this) {
						if (ci.parentNode.nextSibling) {
							ci = ci.parentNode.nextSibling;
							onnext = true;
							break;
						} else ci = ci.parentNode;
					}
					if (ci.parentNode == this && !onnext) break;
				}
			}
		}
		return r;
	};
	$.compat.element.getElementsByClassName.space = /\s+/;
	$.compat.element.canvas = function(t) {
		if (typeof global.G_vmlCanvasManager !== "undefined") G_vmlCanvasManager.initElement(t);
	};
	
	$.js = {
		objectMixin: function(t, parent, opts) {
			if (!t || !parent) return;
			var p = new parent(opts);
			if (arguments.length <= 3) {
				for (var i in p) {
					t[i] = p[i];
				}
			} else {
				for (var i = 3, l = arguments.length; i < l; i++) {
					var ai = arguments[i];
					if (ai in p) t[ai] = p[ai];
				}
			}
		},
		each: function(obj, func) {
			var r = [];
			for (var i = 0, l = obj.length; i < l; i++) {
				var a = func.call(obj[i], i);
				if (a === false) break;
				var nl = obj.length;
				if (nl < l) {
					i -= l - nl;
					l = nl;
				}
				if (a) r.push(a);
			}
			return r;
		},
		roughlyEquals: function(a, b) {
			if (typeof a === "object" && typeof b === "object") {
				for (var i in a) {
					if (a.hasOwnProperty(i) && i in b && b.hasOwnProperty(i)) {
						var ta = typeof a[i];
						if (ta === typeof b[i]) {
							if (ta !== "object" && a[i] != b[i]) return false; 
						} else return false;
					} else if (a.hasOwnProperty(i) && !(i in b)) {
						return false;
					}
				}
				for (var i in a) {
					if (b.hasOwnProperty(i) && i in a && a.hasOwnProperty(i)) {
						var tb = typeof b[i];
						if (tb === typeof a[i]) {
							if (tb !== "object" && b[i] != a[i]) return false; 
						} else return false;
					} else if (b.hasOwnProperty(i) && !(i in a)) {
						return false;
					}
				}
				return true;
			} else return a == b;
		},
		globalEval: function(s) {
			if (global.execScript) global.execScript(s);
			else global.eval ? global.eval.call(global, s) : eval.call(global, s);
		},
		getObjectPropertyCount: function(obj) {
			var count = 0;
			for (k in obj) if (obj.hasOwnProperty(k)) count++;
			return count;
		},
		extend: function() {
			var target = arguments[0] || {},
				i = 1,
				length = arguments.length,
				deep = false,
				options;
			if (typeof target === "boolean") {
				deep = target;
				target = arguments[1] || {};
				i = 2;
			}
			if (typeof target !== "object" && !$.js.isFunction(target)) target = {};

			for (; i < length; i++)
				if ((options = arguments[i]) != null) {
					if ($.js.isArray(options)) {
						if (!$.js.isArray(target)) target = [];
						for (var j = 0, l = options.length; j < l; j++) {
							var src = target[j], copy = options[j];
							if (target === copy) continue;
							if (deep && copy && typeof copy === "object" && !copy.nodeType)
								target[j] = $.js.extend(
									deep, 
									src || {},
									copy
								);
							else if (copy !== undefined) target[j] = copy;
						}
					} else
						for (var name in options) {
							var src = target[name], copy = options[name];
							if (target === copy) continue;
							if (deep && copy && typeof copy === "object" && !copy.nodeType)
								target[name] = $.js.extend(
									deep, 
									src || {},
									copy
								);
							else if (copy !== undefined) target[name] = copy;
						}
				}
			return target;
		},
		isFunction: function(obj) {
			return typeof obj === "function";
		},
		isArray: function(obj) {
			return (Object.prototype.toString.call(obj) === "[object Array]") || (obj && (obj.push && obj.splice && obj.length !== undefined) || ($.browser.engine.name == "Trident" && "item" in obj));
		},
		iso8601DateFromDate: function(date) {
			function f(n) {
				if (n < 10) return ["0", n].join("");
				return n;
			}
			return [
				f(date.getUTCFullYear()),
				"-",
				f(date.getUTCMonth() + 1),
				"-",
				f(date.getUTCDate()),
				"T",
				f(date.getUTCHours()),
				":",
				f(date.getUTCMinutes()),
				":",
				f(date.getUTCSeconds()),
				".",
				f(date.getUTCMilliseconds()),
				"Z"
			].join("");
		},
		iso8601DateFromString: function(dateString) {
			var m = iso8601DateRegEx.exec(dateString);
			if (m) {
				var year = parseInt(m[1], 10);
				var month = (m[2] ? parseInt(m[2], 10) : 1);
				var day = (m[3] ? parseInt(m[3], 10) : 1);
				var hour = (m[4] ? parseInt(m[4], 10) : 0);
				var minute = (m[5] ? parseInt(m[5], 10) : 0);
				var second = (m[6] ? parseInt(m[6], 10) : 0);
				var millisecond = (m[7] ? parseInt(m[7], 10) : 0);
				var TDZ = m[8];
				var date = new Date();
				
				if (TDZ) {
					date.setUTCFullYear(year);
					date.setUTCMonth(month - 1);
					date.setUTCDate(day);
					date.setUTCHours(hour);
					date.setUTCMinutes(minute);
					date.setUTCSeconds(second);
					date.setUTCMilliseconds(millisecond);
					if (TDZ != "Z") {
						m = iso8601TDZRegEx.exec(TDZ);
						var mod = (m[1] == "+" ? -1 : 1);
						date.setUTCMinutes(date.getUTCMinutes() + (parseInt(m[3], 10) * mod));
						date.setUTCHours(date.getUTCHours() + (parseInt(m[2], 10) * mod));
					}
				} else {
					date.setFullYear(year);
					date.setMonth(month - 1);
					date.setDate(day);
					date.setHours(hour);
					date.setMinutes(minute);
					date.setSeconds(second);
					date.setMilliseconds(millisecond);
				}
				
				return date;
			}
			return null;
		}
	};
	
	var iso8601DateRegEx = /^([0-9]{4})(?:-([01][0-9])(?:-([0-3][0-9])(?:T([0-2][0-9]):([0-6][0-9])(?::([0-6][0-9])(?:\.([0-9]+))?)?(Z|[-+][0-2][0-9]:[0-6][0-9]))?)?)?$/;
	var iso8601TDZRegEx = /^([-+])([0-2][0-9]):([0-6][0-9])$/;
	
	$.compat.window(global);
	$.compat.document(document);
	
	$.events = {
		signal: function(name, data) {
			var e = document.createEvent("Event");
			e.initEvent(name, true, false);
			e.signalData = data;
			document.dispatchEvent(e);
		},
		addSignalListener: function(n, f) {
			document.addEventListener(n, f, false);
		},
		removeSignalListener: function(n, f) {
			document.removeEventListener(n, f, false);
		},
		EventTarget: function() {
			var t = this;
			t.__sjEvents__ = {};
			var te = t.__sjEvents__;

			t.addEventListener = function(type, callback, pass) {
				var tte = te;
				if (!(type in tte)) tte[type] = [];
				var tet = tte[type];
				var i = tet.length;
				while (i--) {
					if (callback == tet[i].callback) return this;
				}
				tet.push({callback: callback, pass: pass});
				return this;
			};
			t.removeEventListener = function(type, callback) {
				var tte = te;
				if (!(type in tte)) return this;
				var tet = tte[type];
				var i = tet.length;
				while (i--) {
					if (callback == tet[i].callback) {
						tet.splice(i, 1);
						if (tet.length == 0) delete tte[type];
						return this;
					}
				}
				return this;
			};
			t.dispatchEvent = function(event) {
				if (typeof event !== "object") return this;
				var tte = te;
				var type = event.type;
				if (!type || !(type in tte)) return this;
				var tet = tte[type];
				for (var i = 0, l = tet.length; i < l; i++) {
					var teti = tet[i];
					teti.callback.call(t, event, teti.pass);
				}
				return this;
			};
		}
	};
	
	var boxSizeVars = {
		"height": [
			"paddingTop",
			"paddingBottom",
			"borderTopWidth",
			"borderBottomWidth",
			"marginTop",
			"marginBottom"
		],
		"width": [
			"paddingLeft",
			"paddingRight",
			"borderLeftWidth",
			"borderRightWidth",
			"marginLeft",
			"marginRight"
		]
	};
	$.dom = {
		children: function(t) {
			if (!t) return null;
			if (t.children) return t.children;
			var c = [];
			var tc = t.childNodes;
			for (var i = 0, l = tc.length; i < l; i++) {
				var tci = tc[i];
				if (tci.nodeType != 1) {
					continue;
				}
				c.push(tci);
			}
			return c;
		},
		next: function(t) {
			if (!t) return null;
			if (t && t.nextElementSibling !== undefined) return t.nextElementSibling;
			if (t.nextSibling) {
				if (t.nextSibling.nodeType != 1) {
					return $.dom.next(t.nextSibling);
				} else {
					return t.nextSibling;
				}
			}
			return null;
		},
		previous: function(t) {
			if (!t) return null;
			if (t && t.previousElementSibling !== undefined) return t.previousElementSibling;
			if (t.previousSibling) {
				if (t.previousSibling.nodeType != 1) {
					return $.dom.previous(t.previousSibling);
				} else {
					return t.previousSibling;
				}
			}
			return null;
		},
		prependChild: (function(){
			if ($.browser.engine.name == "Trident") return (function(p, t){p.insertBefore(t)});
			else return (function(p, t){p.insertBefore(t, null)});
		})(),
		emptyNode: function(n) {
			if (!n) return null;
			while (n.childNodes[0]) {
				n.removeChild(n.childNodes[0]);
			}
		},
		isAncestor: function(p, c) {
			var pp = c.parentNode;
			while (pp && pp != p) {
				pp = pp.parentNode;
			}
			if (pp == p) return true;
			return false;
		},
		isDecendent: function(c, p) {
			var cl = $.dom.children(p);
			for (var i = 0, l = cl.length; i < l; i++) {
				var cli = cl[i];
				if (cli == c) return true;
				if ($.dom.isDecendent(c, cli)) return true;
			}
			return false;
		},
		text: function(t, v) {
			if (t.textContent !== undefined) {
				if (v !== undefined) return t.textContent = v;
				return t.textContent;
			}
			if (t.text !== undefined) {
				if (v !== undefined) return t.text = v;
				return t.text;
			}
			if (t.innerText !== undefined) {
				if (v !== undefined) return t.innerText = v;
				return t.innerText;
			}
		},
		dataset: {
			set: function(t, k, v) {
				if (t.dataset) {
					return t.dataset[k] = v;
				}
				var a = t.getAttribute(["data-", k].join(""));
				t.setAttribute(["data-", k].join(""), "" + v);
				return a;
			},
			get: function(t, k) {
				if (t.dataset) {
					return t.dataset[k];
				}
				var a = t.getAttribute(["data-", k].join(""));
				if (!a) return null;
				return a;
			},
			del: function(t, k) {
				if (t.dataset) {
					delete t.dataset[k];
				}
				t.removeAttribute(["data-", k].join(""));
			}
		},
		create: function(name, attrs, children, properties, xmlns) {
			var r = (xmlns && document.createElementNS ? document.createElementNS(xmlns, name) : document.createElement(name));
			if (attrs) {
				for (var i in attrs) {
					var curAttr = attrs[i];
					var ns = i.split(/:/);
					var ns0 = ns[0];
					if (ns.length == 1) {
						if (ns0 == "class") {
							cnl = curAttr.split();
							var l = cnl.length;
							if (l > 0) r.className = cnl[0];
							for (var j = 1; j < l; j++) {
								r.className += [" ", cnl[j]].join("");
							}
						} else r.setAttribute(ns0, curAttr);
					} else {
						if (ns0 == "svg") {
							ns0 = "http://www.w3.org/2000/svg";
						} else if (ns0 == "xlink") {
							ns0 = "http://www.w3.org/1999/xlink";
						} else if (ns0 == "xhtml") {
							ns0 = "http://www.w3.org/1999/xhtml";
						}
						if (r.setAttributeNS) r.setAttributeNS(ns0, ns[1], curAttr);
						else r.setAttribute(i, curAttr);
					}
				}
			}
			if (children) {
				for (var i = 0, l = children.length; i < l; i++) {
					r.appendChild(children[i]);
				}
			}
			if (properties) {
				for (var i in properties) {
					r[i] = properties[i];
				}
			}
			if (r.nodeName.toLowerCase() == "canvas") $.compat.element.canvas(r);
			return $(r);
		},
		computedStyle: function(element) {
			return (typeof element.currentStyle !== "undefined" ? element.currentStyle : document.defaultView.getComputedStyle(element, null));
		},
		/**
		 * start and end values:
		 *   0 = base (width or height)
		 *   1 = padding
		 *   2 = border
		 *   3 = margin
		 * default is equivalent to element.offset{Width,Height}
		 * EXCEPT that is will still give values when not displayed:
		 *   start = 0
		 *   end = 2
		 */
		getBoxSize: function(element, wh, start, end) {
			if (typeof start === "undefined") start = 0;
			if (typeof end === "undefined") end = 2;
			var cs = sj.dom.computedStyle(element);
			var r = 0;
			var vars = [];
			var bsv = boxSizeVars[wh];
			for (var i = 0, l = bsv.length; i < l; i += 2) {
				var a = 0;
				try {
					a = parseFloat(cs[bsv[i]]);
					if (isNaN(a)) a = 0;
				} catch (e) {
					a = 0;
				}
				var b = 0;
				try {
					b = parseFloat(cs[bsv[i + 1]]);
					if (isNaN(b)) b = 0;
				} catch (e) {
					b = 0;
				}
				vars[i / 2] = a + b;
			}
			for (var i = start; i <= end; i++) {
				if (i == 0) {
					var a = (wh == "width" ? element.offsetWidth : element.offsetHeight);
					a -= (vars[0] + vars[1]);
					if (a > 0) r += a;
				} else {
					r += vars[i - 1];
				}
			}
			return r;
		},
		fullOffset: function(element, mod) {
			var r = 0;
			mod = ["offset", mod].join("");
			while (element.offsetParent) {
				r += element[mod];
				element = element.offsetParent;
			}
			return r;
		}
	};
	$.xml = {
		getSelection: function() {
			return typeof(document.selection) === "undefined" ? (global.getSelection || document.getSelection)() : document.selection;
		},
		getRange: function() {
			var s = $.xml.getSelection();
			return typeof(s.createRange) === "undefined" ? s.getRangeAt(0) : s.createRange();
		},
		parseXML: function(s, d) {
			try {
				var x = new ActiveXObject("Microsoft.XMLDOM");
				x.async = "false";
				x.loadXML(s);
				x = d.importNode(x.documentElement, true);
				$.xml.addScriptFromXml(x);
				return x; 
			} catch(e) {
				parser = new DOMParser();
				var x = parser.parseFromString(s, "text/xml");
				x = d.importNode(x.documentElement, true);
				$.xml.addScriptFromXml(x);
				return x;
			}
		},
		addScriptFromXml: function(t) {
			for (var i = 0; i < t.childNodes.length; i++) {
				if (t.childNodes[i].nodeType != 1) {
					continue;
				}
				if (t.childNodes[i].nodeName.toLowerCase() == "script") {
					var an = t.childNodes[i].getAttributeNode("src");
					if (an && an.specified) {
						var s = document.createElement("script");
						s.type = "text/javascript";
						s.src = t.childNodes[i].getAttribute("src");
						document.getElementsByTagName("head")[0].appendChild(s);
					} else $.js.globalEval($.dom.text(t.childNodes[i]));
				} else {
					$.xml.addScriptFromXml(t.childNodes[i]);
				}
			}
		},
		toHTML: function(d) {
			var r = null;
			if (d.nodeType == 1) {
				if ($.browser.name == "Explorer") {
					if (d.nodeName.toLowerCase() == "style") {
						r = document.createStyleSheet();
					} else r = document.createElement(d.nodeName);
				} else {
					r = document.createElement(d.nodeName);
				}
				var l = d.attributes.length;
				for (var i = 0; i < l; i++) {
					var a = d.attributes[i];
					if ($.browser.name == "Explorer") {
						if (d.nodeName.toLowerCase() == "style") {
							if (a.nodeName.toLowerCase() == "href") r.href = a.nodeValue;
						} else r.setAttribute(a.nodeName, a.nodeValue);
					} else r.setAttribute(a.nodeName, a.nodeValue);
				}
			} else if (d.nodeType == 3) {
				r = document.createTextNode(d.data);
			}
			l = d.childNodes.length;
			for (var i = 0; i < l; i++) {
				var n = d.childNodes[i];
				if (n.nodeType == 1 || n.nodeType == 3) {
					var v = $.xml.toHTML(n);
					if ($.browser.name == "Explorer") {
						if ("nodeType" in v) {
							if ("cssText" in r && v.nodeType == 3) {
								r.cssText = v.nodeValue;
							} else if (r.nodeName.toLowerCase() == "script" && v.nodeType == 3) {
								r.text = v.nodeValue;
							} else r.appendChild(v);
						}
					} else r.appendChild(v);
				}
			}
			if ($.browser.name == "Explorer" && "nodeName" in r && r.nodeName.toLowerCase() == "canvas") $.compat.element.canvas(r);
			return r;
		}
	};
	$.http = {
		param: function(a) {
			var s = [];
			var add = function(k, v){
				s.push([encodeURIComponent(k), '=', encodeURIComponent(v)].join(""));
			};
			if ($.js.isArray(a)) {
				for (var ak in a) {
					add(a[ak].name, a[ak].value);
				}
			} else {
				for (var j in a) {
					if ($.js.isArray(a[j])) {
						var l = a[j].length;
						for (var i = 0; i < l; i++) {
							add(j, a[j][i]);
						}
					} else add(j, $.js.isFunction(a[j]) ? a[j]() : a[j]);
				}
			}
			return s.join("&").replace(/%20/g, "+");
		},
		unparam: function(q) {
			var query_string = {};
			var vars = q.split("&");
			var len = vars.length;
			for (var i = 0; i < len; i++) {
				var pair = vars[i].split("=");
				pair[0] = decodeURIComponent(pair[0]);
				pair[1] = decodeURIComponent(pair[1]);
				if (typeof(query_string[pair[0]]) === "undefined") {
					query_string[pair[0]] = pair[1];
				} else if (typeof(query_string[pair[0]]) === "string") {
					var arr = [query_string[pair[0]], pair[1]];
					query_string[pair[0]] = arr;
				} else {
					query_string[pair[0]].push(pair[1]);
				}
			} 
			return query_string;
		},
		httpSuccess: function(xhr) {
			try {
				return !xhr.status && location.protocol == "file:" ||
					(xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || xhr.status == 1223;
			} catch(e){}
			return false;
		},
		httpData: function(xhr, ct) {
			if (typeof ct !== "string") {
				try {
					ct = xhr.getResponseHeader("content-type");
				} catch (e){};
			}
			var d = null;
			if (ct.match(/html/)) {
				var nd = document.createElement("div");
				var text = xhr.responseText.trim();
				text = $.html.appendStyles(text);
				nd.innerHTML = text;
				if (nd.childNodes.length > 1) {
					d = nd;
				} else if (nd.childNodes.length == 1) {
					d = nd.childNodes[0];
				}
				$.xml.addScriptFromXml(d);
			} else if (ct.match(/json/)) {
				d = JSON.parse(xhr.responseText);
			} else if (ct.match(/xml/)) {
				d = xhr.responseXML.documentElement;
				$.xml.addScriptFromXml(d);
				if ($.browser.name == "Explorer") d = $.xml.toHTML(d);
			} else {
				d = xhr.responseText;
			}
			return d;
		},
		createXMLHttpRequest: function() {
			var r = null;
			try {
				r = new XMLHttpRequest();
			} catch (e) {
				r = new ActiveXObject("Microsoft.XMLHTTP");
			}
			return r;
		},
		send: function(o) {
			o = $.js.extend({}, {
				method: "GET",
				url: "",
				async: true,
				user: "",
				password: "",
				timeout: 30000,
				cache: true,
				contentType: "",
				responseContentType: null,
				headers: {},
				data: null,
				processData: true,
				queryData: {},
				withCredentials: true
			}, o);
			
			var r = null;
			if (o.async) r = o.sjhttpsenddelay = new $.delay.Delay();
			
			if (o.url === "") {
				if (o.async) r.error("No URL specified");
				return r;
			}
			if (!o.headers["Content-Type"]) {
				switch (o.contentType) {
					case "text":
						o.headers["Content-Type"] = "text/plain;charset=UTF-8";
						break;
					case "xml":
						o.headers["Content-Type"] = "text/xml;charset=UTF-8";
						break;
					case "json":
						o.headers["Content-Type"] = "application/json;charset=UTF-8";
						break;
					default:
						if (o.contentType == "") {
							if (o.method == "POST") {
								o.headers["Content-Type"] = "application/x-www-form-urlencoded;charset=UTF-8";
							}
						} else {
							o.headers["Content-Type"] = o.contentType;
						}
						break;
				}
			}
			if (!o.headers["Accept"]) {
				o.headers["Accept"] = "text/html,application/xhtml+xml,application/xml,application/json,*/*";
			}
			
			if (typeof o.queryData !== "string") o.queryData = $.http.param(o.queryData);
			if (o.queryData != "") o.url += (o.url.match(/\?/) ? "&" : "?") + o.queryData;
			if (!o.cache) o.url += (o.url.match(/\?/) == null ? "?" : "&") + (new Date()).getTime();
			if (o.headers["Content-Type"] && o.data && o.processData && typeof o.data !== "string") {
				if (typeof o.data === "object" && o.headers["Content-Type"].match(/^application\/json/)) {
					o.data = JSON.stringify(o.data);
				} else if (typeof o.data === "object" && o.headers["Content-Type"].match(/^application\/x-www-form-urlencoded/)) {
					o.data = $.http.param(o.data);
				}
			}
			
			var xhr = $.http.createXMLHttpRequest();
			
			if (!("abort" in xhr)) {
				xhr.abort = function() {
					xhr.onreadystatechange = null;
					if (r) r.error({xhr: xhr, options: o, status: 0, statusText: "Aborted"});
				};
			}
			
			if (r) r.setData("xhr", xhr);
			
			if ("addEventListener" in xhr) {
				if (r) {
					try {
						xhr.addEventListener("progress", function(e) {
							if (e.lengthComputable) r.progress({data: e.loaded / e.total, options: o, xhr: this});
							else r.progress({data: "progress", options: o, xhr: this});
						}, false);
					} catch(e) {};
					try {
						xhr.addEventListener("abort", function(e) {
							r.error({xhr: this, options: o, status: 0, statusText: "Aborted"});
						}, false);
					} catch(e) {};
					try {
						xhr.addEventListener("error", function(e) {
							r.error({xhr: this, options: o, status: 0, statusText: "Error"});
						}, false);
					} catch(e) {};
					try {
						xhr.addEventListener("stalled", function(e) {
							r.progress({data: "stalled", options: o, xhr: this});
						}, false);
					} catch(e) {};
					try {
						xhr.addEventListener("suspend", function(e) {
							r.progress({data: "suspend", options: o, xhr: this});
						}, false);
					} catch(e) {};
					try {
						xhr.addEventListener("load", function(e) {
							if ($.http.httpSuccess(this)) {
								d = $.http.httpData(this, o.responseContentType);
								r.resolve({data: d, xhr: this, options: o});
							} else {
								r.error({xhr: this, options: o, status: this.status, statusText: this.statusText});
							}
						}, false);
					} catch(e) {};
				}
			} else {
				xhr.onreadystatechange = function() {
					$.http._xhr_rsc.call(xhr, o);
				};
			}
			
			xhr.open(o.method, o.url, o.async, o.user, o.password);
			if (typeof xhr.withCredentials !== "undefined") xhr.withCredentials = o.withCredentials;
			xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
			for (var h in o.headers) {
				xhr.setRequestHeader(h, o.headers[h]);
			}
			
			xhr.send(o.data);
			if (!o.async) return $.http._xhr_rsc.call(xhr, o);
			else return r;
		},
		_xhr_rsc: function(o) {
			if (this.readyState == 4) {
				if ($.http.httpSuccess(this)) {
					var d = $.http.httpData(this, o.responseContentType);
					if (o.sjhttpsenddelay) o.sjhttpsenddelay.resolve({data: d, xhr: this, options: o});
					else return d;
				} else {
					if (o.sjhttpsenddelay) o.sjhttpsenddelay.error({xhr: this, options: o, status: this.status, statusText: this.statusText});
					else return null;
				}
			} else {
				if (o.sjhttpsenddelay) o.sjhttpsenddelay.progress({data: "progress", options: o, xhr: this});
			}
		},
		get: function(url, data, pass, responseContentType, async) {
			var async = true;
			if (typeof async === "boolean") async = async;
			return $.http.send({
				url: url,
				method: "GET",
				queryData: data,
				timeout: 15000,
				responseContentType: responseContentType,
				async: async,
				pass: pass
			});
		},
		post: function(url, data, pass, contentType, responseContentType, async) {
			var async = true;
			if (typeof async === "boolean") async = async;
			return $.http.send({
				url: url,
				method: "POST",
				data: data,
				timeout: 30000,
				cache: false,
				contentType: contentType,
				responseContentType: responseContentType,
				async: async,
				pass: pass
			});
		},
		submit: function(url, form, pass) {
			var options = {
				form: form,
				timeout: false,
				cache: false,
				pass: pass,
				iframe: true,
				data: {wrapwith: "textarea"}
			};
			if (!options || !options.form) return null;
			var form = $(options.form);
			var url = form.getAttribute('action').trim();
			if (url) url = (url.match(/^([^#]+)/)||[])[1];
			url = url || global.location.href || ''
			options = $.js.extend({
				url:  url,
				method: form.getAttribute('method') || 'GET'
			}, options || {});
			var veto = {};
			if (veto.veto) {
				return false;
			}
			var a = $.html.formData(form);
			if (options.data) {
				options.extraData = options.data;
				for (var n in options.data) {
					if ($.js.isArray(options.data[n])) {
						for (var k in options.data[n])
							if (n in a) {
								if ($.js.isArray(a[n])) a[n].push(options.data[n][k]); else a[n] = [a[n], options.data[n][k]];
							} else a[n] = options.data[n][k];
						}
					else {
						if (n in a) {
							if ($.js.isArray(a[n])) a[n].push(options.data[n]); else a[n] = [a[n], options.data[n]];
						} else a[n] = options.data[n];
					}
				}
			}
			if (veto.veto) {
				return this;
			}
			var q = $.http.param(a);
			if (options.method.toUpperCase() == 'GET') {
				options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
				options.data = null;
			} else options.data = q;
			var callbacks = [];
			if (options.success)
				callbacks.push(options.success);
			options.success = function(data, status) {
				for (var i = 0, max = callbacks.length; i < max; i++)
					callbacks[i].apply(options, [data, status, form]);
			};
			var ifiles = $.q.t(form, "input");
			var found = false;
			$.js.each(ifiles, function() {
				if (this.type == "file" && this.value) found = true;
			});
			
			var delay = null;
			var multipart = false;
			if (options.iframe || found || multipart) {
				delay = fileUpload();
			} else delay = $.http.send(options);
			
			return delay;
			function fileUpload() {
				var delay = new $.delay.Delay();
				var opts = $.js.extend({}, options);
				var s = $.js.extend({}, opts);
				var id = ['sjFormIO', Math.random(), (new Date().getTime())].join("");
				var io = sj.dom.create("iframe", {
					id: id,
					name: id,
					src: "about:blank",
					style: "position:absolute;top:-1000px;left:-1000px"
				});
				var xhr = {
					aborted: 0,
					responseText: null,
					responseXML: null,
					status: 0,
					statusText: 'n/a',
					getAllResponseHeaders: function() {},
					getResponseHeader: function() {},
					setRequestHeader: function() {},
					abort: function() {
						this.aborted = 1;
						io.setAttribute('src', 'about:blank');
					}
				};
				delay.setData("xhr", xhr);
				if (xhr.aborted) return delay;
				var cbInvoked = 0;
				var timedOut = 0;
				setTimeout(function() {
					var t = form.getAttribute('target'), a = form.getAttribute('action');
					form.setAttribute('target', id);
					var m = form.getAttribute('method');
					if (m != 'POST' && m != 'PUT') form.setAttribute('method', 'POST');
					if (form.getAttribute('action') != opts.url) form.setAttribute('action', opts.url);
					if (!options.skipEncodingOverride) {
						form.setAttribute("encoding", "multipart/form-data");
						form.setAttribute("enctype", "multipart/form-data");
					}
					if (opts.timeout) setTimeout(function() {timedOut = true; cb();}, opts.timeout);
					var extraInputs = [];
					try {
						if (options.extraData)
							for (var n in options.extraData) {
								var ed = options.extraData[n];
								if (!$.js.isArray(ed)) {
									ed = [ed];
								}
								for (var z = 0, len = ed.length; z < len; z++) {
									var ih = $.dom.create("input", {type:"hidden",name:n,value:ed[z]});
									form.appendChild(ih);
									extraInputs.push(ih);
								}
							}
						document.body.appendChild(io);
						io.addEventListener("load", cb, false);
						form.submit();
					}
					finally {
						form.setAttribute('action', a);
						t ? form.setAttribute('target', t) : form.removeAttribute('target');
						$.js.each(extraInputs, function() {
							this.parentNode.removeChild(this);
						});
					}
				}, 10);
				var nullCheckFlag = 0;
				function cb() {
					if (cbInvoked++) return;
					io.removeEventListener("load", cb, false);
					var ok = true;
					try {
						if (timedOut) throw 'timeout';
						var data, doc;
						doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
						if ((doc.body == null || doc.body.innerHTML == '') && !nullCheckFlag) {
							nullCheckFlag = 1;
							cbInvoked--;
							setTimeout(cb, 100);
							return;
						}
						if (xhr.aborted) {
							setTimeout(function() {
								io.parentNode.removeChild(io);
								xhr.responseXML = null;
							}, 100);
							delay.error({xhr: xhr, options: opts, status: 0, statusText: "Aborted"});
							return;
						}
						xhr.responseText = doc.body ? doc.body.innerHTML : null;
						xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
						xhr.getResponseHeader = function(header){
							var headers = {'content-type': "application/json"};
							return headers[header];
						};
						//if (opts.dataType == 'application/json' || opts.dataType == 'script') {
							var ta = doc.getElementsByTagName('textarea')[0];
							xhr.responseText = ta ? ta.value : xhr.responseText;
						//} else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
						//	xhr.responseXML = toXml(xhr.responseText);
						//}
						data = $.http.httpData(xhr);
					} catch(e) {
						ok = false;
						delay.error({options:opts,xhr:xhr,status:0,statusText:"Error",exception:e});
					}
					if (ok) delay.resolve({data: data, options: opts});
					setTimeout(function() {
						io.parentNode.removeChild(io);
						xhr.responseXML = null;
					}, 100);
				};
				function toXml(s, doc) {
					if (global.ActiveXObject) {
						doc = new ActiveXObject('Microsoft.XMLDOM');
						doc.async = 'false';
						doc.loadXML(s);
					}
					else
						doc = (new DOMParser()).parseFromString(s, 'text/xml');
					return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
				};
				return delay;
			};
			return false;
		}
	};
	var urlcacheOsdHandler = null;
	var urlcacheOsdCounter = 0;
	$.urlcache = {
		__cache: {},
		has: function(url) {
			if ($.urlcache.__cache[url]) {
				return true;
			}
			return false;
		},
		get: function(piece, options, params) {
			var url = piece;
			if (!options) options = {};
			options.piece = piece;
			if (!url.match(/^[\/.]/)) url = [sj.env.SITE_ROOT, url].join("")
			if (params && typeof params == "object") {
				url = [url, "?", $.http.param(params)].join("");
			}
			var r = new $.delay.Delay();
			if (url in $.urlcache.__cache) {
				r.resolve({data: $.urlcache._get_cache(url), piece: piece, options: options});
			} else {
				if ($.ui.osd) {
					if (urlcacheOsdHandler == null) urlcacheOsdHandler = $.ui.osd.add(document.createTextNode($.locale("Downloading. Please wait...")));
					urlcacheOsdCounter++;
				}
				$.http.send($.js.extend({
					url: url,
					method: "GET",
					timeout: 15000
				}, options, {delay: r, piece: piece}))
				.addEventListener("success", $.urlcache._get_success, piece)
				.addEventListener("progress", $.urlcache._get_progress, piece)
				.addEventListener("error", $.urlcache._get_failure, piece);
			}
			return r;
		},
		_get_success: function(d, piece) {
			$.urlcache.__cache[d.options.url] = d.data;
			if (urlcacheOsdHandler != null && --urlcacheOsdCounter == 0) {
				$.ui.osd.remove(urlcacheOsdHandler);
				urlcacheOsdHandler = null;
			}
			if (d.options.delay) d.options.delay.resolve({data: $.urlcache._get_cache(d.options.url), piece: piece, options: d.options, piece: d.options.piece});
		},
		_get_failure: function(d, piece) {
			if (urlcacheOsdHandler != null && --urlcacheOsdCounter == 0) {
				$.ui.osd.remove(urlcacheOsdHandler);
				urlcacheOsdHandler = null;
			}
			if (d.options.delay) d.options.delay.error(d);
		},
		_get_progress: function(d, piece) {
			if (d.options.delay) d.options.delay.progress(d);
		},
		_get_cache: function(i) {
			var d = $.urlcache.__cache[i];
			if ($.js.isArray(d)) {
				var c = [];
				for (var i = 0; i < d.length; i++) {
					c.push(d[i].cloneNode(true));
				}
				return c;
			} else if ("cloneNode" in d) {
				return d.cloneNode(true);
			}
			return d;
		}
	};
	$.html = {
		addScript: function(url, onload) {
			var sc = document.getElementsByTagName("script");
			for (var i = 0, l = sc.length; i < l; i++) {
				var sci = sc[i];
				if (sci.src == url) {
					//if (onload) onload.call(sci);
					return sci;
				}
			}
			var s = document.createElement("script");
			var h = document.getElementsByTagName("head")[0];
			s.type = "text/javascript";
			s.src = url;
			if (onload) {
				s.onload = function() {
					s.onreadystatechange = null;
					s.onload = null;
					onload.call(s);
				};
				s.onreadystatechange = function() {
					if (s.readyState == "complete" || s.readyState == "loaded") {
						s.onreadystatechange = null;
						s.onload = null;
						onload.call(s);
					}
				}
			}
			h.appendChild(s);
			return s;
		},
		addStyle: function(url, onload) {
			var sc = $.q.t(document, "link");
			for (var i = 0; i < sc.length; i++) {
				if (sc[i].getAttribute("href") == url) return sc[i];
			}
			var s = document.createElement("link");
			var h = document.getElementsByTagName("head")[0];
			s.rel = "stylesheet";
			s.type = "text/css";
			s.href = url;
			/* TODO: Make load detection for css.
			 * s.onload = onload;
			s.onreadystatechange = function() {
				if (this.readyState == 'complete') {
					onload(this);
				}
			}*/
			h.appendChild(s);
			return s;
		},
		htmlStyleRegExpAll: /<style[^>]*>[\u0001-\uFFFF]*?<\/style>/img,
		htmlStyleRegExp: /<style[^>]*>([\u0001-\uFFFF]*?)<\/style>/im,
		appendStyles: function(text) {
			var sheet = document.getElementById("sjStyleSheetDynamicAppender");
			if (!sheet) {
				sheet = document.createElement("style");
				sheet.id = "sjStyleSheetDynamicAppender";
				sheet.type = "text/css";
				document.getElementsByTagName("head")[0].appendChild(sheet);
			}
			var styles = (text.match($.html.htmlStyleRegExpAll) || []);
			for (var i = 0, l = styles.length; i < l; i++) {
				if ($.browser.name == "Explorer") sheet.styleSheet.cssText += (styles[i].match($.html.htmlStyleRegExp) || [", ", ""])[1];
				else sheet.appendChild(document.createTextNode((styles[i].match($.html.htmlStyleRegExp) || [", ", ""])[1]));
			}
			return text.replace($.html.htmlStyleRegExpAll, "");
		},
		formData: function(form) {
			var r = {};
			var add = function(n, v) {
				if (n in r) {
					if ($.js.isArray(r[n])) r[n].push(v); else r[n] = [r[n], v];
				} else r[n] = v;
			}
			for (var i in {"input":1,"select":1,"textarea":1}) {
				$.js.each($.q.t(form, i), function() {
					var t = this;
					if (t.name && t.name != "" && !t.disabled && !sj.dom.dataset.get(t, "sjexclude")) {
						switch (i) {
							case "input":
								if (this.type == "radio" || this.type == "checkbox") {
									if (this.checked !== undefined && this.checked) add(t.name, t.value);
								} else if (this.value != undefined && t.value != null) {
									add(t.name, t.value);
								}
								break;
							case "select":
								if (this.selectedIndex != -1) {
									add(t.name, t.value);
								}
								break;
							case "textarea":
								if (this.value != undefined && t.value != null) {
									add(t.name, t.value);
								}
								break;
						}
					}
				});
			}
			return r;
		},
		htmlTranslationTable: function(table, quote_style) {
			var entities = {}, hash_map = {}, decimal = 0, symbol = '';
			var constMappingTable = {}, constMappingQuoteStyle = {};
			var useTable = {}, useQuoteStyle = {};
			
			constMappingTable[0] = 'HTML_SPECIALCHARS';
			constMappingTable[1] = 'HTML_ENTITIES';
			constMappingQuoteStyle[0] = 'ENT_NOQUOTES';
			constMappingQuoteStyle[2] = 'ENT_COMPAT';
			constMappingQuoteStyle[3] = 'ENT_QUOTES';

			useTable = !isNaN(table) ? constMappingTable[table] : table ? table.toUpperCase() : 'HTML_SPECIALCHARS';
			useQuoteStyle = !isNaN(quote_style) ? constMappingQuoteStyle[quote_style] : quote_style ? quote_style.toUpperCase() : 'ENT_COMPAT';

			if (useTable !== 'HTML_SPECIALCHARS' && useTable !== 'HTML_ENTITIES') {
				throw new Error("Table: "+useTable+' not supported');
			}

			entities['38'] = '&amp;';
			if (useTable === 'HTML_ENTITIES') {
				entities['160'] = '&nbsp;';
				entities['161'] = '&iexcl;';
				entities['162'] = '&cent;';
				entities['163'] = '&pound;';
				entities['164'] = '&curren;';
				entities['165'] = '&yen;';
				entities['166'] = '&brvbar;';
				entities['167'] = '&sect;';
				entities['168'] = '&uml;';
				entities['169'] = '&copy;';
				entities['170'] = '&ordf;';
				entities['171'] = '&laquo;';
				entities['172'] = '&not;';
				entities['173'] = '&shy;';
				entities['174'] = '&reg;';
				entities['175'] = '&macr;';
				entities['176'] = '&deg;';
				entities['177'] = '&plusmn;';
				entities['178'] = '&sup2;';
				entities['179'] = '&sup3;';
				entities['180'] = '&acute;';
				entities['181'] = '&micro;';
				entities['182'] = '&para;';
				entities['183'] = '&middot;';
				entities['184'] = '&cedil;';
				entities['185'] = '&sup1;';
				entities['186'] = '&ordm;';
				entities['187'] = '&raquo;';
				entities['188'] = '&frac14;';
				entities['189'] = '&frac12;';
				entities['190'] = '&frac34;';
				entities['191'] = '&iquest;';
				entities['192'] = '&Agrave;';
				entities['193'] = '&Aacute;';
				entities['194'] = '&Acirc;';
				entities['195'] = '&Atilde;';
				entities['196'] = '&Auml;';
				entities['197'] = '&Aring;';
				entities['198'] = '&AElig;';
				entities['199'] = '&Ccedil;';
				entities['200'] = '&Egrave;';
				entities['201'] = '&Eacute;';
				entities['202'] = '&Ecirc;';
				entities['203'] = '&Euml;';
				entities['204'] = '&Igrave;';
				entities['205'] = '&Iacute;';
				entities['206'] = '&Icirc;';
				entities['207'] = '&Iuml;';
				entities['208'] = '&ETH;';
				entities['209'] = '&Ntilde;';
				entities['210'] = '&Ograve;';
				entities['211'] = '&Oacute;';
				entities['212'] = '&Ocirc;';
				entities['213'] = '&Otilde;';
				entities['214'] = '&Ouml;';
				entities['215'] = '&times;';
				entities['216'] = '&Oslash;';
				entities['217'] = '&Ugrave;';
				entities['218'] = '&Uacute;';
				entities['219'] = '&Ucirc;';
				entities['220'] = '&Uuml;';
				entities['221'] = '&Yacute;';
				entities['222'] = '&THORN;';
				entities['223'] = '&szlig;';
				entities['224'] = '&agrave;';
				entities['225'] = '&aacute;';
				entities['226'] = '&acirc;';
				entities['227'] = '&atilde;';
				entities['228'] = '&auml;';
				entities['229'] = '&aring;';
				entities['230'] = '&aelig;';
				entities['231'] = '&ccedil;';
				entities['232'] = '&egrave;';
				entities['233'] = '&eacute;';
				entities['234'] = '&ecirc;';
				entities['235'] = '&euml;';
				entities['236'] = '&igrave;';
				entities['237'] = '&iacute;';
				entities['238'] = '&icirc;';
				entities['239'] = '&iuml;';
				entities['240'] = '&eth;';
				entities['241'] = '&ntilde;';
				entities['242'] = '&ograve;';
				entities['243'] = '&oacute;';
				entities['244'] = '&ocirc;';
				entities['245'] = '&otilde;';
				entities['246'] = '&ouml;';
				entities['247'] = '&divide;';
				entities['248'] = '&oslash;';
				entities['249'] = '&ugrave;';
				entities['250'] = '&uacute;';
				entities['251'] = '&ucirc;';
				entities['252'] = '&uuml;';
				entities['253'] = '&yacute;';
				entities['254'] = '&thorn;';
				entities['255'] = '&yuml;';
			}

			if (useQuoteStyle !== 'ENT_NOQUOTES') {
				entities['34'] = '&quot;';
			}
			if (useQuoteStyle === 'ENT_QUOTES') {
				entities['39'] = '&#39;';
			}
			entities['60'] = '&lt;';
			entities['62'] = '&gt;';

			for (decimal in entities) {
				symbol = String.fromCharCode(decimal);
				hash_map[symbol] = entities[decimal];
			}
			
			return hash_map;
		},
		htmlspecialchars: function(string, quote_style) {
			if (!string) return string;
			var hash_map = {}, symbol = '', tmp_str = '', entity = '';
			tmp_str = string.toString();
			
			if (false === (hash_map = $.html.htmlTranslationTable('HTML_SPECIALCHARS', quote_style))) {
				return false;
			}
			
			hash_map["'"] = '&#039;';
			for (symbol in hash_map) {
				entity = hash_map[symbol];
				tmp_str = tmp_str.split(symbol).join(entity);
			}
			
			return tmp_str;
		},
		htmlspecialcharsDecode: function(string, quote_style) {
			string = string.toString();

			string = string.replace(/&amp;/g, '&');
			string = string.replace(/&lt;/g, '<');
			string = string.replace(/&gt;/g, '>');

			if (quote_style == 'ENT_QUOTES') {
				string = string.replace(/&quot;/g, '"');
				string = string.replace(/&#039;/g, '\'');
			} else if (quote_style != 'ENT_NOQUOTES') {
				string = string.replace(/&quot;/g, '"');
			}

			return string;
		},
		window: {
			innerHeight: (function(){
				if (typeof window.innerHeight === "undefined") return (function(){return document.body.parentNode.clientHeight})
				else return (function(){return window.innerHeight});
			})(),
			innerWidth: (function(){
				if (typeof window.innerWidth === "undefined") return (function(){return document.body.parentNode.clientWidth})
				else return (function(){return window.innerWidth});
			})(),
			pageXOffset: (function(){
				if (typeof window.pageXOffset === "undefined") return (function(){return document.body.scrollLeft})
				else return (function(){return window.pageXOffset});
			})(),
			pageYOffset: (function(){
				if (typeof window.pageYOffset === "undefined") return (function(){return document.body.scrollTop})
				else return (function(){return window.pageYOffset});
			})(),
			screenX: (function(){
				if (typeof window.screenX === "undefined") return (function(){return window.screenLeft})
				else return (function(){return window.screenX});
			})(),
			screenY: (function(){
				if (typeof window.screenY === "undefined") return (function(){return window.screenTop})
				else return (function(){return window.screenY});
			})()
		},
		Animation: function(elements, properties, duration) {
			if (!sj.js.isArray(elements)) elements = [elements];
			var t = this;
			t.elements = elements;
			t.properties = {};
			t.duration = duration;
			t._listeners = [];
			for (var i in properties) {
				t.addProperty(i, properties[i]);
			}
		}
	};
	var htmlAnimationFinishListener = function(event) {
		var updater = event.target;
		var animation = updater.htmlAnimation;
		var ap = animation.properties;
		var counter = 0;
		for (var i in ap) {
			var api = ap[i];
			if (api.updater == updater) {
				api.state = "finish";
			} else if (api.state != "finish") counter++;
		}
		this.started = false;
		this.stopped = false;
		this.finished = true;
		this.paused = false;
		if (counter == 0) {
			var lt = animation._listeners;
			for (var i = 0, l = lt.length; i < l; i++) {
				var lti = lt[i];
				lti.callback.call(animation, lti.pass);
			}
		}
	};
	var htmlAnimationResetListener = function(event) {
		var updater = event.target;
		var ap = updater.htmlAnimation.properties;
		for (var i in ap) {
			var api = ap[i];
			if (api.updater == updater) {
				api.state = "reset";
				break;
			}
		}
		this.started = false;
		this.stopped = true;
		this.finished = false;
		this.paused = false;
	};
	$.html.Animation.prototype = {
		started: false,
		paused: false,
		stopped: true,
		finished: false,
		addElement: function(element) {
			if (this.elements.indexOf(element) > -1) return; 
			this.elements.push(element);
			return this;
		},
		removeElement: function(element) {
			if (this.elements.indexOf(element) > -1) this.elements.splice(this.elements.indexOf(element), 1);
			return this;
		},
		clearElements: function() {
			this.elements = [];
			return this;
		},
		addProperty: function(name, options) {
			var t = this;
			if (name in t.properties) return t;
			if (typeof options !== "object") {
				options = {
					from: null,
					to: options,
					dur: t.duration,
					finish: "freeze"
				};
			}
			options = sj.js.extend({}, {
				from: null,
				to: null,
				dur: t.duration,
				finish: "freeze"
			}, options, {htmlAnimation: t, skipReset: true});
			t.properties[name] = {
				updater: (new $.updater.Updater(options)).addValueListener(function(v){
					for (var i = 0, l = t.elements.length; i < l; i++) {
						t.elements[i].style[name] = v;
					}
				}).addEventListener("finish", htmlAnimationFinishListener)
				.addEventListener("reset", htmlAnimationResetListener),
				status: "reset"
			};
			return this;
		},
		removeProperty: function(name) {
			if (!(name in this.properties)) return this;
			this.properties[name].updater.stop();
			delete this.properties[name];
			return this;
		},
		reset: function() {
			for (var i in this.properties) {
				var updater = this.properties[i].updater;
				if (this.elements.length > 0) {
					if (updater.from == null) updater.from = $.dom.computedStyle(this.elements[0])[i] || 0;
					if (updater.by == null && updater.to == null) updater.to = $.dom.computedStyle(this.elements[0])[i] || 1;
				}
				updater.reset();
			}
			return this;
		},
		start: function() {
			if (!this.paused) this.reset();
			for (var i in this.properties) {
				this.properties[i].updater.start();
			}
			this.started = true;
			this.stopped = false;
			this.finished = false;
			this.paused = false;
			return this;
		},
		pause: function() {
			for (var i in this.properties) {
				this.properties[i].updater.pause();
			}
			this.started = false;
			this.stopped = false;
			this.finished = false;
			this.paused = true;
			return this;
		},
		stop: function() {
			for (var i in this.properties) {
				this.properties[i].updater.stop();
			}
			this.started = false;
			this.stopped = true;
			this.finished = false;
			this.paused = false;
			return this;
		},
		reverse: function() {
			for (var i in this.properties) {
				this.properties[i].updater.reverse();
			}
			return this;
		},
		addFinishListener: function(callback, pass) {
			var lt = this._listeners;
			for (var i = 0, l = lt.length; i < l; i++) {
				if (lt[i].callback == callback) return this;
			}
			lt.push({callback: callback, pass: pass});
			return this;
		},
		removeFinishListener: function(callback) {
			var lt = this._listeners;
			for (var i = 0, l = lt.length; i < l; i++) {
				if (lt[i].callback == callback) {
					lt.splice(i, 1);
					break;
				}
			}
			return this;
		}
	};
	
	$.css = {
		center: function(element, h, v) {
			var cs = $.dom.computedStyle(element);
			if (typeof h === "undefined" || h) {
				if (cs.position == "static") {
					element.style.marginLeft = "auto";
					element.style.marginRight = "auto";
				} else {
					element.style.left = [(cs.position == "absolute" ? element.parentNode.offsetWidth / 2 : $.html.window.innerWidth() / 2) - (element.offsetWidth / 2), "px"].join("");
				}
			}
			if (typeof v === "undefined" || v) {
				if (cs.position == "static") {
					// TODO
				} else {
					element.style.top = [(cs.position == "absolute" ? element.parentNode.offsetHeight / 2 : $.html.window.innerHeight() / 2) - (element.offsetHeight / 2), "px"].join("");
				}
			}
		},
		applyBorderRadius: function(element, style) {
			if (!style) return;
			if ($.browser.engine.name == "Gecko") {
				if ($.browser.engine.version >= "1.9.1") element.style.MozBorderRadius = style;
				else element.style.MozBorderRadius = style.split(/\//)[0].trim();
			} else if ($.browser.engine.name == "WebKit") {
				var ellipse = style.split(/\//);
				ellipse[0] = (ellipse[0] ? ellipse[0].trim() : "");
				ellipse[1] = (ellipse[1] ? ellipse[1].trim() : "");
				var rad = [];
				for (var i = 0; i < 2; i++) {
					var radi = rad[i] = [];
					var parts = ellipse[i].split(/\s+/);
					for (var j = 0; j < 4; j++) {
						radi[j] = (j in parts ? parts[j] : null);
					}
					if (radi[2] == null && radi[0] != null) radi[2] = radi[0];
					if (radi[3] == null && radi[1] != null) radi[3] = radi[1];
					if (i == 1) {
						var rad0 = rad[0];
						for (var j = 0; j < 4; j++) {
							if (rad0[j] != null && !radi[j]) radi[j] = rad0[j];
						}
					}
				}
				element.style.webkitBorderTopLeftRadius = [rad[0][0], " ", rad[1][0]].join("");
				element.style.webkitBorderTopRightRadius = [rad[0][1], " ", rad[1][1]].join("");
				element.style.webkitBorderBottomRightRadius = [rad[0][2], " ", rad[1][2]].join("");
				element.style.webkitBorderBottomLeftRadius = [rad[0][3], " ", rad[1][3]].join("");
			} else element.style.borderRadius = style;
		}
	};
	
	$.hash = {
		_binds: {},
		currentHash: null,
		watcher: null,
		checkHash: function() {
			if (global.location.hash != $.hash.currentHash) {
				$.hash.currentHash = global.location.hash;
				$.hash.call();
			}
			if (!("onhashchange" in global)) $.hash.watcher = setTimeout($.hash.checkHash, 40);
		},
		startWatcher: function() {
			if ("onhashchange" in global) global.addEventListener("hashchange", $.hash.checkHash, false);
			else $.hash.watcher = setTimeout($.hash.checkHash, 40);
		},
		stopWatcher: function() {
			if ("onhashchange" in global) global.removeEventListener("hashchange", $.hash.checkHash, false);
			else if ($.hash.__watcher != null) clearTimeout($.hash.__watcher);
		},
		bind: function(h, f) {
			$.hash._binds[h] = f;
		},
		call: function(h, d) {
			if (typeof h !== "string") {
				h = global.location.hash.substring(1);
			}
			var c = false;
			for (var b in $.hash._binds) {
				var r = new RegExp(b);
				var m = r.exec(h);
				if (m) {
					c = true;
					$.hash._binds[b](m);
				}
			}
			if (c == false) {
				var m = h.match(/^([a-zA-Z]+)(?:[\/\?]+(.*))?/);
				if (m && m.length >= 2) {
					$.sys.process.exec(m[1], {hashURL: m[2]});
				}
			}
			return false;
		},
		set: function(h, doAction) {
			if (doAction === undefined) doAction = true;
			var nh = ["#", h].join("");
			if (global.location.hash != nh) {
				$.hash.currentHash = nh;
				global.location.hash = nh;
			}
			if (doAction) {
				$.hash.call();
			}
		},
		link: function(t, url) {
			if (!url && $(t).nodeName.toLowerCase() == "a") url = t.href;
			var b = [global.location.protocol, "//", global.location.host, $.env.SITE_ROOT].join("");
			url = url.replace(b, "");
			t.setUserData("sj.hash.link", url, null);
			t.addEventListener("click", $.hash.linkOnClick, false);
		},
		linkOnClick: function(e) {
			e.preventDefault();
			$.hash.set(this.getUserData("sj.hash.link"));
			return false;
		}
	};
	$.util = {
		basename: function(path, suffix) {
			var b = path.replace(/\/$/, '').replace(/^.*[\/\\]/g, '');
			if (typeof(suffix) == 'string' && b.substr(b.length - suffix.length) == suffix) {
				b = b.substr(0, b.length - suffix.length);
			}
			return b;
		}
	};
	$.timer = {
		Timer: function(options) {
			sj.js.extend(this, options);
			$.js.objectMixin(this, $.events.EventTarget);
			this._time = this.startTime;
			this.lastTime = this._time;
		}
	};
	var TimerPrototype = function() {
		this.interval = 20;
		this.currentInterval = 0;
		this._time = 0;
		this._inTick = false;
		this.status = 0;
		this.onEndTime = "pause";
		this.startTime = 0;
		this.endTime = Infinity;
		this.lastTime = 0;
		
		this.isTicking = function() {
			if (this.status == 1) return true;
			return false;
		};
		this.isPaused = function() {
			if (this.status == 2) return true;
			return false;
		};
		
		this.reset = function() {
			this._time = this.startTime;
			this.lastTime = this._time;
			this.currentInterval = 0;
			this.dispatchEvent({type: "reset", time: this._time});
			return this;
		};
		
		this.setTime = function(time) {
			this._time = time;
			this.dispatchEvent({type: "settime", time: this._time});
			return this;
		};
		this.getTime = function() {
			return this._time;
		};
		this.tick = function() {
			if (this._inTick) {
				this.currentInterval += this.interval;
				return;
			}
			this._inTick = true;
			this.lastTime = this._time;
			this._time += this.currentInterval;
			this.currentInterval = this.interval;
			if ((this.interval > 0 && 
					(this.startTime < this.endTime && this._time >= this.endTime) || 
					(this.startTime > this.endTime && this._time >= this.startTime)
				) ||
				(this.interval < 0 &&	
					(this.startTime < this.endTime && this._time <= this.endTime) || 
					(this.startTime > this.endTime && this._time <= this.startTime)
				)) {
				this._time = this.endTime;
				this.dispatchEvent({type: "tick", time: this._time, span: this._time - this.lastTime});
				this.dispatchEvent({type: "endtime", time: this._time});
				switch (this.onEndTime) {
					case "pause":
						this.pause();
						break;
					case "stop":
						this.stop();
						break;
					case "repeat":
						this._time = this.startTime;
						break;
					case "reverse":
						this.interval = -this.interval;
						break;
					default:
						this.pause();
						break;
				}
			} else {
				this.dispatchEvent({type: "tick", time: this._time, span: this._time - this.lastTime});
			}
			this._inTick = false;
			return this;
		};
		this.start = function() {
			var t = this;
			if (typeof t.ticker === "number") return t;
			t.status = 1;
			t.dispatchEvent({type: "start", time: t._time});
			t.currentInterval = t.interval;
			t.ticker = global.setInterval(function(late) {
				if (late) t.currentInterval += late;
				t.tick();
			}, t.interval);
			return t;
		};
		this.stop = function() {
			if (typeof this.ticker === "number") global.clearInterval(this.ticker);
			this.ticker = null;
			this.status = 0;
			this._time = this.startTime;
			this.dispatchEvent({type: "stop", time: this._time});
			return this;
		};
		this.pause = function() {
			if (typeof this.ticker === "number") global.clearInterval(this.ticker);
			this.ticker = null;
			this.status = 2;
			this.dispatchEvent({type: "pause", time: this._time});
			return this;
		};
	};
	$.timer.Timer.prototype = new TimerPrototype();
	
	var updaterActives = [];
	var updaterTick = function(e) {
		var t = e.span;
		var ua = updaterActives.slice();
		var l = ua.length;
		for (var i = 0; i < l; i++) {
			var uai = ua[i];
			uai.update(uai.time + (t * uai.multiplier));
		}
	};
	var updaterTimer = (new $.timer.Timer({interval:20})).addEventListener("tick", updaterTick);
	$.updater = {
		Updater: function(options) {
			this._valueListeners = [];
			$.js.objectMixin(this, $.events.EventTarget);
			sj.js.extend(this, {
				dur: Infinity,
				from: null,
				to: null,
				by: null,
				begin: 0,
				end: Infinity,
				delay: 0,
				max: null,
				min: null,
				values: null,
				keyTimes: null,
				keySplines: null,
				calcMode: "linear",
				repeatCount: null,
				finish: "freeze",
				multiplier: 1,
				skipReset: false
			}, options);
			if (!this.skipReset) this.reset();
		},
		UpdaterStatus: function(dur, calcMode, finish) {
			this.values = [];
			this.keyTimes = [];
			this.keySplines = [];
			this.dur = dur;
			this.calcMode = calcMode;
			this.finish = finish;
		}
	};
	
	var updaterRemoveActive = function(u) {
		var l = updaterActives.length;
		for (var i = 0; i < l; i++) {
			if (updaterActives[i] == u) {
				updaterActives.splice(i, 1);
				break;
			}
		}
	};
	
	var UpdaterPrototype = function() {
		this._added = false;
		this.addValueListener = function(callback) {
			if (!(callback in this._valueListeners)) this._valueListeners.push(callback);
			return this;
		};
		this.start = function() {
			if (this._added == false) {
				updaterActives.push(this);
				this._added = true;
			}
			this.dispatchEvent({type:"start",target:this});
			return this;
		};
		this.pause = function() {
			if (this._added == true) {
				updaterRemoveActive(this);
				this._added = false;
			}
			this.dispatchEvent({type:"pause",target:this});
			return this;
		};
		this.stop = function() {
			if (this._added == true) {
				updaterRemoveActive(this);
				this._added = false;
			}
			this.dispatchEvent({type:"stop",target:this});
			this.reset();
			return this;
		};
		this.reverse = function() {
			this.multiplier = -this.multiplier;
			this.dispatchEvent({type:"reverse",target:this});
			return this;
		};
		this.setTime = function(time) {
			this.time = time;
			this.dispatchEvent({type:"settime",target:this,time:time});
			return this;
		};
		this.getTime = function() {
			return time;
		};
		this.getValue = function() {
			return this.value;
		};
		this.reset = function() {
			this.time = this.begin;
			this.currentRepeat = 0;
			this.value = this.from;
			var status = new $.updater.UpdaterStatus(this.dur, this.calcMode, this.finish);
			var statusValues = status.values;
			var statusKeyTimes = status.keyTimes;
			
			if (typeof this.values === "string") {
				statusValues = status.values = this.values.split(/;/);
				var l = statusValues.length;
				for (var i = 0; i < l; i++) {
					statusValues[i] = statusValues[i].trim();
				}
			} else {
				if (!this.by && (this.to === null || this.to === undefined)) {
					throw "Invalid Updater Parameters";
				}
				if (this.from === null) {
					throw "Invalid Updater Parameters";
				}
				else statusValues[0] = "" + this.from;
				
				if (this.to !== null && this.to !== undefined) {
					statusValues.push("" + this.to);
				} else {
					var v = parseFloat(statusValues[0]);
					var v1 = parseFloat(this.by);
					if (isNaN(v) || isNaN(v1)) {
						throw "Invalid Updater Parameters";
					}
					var v1u = ("" + this.by).replace("" + v1, "");
					statusValues.push([(v + v1), v1u].join(""));
				}
			}
			if (this.dur !== Infinity && this.calcMode != "paced") {
				if (this.delay >= this.dur) throw "Invalid Updater Paramaters";
				if (typeof this.keyTimes === "string") {
					statusKeyTimes = status.keyTimes = this.keyTimes.split(/;/);
					for (var i = 0, l = statusKeyTimes.length; i < l; i++) {
						statusKeyTimes[i] = parseFloat(statusKeyTimes[i].trim());
						var statusKeyTimesi = statusKeyTimes[i];
						if ((statusKeyTimesi < 0 || statusKeyTimesi > 1) || (i > 0 && statusKeyTimesi <= statusKeyTimes[i - 1])) {
							throw "Invalid Updater Parameters";
						}
					}
					if (statusKeyTimes.length != statusValues.length) {
						throw "Invalid Updater Parameters";
					}
					if (statusKeyTimes[0] != 0) {
						throw "Invalid Updater Parameters";
					}
					if ((this.calcMode == "linear" || this.calcMode == "spline") && statusKeyTimes[statusKeyTimes.length - 1] != 1) {
						throw "Invalid Updater Parameters";
					}
				} else {
					var l = statusValues.length;
					for (var i = 0; i < l; i++) {
						if (l == 1) {
							statusKeyTimes[0] = 0;
						} else {
							statusKeyTimes[i] = i / (l - 1);
						}
					}
				}
			}
			this.status = status;
			this.dispatchEvent({type:"reset",target:this});
			return this;
		};
		this.update = function(time) {
			this.time = time;
			var delay = this.delay;
			if (time < delay) return;
			var status = this.status;
			var dur = status.dur;
			var values = status.values;
			var calcMode = status.calcMode;
			var keyTimes = status.keyTimes;
			var finish = status.finish;
			
			var percent = null;
			
			if (dur === Infinity) {
				var v1 = parseFloat(this.by);
				if (!isNaN(v1)) {
					this.value = Math.ceil(((time - delay) / this.interval) * v1);
				} else {
					this.value = "" + values[0];
				}
			} else {
				percent = (time - delay) / (dur - delay);
				var l = keyTimes.length;
				for (var i = 0; i < l; i++) {
					var keyTimesi = keyTimes[i];
					var valuesi = values[i];
					if  (i + 1 < l) {
						var keyTimesi1 = keyTimes[i + 1];
						var valuesi1 = values[i + 1];
						if (keyTimesi <= percent && keyTimesi1 > percent) {
							var ktp = (percent - keyTimesi) / (keyTimesi1 - keyTimesi);
							var v0 = parseFloat(valuesi);
							var v1 = parseFloat(valuesi1);
							var v0u = valuesi.replace("" + v0, "");
							var v1u = valuesi1.replace("" + v1, "");
							if (isNaN(v0) || isNaN(v1) || (v0u != v1u)) calcMode = "discrete";
							if (calcMode == "discrete") this.value = (ktp >= 1 ? valuesi1 : valuesi);
							else if (calcMode == "linear") this.value = [v0 + ((v1 - v0) * ktp), v1u].join("");
							else if (calcMode == "paced") {
								// This makes an even pace between the this.values (all numbers).
							} else if (calcMode == "spline") {
								// beziers...
							}
							break;
						}
					} else {
						if (keyTimesi <= percent) {
							this.value = valuesi;
							break;
						}
					}
				}
			}
			
			for (var i = 0, l = this._valueListeners.length; i < l; i++) {
				this._valueListeners[i].call(this, this.value);
			}
			
			var floatVal = parseFloat(this.value);
			var finishType = null;
			if (
				(percent != null && (percent >= 1 || percent < 0) && (finishType = "percent")) || 
				(this.max != null && floatVal >= this.max && (finishType = "max")) || 
				(this.min != null && floatVal <= this.min && (finishType = "min"))
				) {
				this.dispatchEvent({type:"finish",target:this,finish:finish,finishType:finishType});
				switch (finish) {
					case "reset":
						this.value = this.from;
						this.stop();
						break;
					case "freeze":
						this.pause();
						break;
					case "repeat":
						this.value = this.from;
						this.time = this.begin;
						break;
					case "reverse":
						this.reverse();
						break;
				}
			}
		}
	};
	$.updater.Updater.prototype = new UpdaterPrototype();
	
	$.locale = function(id, r) {
		var s = id;
		if ($.locale.catalog[id]) {
			s = $.locale.catalog[id];
		}
		if (typeof(r) == "object") {
			for (p in r) {
				s = s.replace(["%(", p, ")s"].join(""), r[p]);
			}
		}
		return s;
	};
	$.locale.currentLocale = "en";
	$.locale.catalog = {};
	$.locale.getCatalog = function(url) {
		$.html.addScript(
			[url, "locale/", $.locale.currentLocale, "/LC_MESSAGES/messages.js"].join("")
		);
	};
	var modules = {};
	$.modules = {
		defined: function(name) {
			if (name in modules) return true;
			return false;
		},
		get: function(name) {
			return modules[name];
		},
		define: function(name, exports) {
			if (!$.modules.defined(name)) modules[name] = exports;
		}
	};
	var requireOsdHandle = null;
	var requireOsdCount = 0;
	$.require = function(name) {
		var delay = new $.delay.Delay();
		if ($.modules.defined(name)) delay.resolve({name: name, exports: modules[name]});
		else {
			if ($.ui.osd) {
				if (requireOsdHandle == null) requireOsdHandle = $.ui.osd.add(document.createTextNode(sj.locale("Downloading Components...")));
				requireOsdCount++;
			}
			$.html.addScript([$.env.MODULE_ROOT, [($.require.main ? [$.require.main, "."].join("") : ""), name].join("").replace(/\./g, "/"), ".js"].join(""), function() {
				if (requireOsdHandle != null && --requireOsdCount == 0) {
					$.ui.osd.remove(requireOsdHandle);
					requireOsdHandle = null;
				}
				if ($.modules.defined(name)) delay.resolve({name: name, exports: modules[name]});
				else delay.error(["Failed to load module '", name, "'"].join(""));
			});
		}
		return delay;
	};
	$.requireReturn = function(name) {
		if ($.modules.defined(name)) return modules[name];
		throw "Module does not exist";
	};
	$.requireAll = function() {
		var delay = new $.delay.Delay();
		var args = arguments;
		var l = args.length;
		var mods = {};
		for (var i = 0; i < l; i++) {
			$.require(arguments[i])
			.addEventListener("success", function(data) {
				mods[data.name] = data.exports;
				delay.progress(data.name);
				var counter = 0;
				for (var m in mods) {
					for (var i = 0; i < l; i++) {
						if (m == args[i]) counter++;
					}
				}
				if (counter == l) delay.resolve(mods);
			}).addEventListener("error", function(reason) {
				delay.error(reason);
			});
		}
		return delay;
	};
	$.require.main = "";
	var System = function() {
		var pid = 0;
		var procs = [];
		var programs = {};
		var loading = {};
		var programProcessMap = {};
		
		var killProcess = function() {
			var pid = this.pid;
			if (pid in programProcessMap) {
				delete programProcessMap[pid];
			}
			for (var i = 0, l = procs.length; i < l; i++) {
				if (procs[i] == this) {
					procs.splice(i, 1);
					return;
				}
			}
		};
		
		this.process = {
			Process: function() {
				var program = null;
				var ecb = {};
				var terminating = false;
				
				this.pid = ++pid;
				
				this.terminate = function() {
					if (terminating === true) return;
					terminating = true;
					this.dispatchEvent({type:"TERM"});
					this.dispatchEvent({type:"KILL"});
					program = null;
					ecb = null;
				};
				this.addEventListener = function(name, cb) {
					if (!(name in ecb)) ecb[name] = [];
					var ecbn = ecb[name];
					for (var i = 0, l = ecbn.length; i < l; i++) {
						if (ecbn[i] == cb) return this;
					}
					ecbn.push(cb);
				};
				this.removeEventListener = function(name, cb) {
					if (!(name in ecb)) return;
					var ecbn = ecb[name];
					for (var i = 0, l = ecbn.length; i < l; i++) {
						if (ecbn[i] == cb) {
							ecbn.splice(i, 1);
							if (ecbn.length == 0) delete ecb[name];
							break;
						}
					}
					return this;
				};
				this.dispatchEvent = function(event) {
					if (typeof event !== "object" || !event.type) return false;
					var et = event.type;
					if (!(et in ecb)) return true;
					var ecbet = ecb[et];
					for (var i = 0, l = ecbet.length; i < l; i++) {
						ecbet[i].call(this, event);
					}
					return true;
				};
				
				this.getProgram = function() {
					return program;
				};
				this.startProgram = function(name, options) {
					var t = this;
					$.sys.process.loadProgram(name).addEventListener("success", function(data) {
						program = new data(options);
						programProcessMap[t.pid] = {name: data.about.name, program: program};
						if ($.js.isFunction(program.exec)) {
							if (program.exec(options) === false) t.terminate();
							else {
								t.dispatchEvent({
									type: "programstarted",
									program: program,
									programName: data.about.name
								});
								$.events.signal(["sj.sys.process.program.", data.about.name, ".start"].join(""), program);
							}
						} else {
							t.dispatchEvent({
								type: "programstarted",
								program: program,
								programName: data.about.name
							});
							$.events.signal(["sj.sys.process.program.", data.about.name, ".start"].join(""), program);
						}
					}).addEventListener("error", function(reason) {
						$.console.error(reason);
					});
					return t;
				};
				
				this.addEventListener("KILL", killProcess);
				
				procs.push(this);
			},
			getAllProcesses: function() {
				return procs.slice();
			},
			getProcess: function(pid) {
				var l = procs.length;
				for (var i = 0; i < l; i++) {
					if (procs[i].pid == parseInt(pid)) return procs[i];
				}
				return null;
			},
			getProgramProcess: function(program) {
				for (var i in programProcessMap) {
					if (programProcessMap[i].program == program) {
						return $.sys.process.getProcess(i);
					}
				}
			},
			getProgramProcesses: function(name) {
				var r = [];
				for (var i in programProcessMap) {
					if (programProcessMap[i].name == name) {
						r.push($.sys.process.getProcess(i));
					}
				}
				return r;
			},
			getUniqueProgram: function(name) {
				for (var i in programProcessMap) {
					if (programProcessMap[i].name == name) return programProcessMap[i].program;
				}
			},
			registerProgram: function(program) {
				if (program && program.about && program.about.name) {
					var name = program.about.name;
					if (!(name in programs)) {
						programs[name] = program;
						if (name in loading) {
							var l = loading[name].length;
							for (var i = 0; i < l; i++) {
								loading[name][i].call(program);
							}
							delete loading[name];
						}
						sj.events.signal("sj.sys.process.program.load", program);
					}
				}
			},
			loadProgram: function(name) {
				var delay = new $.delay.Delay();
				if (name in programs) {
					delay.resolve(programs[name]);
				} else {
					var osd = null;
					if ($.ui.osd) {
						osd = $.ui.osd.add(document.createTextNode(sj.locale("Downloading %(name)s. Please wait...", {name: name})));
					}
					if (!(name in loading)) loading[name] = [];
					loading[name].push(function() {
						if (osd != null) $.ui.osd.remove(osd);
						delay.resolve(this);
					});
					$.html.addScript([$.env.PROGRAM_ROOT, name, "/", name, ".js"].join(""));
				}
				return delay;
			},
			exec: function(name, options) {
				return (new $.sys.process.Process()).startProgram(name, options);
			},
			uniqueExec: function(name, options, cb) {
				if (name in programs) {
					var p = $.sys.process.getProgramProcesses(name);
					var l = p.length;
					if (l == 0) return $.sys.process.exec(name, options);
					if ($.js.isFunction(cb)) {
						for (var i = 0; i < l; i++) {
							cb.call(p[i]);
						}
					}
				} else return $.sys.process.exec(name, options);
			}
		};
	};
	$.sys = new System();
	
	var DelayPrototype = function() {
		/* States:
		 * 	0: Waiting
		 * 	1: Resolved
		 *	2: Error
		 */
		this._state = 0;
		this._result = undefined;
		this._reason = "Unknown";
		this.addEventListener = function(type, cb, pass) {
			switch(type.toLowerCase()) {
				case "success":
					this._scb.push([cb, pass]);
					if (this._state == 1) cb.call(global, this._result, pass);
					break;
				case "error":
					this._ecb.push([cb, pass]);
					if (this._state == 2) cb.call(global, this._reason, pass);
					break;
				case "progress":
					this._pcb.push([cb, pass]);
					break;
				case "finish":
					this._fcb.push([cb, pass]);
					break;
			}
			return this;
		};
		this.removeEventListener = function(type, cb) {
			var m = null;
			switch(type.toLowerCase()) {
				case "success":
					m = this._scb;
					break;
				case "error":
					m = this._ecb;
					break;
				case "progress":
					m = this._pcb;
					break;
				case "finish":
					m = this._fcb;
					break;
			}
			if (m) {
				for (var i = 0, l = m.length; i < l; i++) {
					if (m[i][0] == cb) {
						m.splice(i, 1);
						break;
					}
				}
			}
			return this;
		};
		this.cancel = function() {
			if ($.js.isFunction(this._ccb)) this._ccb();
			this.error("Canceled");
			return this;
		};
		this.resolve = function(value) {
			this._result = value;
			this._state = 1;
			var s = this._scb;
			for (var i = 0, l = s.length; i < l; i++) {
				var si = s[i];
				si[0].call(global, value, si[1], this.data);
			}
			return this.finish();
		};
		this.finish = function() {
			var s = this._fcb;
			for (var i = 0, l = s.length; i < l; i++) {
				var si = s[i];
				si[0].call(global, this, si[1], this.data);
			}
			return this;
		};
		this.error = function(reason) {
			this._result = undefined;
			this._state = 2;
			this._reason = reason;
			var s = this._ecb;
			for (var i = 0, l = s.length; i < l; i++) {
				var si = s[i];
				si[0].call(global, reason, si[1], this.data);
			}
			return this.finish();
		};
		this.progress = function(data) {
			this._result = undefined;
			this._state = 0;
			var s = this._pcb;
			for (var i = 0, l = s.length; i < l; i++) {
				var si = s[i];
				si[0].call(global, data, si[1], this.data);
			}
			return this;
		};
		this.get = function() {
			if (this._state == 1) {
				return this._result;
			} else {
				if (this._state == 2) throw "In Error State";
				else throw "Not Resolved";
			}
		};
		this.isResolved = function() {
			if (this._state == 1) return true;
			return false;
		};
		this.setData = function(key, value) {
			this.data[key] = value;
			return this;
		};
		this.getData = function(key) {
			return this.data[key];
		};
	};
	$.delay = {
		Delay: function(cancelCallback) {
			this._scb = [];
			this._ecb = [];
			this._pcb = [];
			this._fcb = [];
			this._ccb = cancelCallback;
			this.data = {};
		}
	};
	$.delay.Delay.prototype = new DelayPrototype();
	
	var iecheck = function() {
		if ($.isReady) return;
		try {
			document.documentElement.doScroll("left");
		} catch (error) {
			setTimeout(iecheck, 1);
			return;
		}
		$.init();
	};
	if ($.browser.engine.name == "Trident") {
		document.attachEvent("onreadystatechange", $.init);
		global.addEventListener("load", $.init, false);
		var toplevel = false;
		try {
			toplevel = window.frameElement == null;
		} catch(e) {}
		if (document.documentElement.doScroll && toplevel) {
			iecheck();
		}
	} else {
		document.addEventListener("DOMContentLoaded", $.init, false);
		global.addEventListener("load", $.init, false);
	}
})();

;(function() {
	sj.ui = {};
	sj.ui.loader = new Image();
	sj.ui.getLoader = function() {
		return sj.ui.loader.cloneNode(false);
	};
	sj.ui.theme = {
		current: null,
		list: {},
		set: function(name) {
			if (sj.ui.theme.current != name) {
				var osd = null;
				if (sj.ui.osd) {
					osd = sj.ui.osd.add(document.createTextNode(sj.locale("Loading theme %(name)s...", {name: name})));
				}
				var t = sj.html.addStyle([sj.env.SITE_ROOT, "static/themes/", name, "/main.css"].join(""), null);
				if (!t) {
					t = {};
				}
				t = sj.html.addScript([sj.env.SITE_ROOT, "static/themes/", name, "/main.js"].join(""), function(){
					this.theme = name;
					sj.ui.theme._set_onload.call(this);
					if (osd != null) sj.ui.osd.remove(osd);
				});
			}
		},
		_set_onload: function(e) {
			var t = this;
			var old_theme = null;
			var olds = [];
			if (sj.ui.theme.current) {
				old_theme = ["theme_", sj.ui.theme.current].join("");
				olds = sj.q.c(document, old_theme);
			}
			if (olds.length == 0) {
				sj(document.body).classList.add(["theme_", t.theme].join(""));
			} else {
				var nc = ["theme_", t.theme].join("");
				sj.js.each(olds, function() {
					this.classList.remove(old_theme);
					this.classList.add(nc);
				});
			}
			if (sj.js.isFunction(sj.ui.theme.list[t.theme])) {
				sj.ui.theme.list[t.theme]();
			}
			sj.ui.theme.current = t.theme;
			cui.recursiveRender(document.body);
		}
	};
	var DOMStringList = function DOMStringList(strList) {
		if (!strList) strList = [];
		this.list = strList;
		this.length = this.list.length;
	};
	DOMStringList.prototype = {
		length: 0,
		item: function(index) {
			return this.list[index];
		},
		contains: function(str) {
			return (this.list.indexOf(str) == -1 ? false : true);
		}
	};
	sj.ui.events = {
		__preparing_drag: null,
		__current_drag: null,
		DragInfo: function(target) {
			this.target = target;
			this.image = null;
			this.image_x_offset = 0;
			this.image_y_offset = 0;
			this.dataTransfer = new sj.ui.events.DataTransfer();
		},
		DataTransfer: function DataTransfer() {
			this.draggingNodes = [];
			this.imageNodes = [];
			this.types = new DOMStringList();
			this.__types_data = {};
		},
		enable_dispatcher: function(type, target) {
			if (type == "dragstart") {
				if (document.createEvent("MouseEvent").dataTransfer === null) {
					target.setAttribute("draggable", "true");
				} else {
					target.addEventListener("mousedown", sj.ui.events._prepare_dragstart, false);
				}
			}
		},
		disable_dispatcher: function(type, target) {
			if (type == "dragstart") {
				if (target.draggable !== undefined) {
					target.draggable = false;
				} else {
					target.removeEventListener("mousedown", sj.ui.events._prepare_dragstart, false);
				}
			}
		},
		_prepare_dragstart: function(e) {
			sj.ui.events.__preparing_drag = this;
			document.addEventListener("mouseup", sj.ui.events._unprepare_dragstart, false);
			document.addEventListener("mousemove", sj.ui.events._dispatch_dragstart, false);
			e.preventDefault();
		},
		_unprepare_dragstart: function() {
			document.removeEventListener("mouseup", sj.ui.events._unprepare_dragstart, false);
			document.removeEventListener("mousemove", sj.ui.events._dispatch_dragstart, false);
			sj.ui.events.__preparing_drag = null;
		},
		_dispatch_dragstart: function(e) {
			e.preventDefault();
			e.stopPropagation();
			if (sj.ui.events.__current_drag) {
				window.clearInterval(sj.ui.events.__current_drag.timer);
				sj.ui.events._cancel_drag();
				sj.ui.events._update_drag();
			}
			sj.ui.events.__current_drag = new sj.ui.events.DataTransfer();
			sj.ui.events.__current_drag.draggingNodes.push(sj.ui.events.__preparing_drag);
			sj.ui.events._unprepare_dragstart();
			var ev = document.createEvent("MouseEvents");
			ev.initMouseEvent("dragstart", true, true, window, 0, sj.env.POINTER_DATA.screenX, sj.env.POINTER_DATA.screenY, sj.env.POINTER_DATA.clientX, sj.env.POINTER_DATA.clientY, sj.env.POINTER_DATA.ctrlKey, sj.env.POINTER_DATA.altKey, sj.env.POINTER_DATA.shiftKey, sj.env.POINTER_DATA.metaKey, sj.env.POINTER_DATA.button, null);
			ev.fromjs = true;
			ev.dataTransfer = sj.ui.events.__current_drag;
			
			for (var i = 0, l = ev.dataTransfer.draggingNodes.length; i < l; i++) {
				if (ev.dataTransfer.draggingNodes[i].dispatchEvent(ev) === false) {
					sj.ui.events.__current_drag = null;
					return false;
				}
			}
			
			ev.dataTransfer.immediateUserSelection = null;
			ev.dataTransfer.currentTargetElement = null;
			ev.dataTransfer.currentDragOperation = "none";
			ev.dataTransfer.updating = false;
			
			document.addEventListener("mouseup", sj.ui.events._cancel_drag, true);
			ev.dataTransfer.timer = window.setInterval(sj.ui.events._update_drag, 50);
			return true;
		},
		_cancel_drag: function() {
			sj.ui.events.__current_drag.cancelled = true;
		},
		_update_drag: function() {
			var d = sj.ui.events.__current_drag;
			if (d.updating) return;
			d.updating = true;
			var cancelled = false;
			
			for (var i = 0; i < d.draggingNodes.length; i++) {
				if (sj.ui.events._dispatch_drag(d.draggingNodes[i]) === false) {
					d.currentDragOperation = "none";
					cancelled = true;
					break;
				}
			}
			
			if (cancelled || sj.ui.events.__current_drag.cancelled) {
				window.clearInterval(d.timer);
				if (d.currentDragOperation == "none" || d.currentTargetElement === null) {
					if (d.currentTargetElement !== null) {
						sj.ui.events._dispatch_dragleave(d.currentTargetElement);
					}
				} else {
					d.dropEffect = d.currentDragOperation;
					if (sj.ui.events._dispatch_drop(d.currentTargetElement) === false) {
						d.currentDragOperation = d.dropEffect;
					} else {
						d.currentDragOperation = "none";
					}
				}
				d.dropEffect = d.currentDragOperation;
				for (var i = 0; i < d.draggingNodes.length; i++) {
					sj.ui.events._dispatch_dragend(d.draggingNodes[i]);
				}
				sj.ui.events.__current_drag = null;
				document.body.style.cursor = "default";
				return;
			} else {
				var ctec = false;
				var pcte = d.currentTargetElement;
				var pius = d.immediateUserSelection;
				d.immediateUserSelection = sj.env.POINTER_DATA.target;
				if (pius != d.immediateUserSelection && d.immediateUserSelection != d.currentTargetElement) {
					if (d.immediateUserSelection == null) {
						d.currentTargetElement = d.immediateUserSelection;
						ctec = true;
					} else {
						if (sj.ui.events._dispatch_dragenter(d.immediateUserSelection) === false) {
							d.currentTargetElement = d.immediateUserSelection;
							ctec = true;
						} else if (d.currentTargetElement != document.body) {
							d.currentTargetElement = document.body;
							ctec = true;
							if (document.body) sj.ui.events._dispatch_dragenter(document.body);
						}
					}
				}
				if (ctec && pcte != null) {
					sj.ui.events._dispatch_dragleave(pcte);
				}
				if (d.currentTargetElement !== null) {
					if (sj.ui.events._dispatch_dragover(d.currentTargetElement) !== false) {
						d.dropEffect = "none";
						document.body.style.cursor = "no-drop";
					} else {
						if (["uninitialized", "copy", "copyLink", "copyMove", "all"].indexOf(d.effectAllowed) != -1) {
							d.dropEffect = "copy";
							document.body.style.cursor = "move";
						} else if (["uninitialized", "move", "copyMove", "linkMove", "all"].indexOf(d.effectAllowed) != -1) {
							d.dropEffect = "move";
							document.body.style.cursor = "move";
						} else if (["uninitialized", "link", "copyLink", "linkMove", "all"].indexOf(d.effectAllowed) != -1) {
							d.dropEffect = "link";
							document.body.style.cursor = "move";
						} else {
							d.dropEffect = "none";
							document.body.style.cursor = "no-drop";
						}
					}
					d.currentDragOperation = d.dropEffect;
				}
			}
			d.updating = false;
		},
		_dispatch_dragenter: function(n) {
			var ev = document.createEvent("MouseEvents");
			ev.initMouseEvent("dragenter", true, true, window, 0, sj.env.POINTER_DATA.screenX, sj.env.POINTER_DATA.screenY, sj.env.POINTER_DATA.clientX, sj.env.POINTER_DATA.clientY, sj.env.POINTER_DATA.ctrlKey, sj.env.POINTER_DATA.altKey, sj.env.POINTER_DATA.shiftKey, sj.env.POINTER_DATA.metaKey, sj.env.POINTER_DATA.button, null);
			ev.fromjs = true;
			ev.dataTransfer = sj.ui.events.__current_drag;
			return n.dispatchEvent(ev);
		},
		_dispatch_dragover: function(n) {
			var ev = document.createEvent("MouseEvents");
			ev.initMouseEvent("dragover", true, true, window, 0, sj.env.POINTER_DATA.screenX, sj.env.POINTER_DATA.screenY, sj.env.POINTER_DATA.clientX, sj.env.POINTER_DATA.clientY, sj.env.POINTER_DATA.ctrlKey, sj.env.POINTER_DATA.altKey, sj.env.POINTER_DATA.shiftKey, sj.env.POINTER_DATA.metaKey, sj.env.POINTER_DATA.button, null);
			ev.fromjs = true;
			ev.dataTransfer = sj.ui.events.__current_drag;
			return n.dispatchEvent(ev);
		},
		_dispatch_dragleave: function(n) {
			var ev = document.createEvent("MouseEvents");
			ev.initMouseEvent("dragleave", true, false, window, 0, sj.env.POINTER_DATA.screenX, sj.env.POINTER_DATA.screenY, sj.env.POINTER_DATA.clientX, sj.env.POINTER_DATA.clientY, sj.env.POINTER_DATA.ctrlKey, sj.env.POINTER_DATA.altKey, sj.env.POINTER_DATA.shiftKey, sj.env.POINTER_DATA.metaKey, sj.env.POINTER_DATA.button, null);
			ev.fromjs = true;
			ev.dataTransfer = sj.ui.events.__current_drag;
			return n.dispatchEvent(ev);
		},
		_dispatch_drop: function(n) {
			var ev = document.createEvent("MouseEvents");
			ev.initMouseEvent("drop", true, true, window, 0, sj.env.POINTER_DATA.screenX, sj.env.POINTER_DATA.screenY, sj.env.POINTER_DATA.clientX, sj.env.POINTER_DATA.clientY, sj.env.POINTER_DATA.ctrlKey, sj.env.POINTER_DATA.altKey, sj.env.POINTER_DATA.shiftKey, sj.env.POINTER_DATA.metaKey, sj.env.POINTER_DATA.button, null);
			ev.fromjs = true;
			ev.dataTransfer = sj.ui.events.__current_drag;
			return n.dispatchEvent(ev);
		},
		_dispatch_dragend: function(n) {
			document.removeEventListener("mouseup", sj.ui.events._cancel_drag, true);
			var ev = document.createEvent("MouseEvents");
			ev.initMouseEvent("dragend", true, false, window, 0, sj.env.POINTER_DATA.screenX, sj.env.POINTER_DATA.screenY, sj.env.POINTER_DATA.clientX, sj.env.POINTER_DATA.clientY, sj.env.POINTER_DATA.ctrlKey, sj.env.POINTER_DATA.altKey, sj.env.POINTER_DATA.shiftKey, sj.env.POINTER_DATA.metaKey, sj.env.POINTER_DATA.button, null);
			ev.fromjs = true;
			ev.dataTransfer = sj.ui.events.__current_drag;
			n.dispatchEvent(ev);
			return true;
		},
		_dispatch_drag: function(n) {
			var ev = document.createEvent("MouseEvents");
			ev.initMouseEvent("drag", true, true, window, 0, sj.env.POINTER_DATA.screenX, sj.env.POINTER_DATA.screenY, sj.env.POINTER_DATA.clientX, sj.env.POINTER_DATA.clientY, sj.env.POINTER_DATA.ctrlKey, sj.env.POINTER_DATA.altKey, sj.env.POINTER_DATA.shiftKey, sj.env.POINTER_DATA.metaKey, sj.env.POINTER_DATA.button, null);
			ev.fromjs = true;
			ev.dataTransfer = sj.ui.events.__current_drag;
			return n.dispatchEvent(ev);
		}
	};

	sj.ui.events.DataTransfer.prototype.dropEffect = "none";
	sj.ui.events.DataTransfer.prototype.effectAllowed = "uninitialized";
	sj.ui.events.DataTransfer.prototype.addElement = function(element) {
		this.imageNodes.push(element);
	};
	sj.ui.events.DataTransfer.prototype.clearData = function(type) {
		if (type) {
			var i = this.types.list.indexOf(type);
			if (i != -1) {
				this.types.list.splice(i, 1);
				delete this.__types_data[type];
			}
		} else {
			this.types.list = [];
			this.__types_data = {};
		}
	};
	sj.ui.events.DataTransfer.prototype.getData = function(type) {
		if (type in this.__types_data) return this.__types_data[type];
		return "";
	};
	sj.ui.events.DataTransfer.prototype.setData = function(type, data) {
		if (!this.types.contains(type)) this.types.list.push(type);
		this.__types_data[type] = data;
	};
	sj.ui.events.DataTransfer.prototype.setDragImage = function(img, x, y) {
		this.imageNodes = [img];
		this.image_x_offset = x;
		this.image_y_offset = y;
	};

	var preventDefault = function(e) {
		e.preventDefault();
	};

	sj.ui.widgets = {
		__list: [],
		__locked: false,
		__theme: "ColourWindow",
		themes: {},
		get: function(id) {
			return sj.ui.widgets.__list[id];
		},
		getAll: function() {
			return sj.ui.widgets.__list;
		},
		appFocus: function() {
			if (typeof this.widget === "number") {
				var w = sj.ui.widgets.get(this.widget);
				if (w) w.focus();
			}
		},
		Widget: function(options, cb) {
			var wl = sj.ui.widgets.__list;
			this.id = wl.length;
			wl.push(this);
			options = sj.js.extend({}, {
				title: "",
				theme: sj.ui.widgets.__theme,
				parent: sj.sys.process.getUniqueProgram("KeeWorld").getAir(),
				x: "20px",
				y: "20px",
				favicon: [sj.env.SITE_ROOT, "static/img/favicon.png"].join("")
			}, options);
			this.options = options;
			if (typeof options.onthemeload == "function") {
				this.onthemeload = options.onthemeload;
			}
			if (typeof options.onclose == "function") {
				this.onclose = options.onclose;
			}
			if (typeof options.onresize == "function") {
				this.onresize = options.onresize;
			}
			this.__width = options.width;
			this.__height = options.height;
			if (typeof options.x === "number" || typeof options.x === "string") this.__position_x = options.x;
			if (typeof options.y === "number" || typeof options.y === "string") this.__position_y = options.y;
			if (typeof options.onmouseover === "function") this.onmouseover = options.onmouseover;
			if (typeof options.onmouseout === "function") this.onmouseout = options.onmouseout;
			if (typeof options.onmousedown === "function") this.onmousedown = options.onmousedown;
			if (typeof options.onmouseup === "function") this.onmouseup = options.onmouseup;
			if (typeof options.onmousemove === "function") this.onmousemove = options.onmousemove;
			if (typeof options.onresizestart === "function") this.onresizestart = options.onresizestart;
			if (typeof options.onresizeend === "function") this.onresizeend = options.onresizeend;
			if (options.data) this.data = options.data;
			if (options.contentUserData) this.contentUserData = options.contentUserData;
			if (options.title) this.set_title(options.title);
			if (options.parent) this.set_parent(options.parent);
			if (typeof(options.x_locked) !== "undefined") this.x_locked = options.x_locked;
			if (typeof(options.y_locked) !== "undefined") this.y_locked = options.y_locked;
			if (typeof(options.alwaysOnBottom) !== "undefined") this.alwaysOnBottom = options.alwaysOnBottom;
			if (options.hashFocusable !== undefined) this.hashFocusable = options.hashFocusable;
			else this.hashFocusable = true;
			if (options.currentURL) this.currentURL = options.currentURL;
			if (typeof(options.restrain_to_parent) === "boolean") this.restrain_to_parent = options.restrain_to_parent;
			this.favicon = options.favicon;
			
			var resizable = true;
			var moveable = true;
			var maximizable = true;
			var minimizable = true;
			var shadable = true;
			var closable = true;
			var decorated = (typeof(options.decorated) === "boolean" ? options.decorated : true);
			
			this.isResizable = function(){return resizable};
			this.isMoveable = function(){return moveable};
			this.isMaximizable = function(){return maximizable};
			this.isMinimizable = function(){return minimizable};
			this.isShadable = function(){return shadable};
			this.isClosable = function(){return closable};
			this.isDecorated = function(){return decorated};
			
			this.setResizable = function(v) {
				resizable = v;
				var t = sj.ui.widgets.themes[this.__theme];
				if (this.content) {
					if (v) {
						if (t.onEnableResizable) t.onEnableResizable.call(this.content);
					} else {
						if (t.onDisableResizable) t.onDisableResizable.call(this.content);
					}
				}
			};
			this.setMoveable = function(v) {
				moveable = v;
				var t = sj.ui.widgets.themes[this.__theme];
				if (this.content) {
					if (v) {
						if (t.onEnableMoveable) t.onEnableMoveable.call(this.content);
					} else {
						if (t.onDisableMoveable) t.onDisableMoveable.call(this.content);
					}
				}
			};
			this.setMaximizable = function(v) {
				maximizable = v;
				var t = sj.ui.widgets.themes[this.__theme];
				if (this.content) {
					if (v) {
						if (t.onEnableMaximizable) t.onEnableMaximizable.call(this.content);
					} else {
						if (t.onDisableMaximizable) t.onDisableMaximizable.call(this.content);
					}
				}
			};
			this.setMinimizable = function(v) {
				minimizable = v;
				var t = sj.ui.widgets.themes[this.__theme];
				if (this.content) {
					if (v) {
						if (t.onEnableMinimizable) t.onEnableMinimizable.call(this.content);
					} else {
						if (t.onDisableMinimizable) t.onDisableMinimizable.call(this.content);
					}
				}
			};
			this.setShadable = function(v) {
				shadable = v;
				var t = sj.ui.widgets.themes[this.__theme];
				if (this.content) {
					if (v) {
						if (t.onEnableShadable) t.onEnableShadable.call(this.content);
					} else {
						if (t.onDisableShadable) t.onDisableShadable.call(this.content);
					}
				}
			};
			this.setClosable = function(v) {
				closable = v;
				var t = sj.ui.widgets.themes[this.__theme];
				if (this.content) {
					if (v) {
						if (t.onEnableClosable) t.onEnableClosable.call(this.content);
					} else {
						if (t.onDisableClosable) t.onDisableClosable.call(this.content);
					}
				}
			};
			this.setDecorated = function(v) {
				decorated = v;
				var t = sj.ui.widgets.themes[this.__theme];
				if (this.content) {
					if (v) {
						if (t.onDecorate) t.onDecorate.call(this.content);
					} else {
						if (t.onUndecorate) t.onUndecorate.call(this.content);
					}
				}
			};
			if (cb) cb.call(this);
			if (options.theme) this.set_theme(options.theme);
		},
		content_templates: {
			error_message: function(w, msgs) {
				var c = w.content.getElementsByClassName("content")[0];
				w.set_size(395, 145);
				w.setResizable(false);
				var b = sj.dom.create("div", {style:"width:100%;height:100%","class":"widget_panel"});
				c.appendChild(b);
				b = sj.dom.create("ul");
				for (var i = 0; i < msgs.length; i++) {
					var d = document.createElement("li");
					sj.dom.text(d, msgs[i]);
					b.appendChild(d);
				}
				c.appendChild(b);
				var d2 = sj.dom.create("div", {
					style: "width:70px;height:25px",
					"class": "cuib"
				});
				sj.dom.text(d2, sj.locale("Close"));
				sj.dom.dataset.set(d2, "cuibackgroundid", "widget_button");
				sj.dom.dataset.set(d2, "cuibackgroundevents", "mousedown,mouseover,mouseup,mouseout");
				d2.addEventListener("click", function(){w.close()},false);
				c.appendChild(d2);
				w.set_position((window.innerWidth / 2) - (395 / 2), (window.innerHeight / 2) - (125 / 2));
				w.show();
			}
		},
		_theme_get_success: function(d) {
			var pass = d.options.pass;
			var piece = d.piece;
			d = d.data;
			pass.__theme = sj.util.basename(piece, ".xml");
			pass.content = sj(d);
			pass.content.sj_ui_widgets_Widget = pass.id;
			pass.content.setUserData("sj.ui.widgets.Widget", pass, null);
			if (pass.contentUserData) {
				var contentUserData = pass.contentUserData;
				for (var i in contentUserData) {
					if (contentUserData.hasOwnProperty(i)) pass.content.setUserData(i, contentUserData[i], null);
				}
			}
			if (!pass.__visible) pass.hide();
			pass.set_parent();
			var a = pass.content.getElementsByClassName("widget_title");
			for (var i = 0, l = a.length; i < l; i++) {
				sj(a[i]).addEventListener("mousedown", preventDefault, false);
			}
			a = pass.content.getElementsByClassName("widget_favicon");
			if (a && a.length > 0) {
				var favicon = new Image();
				favicon.src = pass.favicon;
				favicon.width = 16;
				favicon.height = 16;
				a[0].appendChild(favicon);
			}
			for (var sel in {"closer":1,"maximizer":1,"unmaximizer":1,"minimizer":1,"shader":1,"unshader":1}) {
				a = pass.content.getElementsByClassName(sel);
				for (var i = 0, l = a.length; i < l; i++) {
					var ai = sj(a[i]);
					ai.setUserData("sj.ui.widgets.Widget", pass, null);
					ai.addEventListener("click", sj.ui.widgets[["_", sel, "_onclick"].join("")], false);
				}
			}
			var wtheme = sj.ui.widgets.themes[pass.__theme];
			if (wtheme.onload) pass.content.addEventListener("load", wtheme.onload, false);
			if (wtheme.onresize) pass.content.addEventListener("resize", wtheme.onresize, false);
			if (wtheme.onfocusin) pass.content.addEventListener("focusin", wtheme.onfocusin, false);
			if (sj.ui.widgets._onfocusin) pass.content.addEventListener("focusin", sj.ui.widgets._onfocusin, false);
			if (sj.ui.widgets._onfocusin) pass.content.addEventListener("activate", sj.ui.widgets._onfocusin, false);
			if (sj.ui.widgets._onfocusin) pass.content.addEventListener("mousedown", sj.ui.widgets._onfocusin, false);
			if (wtheme.onfocusout) pass.content.addEventListener("focusout", wtheme.onfocusout, false);
			if (wtheme.content_DOMSubtreeModified) pass.content.getElementsByClassName("content")[0].addEventListener("DOMSubtreeModified", wtheme.content_DOMSubtreeModified, false);
			a = pass.content.getElementsByClassName("mover");
			for (var i = 0, l = a.length; i < l; i++) {
				var ai = sj(a[i]);
				ai.setUserData("sj.ui.widgets.Widget", pass, null);
				ai.addEventListener("mousedown", sj.ui.widgets._startmove, false);
			}
			a = pass.content.getElementsByClassName("widget_resizer");
			for (var i = 0, l = a.length; i < l; i++) {
				var ai = sj(a[i]);
				ai.setUserData("sj.ui.widgets.Widget", pass, null);
				ai.addEventListener("mousedown", sj.ui.widgets._startresize, false);
			}
			pass.setDecorated(pass.isDecorated());
			if (pass.onmouseover) pass.content.addEventListener("mouseover", function(e){pass.onmouseover.call(pass, e)}, false);
			if (pass.onmouseout) pass.content.addEventListener("mouseout", function(e){pass.onmouseout.call(pass, e)}, false);
			if (pass.onmouseup) pass.content.addEventListener("mouseup", function(e){pass.onmouseup.call(pass, e)}, false);
			if (pass.onmousedown) pass.content.addEventListener("mousedown", function(e){pass.onmousedown.call(pass, e)}, false);
			if (pass.onmousemove) pass.content.addEventListener("mousemove", function(e){pass.onmousemove.call(pass, e)}, false);
			if (pass.onresize) pass.content.addEventListener("resize", pass.onresize, false);
			
			pass.set_position();
			pass.set_title();
			pass.set_size();
			var ev = document.createEvent("Events");
			ev.initEvent("load", false, false);
			pass.content.dispatchEvent(ev);
			if (pass.onthemeload) pass.onthemeload.call(pass);
		},
		_theme_get_failure: function(r, s, e, piece, pass) {
			
		},
		_closer_onclick: function(e) {
			e.preventDefault();
			e.stopPropagation();
			var w = this.getUserData("sj.ui.widgets.Widget");
			if (!w.onclose || w.onclose.call(w) !== false) w.close();
		},
		_maximizer_onclick: function(e) {
			e.preventDefault();
			e.stopPropagation();
			this.getUserData("sj.ui.widgets.Widget").maximize();
		},
		_unmaximizer_onclick: function(e) {
			e.preventDefault();
			e.stopPropagation();
			this.getUserData("sj.ui.widgets.Widget").unmaximize();
		},
		_minimizer_onclick: function(e) {
			e.preventDefault();
			e.stopPropagation();
			this.getUserData("sj.ui.widgets.Widget").minimize();
		},
		_shader_onclick: function(e) {
			e.preventDefault();
			e.stopPropagation();
			this.getUserData("sj.ui.widgets.Widget").shade();
		},
		_unshader_onclick: function(e) {
			e.preventDefault();
			e.stopPropagation();
			this.getUserData("sj.ui.widgets.Widget").unshade();
		},
		_onfocusin: function(e) {
			this.getUserData("sj.ui.widgets.Widget").focus();
		},
		__topzindex: 5,
		_startmove: function(e) {
			var w = this.getUserData("sj.ui.widgets.Widget");
			if (!w.isMoveable() || w.__maximized || w.__minimized) return;
			var xy = w.get_position();
			this.setUserData("drag_data", {
				dx: e.clientX - parseInt(xy.x.replace(/[^-.\d]+/, "")),
				dy: e.clientY - parseInt(xy.y.replace(/[^-.\d]+/, ""))
			}, null);
			document.setUserData("sj.currentDrag", this, null);
			document.addEventListener("mousemove", sj.ui.widgets._move, false);
			document.addEventListener("mouseup", sj.ui.widgets._endmove, false);
			return false;
		},
		_endmove: function(e) {
			e.stopPropagation();
			document.removeEventListener("mousemove", sj.ui.widgets._move, false);
			document.removeEventListener("mouseup", sj.ui.widgets._endmove, false);
			var t = document.getUserData("sj.currentDrag");
			t.setUserData("drag_data", null, null);
			document.setUserData("sj.currentDrag", null, null);
		},
		_move: function(e) {
			e.preventDefault();
			e.stopPropagation();
			var t = document.getUserData("sj.currentDrag");
			var drag_data = t.getUserData("drag_data");
			var x = e.clientX - drag_data.dx;
			var y = e.clientY - drag_data.dy;
			t.getUserData("sj.ui.widgets.Widget").set_position(x + "px", y + "px");
			return false;
		},
		_startresize: function(e) {
			e.preventDefault();
			e.stopPropagation();
			var w = this.getUserData("sj.ui.widgets.Widget");
			if (!w.isResizable() || w.__maximized || w.__minimized) {
				e.preventDefault();
				return false;
			}
			document.body.setUserData("sj.currentDrag", this, null);
			if (w.onresizestart) w.onresizestart.call(w.content, e);
			var c = sj.ui.widgets.themes[w.__theme];
			if (c.onresizestart) c.onresizestart.call(w, e);
			document.addEventListener("mousemove", sj.ui.widgets._resize, false);
			document.addEventListener("mouseup", sj.ui.widgets._endresize, false);
			return false;
		},
		_endresize: function(e) {
			e.stopPropagation();
			document.removeEventListener("mousemove", sj.ui.widgets._resize, false);
			document.removeEventListener("mouseup", sj.ui.widgets._endresize, false);
			var w = document.body.getUserData("sj.currentDrag").getUserData("sj.ui.widgets.Widget");
			var c = sj.ui.widgets.themes[w.__theme];
			if (c.onresizeend) c.onresizeend.call(w, e);
			if (w.onresizeend) w.onresizeend.call(w.content, e);
			var t = document.body.getUserData("sj.currentDrag");
			document.body.setUserData("sj.currentDrag", null, null);
		},
		_resize: function(e) {
			e.preventDefault();
			e.stopPropagation();
			var th = document.body.getUserData("sj.currentDrag");
			var widget = th.getUserData("sj.ui.widgets.Widget");
			var lt = widget.get_position();
			var wh = widget.get_size();
			var w = parseInt(wh.width);
			var h = parseInt(wh.height);
			var l = parseInt(lt.x);
			var t = parseInt(lt.y);
			var nt = t;
			var nl = l;
			var nw = w;
			var nh = h;
			if (e.clientY - sj.dom.fullOffset(widget.__parent, "Top") + 2 > 0 && th.classList.contains("widget_top")) {
				nt = e.clientY - sj.dom.fullOffset(widget.__parent, "Top") + 2;
				nh = h - (nt - t);
			}
			if (e.clientY - sj.dom.fullOffset(widget.__parent, "Top") + 2 < widget.get_parent().offsetHeight && th.classList.contains("widget_bottom")) {
				nh = e.clientY - sj.dom.fullOffset(widget.__parent, "Top") + 2 - t;
			}
			if (e.clientX - sj.dom.fullOffset(widget.__parent, "Left") + 2 > 0 && th.classList.contains("widget_left")) {
				nl = e.clientX - sj.dom.fullOffset(widget.__parent, "Left") + 2;
				nw = w - (nl - l);
			}
			if (e.clientX - sj.dom.fullOffset(widget.__parent, "Left") + 2 < widget.get_parent().offsetWidth && th.classList.contains("widget_right")) {
				nw = e.clientX - sj.dom.fullOffset(widget.__parent, "Left") + 2 - l;
			}
			var ns = widget.set_size(nw, nh);
			if (ns.width == null) nl = null;
			if (ns.height == null) nt = null;
			widget.set_position([nl, "px"].join(""), [nt, "px"].join(""));
			return false;
		}
	};

	sj.ui.widgets.Widget.prototype.__title = "";
	sj.ui.widgets.Widget.prototype.set_title = function(title) {
		if (!title) title = this.__title;
		if (this.content) sj.dom.text(this.content.getElementsByClassName("widget_title")[0], title);
		this.__title = title;
	};
	sj.ui.widgets.Widget.prototype.get_title = function() {
		return this.__title;
	};
	sj.ui.widgets.Widget.prototype.__parent = null;
	sj.ui.widgets.Widget.prototype.content = null;
	sj.ui.widgets.Widget.prototype.maximized = false;
	sj.ui.widgets.Widget.prototype.minimized = false;
	sj.ui.widgets.Widget.prototype.alwaysOnBottom = false;
	sj.ui.widgets.Widget.prototype.__last_size = null;
	sj.ui.widgets.Widget.prototype.__last_position = null;
	sj.ui.widgets.Widget.prototype.restrain_to_parent = true;
	sj.ui.widgets.Widget.prototype.x_locked = false;
	sj.ui.widgets.Widget.prototype.y_locked = false;
	sj.ui.widgets.Widget.prototype.min_width = 100;
	sj.ui.widgets.Widget.prototype.min_height = 25;
	sj.ui.widgets.Widget.prototype.max_width = null;
	sj.ui.widgets.Widget.prototype.max_height = null;
	sj.ui.widgets.Widget.prototype.focus = function() {
		var content = this.content;
		var contentStyle = content.style;
		if (content && !this.focused && !this.alwaysOnBottom) {
			contentStyle.zIndex = ++sj.ui.widgets.__topzindex;
			var wl = sj.ui.widgets.__list;
			for (var i = 0, l = wl.length; i < l; i++) {
				var wli = wl[i];
				if (!wli || !wli.content) continue;
				if (wli.__parent == this.__parent) {
					if (wli === this) {
						if (this.minimized) {
							contentStyle.zIndex--;
							this.unminimize();
							return;
						} else {
							content.classList.add("widget_focused");
							this.focused = true;
							this.setHash();
							cui.recursiveRender(content);
						}
					} else {
						var wlic = wli.content;
						if (wlic.classList.contains("widget_focused")) {
							wlic.classList.remove("widget_focused");
							wli.focused = false;
							cui.recursiveRender(wlic);
						}
					}
				}
			}
		}
	};
	sj.ui.widgets.Widget.prototype.maximize = function() {
		if (!this.isMaximizable() || this.maximized) {
			return false;
		}
		this.__last_size = this.get_size();
		this.__last_position = this.get_position();
		this.maximized = true;
		this.set_position(0, 0);
		this.set_size(this.__parent.clientWidth, this.__parent.clientHeight);
		var c = sj.ui.widgets.themes[this.__theme];
		if (c && c.onmaximize) {
			c.onmaximize.call(this.content);
		}
		cui.recursiveRender(this.content);
	};
	sj.ui.widgets.Widget.prototype.unmaximize = function() {
		if (!this.isMaximizable() || !this.maximized) {
			return false;
		}
		this.maximized = false;
		this.set_size(this.__last_size.width, this.__last_size.height);
		this.set_position(this.__last_position.x, this.__last_position.y);
		var c = sj.ui.widgets.themes[this.__theme];
		if (c && c.onunmaximize) {
			c.onunmaximize.call(this.content);
		}
		cui.recursiveRender(this.content);
	};
	sj.ui.widgets.Widget.prototype.minimize = function() {
		if (!this.isMinimizable() || this.minimized) {
			return false;
		}
		this.__last_size = this.get_size();
		this.__last_position = this.get_position();
		this.__parent.removeChild(this.content);
		this.content.classList.remove("widget_focused");
		this.focused = false;
		var c = sj.ui.widgets.themes[this.__theme];
		if (c && c.onminimize) {
			c.onminimize.call(this.content);
		}
		sj.ui.widgets.minimizeHandler.add(this);
		this.minimized = true;
	};
	sj.ui.widgets.Widget.prototype.unminimize = function() {
		if (!this.isMinimizable() || !this.minimized) {
			return false;
		}
		this.__parent.appendChild(this.content);
		this.set_size(this.__last_size.width, this.__last_size.height);
		this.set_position(this.__last_position.x, this.__last_position.y);
		var c = sj.ui.widgets.themes[this.__theme];
		if (c && c.onunminimize) {
			c.onunminimize.call(this.content);
		}
		sj.ui.widgets.minimizeHandler.remove(this);
		this.minimized = false;
		this.focus();
	};
	sj.ui.widgets.Widget.prototype.shaded = false;
	sj.ui.widgets.Widget.prototype.__theme = null;
	sj.ui.widgets.Widget.prototype.__visible = false;
	sj.ui.widgets.Widget.prototype.get_position = function() {
		var x = this.content.style.left;
		var y = this.content.style.top;
		return {x:x,y:y};
	};
	sj.ui.widgets.Widget.prototype.__position_x = "20px";
	sj.ui.widgets.Widget.prototype.__position_y = "20px";
	sj.ui.widgets.Widget.prototype.set_position = function(x, y) {
		if (this.isMoveable() && this.content) {
			if (!this.x_locked) {
				if (x === undefined || x === null) x = this.__position_x;
				if (typeof(x) == "number") x = x + "px";
				if (this.restrain_to_parent) {
					var nx = parseInt(x.replace(/[^-.\d]+/g, ""));
					if (nx < 0) {
						nx = 0;
					} else if (nx + this.content.offsetWidth > this.__parent.offsetWidth && this.content.offsetWidth < this.__parent.offsetWidth) {
						nx = this.__parent.offsetWidth - this.content.offsetWidth;
					}
					this.content.style.left = nx + "px";
					this.__position_x = nx + "px";
				}  else {
					this.content.style.left = x;
					this.__position_x = x;
				}
			}
			if (!this.y_locked) {
				if (y === undefined || y === null) y = this.__position_y;
				if (typeof(y) == "number") y = y + "px";
				if (this.restrain_to_parent) {
					var ny = parseInt(y.replace(/[^-.\d]+/g, ""));
					if (ny < 0) {
						ny = 0;
					} else if (ny + this.content.offsetHeight > this.__parent.offsetHeight && this.content.offsetHeight < this.__parent.offsetHeight) {
						ny = this.__parent.offsetHeight - this.content.offsetHeight;
					}
					this.content.style.top = ny + "px";
					this.__position_y = ny + "px";
				} else {
					this.content.style.top = y;
					this.__position_y = y;
				}
			}
			return true;
		} else {
			return false;
		}
	};
	sj.ui.widgets.Widget.prototype.move_position = function(x, y) {
		if (this.isMoveable() && this.content) {
			if (x) {
				var left = this.content.style.left.replace(/[^-.\d]+/g, "");
				this.content.style.left = (parseInt(left) + x) + "px";
			}
			if (y) {
				var top = this.content.style.top.replace(/[^-.\d]+/g, "");
				this.content.style.top = (parseInt(top) + y) + "px";
			}
		}
	};
	sj.ui.widgets.Widget.prototype.get_parent = function() {
		return this.__parent;
	};
	sj.ui.widgets.Widget.prototype.set_parent = function(parent) {
		if (!parent) {
			parent = this.__parent;
		}
		if (this.content) {
			var c = sj.dom.children(this.__parent);
			if (c) {
				for (var i = 0; i < c.length; i++) {
					if (c[i] == this.content) {
						this.__parent.removeChild(this.content);
					}
				}
			}
			parent.appendChild(this.content);
		}
		this.__parent = parent;
	};
	sj.ui.widgets.Widget.prototype.focus_on_show = true;
	sj.ui.widgets.Widget.prototype.show = function() {
		this.__visible = true;
		if (this.content) {
			if (this.alwaysOnBottom) this.content.style.zIndex = 4;
			this.content.style.display = "block";
			var w = sj.ui.widgets.themes[this.__theme];
			if (w && w.onshow) w.onshow.call(this);
			if (this.onshow) {
				this.onshow();
			}
			if (this.focus_on_show) {
				this.focus();
			} else {
				cui.recursiveRender(this.content);
			}
		}
	};
	sj.ui.widgets.Widget.prototype.hide = function() {
		if (this.content) {
			this.content.style.display = "none";
		}
		this.__visible = false;
	};
	sj.ui.widgets.Widget.prototype.get_size = function() {
		var w = this.content.style.width;
		var h = this.content.style.height;
		return {width:w,height:h};
	};
	sj.ui.widgets.Widget.prototype.__width = "50px";
	sj.ui.widgets.Widget.prototype.__height = "50px";
	sj.ui.widgets.Widget.prototype.set_size = function(w, h) {
		var ret = {width:null,height:null};
		if (!this.isResizable()) {
			return ret;
		}
		if (typeof w === "undefined") w = this.__width;
		if (typeof h === "undefined") h = this.__height;
		var a = false;
		if (typeof(w) === "number") w = w + "px";
		if (typeof(w) === "string") {
			var ok = true;
			if (this.min_width != null || this.max_width != null) {
				w = parseInt(w.replace(/[^-.\d]+/g, ""));
				if (this.min_width != null && w < this.min_width) ok = false;
				if (ok && this.max_width != null && w > this.max_width) ok = false;
				w += "px";
			}
			if (ok) {
				this.content.style.width = w;
				ret.width = w;
				a = true;
			} else {
				w = this.content.style.width;
			}
		}
		if (typeof(h) === "number") h = h + "px";
		if (typeof(h) === "string") {
			var ok = true;
			if (this.min_height != null || this.max_height != null) {
				h = parseInt(h.replace(/[^-.\d]+/g, ""));
				if (this.min_height != null && h < this.min_height) ok = false;
				if (ok && this.max_height != null && h > this.max_height) ok = false;
				h += "px";
			}
			if (ok) {
				this.content.style.height = h;
				ret.height = h;
				a = true;
			} else {
				h = this.content.style.height;
			}
		}
		if (a === true) {
			this.__width = w;
			this.__height = h;
			var e = document.createEvent("UIEvents");
			e.initUIEvent("resize", false, false, window, null);
			e.resizeWidth = w;
			e.resizeHeight = h;
			this.content.dispatchEvent(e);
		}
		cui.recursiveRender(this.content);
		return ret;
	};
	sj.ui.widgets.Widget.prototype.shade = function() {
		if (!this.isShadable() || this.shaded) {
			return false;
		}
		var w = sj.ui.widgets.themes[this.__theme];
		if (w && w.onshade) {
			w.onshade();
			return true;
		}
		return false;
	};
	sj.ui.widgets.Widget.prototype.unshade = function() {
		if (!this.isShadable() || !this.shaded) {
			return false;
		}
		var w = sj.ui.widgets.themes[this.__theme];
		if (w && w.onunshade) {
			w.onunshade();
			return true;
		}
		return false;
	};
	sj.ui.widgets.Widget.prototype.get_theme = function() {
		return this.__theme;
	};
	sj.ui.widgets.Widget.prototype.set_theme = function(theme) {
		sj.urlcache.get(
			["static/templates/widgets/themes/", theme, "/", theme, ".xml"].join(""),
			{pass: this, responseContentType: "text/xml"}
		).addEventListener("success", sj.ui.widgets._theme_get_success)
		.addEventListener("error", sj.ui.widgets._theme_get_failure);
	};
	sj.ui.widgets.Widget.prototype.close = function() {
		var hz = null;
		var len = sj.ui.widgets.__list.length;
		for (var i = 0; i < len; i++) {
			if (!sj.ui.widgets.__list[i] || !sj.ui.widgets.__list[i].content) continue;
			if (hz == null || sj.ui.widgets.__list[i].content.style.zIndex > hz.content.style.zIndex) {
				hz = sj.ui.widgets.__list[i];
			}
		}
		if (hz != null) hz.focus();
		if (hz == null || !hz.hashFocusable) sj.hash.set("", false);
		if (this.content) this.content.parentNode.removeChild(this.content);
		this.content = null;
		len = sj.ui.widgets.__list.length;
		for (var i = 0; i < len; i++) {
			if (sj.ui.widgets.__list[i] == this) {
				sj.ui.widgets.__list[i] = null;
				break;
			}
		}
	};
	sj.ui.widgets.Widget.prototype.setHash = function() {
		if (this.hashFocusable === undefined || this.hashFocusable == true && this.focused == true) {
			if (this.currentURL) {
				sj.hash.set(this.currentURL, false);
			} else if (this.widgetApp) {
				sj.hash.set((this.widgetApp.currentURL ? this.widgetApp.currentURL : this.widgetApp.about.name), false);
			}
		}
	};
	sj.ui.widgets.interfaces = {
		widgetApp: function(options) {
			var t = this;
			sj.ui.widgets.initWidgetApp(t, options);
			if (options.createExec === undefined || options.createExec === true) {
				t.exec = function() {
					if (options.createTerminate === undefined || options.createTerminate === true) {
						sj.sys.process.getProgramProcess(this).addEventListener("TERM", function() {
							var d = sj.ui.widgets.get(this.getProgram().widget);
							if (d) d.close();
						});
					}
					this.initWidget();
				}
			}
		}
	};
	sj.ui.widgets.initWidgetApp = function(t, options) {
		if (!options) options = {};
		if (!options.widgetOptions) options.widgetOptions = {};
		var wo = sj.js.extend(
			{},
			{
				content: null,
				showByDefault: true
			},
			options.widgetOptions,
			{
				properties: sj.js.extend({}, {
					title: "New Widget",
					theme: "ColourWindow",
					x: "20px",
					y: "20px",
					onclose: function() {
						sj.sys.process.getProgramProcess(this.data).terminate();
					}
				}, options.widgetOptions.properties)
			}
		);
		
		t.initWidget = function() {
			if (wo.properties.parent) {
				this.parent = wo.properties.parent;
			} else {
				this.parent = sj.sys.process.getUniqueProgram("KeeWorld").getAir();
				wo.properties.parent = this.parent;
			}
			wo.properties.data = this;
			var nw = new sj.ui.widgets.Widget(wo.properties, function() {
				this.data.widget = this.id;
				this.widgetApp = this.data;
			});
		};
		
		t.widgetOnThemeLoad = function() {
			var w = this;
			var wo = w.data.widgetOptions;
			if (wo.showByDefault) w.show();
			if (wo.properties.min_height !== undefined) w.min_height = wo.properties.min_height;
			if (wo.properties.min_width !== undefined) w.min_width = wo.properties.min_width;
			if (wo.properties.max_height !== undefined) w.max_height = wo.properties.max_height;
			if (wo.properties.max_width !== undefined) w.max_width = wo.properties.max_width;
			if (wo.properties.restrain_to_parent !== undefined) w.restrain_to_parent = wo.properties.restrain_to_parent;
			
			if (wo.properties.maximizable !== undefined) w.setMaximizable(wo.properties.maximizable);
			if (wo.properties.minimizable !== undefined) w.setMinimizable(wo.properties.minimizable);
			if (wo.properties.closable !== undefined) w.setClosable(wo.properties.closable);
			if (wo.properties.shadable !== undefined) w.setShadable(wo.properties.shadable);
			
			//if (wo.properties.x || wo.properties.y) w.set_position(wo.properties.x, wo.properties.y);
			//if (wo.properties.width || wo.properties.height) w.set_size(wo.properties.width, wo.properties.height);
			
			if (wo.properties.moveable !== undefined) w.setMoveable(wo.properties.moveable);
			if (wo.properties.resizable !== undefined) w.setResizable(wo.properties.resizable);
			
			w.data.widgetRenderContent();
		};
		
		t.widgetRenderContent = function() {
			var w = sj.ui.widgets.get(this.widget);
			var c = w.content.getElementsByClassName("content")[0];
			var wo = w.data.widgetOptions;
			sj.dom.emptyNode(c);
			if (wo.content) {
				var a = sj.dom.create("div", {
					style: ["position:relative;top:", (c.offsetHeight / 2) - 20, "px;height:40px;margin:0 auto;text-align:center"].join(""),
					"class": "widget_panel"
				});
				a.appendChild(sj.ui.loader);
				a.appendChild(document.createElement("br"));
				a.appendChild(document.createTextNode(sj.locale("Loading...")));
				c.appendChild(a);
				sj.urlcache.get(
					wo.content,
					{pass: this}
				).addEventListener("success", this.widgetContentSuccess)
				.addEventListener("error", this.widgetContentFailure);
			} else {
				if (wo.processContent) wo.processContent.call(this, c, w);
				if (wo.properties.onresize) wo.properties.onresize.call(w.content);
				sj.compat.boxdisplay(sj.q.c(c, "box"));
				cui.recursiveRender(w.content);
			}
		};
		
		if (wo.properties.onthemeload) {
			var otl = wo.properties.onthemeload;
			wo.properties.onthemeload = function(){otl.call(this);t.widgetOnThemeLoad.call(this)};
		} else {
			wo.properties.onthemeload = t.widgetOnThemeLoad;
		}
		t.widget = null;
		t.widgetContentSuccess = function(d) {
			var tt = d.options.pass;
			var data = d.data;
			var w = sj.ui.widgets.get(tt.widget);
			var c = w.content.getElementsByClassName("content")[0];
			sj.dom.emptyNode(c);
			if (!data || data.nodeName == "parsererror") {
				t.widgetContentFailure.call(this, d);
				return false;
			}
			if (wo.processContent) wo.processContent.call(tt, data, w);
			c.appendChild(data);
			if (wo.properties.onresize) wo.properties.onresize.call(w.content);
			sj.compat.boxdisplay(sj.q.c(c, "box"));
			cui.recursiveRender(w.content);
		};
		t.widgetContentFailure = function(d) {
			var tt = d.options.pass;
			var message = sj.locale("A system error has occured (Could not find a UI component). Please try again.");
			if (d.statusText == "timeout") {
				message = sj.locale("Could not connect to the server. You might not be connected to the internet or we might be have difficulties. Please try again.");
			}
			sj.sys.process.getProgramProcess(tt).terminate();
			sj.ui.passive.add(message, {timeout: 5000});
		}
		t.widgetOptions = wo;
	};

	sj.ui.widgets.minimizeHandler = {
		add: function(w) {
			if (sj.ui.widgets.minimizeHandler.listView) sj.ui.widgets.minimizeHandler.listView.push({containerId:"0",data:w});
			sj.events.signal("sj.ui.widgets.minimizeHandler.add", w);
		},
		remove: function(w) {
			if (sj.ui.widgets.minimizeHandler.listView != null) {
				var l = sj.ui.widgets.minimizeHandler.listView.length;
				for (var i = 0; i < l; i++) {
					if (sj.ui.widgets.minimizeHandler.listView.get(i).data == w) {
						sj.ui.widgets.minimizeHandler.listView.splice(i, 1);
						break;
					}
				}
			}
			sj.events.signal("sj.ui.widgets.minimizeHandler.remove", w);
		},
		wid: null,
		show: function() {
			if (sj.ui.widgets.minimizeHandler.wid != null) {
				sj.ui.widgets.get(sj.ui.widgets.minimizeHandler.wid).focus();
			} else {
				var w = new sj.ui.widgets.Widget({
					title: sj.locale("Minimized Widgets"),
					parent: sj.sys.process.getUniqueProgram("KeeWorld").getAir(),
					hashFocusable: false,
					onthemeload: sj.ui.widgets.minimizeHandler.onthemeload,
					onclose: sj.ui.widgets.minimizeHandler.onclose
				});
				sj.ui.widgets.minimizeHandler.wid = w.id;
			}
		},
		onclose: function() {
			sj.ui.widgets.minimizeHandler.wid = null;
		},
		hide: function() {
			if (sj.ui.widgets.minimizeHandler.wid != null) {
				var w = sj.ui.widgets.get(sj.ui.widgets.minimizeHandler.wid);
				if (w) w.close();
				sj.ui.widgets.minimizeHandler.wid = null;
			}
		},
		listView: null,
		onthemeload: function() {
			var t = this;
			t.setMinimizable(false);
			var ic = sj.dom.create("div", {
				style: "width:100%;height:26px",
				"class": "box horizontal cuib"
			});
			sj.dom.dataset.set(ic, "cuibackgroundid", "widget_panel");
			var favicon = sj.dom.create("div", {"class":"widget_favicon",style:"height:16px;width:16px;line-height:16px;vertical-align:middle;margin:5px"});
			ic.appendChild(favicon);
			var title = sj.dom.create("div", {"class":"widget_title flexer",style:"height:26px;line-height:26px"});
			ic.appendChild(title);
			t.show();
			t.set_size(230, 200);
			t.set_position(t.get_parent().offsetWidth - 240, t.get_parent().offsetHeight - 100);
			sj.require("ui.listview").addEventListener("success", function(llv){
				sj.ui.widgets.minimizeHandler.listView = new llv.exports.ListView({
					listContainer: sj.q.c(t.content, "content", true),
					itemContainers: {"0":ic},
					onrenderitem: sj.ui.widgets.minimizeHandler.onrenderitem
				});
				sj.js.each(sj.ui.widgets.getAll(), function() {
					if (this.minimized) sj.ui.widgets.minimizeHandler.listView.push({containerId:"0",data:this});
				});
			});
		},
		onrenderitem: function() {
			sj.q.c(this.container, "widget_title", true).innerHTML = this.data.__title;
			var img = new Image();
			img.src = this.data.favicon;
			img.width = 16;
			img.height = 16;
			sj.q.c(this.container, "widget_favicon", true).appendChild(img);
			this.container.setUserData("sj.ui.widgets.minimizeHandler.item", this, null);
			this.container.addEventListener("click", sj.ui.widgets.minimizeHandler.onitemclick, false);
			this.container.addEventListener("mouseover", sj.ui.widgets.minimizeHandler.onitemmouseover, false);
			this.container.addEventListener("mouseout", sj.ui.widgets.minimizeHandler.onitemmouseout, false);
			sj.compat.boxdisplay([this.container]);
		},
		onitemclick: function() {
			var i = this.getUserData("sj.ui.widgets.minimizeHandler.item");
			i.data.unminimize();
		},
		onitemmouseover: function() {
			sj.q.c(this, "widget_title", true).style.fontSize = ["solid 5px ", cui.getStyle("outline")].join("");
		},
		onitemmouseout: function() {
			sj.q.c(this, "widget_title", true).style.fontSize = "auto";
		}
	};
})();

sj.compat.boxdisplay = function(pl) {
	var $ = sj;
	for (var pi = 0, pll = pl.length; pi < pll; pi++) {
		var p = sj(pl[pi]);
		var orient = "horizontal";
		if (p.classList.contains("vertical")) orient = "vertical";
		var c = sj.dom.children(p);
		var l = c.length;
		var flexerCount = 0;
		var total = 0;
		if (orient == "horizontal") {
			for (var i = 0; i < l; i++) {
				var ci = c[i];
				if (ci.style.position == "absolute" || ci.style.position == "fixed") continue;
				ci.style[($.browser.name == "Explorer" ? "styleFloat" : "cssFloat")] = "left";
				ci.style.position = "relative";
				if (i == l - 1) ci.style.clear = "right";
				if (ci.style.display != "none") {
					var isflexer = sj(ci).getUserData("boxflexer");
					if (isflexer === true || (isflexer !== false && ci.classList.contains("flexer"))) {
						flexerCount++;
						if (!isflexer) ci.setUserData("boxflexer", true, null);
					} else if (!ci.getUserData("nobox")) {
						total += sj.dom.getBoxSize(ci, "width", 0, 3);
					}
				}
			}
			var pm = sj.dom.getBoxSize(p, "width", 0, 0);
			for (var i = 0; i < l; i++) {
				var ci = c[i];
				if (ci.style.position == "absolute" || ci.style.position == "fixed") continue;
				if (ci.getUserData("boxflexer")) {
					if (sj.dom.getBoxSize(ci, "height", 0, 3) <= 0) ci.style.height = "100%";
					var ww = (pm - sj.dom.getBoxSize(ci, "width", 1, 3) - total) / flexerCount;
					if (ww < 0) ww = 0;
					ci.style.width = [ww, "px"].join("");
				}
			}
		} else {
			for (var i = 0; i < l; i++) {
				var ci = c[i];
				if (ci.style.position == "absolute" || ci.style.position == "fixed") continue;
				if (ci.style.display != "none") {
					var isflexer = sj(ci).getUserData("boxflexer");
					if (isflexer === true || (isflexer !== false && ci.classList.contains("flexer"))) {
						flexerCount++;
						if (!isflexer) ci.setUserData("boxflexer", true, null);
					} else if (!ci.getUserData("nobox")) {
						total += sj.dom.getBoxSize(ci, "height", 0, 3);
					}
				}
			}
			var pm = sj.dom.getBoxSize(p, "height", 0, 0);
			for (var i = 0; i < l; i++) {
				var ci = c[i];
				if (ci.style.position == "absolute" || ci.style.position == "fixed") continue;
				if (ci.getUserData("boxflexer")) {
					if (sj.dom.getBoxSize(ci, "width", 0, 3) <= 0) ci.style.width = "100%";
					var hh = (pm - sj.dom.getBoxSize(ci, "height", 1, 3) - total) / flexerCount;
					if (hh < 0) hh = 0;
					ci.style.height = [hh, "px"].join("");
				}
			}
		}
		cui.recursiveRender(p, {type:"rerender",target:p});
	}
};

sj.compat.watcher = function(e) {
	var et = sj(e.target);
	if (et.nodeType == 1) {
		switch (sj.dom.dataset.get(et, "sjcompatwatches")) {
			case "chromeoverflow":
				if (navigator.userAgent.toLowerCase().indexOf("webkit") > -1) et.style.overflow = "scroll";
				break;
			default:
				break;
		}
		for (var i = 0; i < et.childNodes.length; i++) {
			sj.compat.watcher({target: et.childNodes[i]});
		}
	}
};

//document.addEventListener("DOMNodeInserted", sj.compat.watcher, false);

;(function(){
	var $ = sj;
	if (!$.ui) $.ui = {};
	
	var c = $.dom.create("div", {"class":"sj-dialog-container cuib"});
	$.dom.dataset.set(c, "cuibackgroundid", "widget_panel");
	var ct = $.dom.create("div", {"class":"sj-dialog"}, [
		$.dom.create("div", {"class":"cuib sj-dialog-title", "data-cuibackgroundid":"widget_header"}),
		$.dom.create("div", {"class":"sj-dialog-message"}),
		$.dom.create("div", {"class":"sj-dialog-buttons"})
	]);
	
	var cv = false;
	var counter = 0;
	
	function onButtonClick(e) {
		e.preventDefault();
		e.stopPropagation();
		
		var d = this.parentNode.parentNode;
		var c = d.parentNode;
		c.removeChild(d);
		
		if (--counter <= 0) {
			document.body.removeChild(c);
			cv = false;
		} else {
			cui.recursiveRender(c);
			$.css.center(c, true, true);
		}
		var f = this.getUserData("sj.ui.dialog.callback");
		f();
	}
	
	var k = {
		show: function(title, msg, cbs) {
			counter++;
			
			var d = ct.cloneNode(true);
			var t = null;
			
			if (title) $.dom.text($.q.c(d, "sj-dialog-title", true), title);
			else {
				t = $.q.c(d, "sj-dialog-title", true);
				t.parentNode.removeChild(t);
			}
			if (msg) $.dom.text($.q.c(d, "sj-dialog-message", true), msg);
			else {
				t = $.q.c(d, "sj-dialog-message", true);
				t.parentNode.removeChild(t);
			}
			
			t = $.q.c(d, "sj-dialog-buttons", true);
			
			for (var i = 0, l = cbs.length; i < l; i++) {
				var b = $($.dom.create("div", {"class": "cuib widget_button sj-dialog-button", "data-cuibackgroundid": "widget_button", "data-cuibackgroundevents": "mouseover,mousedown,mouseup,mouseout"}));
				$.dom.text(b, cbs[i][0]);
				b.setUserData("sj.ui.dialog.callback", cbs[i][1], null);
				b.addEventListener("click", onButtonClick, false);
				t.appendChild(b);
			}
			
			c.appendChild(d);
			
			if (!cv) {
				document.body.appendChild(c);
				cv = true;
			}
			cui.recursiveRender(c);
			$.css.center(c, true, true);
		}
	}
	$.modules.define("ui.dialog", k);
	sj.ui.dialog = k;
})();

;(function(){
	var $ = sj;
	if (!$.ui) $.ui = {};
	var k = {
		IndividualList: function(options) {
			options = $.js.extend({}, {
				timeout: null,
				parent: document.body,
				container: null,
				itemContainer: null,
				showAnimation: null,
				hideAnimation: null,
				addAnimation: null,
				removeAnimation: null
			}, options);
			var t = this;
			t.style = options.style;
			t.timeout = options.timeout;
			t.container = options.container;
			t.itemContainer = options.itemContainer || $.dom.create("div");
			t.parent = options.parent;
			t.showAnimation = options.showAnimation || null;
			t.hideAnimation = options.hideAnimation || null;
			if (t.hideAnimation) t.hideAnimation.addFinishListener(function() {
				t.container.parentNode.removeChild(t.container);
			});
			t.addAnimation = options.addAnimation || null;
			t.removeAnimation = options.removeAnimation || null;
		}
	};
	var IndividualListPrototype = function() {
		this.add = function(content, options) {
			options = $.js.extend({timeout: this.timeout}, options);
			var t = this;
			var style = t.style;
			var container = t.container;
			var parent = t.parent;
			var itemContainer = t.itemContainer;
			if (!container || !parent) return;
			
			var first = false;
			if (!container.parentNode) {
				first = true;
				var tStyle = $.js.extend({}, style);
				if (tStyle.left == "center") delete tStyle["left"];
				if (tStyle.top == "center") delete tStyle["top"];
				$.js.extend(container.style, tStyle);
				$.css.applyBorderRadius(container, style.borderRadius);
			}
			
			var id = t.idCounter++;
			if (typeof content === "string") content = document.createTextNode(content);
			var cc = $(itemContainer.cloneNode(true));
			cc.setUserData("ui.individuallist.contentId", id, null);
			if (cc.style) $.js.extend(cc.style, options.style);
			cc.appendChild(content);
			
			if (first) parent.appendChild(container);
			container.insertBefore(cc, container.childNodes[0] || null);
			if (style.left == "center") $.css.center(container, true, false);
			else container.style.left = style.left;
			if (style.top == "center") $.css.center(container, false, true);
			else container.style.top = style.top;
			if (first) {
				if (t.hideAnimation && t.hideAnimation.started) t.hideAnimation.stop();
				if (t.showAnimation) t.showAnimation.clearElements().addElement(container).start();
			}
			if (t.addAnimation) cc.setUserData("ui.individuallist.addanimation", (new $.html.Animation([cc], t.addAnimation)).start(), null);
			
			if (options.timeout) {
				(new $.timer.Timer({endTime: options.timeout})).addEventListener("endtime", function() {
					t.remove(id);
				}).start();
			}
			
			return id;
		};
		this.remove = function(id) {
			var t = this;
			var container = t.container;
			var style = t.style;
			if (!container) return;
			var c = $.dom.children(container);
			for (var i = 0, l = c.length; i < l; i++) {
				var ci = c[i];
				if (ci.getUserData("ui.individuallist.contentId") == id) {
					if (t.removeAnimation) {
						var aa = ci.getUserData("ui.individuallist.addanimation");
						if (aa && aa.started) aa.stop();
						(new $.html.Animation([ci], t.removeAnimation)).addFinishListener(function() {
							var rem = this.elements[0];
							rem.parentNode.removeChild(rem);
							if ($.dom.children(container).length == 0) {
								if (t.hideAnimation) {
									if (t.hideAnimation.started) t.hideAnimation.stop();
									t.hideAnimation.clearElements().addElement(container).start();
								} else container.parentNode.removeChild(container);
							} else {
								var style = t.style;
								if (style.left == "center") $.css.center(container, true, false);
								else container.style.left = style.left;
								if (style.top == "center") $.css.center(container, false, true);
								else container.style.top = style.top;
							}
						}).start();
					} else {
						ci.parentNode.removeChild(ci);
						if (l == 1) {
							if (t.hideAnimation) {
								if (t.hideAnimation.started) t.hideAnimation.stop();
								this.hideAnimation.clearElements().addElement(container).start();
							} else container.parentNode.removeChild(container);
						} else {
							var style = t.style;
							if (style.left == "center") $.css.center(container, true, false);
							else container.style.left = style.left;
							if (style.top == "center") $.css.center(container, false, true);
							else container.style.top = style.top;
						}
					}
					break;
				}
			}
		};
		this.length = 0;
		this.idCounter = 1;
		this.getContent = function(id) {
			var t = this;
			var container = t.container;
			if (!container) return;
			var c = $.dom.children(container);
			for (var i = 0, l = c.length; i < l; i++) {
				var ci = c[i];
				if (ci.getUserData("ui.individuallist.contentId") == id) return ci;
			}
			return null;
		};
	};
	k.IndividualList.prototype = new IndividualListPrototype();
	
	$.modules.define("ui.individuallist", k);
	sj.ui.individuallist = k;
})();

;(function(){
	var cui = new function() {
		this.options = {
			canvasBackgroundClass: "cuib"
		};
		this.init = function() {
			if (hw) {
				w = new Worker([sj.env.SITE_ROOT, "static/js/cuiWorker.js"].join(""));
				w.onerror = function(e) {
					alert("Error in cui worker");
				}
				w.onmessage = function(e) {
					var d = JSON.parse(e.data);
					for (var i in d) {
						if (!(i in uuids)) continue;
						var ui = uuids[i];
						ui.uvars = d[i];
						ui.base.renderer.apply(ui.base.renderer, ui.rendererArgs);
					}
				}
			}
		};
		var s = {};
		this.setStyle = function(k, o) {
			s[k] = o;
		};
		this.getStyle = function(k, c, x1, y1, x2, y2, r1, r2) {
			if (!(k in s)) return "#000000";
			var o = s[k];
			if (typeof(o) === "string") return o;
			if (typeof(o) === "number") return o;
			if (!o.type) o.type = "linear";
			if (typeof(x1) !== "number") x1 = 0;
			if (typeof(y1) !== "number") y1 = 0;
			if (typeof(x2) !== "number") x2 = 0;
			if (typeof(y2) !== "number") y2 = (c ? c.canvas.height : 0);
			if (typeof(r1) !== "number") r1 = 0;
			if (typeof(r2) !== "number") r2 = 0;
			var g = "#000000";
			switch (o.type) {
				case "linear":
					if (!c) return o.stops[0][1];
					g = c.createLinearGradient(x1, y1, x2, y2);
					for (var i = 0; i < o.stops.length; i++) {
						g.addColorStop(o.stops[i][0], o.stops[i][1]);
					}
					break;
				case "radial":
					if (!c) return o.stops[0][1];
					g = c.createRadialGradient(x1, y1, r1, x2, y2, r2);
					for (var i = 0; i < o.stops.length; i++) {
						g.addColorStop(o.stops[i][0], o.stops[i][1]);
					}
					break;
				case "pattern":
					if (!c) return "#000000";
					if (typeof(o.repetition) !== "string") o.repetition = "repeat";
					g = c.createPattern(o.image, o.repetition);
					break;
				default:
					break;
			}
			return g;
		};
		var u = {};
		var ul = 0;
		var ut = null;
		var interval = 25;
		var uuids = {};
		var uuidsLength = 0;
		var hw = typeof(Worker) !== "undefined";
		var w = null;
		
		this.setVariableUpdater = function(k, o) {
			if (typeof(o.period) !== "number") o.period = interval;
			u[k] = o;
			if (hw) w.postMessage(JSON.stringify({cmd:"setVariableUpdater",id:k,options:sj.js.extend({}, o, {renderer:null,rendererArgs:[]})}));
		};
		this.removeVariableUpdater = function(k) {
			if (!(k in u)) return;
			if (hw) {
				w.postMessage(JSON.stringify({cmd:"removeVariableUpdater",id:k}));
			} else {
				if (ul == 1 && ut != null) {
					clearInterval(ut);
					ut = null;
				}
			}
			for (var i in uuids) {
				if (uuids[i].base === u[k]) delete uuids[i];
			}
			delete u[k];
		};
		this.startVariableUpdater = function(k, args) {
			if (!(k in u)) return null;
			var uuid = "" + (new Date().getTime() + Math.random());
			uuids[uuid] = {};
			if (hw) {
				w.postMessage(JSON.stringify({
					cmd: "startVariableUpdater",
					uuid: uuid,
					id: k
				}));
			} else {
				var o = u[k];
				uuids[uuid] = {
					base: o,
					vars: {},
					uvars: {},
					active: true,
					timer: 0
				};
				uuidsLength++;
				for (var v in o.vars) {
					uuids[uuid].vars[v] = {
						active: true,
						step: o.vars[v].step
					};
					uuids[uuid].uvars[v] = o.vars[v].base;
				}
				if (ut == null) ut = setInterval(update, interval);
			}
			uuids[uuid].base = u[k];
			if (!sj.js.isArray(args)) args = [];
			uuids[uuid].rendererArgs = (typeof(args) === "undefined" ? u[k].rendererArgs : args);
			return uuid;
		};
		this.stopVariableUpdater = function(k) {
			if (!(k in uuids)) return null;
			if (hw) {
				w.postMessage(JSON.stringify({cmd:"stopVariableUpdater",uuid:k}));
			} else {
				if (uuidsLength == 1 && ut != null)  {
					clearInterval(ut);
					ut = null;
				}
			}
			uuidsLength--;
			delete uuids[k];
		};
		this.getDefaultVariables = function(k) {
			if (!(k in u)) return null;
			var v = {};
			for (var i in u[k].vars) {
				v[i] = u[k].vars[i].base;
			}
			return v;
		};
		this.getVariables = function(k) {
			if (!(k in uuids)) return null;
			var uk = uuids[k];
			var r = uk.uvars;
			if (!r) {
				r = {};
				for (var i in uk.base.vars) {
					r[i] = uk.base.vars[i].base;
				}
			}
			return r;
		};
		this.pauseVariableUpdater = function(k) {
			if (!(k in uuids)) return;
			uuids[k].active = false;
			if (hw) w.postMessage(JSON.stringify({cmd:"pauseVariableUpdater",uuid:k}));
		};
		this.resumeVariableUpdater = function(k) {
			if (!(k in uuids)) return;
			uuids[k].active = true;
			if (hw) w.postMessage(JSON.stringify({cmd:"resumeVariableUpdater",uuid:k}));
		};
		var updating = false;
		var timePassed = 0;
		function update() {
			timePassed += interval;
			if (updating) return;
			updating = true;
			for (var k in uuids) {
				var o = uuids[k];
				var uo = o.base;
				if (!o.active) continue;
				o.timer += timePassed;
				if (o.timer < uo.period) continue;
				o.timer = 0;
				var dirty = false;
				for (var v in uo.vars) {
					var vo = uo.vars[v];
					if (!o.vars[v].active) continue;
					var cval = o.uvars[v];
					cval += o.vars[v].step;
					if (typeof(vo.max) === "number" && cval >= vo.max) {
						cval = vo.max;
						if (!vo.limitAction || vo.limitAction == "stop") {
							o.vars[v].active = false;
						} else if (vo.limitAction == "repeat") {
							cval = vo.base;
						} else if (vo.limitAction == "reverse") {
							o.vars[v].step = -o.vars[v].step;
						}
					} else if (typeof(vo.min) === "number" && cval <= vo.min) {
						cval = vo.min;
						if (!vo.limitAction || vo.limitAction == "stop") {
							o.vars[v].active = false;
						} else if (vo.limitAction == "repeat") {
							cval = vo.base;
						} else if (vo.limitAction == "reverse") {
							o.vars[v].step = -o.vars[v].step;
						}
					}
					o.uvars[v] = cval;
					dirty = true;
				}
				if (dirty) uo.renderer.apply(uo.renderer, o.rendererArgs);
			}
			timePassed = 0;
			updating = false;
		}
		this.events = {
			DOMNodeInserted: function(e) {
				if (e.target.nodeType == 1) cui.recursiveRender(e.target, e);
			}
		};
		this.eventRender = function(e) {
			var c = this.getUserData(["cui.background.events.", e.type, "nocache"].join(""));
			if (typeof(c) !== "boolean") c = false;
			cui.render(this, sj.dom.dataset.get(this, "cuibackgroundid"), e, !c);
		};
		this.nodeInsertedCompat = function(t) {
			if (sj.browser.name == "Explorer") cui.recursiveRender(t, {type:"DOMNodeInserted",target:t});
		};
		this.recursiveRender = function(t, e) {
			sj(t);
			if (!e) e = {type:"render"};
			if (!e.target) e.target = t;
			var ch = t.getElementsByClassName(cui.options.canvasBackgroundClass);
			for (var i = ch.length; i >= 0; i--) {
				var pe = e;
				var chi = null;
				if (i == ch.length) {
					chi = t;
				} else {
					chi = ch[i];
				}
				var id = sj.dom.dataset.get(chi, "cuibackgroundid");
				if (id) {
					if (e.type == "DOMNodeInserted" && !sj(chi).getUserData("cui.background.initialized")) {
						var events = sj.dom.dataset.get(chi, "cuibackgroundevents");
						if (events) {
							var es = events.split(",");
							for (var j = 0; j < es.length; j++) {
								var esc = es[j].split(":");
								var el = esc.length;
								if (el > 1) {
									for (var k = 0; k < el; k++) {
										if (esc[k] == "nocache") chi.setUserData(["cui.background.events.", esc[0], "nocache"].join(""), true, null);
									}
								}
								chi.addEventListener(esc[0], cui.eventRender, true);
							}
						}
						pe = {type:"render",target:chi};
						chi.setUserData("cui.background.initialized", true, null);
					}
					cui.render(chi, id, pe);
				}
			}
		};
		this.render = function(t, id, event, useCache) {
			if (!id) id = sj.dom.dataset.get(t, "cuibackgroundid");
			var r = cui.getBackground(id);
			if (r) {
				sj(t);
				var w = sj.dom.getBoxSize(t, "width", 0, 1);
				var h = sj.dom.getBoxSize(t, "height", 0, 1);
				if (w == 0 && h == 0) {
					var p = t;
					if (p.parentNode == null) return;
					while (p != p.parentNode && p != document.documentElement && (typeof HTMLDocument === "undefined" || !(p instanceof HTMLDocument))) {
						var cs = sj.dom.computedStyle(p);
						if (cs && (cs["display"] == "none" || cs["visibility"] == "hidden")) return;
						p = p.parentNode;
						if (!p) return;
					}
					setTimeout(cui.render, 20, t, id, event, useCache);
					return;
				} else if (w == 0 || h == 0) {
					return;
				}
				if (!event) event = {type:"render"};
				if (event.type == "render") event.target = t;
				var c = null;
				var tc = t.childNodes;
				var l = tc.length;
				for (var i = 0; i < l; i++) {
					var tci = tc[i];
					if (tci.nodeName.toLowerCase() == "canvas" && tci.getUserData("cbc")){
						c = tci;
						break;
					}
				}
				var cs = sj.dom.computedStyle(t);
				if (c === null) {
					c = sj.dom.create("canvas");
					c.setUserData("cbc", true, null);
					c.setUserData("nobox", true, null);
					c.style.display = "block";
					c.width = w;
					c.height = h;
					c.style.marginRight = [-w - parseInt(cs.borderRightWidth), "px"].join("");
					c.style.marginLeft = [-parseInt(cs.paddingLeft), "px"].join("");
					c.style.marginBottom = [-h - parseInt(cs.borderBottomWidth), "px"].join("");
					c.style.marginTop = [-parseInt(cs.paddingTop), "px"].join("");
					if (sj.browser.name == "Explorer") {
						for (var i = 0; i < l; i++) {
							var tci = tc[i];
							// Z-Order fixing for IE. Making all the canvases siblings relative will create a new order context.
							if (tci.nodeType == 1) {
								if (!tci.style.position || tci.style.position == "static") tci.style.position = "relative";
							} else if (tci.nodeType == 3) {
								var span = document.createElement("span");
								span.innerText = tci.nodeValue;
								span.style.position = "relative";
								tci.replaceNode(span);
							}
						}
					}
					if (l > 0) t.insertBefore(c, tc[0]);
					else t.insertBefore(c, null);
				} else {
					c.style.marginLeft = [-parseInt(cs.paddingLeft), "px"].join("");
					c.style.marginRight = [-w - parseInt(cs.borderRightWidth), "px"].join("");
					c.width = w;
					c.height = h;
					c.style.marginBottom = [-h - parseInt(cs.borderBottomWidth), "px"].join("");
					c.style.marginTop = [-parseInt(cs.paddingTop), "px"].join("");
				}
				if (event.type == "rerender") event = c.getUserData(["cui.lastevent.", id].join("")) || {type:"render",target:t};
				else c.setUserData(["cui.lastevent.", id].join(""), event, null);
				var ctx = c.getContext("2d");
				r.call(t, ctx, event, id);
			}
		};
		var bgl = {};
		this.getBackground = function(id) {
			return bgl[id];
		};
		this.setBackground = function(id, renderer) {
			bgl[id] = renderer;
		};
		var lr = {};
		this.setLayerRenderer = function(id, renderer) {
			lr[id] = renderer;
		};
		this.removeLayerRenderer = function(id) {
			if (id in lr) delete lr[id];
		};
		var stacks = {};
		this.setStack = function(id, layers) {
			stacks[id] = sj.js.each(layers, function() {
				return {
					lid: this.id,
					alpha: (typeof(this.alpha) === "number" ? this.alpha : 1.0),
					compositeOperation: (typeof(this.compositeOperation) === "string" ? this.compositeOperation : "source-over"),
					dirty: true,
					cache: null
				};
			});
		};
		this.removeStack = function(id) {
			if (id in stacks) delete stacks[id];
		}
		this.setStackLayerDirty = function(id, i) {
			if (id in stacks && i in stacks[id]) stacks[id][i].dirty = true;
		};
		this.setStackLayerClean = function(id, i) {
			if (id in stacks && i in stacks[id]) stacks[id][i].dirty = false;
		};
		var stackIsRendering = false;
		this.renderStack = function(id, cw, height) {
			if (!(id in stacks)) return false;
			if (stackIsRendering) return;
			stackIsRendering = true;
			var hc = (typeof(cw) !== "number" ? true : false);
			if (typeof(cw) === "number") cw = sj.dom.create("canvas", {width:cw,height:height});
			var s = stacks[id];
			var cc = cw.getContext("2d");
			for (var i = 0; i < s.length; i++) {
				var si = s[i];
				if (!si.cache || si.dirty == true) {
					if (!si.cache) si.cache = sj.dom.create("canvas", {width:cw.width,height:cw.height});
					lr[si.lid](si.cache.getContext("2d"));
					si.dirty = false;
				}
				cc.save();
				cc.globalAlpha = si.alpha;
				cc.globalCompositeOperation = si.compositeOperation;
				cc.drawImage(si.cache, 0, 0);
				cc.restore();
			}
			stackIsRendering = false;
			if (!hc) return cw.toDataURL("image/png");
		};
	}();
	document.addEventListener("DOMNodeInserted", cui.events.DOMNodeInserted, false);
	sj.modules.define("ui.cui", cui);
	window.cui = cui;
})();
	
(function($){
	var k = {
		defaultStyle: {
			position: "absolute",
			zIndex: 999999
		},
		Tooltip: function(options) {
			var t = this;
			t.options = options = $.js.extend({
				timeout: null,
				x: 0,
				y: -20,
				baseX: "mouse",
				baseY: "mouse",
				followMouse: true,
				content: null,
				parent: document,
				showAnimation: {
					"opacity": {
						from: 0,
						to: 1,
						dur: 200
					},
					"padding": {
						from: "0px",
						dur: 200
					}
				},
				hideAnimation: {
					"opacity": {
						from: 1,
						to: 0,
						dur: 200
					},
					"padding": {
						to: "0px",
						dur: 200
					}
				}
			}, options);
			t.style = $.js.extend({}, k.defaultStyle, options.style);
			$.js.objectMixin(t, $.events.EventTarget);
			var content = options.content;
			if (content && options.showAnimation) {
				t.showAnimation = new $.html.Animation([content], options.showAnimation);
			}
			if (content && options.hideAnimation) {
				t.hideAnimation = new $.html.Animation([content], options.hideAnimation);
			}
			var showTrigger = options.showTrigger;
			if (showTrigger) {
				for (var i in showTrigger) {
					var sti = showTrigger[i];
					if (typeof i === "string") i = options.parent;
					$(i).addEventListener(sti, function(e){t.show(e)}, false);
				}
			}
			var hideTrigger = options.hideTrigger;
			if (hideTrigger) {
				for (var i in hideTrigger) {
					var hti = hideTrigger[i];
					if (typeof i === "string") i = options.parent;
					$(i).addEventListener(hti, function(e){t.hide(e)}, false);
				}
			}
		}
	};
	var TooltipPrototype = function() {
		this.show = function(e) {
			var t = this;
			var o = t.options;
			var content = o.content;
			if (typeof e === "string") content = document.createTextNode(e);
			if (!content) return t;
			t.dispatchEvent({type: "show", triggerEvent: e, target: t});
			t.position();
			$.js.extend(content.style, t.style);
			if (!content.parentNode || content.parentNode.nodeName == "#document-fragment") document.body.appendChild(content);
			if (!t.showAnimation && o.showAnimation) t.showAnimation = new $.html.Animation([content], o.showAnimation);
			if (o.followMouse) {
				t.followMouseHandler = function() {
					t.position();
				};
				document.addEventListener("mousemove", t.followMouseHandler, false);
			}
			if (t.hideAnimation && t.hideAnimation.started) t.hideAnimation.pause();
			if (t.showAnimation) t.showAnimation.reset().start();
			if (o.timeout) {
				(new $.timer.Timer({
					endTime: o.timeout
				})).addEventListener("endtime", function(e, pass) {
					pass.hide();
				}, t).start();
			}
			return t;
		};
		var removeTooltip = function(t) {
			var content = t.options.content;
			if (!content || !content.parentNode) return;
			content.parentNode.removeChild(content);
		};
		this.hide = function(e) {
			var t = this;
			var o = t.options;
			if (t.followMouseHandler) document.removeEventListener("mousemove", t.followMouseHandler, false);
			var content = o.content;
			if (!content) return t;
			t.dispatchEvent({type: "hide", triggerEvent: e, target: t});
			if (!t.hideAnimation && o.hideAnimation) t.hideAnimation = new $.html.Animation([content], o.hideAnimation);
			if (t.showAnimation && t.showAnimation.started) t.showAnimation.pause();
			if (t.hideAnimation) t.hideAnimation.addFinishListener(removeTooltip, t).reset().start();
			else removeTooltip(t);
			return t;
		};
		this.position = function() {
			var o = this.options;
			var content = o.content;
			if (!content) return this;
			var pd = $.env.POINTER_DATA;
			if (!pd) return this;
			var baseX = o.baseX;
			var baseY = o.baseY;
			if (baseX == "mouse") baseX = pd.clientX;
			else if (baseX == "parent") baseX = $.dom.fullOffset(o.parent, "Left");
			else if (typeof baseX !== "number") baseX = 0;
			if (baseY == "mouse") baseY = pd.clientY;
			else if (baseY == "parent") baseY = $.dom.fullOffset(o.parent, "Top");
			else if (typeof baseY !== "number") baseY = 0;
			
			if (baseX < 0) baseX = 0;
			else if (content.offsetWidth + baseX > sj.html.window.innerWidth() + sj.html.window.pageXOffset()) baseX = sj.html.window.innerWidth() + sj.html.window.pageXOffset() - content.offsetWidth;
			if (baseY < 0) baseY = 0;
			else if (content.offsetHeight + baseY > sj.html.window.innerHeight() + sj.html.window.pageYOffset()) baseY = sj.html.window.innerHeight() + sj.html.window.pageYOffset() - content.offsetHeight;
			
			content.style.left = [baseX + o.x, "px"].join("");
			content.style.top = [baseY + o.y, "px"].join("");
		};
	};
	k.Tooltip.prototype = new TooltipPrototype();
	$.modules.define("ui.tooltip", k);
	$.ui.tooltip = k;
})(sj);

;(function($){
	var osdCounter = 0;
	var osdHandle = null;
	var osdFunction = function() {
		if (osdHandle != null && --osdCounter <= 0) {
			$.ui.osd.remove(osdHandle);
			osdHandle = null;
			osdCounter = 0;
		}
	};
	
	var k = {
		get: function(app, keys, values) {
			if ($.ui.osd) {
				if (osdHandle == null) osdHandle = $.ui.osd.add(document.createTextNode($.locale("Working with settings...")));
				osdCounter++;
			}
			var data = {};
			if (keys) data.k = keys;
			if (values) data.v = values;
			var delay = $.http.send({
				url: [$.env.SITE_ROOT, "api/apps/data/", app, "/"].join(""),
				method: "GET",
				timeout: 15000,
				cache: false,
				queryData: data
			});
			if (osdHandle != null) delay.addEventListener("success", osdFunction).addEventListener("error", osdFunction);
			return delay;
		},
		create: function(app, o) {
			if ($.ui.osd) {
				if (osdHandle == null) osdHandle = $.ui.osd.add(document.createTextNode($.locale("Working with settings...")));
				osdCounter++;
			}
			var delay = $.http.send({
				dataType: "json",
				url: [$.env.SITE_ROOT, "api/apps/data/", app, "/"].join(""),
				method: "POST",
				timeout: 15000,
				cache: false,
				processData: false,
				contentType: "json",
				data: JSON.stringify(o)
			});
			if (osdHandle != null) delay.addEventListener("success", osdFunction).addEventListener("error", osdFunction);
			return delay;
		},
		update: function(app, o) {
			if ($.ui.osd) {
				if (osdHandle == null) osdHandle = $.ui.osd.add(document.createTextNode($.locale("Working with settings...")));
				osdCounter++;
			}
			var delay = $.http.send({
				dataType: "json",
				url: [$.env.SITE_ROOT, "api/apps/data/", app, "/"].join(""),
				method: "PUT",
				timeout: 15000,
				cache: false,
				processData: false,
				contentType: "json",
				data: JSON.stringify(o)
			});
			if (osdHandle != null) delay.addEventListener("success", osdFunction).addEventListener("error", osdFunction);
			return delay;
		},
		remove: function(app, keys, values) {
			if ($.ui.osd) {
				if (osdHandle == null) osdHandle = $.ui.osd.add(document.createTextNode($.locale("Working with settings...")));
				osdCounter++;
			}
			var data = {};
			if (keys) data.k = keys;
			if (values) data.v = values;
			var delay = $.http.send({
				url: [$.env.SITE_ROOT, "api/apps/data/", app, "/"].join(""),
				method: "DELETE",
				timeout: 15000,
				cache: false,
				queryData: data
			});
			if (osdHandle != null) delay.addEventListener("success", osdFunction).addEventListener("error", osdFunction);
			return delay;
		}
	};
	
	$.modules.define("program.settings", k);
})(sj);

;(function($){
	var k = {
		rpcUrl: "/rpc/",
		Request: function(method, params, httpMethod, httpParams) {
			if (typeof method === "object") {
				
			} else {
				this.method = method;
				this.params = params;
				if (httpMethod) this.httpMethod = httpMethod;
				if (httpParams) this.httpParams = httpParams;
			}
		},
		BatchRequest: function() {
			this.requests = {};
		},
		newBatch: function() {
			return new k.BatchRequest();
		}
	};
	
	var requestSuccess = function(d, callback) {
		if (callback) callback(d.data);
	};
	
	var batchRequestSuccess = function(d, callback) {
		if (callback) {
			d = d.data;
			if (d.error) callback(d);
			else {
				var r = {};
				d = d.result;
				for (var i = 0, l = d.length; i < l; i++) {
					var di = d[i];
					r[di.id] = di.result;
				}
				callback(r);
			}
		}
	};
	
	k.Request.prototype = {
		execute: function(callback) {
			return $.http.send({
				url: k.rpcUrl,
				contentType: "json",
				method: this.httpMethod,
				data: {
					id: "_" + (new Date()).getTime(),
					method: this.method,
					params: this.params
				}
			}).addEventListener("success", requestSuccess, callback);
		},
		method: null,
		params: null,
		httpMethod: "POST"
	};
	k.BatchRequest.prototype = {
		execute: function(callback) {
			var r = [];
			var tr = this.requests;
			for (var i in tr) {
				var tri = tr[i];
				r.push({
					id: i,
					method: tri.method,
					params: tri.params
				});
			} 
			return $.http.send({
				url: k.rpcUrl,
				contentType: "json",
				method: "POST",
				data: r
			}).addEventListener("success", batchRequestSuccess, callback);
		},
		add: function(key, request) {
			this.requests[key] = request;
			return this;
		}
	}
	
	k.http = {
		head: function(params) {
			return new k.Request(params);
		},
		get: function(params) {
			return new k.Request(params);
		},
		post: function(params) {
			return new k.Request(params);
		},
		put: function(params) {
			return new k.Request(params);
		},
		"delete": function(params) {
			return new k.Request(params);
		}
	};
	
	$.modules.define("osapi", k);
	osapi = k;
})(sj);

(function($){
	var k = {
		ListView: function(o) {
			var items = [];
			
			this.get = function(i) {
				if (i in items) return items[i];
			};
			this.push = function() {
				var t = this;
				var a = arguments;
				var l = items.length;
				$.js.each(a, function(i){
					$.js.extend(this, {
						container: null,
						dirty: true,
						parent: t,
						index: l + i
					});
				});
				var r = items.push.apply(items, a);
				this.length = items.length;
				t.renderItems();
				return r;
			};
			this.pop = function() {
				var i = this.get(this.length - 1);
				if (i && i.container) i.container.parentNode.removeChild(i.container);
				var r = items.pop.apply(items, arguments);
				this.length = items.length;
				return r;
			};
			this.splice = function(idx, h) {
				var t = this;
				if (h > 0) {
					var l = t.length;
					for (var i = idx; i < idx + h && i < l; i++) {
						var it = t.get(i);
						var c = it.container;
						if (c) c.parentNode.removeChild(c);
					}
				}
				var a = arguments;
				var l = items.length;
				$.js.each(a, function(i){
					if (i > 1) {
						$.js.extend(this, {
							container: null,
							dirty: true,
							parent: t,
							index: l + i
						});
					}
				});
				var r = items.splice.apply(items, arguments);
				this.length = items.length;
				t.dirtyAllItems();
				t.renderItems();
				return r;
			};
			this.indexOf = function() {
				return items.indexOf.apply(items, arguments);
			};
			this.reverse = function() {
				return items.reverse.apply(items, arguments);
			};
			this.shift = function() {
				return items.shift.apply(items, arguments);
			};
			this.unshift = function() {
				return items.unshift.apply(items, arguments);
			};
			this.slice = function() {
				return items.slice.apply(items, arguments);
			};
			this.sort = function() {
				return items.sort.apply(items, arguments);
			};
			this.clear = function() {
				if (this.options.listContainer) $.dom.emptyNode(this.options.listContainer);
				items = [];
				this.length = items.length;
			};
			this.length = items.length;
			
			this.options = $.js.extend(
				{
					listContainer: null,
					itemsSelectable: false,
					itemsSelectionMethod: "checkbox",
					itemContainers: {},
					itemShowAnimation: [{attr:"opacity",dur:100,from:"0",to:"1"}],
					onrenderitem: null
				},
				o
			);
		}
	};
	k.ListView.prototype.renderItem = function(i) {
		var item = this.get(i);
		if (!item) return this;
		var o = this.options;
		if (!item.containerId) {
			for (var id in o.itemContainers) {
				item.containerId = id;
				break;
			}
		}
		if (!(item.containerId in o.itemContainers) || !o.itemContainers[item.containerId].cloneNode) return this;
		item.container = o.itemContainers[item.containerId].cloneNode(true);
		
		if (o.listContainer) {
			var added = false;
			var children = $.dom.children(o.listContainer);
			if (i > children.length - 1) o.listContainer.appendChild(item.container);
			else o.listContainer.replaceChild(item.container, children[i]);
			if (o.onrenderitem) o.onrenderitem.call(item);
			if (o.itemShowAnimation) {
				var toi = o.itemShowAnimation;
				var l = toi.length;
				for (var i = 0; i < l; i++) {
					var opts = {};
					opts[toi[i].attr] = {
						dur: toi[i].dur,
						from: toi[i].from,
						to: toi[i].to,
						finish: "freeze"
					};
					(new $.html.Animation(item.container, opts)).start();
				}
			}
		}
		item.dirty = false;
		
		return this;
	};
	k.ListView.prototype.renderItems = function() {
		var l = this.length;
		for (var i = 0; i < l; i++) {
			var it = this.get(i);
			if (it.dirty) this.renderItem(i);
		}
		return this;
	};
	k.ListView.prototype.dirtyAllItems = function() {
		var l = this.length;
		for (var i = 0; i < l; i++) {
			var it = this.get(i);
			it.dirty = true;
		}
		return this;
	}
	
	$.modules.define("ui.listview", k);
})(sj);

;(function(){
	var $ = sj;
	if (!$.ui) $.ui = {};
	
	var c = $.dom.create("div", {"class":"sj-dialog-container"});
	var ct = $.dom.create("div", {"class":"sj-dialog"}, [
		$.dom.create("div", {"class":"sj-dialog-title"}),
		$.dom.create("div", {"class":"sj-dialog-message"}),
		$.dom.create("div", {"class":"sj-dialog-buttons"})
	]);
	
	var cv = false;
	var counter = 0;
	
	function onButtonClick(e) {
		e.preventDefault();
		e.stopPropagation();
		
		var d = this.parentNode.parentNode;
		var c = d.parentNode;
		c.removeChild(d);
		
		if (--counter <= 0) {
			document.body.removeChild(c);
			cv = false;
		} else {
			$.css.center(c, true, true);
		}
		var f = this.getUserData("sj.ui.dialog.callback");
		f();
	}
	
	var k = {
		show: function(title, msg, cbs) {
			counter++;
			
			var d = ct.cloneNode(true);
			var t = null;
			
			if (title) $.dom.text($.q.c(d, "sj-dialog-title", true), title);
			else {
				t = $.q.c(d, "sj-dialog-title", true);
				t.parentNode.removeChild(t);
			}
			if (msg) $.dom.text($.q.c(d, "sj-dialog-message", true), msg);
			else {
				t = $.q.c(d, "sj-dialog-message", true);
				t.parentNode.removeChild(t);
			}
			
			t = $.q.c(d, "sj-dialog-buttons", true);
			
			for (var i = 0, l = cbs.length; i < l; i++) {
				var b = $($.dom.create("div", {"class": "button sj-dialog-button"}));
				$.dom.text(b, cbs[i][0]);
				b.setUserData("sj.ui.dialog.callback", cbs[i][1], null);
				b.addEventListener("click", onButtonClick, false);
				t.appendChild(b);
			}
			
			c.appendChild(d);
			
			if (!cv) {
				document.body.appendChild(c);
				cv = true;
			}
			$.css.center(c, true, true);
		}
	}
	$.modules.define("ui.dialog", k);
	sj.ui.dialog = k;
})();


