﻿(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 ($.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 = 0.75;
    $.env = {
        SITE_ROOT: "/",
        JSON_URL: "/static/js/json2.js",
        PROGRAM_ROOT: "/static/apps/",
        MODULE_ROOT: "/static/modules/",
        JSON_REQUIRED: false
    };

    $.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, "Images/Common/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 (!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 (!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 = [];
    $.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);
    };
    $.compat.node.hasAttributeNS = function(ns, attr) {
        return this.hasAttribute(attr);
    };
    $.compat.node.setAttributeNS = function(ns, attr, value) {
        return this.setAttribute(attr, value);
    };
    $.compat.node.getAttributeNS = function(ns, attr) {
        return this.getAttribute(attr);
    };
    // 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 {
                    while (ci.parentNode != this) {
                        if (ci.parentNode.nextSibling) {
                            ci = ci.parentNode.nextSibling;
                            break;
                        } else ci = ci.parentNode;
                    }
                    if (ci.parentNode == this) break;
                }
            } else {
                if (ci.nextSibling) ci = ci.nextSibling;
                else {
                    while (ci.parentNode != this) {
                        if (ci.parentNode.nextSibling) {
                            ci = ci.parentNode.nextSibling;
                            break;
                        } else ci = ci.parentNode;
                    }
                    if (ci.parentNode == this) 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)
                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",
			"borderTop",
			"borderBottom",
			"marginTop",
			"marginBottom"
		],
        "width": [
			"paddingLeft",
			"paddingRight",
			"borderLeft",
			"borderRight",
			"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) {
                try {
                    if (t.dataset && (typeof t.dataset[k] != 'undefined')) {
                        return t.dataset[k];
                    }
                }
                catch (err) {
                    // Internet Explorer might fail
                }
                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") r.className = curAttr;
                        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;
            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.getAttribute("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) {
                        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 : 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 : 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.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 ? v1u : v0u);
                            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 $.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();

    $.console = global.console || {
        logBuffer: [],
        log: function() {
            var l = arguments.length;
            for (var i = 0; i < l; i++) {
                $.console.logBuffer.push(arguments[i].toString());
            }
        },
        debugBuffer: [],
        debug: function() {
            var l = arguments.length;
            for (var i = 0; i < l; i++) {
                $.console.debugBuffer.push(arguments[i].toString());
            }
        },
        warnBuffer: [],
        warn: function() {
            var l = arguments.length;
            for (var i = 0; i < l; i++) {
                $.console.warnBuffer.push(arguments[i].toString());
            }
        },
        errorBuffer: [],
        error: function() {
            var l = arguments.length;
            for (var i = 0; i < l; i++) {
                $.console.errorBuffer.push(arguments[i].toString());
            }
        }
    };

    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(){
	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;
				$.js.extend(container.style, style);
				$.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 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);

