/* Modernizr 2.8.3 (Custom Build) | MIT & BSD
 * Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-flexboxlegacy-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-cssclasses-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-blob_constructor-canvas_todataurl_type-cors-json-url_data_uri-load
 */
; window.Modernizr = function (a, b, c) { function C(a) { j.cssText = a } function D(a, b) { return C(n.join(a + ";") + (b || "")) } function E(a, b) { return typeof a === b } function F(a, b) { return !!~("" + a).indexOf(b) } function G(a, b) { for (var d in a) { var e = a[d]; if (!F(e, "-") && j[e] !== c) return b == "pfx" ? e : !0 } return !1 } function H(a, b, d) { for (var e in a) { var f = b[a[e]]; if (f !== c) return d === !1 ? a[e] : E(f, "function") ? f.bind(d || b) : f } return !1 } function I(a, b, c) { var d = a.charAt(0).toUpperCase() + a.slice(1), e = (a + " " + p.join(d + " ") + d).split(" "); return E(b, "string") || E(b, "undefined") ? G(e, b) : (e = (a + " " + q.join(d + " ") + d).split(" "), H(e, b, c)) } function J() { e.input = function (c) { for (var d = 0, e = c.length; d < e; d++) u[c[d]] = c[d] in k; return u.list && (u.list = !!b.createElement("datalist") && !!a.HTMLDataListElement), u }("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")), e.inputtypes = function (a) { for (var d = 0, e, f, h, i = a.length; d < i; d++) k.setAttribute("type", f = a[d]), e = k.type !== "text", e && (k.value = l, k.style.cssText = "position:absolute;visibility:hidden;", /^range$/.test(f) && k.style.WebkitAppearance !== c ? (g.appendChild(k), h = b.defaultView, e = h.getComputedStyle && h.getComputedStyle(k, null).WebkitAppearance !== "textfield" && k.offsetHeight !== 0, g.removeChild(k)) : /^(search|tel)$/.test(f) || (/^(url|email)$/.test(f) ? e = k.checkValidity && k.checkValidity() === !1 : e = k.value != l)), t[a[d]] = !!e; return t }("search tel url email datetime date month week time datetime-local number range color".split(" ")) } var d = "2.8.3", e = {}, f = !0, g = b.documentElement, h = "modernizr", i = b.createElement(h), j = i.style, k = b.createElement("input"), l = ":)", m = {}.toString, n = " -webkit- -moz- -o- -ms- ".split(" "), o = "Webkit Moz O ms", p = o.split(" "), q = o.toLowerCase().split(" "), r = { svg: "http://www.w3.org/2000/svg" }, s = {}, t = {}, u = {}, v = [], w = v.slice, x, y = function (a, c, d, e) { var f, i, j, k, l = b.createElement("div"), m = b.body, n = m || b.createElement("body"); if (parseInt(d, 10)) while (d--) j = b.createElement("div"), j.id = e ? e[d] : h + (d + 1), l.appendChild(j); return f = ["&#173;", '<style id="s', h, '">', a, "</style>"].join(""), l.id = h, (m ? l : n).innerHTML += f, n.appendChild(l), m || (n.style.background = "", n.style.overflow = "hidden", k = g.style.overflow, g.style.overflow = "hidden", g.appendChild(n)), i = c(l, a), m ? l.parentNode.removeChild(l) : (n.parentNode.removeChild(n), g.style.overflow = k), !!i }, z = function () { function d(d, e) { e = e || b.createElement(a[d] || "div"), d = "on" + d; var f = d in e; return f || (e.setAttribute || (e = b.createElement("div")), e.setAttribute && e.removeAttribute && (e.setAttribute(d, ""), f = E(e[d], "function"), E(e[d], "undefined") || (e[d] = c), e.removeAttribute(d))), e = null, f } var a = { select: "input", change: "input", submit: "form", reset: "form", error: "img", load: "img", abort: "img" }; return d }(), A = {}.hasOwnProperty, B; !E(A, "undefined") && !E(A.call, "undefined") ? B = function (a, b) { return A.call(a, b) } : B = function (a, b) { return b in a && E(a.constructor.prototype[b], "undefined") }, Function.prototype.bind || (Function.prototype.bind = function (b) { var c = this; if (typeof c != "function") throw new TypeError; var d = w.call(arguments, 1), e = function () { if (this instanceof e) { var a = function () { }; a.prototype = c.prototype; var f = new a, g = c.apply(f, d.concat(w.call(arguments))); return Object(g) === g ? g : f } return c.apply(b, d.concat(w.call(arguments))) }; return e }), s.flexbox = function () { return I("flexWrap") }, s.flexboxlegacy = function () { return I("boxDirection") }, s.canvas = function () { var a = b.createElement("canvas"); return !!a.getContext && !!a.getContext("2d") }, s.canvastext = function () { return !!e.canvas && !!E(b.createElement("canvas").getContext("2d").fillText, "function") }, s.webgl = function () { return !!a.WebGLRenderingContext }, s.touch = function () { var c; return "ontouchstart" in a || a.DocumentTouch && b instanceof DocumentTouch ? c = !0 : y(["@media (", n.join("touch-enabled),("), h, ")", "{#modernizr{top:9px;position:absolute}}"].join(""), function (a) { c = a.offsetTop === 9 }), c }, s.geolocation = function () { return "geolocation" in navigator }, s.postmessage = function () { return !!a.postMessage }, s.websqldatabase = function () { return !!a.openDatabase }, s.indexedDB = function () { return !!I("indexedDB", a) }, s.hashchange = function () { return z("hashchange", a) && (b.documentMode === c || b.documentMode > 7) }, s.history = function () { return !!a.history && !!history.pushState }, s.draganddrop = function () { var a = b.createElement("div"); return "draggable" in a || "ondragstart" in a && "ondrop" in a }, s.websockets = function () { return "WebSocket" in a || "MozWebSocket" in a }, s.rgba = function () { return C("background-color:rgba(150,255,150,.5)"), F(j.backgroundColor, "rgba") }, s.hsla = function () { return C("background-color:hsla(120,40%,100%,.5)"), F(j.backgroundColor, "rgba") || F(j.backgroundColor, "hsla") }, s.multiplebgs = function () { return C("background:url(https://),url(https://),red url(https://)"), /(url\s*\(.*?){3}/.test(j.background) }, s.backgroundsize = function () { return I("backgroundSize") }, s.borderimage = function () { return I("borderImage") }, s.borderradius = function () { return I("borderRadius") }, s.boxshadow = function () { return I("boxShadow") }, s.textshadow = function () { return b.createElement("div").style.textShadow === "" }, s.opacity = function () { return D("opacity:.55"), /^0.55$/.test(j.opacity) }, s.cssanimations = function () { return I("animationName") }, s.csscolumns = function () { return I("columnCount") }, s.cssgradients = function () { var a = "background-image:", b = "gradient(linear,left top,right bottom,from(#9f9),to(white));", c = "linear-gradient(left top,#9f9, white);"; return C((a + "-webkit- ".split(" ").join(b + a) + n.join(c + a)).slice(0, -a.length)), F(j.backgroundImage, "gradient") }, s.cssreflections = function () { return I("boxReflect") }, s.csstransforms = function () { return !!I("transform") }, s.csstransforms3d = function () { var a = !!I("perspective"); return a && "webkitPerspective" in g.style && y("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}", function (b, c) { a = b.offsetLeft === 9 && b.offsetHeight === 3 }), a }, s.csstransitions = function () { return I("transition") }, s.fontface = function () { var a; return y('@font-face {font-family:"font";src:url("https://")}', function (c, d) { var e = b.getElementById("smodernizr"), f = e.sheet || e.styleSheet, g = f ? f.cssRules && f.cssRules[0] ? f.cssRules[0].cssText : f.cssText || "" : ""; a = /src/i.test(g) && g.indexOf(d.split(" ")[0]) === 0 }), a }, s.generatedcontent = function () { var a; return y(["#", h, "{font:0/0 a}#", h, ':after{content:"', l, '";visibility:hidden;font:3px/1 a}'].join(""), function (b) { a = b.offsetHeight >= 3 }), a }, s.video = function () { var a = b.createElement("video"), c = !1; try { if (c = !!a.canPlayType) c = new Boolean(c), c.ogg = a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/, ""), c.h264 = a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/, ""), c.webm = a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/, "") } catch (d) { } return c }, s.audio = function () { var a = b.createElement("audio"), c = !1; try { if (c = !!a.canPlayType) c = new Boolean(c), c.ogg = a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ""), c.mp3 = a.canPlayType("audio/mpeg;").replace(/^no$/, ""), c.wav = a.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ""), c.m4a = (a.canPlayType("audio/x-m4a;") || a.canPlayType("audio/aac;")).replace(/^no$/, "") } catch (d) { } return c }, s.localstorage = function () { try { return localStorage.setItem(h, h), localStorage.removeItem(h), !0 } catch (a) { return !1 } }, s.sessionstorage = function () { try { return sessionStorage.setItem(h, h), sessionStorage.removeItem(h), !0 } catch (a) { return !1 } }, s.webworkers = function () { return !!a.Worker }, s.applicationcache = function () { return !!a.applicationCache }, s.svg = function () { return !!b.createElementNS && !!b.createElementNS(r.svg, "svg").createSVGRect }, s.inlinesvg = function () { var a = b.createElement("div"); return a.innerHTML = "<svg/>", (a.firstChild && a.firstChild.namespaceURI) == r.svg }, s.smil = function () { return !!b.createElementNS && /SVGAnimate/.test(m.call(b.createElementNS(r.svg, "animate"))) }, s.svgclippaths = function () { return !!b.createElementNS && /SVGClipPath/.test(m.call(b.createElementNS(r.svg, "clipPath"))) }; for (var K in s) B(s, K) && (x = K.toLowerCase(), e[x] = s[K](), v.push((e[x] ? "" : "no-") + x)); return e.input || J(), e.addTest = function (a, b) { if (typeof a == "object") for (var d in a) B(a, d) && e.addTest(d, a[d]); else { a = a.toLowerCase(); if (e[a] !== c) return e; b = typeof b == "function" ? b() : b, typeof f != "undefined" && f && (g.className += " " + (b ? "" : "no-") + a), e[a] = b } return e }, C(""), i = k = null, function (a, b) { function l(a, b) { var c = a.createElement("p"), d = a.getElementsByTagName("head")[0] || a.documentElement; return c.innerHTML = "x<style>" + b + "</style>", d.insertBefore(c.lastChild, d.firstChild) } function m() { var a = s.elements; return typeof a == "string" ? a.split(" ") : a } function n(a) { var b = j[a[h]]; return b || (b = {}, i++, a[h] = i, j[i] = b), b } function o(a, c, d) { c || (c = b); if (k) return c.createElement(a); d || (d = n(c)); var g; return d.cache[a] ? g = d.cache[a].cloneNode() : f.test(a) ? g = (d.cache[a] = d.createElem(a)).cloneNode() : g = d.createElem(a), g.canHaveChildren && !e.test(a) && !g.tagUrn ? d.frag.appendChild(g) : g } function p(a, c) { a || (a = b); if (k) return a.createDocumentFragment(); c = c || n(a); var d = c.frag.cloneNode(), e = 0, f = m(), g = f.length; for (; e < g; e++) d.createElement(f[e]); return d } function q(a, b) { b.cache || (b.cache = {}, b.createElem = a.createElement, b.createFrag = a.createDocumentFragment, b.frag = b.createFrag()), a.createElement = function (c) { return s.shivMethods ? o(c, a, b) : b.createElem(c) }, a.createDocumentFragment = Function("h,f", "return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&(" + m().join().replace(/[\w\-]+/g, function (a) { return b.createElem(a), b.frag.createElement(a), 'c("' + a + '")' }) + ");return n}")(s, b.frag) } function r(a) { a || (a = b); var c = n(a); return s.shivCSS && !g && !c.hasCSS && (c.hasCSS = !!l(a, "article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")), k || q(a, c), a } var c = "3.7.0", d = a.html5 || {}, e = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i, f = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i, g, h = "_html5shiv", i = 0, j = {}, k; (function () { try { var a = b.createElement("a"); a.innerHTML = "<xyz></xyz>", g = "hidden" in a, k = a.childNodes.length == 1 || function () { b.createElement("a"); var a = b.createDocumentFragment(); return typeof a.cloneNode == "undefined" || typeof a.createDocumentFragment == "undefined" || typeof a.createElement == "undefined" }() } catch (c) { g = !0, k = !0 } })(); var s = { elements: d.elements || "abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video", version: c, shivCSS: d.shivCSS !== !1, supportsUnknownElements: k, shivMethods: d.shivMethods !== !1, type: "default", shivDocument: r, createElement: o, createDocumentFragment: p }; a.html5 = s, r(b) }(this, b), e._version = d, e._prefixes = n, e._domPrefixes = q, e._cssomPrefixes = p, e.hasEvent = z, e.testProp = function (a) { return G([a]) }, e.testAllProps = I, e.testStyles = y, g.className = g.className.replace(/(^|\s)no-js(\s|$)/, "$1$2") + (f ? " js " + v.join(" ") : ""), e }(this, this.document), function (a, b, c) { function d(a) { return "[object Function]" == o.call(a) } function e(a) { return "string" == typeof a } function f() { } function g(a) { return !a || "loaded" == a || "complete" == a || "uninitialized" == a } function h() { var a = p.shift(); q = 1, a ? a.t ? m(function () { ("c" == a.t ? B.injectCss : B.injectJs)(a.s, 0, a.a, a.x, a.e, 1) }, 0) : (a(), h()) : q = 0 } function i(a, c, d, e, f, i, j) { function k(b) { if (!o && g(l.readyState) && (u.r = o = 1, !q && h(), l.onload = l.onreadystatechange = null, b)) { "img" != a && m(function () { t.removeChild(l) }, 50); for (var d in y[c]) y[c].hasOwnProperty(d) && y[c][d].onload() } } var j = j || B.errorTimeout, l = b.createElement(a), o = 0, r = 0, u = { t: d, s: c, e: f, a: i, x: j }; 1 === y[c] && (r = 1, y[c] = []), "object" == a ? l.data = c : (l.src = c, l.type = a), l.width = l.height = "0", l.onerror = l.onload = l.onreadystatechange = function () { k.call(this, r) }, p.splice(e, 0, u), "img" != a && (r || 2 === y[c] ? (t.insertBefore(l, s ? null : n), m(k, j)) : y[c].push(l)) } function j(a, b, c, d, f) { return q = 0, b = b || "j", e(a) ? i("c" == b ? v : u, a, b, this.i++, c, d, f) : (p.splice(this.i++, 0, a), 1 == p.length && h()), this } function k() { var a = B; return a.loader = { load: j, i: 0 }, a } var l = b.documentElement, m = a.setTimeout, n = b.getElementsByTagName("script")[0], o = {}.toString, p = [], q = 0, r = "MozAppearance" in l.style, s = r && !!b.createRange().compareNode, t = s ? l : n.parentNode, l = a.opera && "[object Opera]" == o.call(a.opera), l = !!b.attachEvent && !l, u = r ? "object" : l ? "script" : "img", v = l ? "script" : u, w = Array.isArray || function (a) { return "[object Array]" == o.call(a) }, x = [], y = {}, z = { timeout: function (a, b) { return b.length && (a.timeout = b[0]), a } }, A, B; B = function (a) { function b(a) { var a = a.split("!"), b = x.length, c = a.pop(), d = a.length, c = { url: c, origUrl: c, prefixes: a }, e, f, g; for (f = 0; f < d; f++) g = a[f].split("="), (e = z[g.shift()]) && (c = e(c, g)); for (f = 0; f < b; f++) c = x[f](c); return c } function g(a, e, f, g, h) { var i = b(a), j = i.autoCallback; i.url.split(".").pop().split("?").shift(), i.bypass || (e && (e = d(e) ? e : e[a] || e[g] || e[a.split("/").pop().split("?")[0]]), i.instead ? i.instead(a, e, f, g, h) : (y[i.url] ? i.noexec = !0 : y[i.url] = 1, f.load(i.url, i.forceCSS || !i.forceJS && "css" == i.url.split(".").pop().split("?").shift() ? "c" : c, i.noexec, i.attrs, i.timeout), (d(e) || d(j)) && f.load(function () { k(), e && e(i.origUrl, h, g), j && j(i.origUrl, h, g), y[i.url] = 2 }))) } function h(a, b) { function c(a, c) { if (a) { if (e(a)) c || (j = function () { var a = [].slice.call(arguments); k.apply(this, a), l() }), g(a, j, b, 0, h); else if (Object(a) === a) for (n in m = function () { var b = 0, c; for (c in a) a.hasOwnProperty(c) && b++; return b }(), a) a.hasOwnProperty(n) && (!c && !--m && (d(j) ? j = function () { var a = [].slice.call(arguments); k.apply(this, a), l() } : j[n] = function (a) { return function () { var b = [].slice.call(arguments); a && a.apply(this, b), l() } }(k[n])), g(a[n], j, b, n, h)) } else !c && l() } var h = !!a.test, i = a.load || a.both, j = a.callback || f, k = j, l = a.complete || f, m, n; c(h ? a.yep : a.nope, !!i), i && c(i) } var i, j, l = this.yepnope.loader; if (e(a)) g(a, 0, l, 0); else if (w(a)) for (i = 0; i < a.length; i++) j = a[i], e(j) ? g(j, 0, l, 0) : w(j) ? B(j) : Object(j) === j && h(j, l); else Object(a) === a && h(a, l) }, B.addPrefix = function (a, b) { z[a] = b }, B.addFilter = function (a) { x.push(a) }, B.errorTimeout = 1e4, null == b.readyState && b.addEventListener && (b.readyState = "loading", b.addEventListener("DOMContentLoaded", A = function () { b.removeEventListener("DOMContentLoaded", A, 0), b.readyState = "complete" }, 0)), a.yepnope = k(), a.yepnope.executeStack = h, a.yepnope.injectJs = function (a, c, d, e, i, j) { var k = b.createElement("script"), l, o, e = e || B.errorTimeout; k.src = a; for (o in d) k.setAttribute(o, d[o]); c = j ? h : c || f, k.onreadystatechange = k.onload = function () { !l && g(k.readyState) && (l = 1, c(), k.onload = k.onreadystatechange = null) }, m(function () { l || (l = 1, c(1)) }, e), i ? k.onload() : n.parentNode.insertBefore(k, n) }, a.yepnope.injectCss = function (a, c, d, e, g, i) { var e = b.createElement("link"), j, c = i ? h : c || f; e.href = a, e.rel = "stylesheet", e.type = "text/css"; for (j in d) e.setAttribute(j, d[j]); g || (n.parentNode.insertBefore(e, n), m(c, 0)) } }(this, document), Modernizr.load = function () { yepnope.apply(window, [].slice.call(arguments, 0)) }, Modernizr.addTest("blobconstructor", function () { try { return !!(new Blob) } catch (a) { return !1 } }), function () { if (!Modernizr.canvas) return !1; var a = new Image, b = document.createElement("canvas"), c = b.getContext("2d"); a.onload = function () { c.drawImage(a, 0, 0), Modernizr.addTest("todataurljpeg", function () { return b.toDataURL("image/jpeg").indexOf("data:image/jpeg") === 0 }), Modernizr.addTest("todataurlwebp", function () { return b.toDataURL("image/webp").indexOf("data:image/webp") === 0 }) }, a.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==" }(), Modernizr.addTest("cors", !!(window.XMLHttpRequest && "withCredentials" in new XMLHttpRequest)), Modernizr.addTest("json", !!window.JSON && !!JSON.parse), function () { var a = new Image; a.onerror = function () { Modernizr.addTest("datauri", function () { return !1 }) }, a.onload = function () { Modernizr.addTest("datauri", function () { return a.width == 1 && a.height == 1 }) }, a.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==" }();var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};

(function (window) {

    "use strict";
    JM.BigIdea.Enums = JM.BigIdea.Enums || {};

    JM.BigIdea.Enums.Browsers = {
        Chrome: "0",
        Safari: "1",
        Firefox: "2",
        InternetExplorer: "3"
    };

    JM.BigIdea.Enums.OS = {
        Windows: "0",
        Mac: "1"
    };

    JM.BigIdea.Platform = (function () {
        var
            _userAgent = window.navigator.userAgent ? window.navigator.userAgent.toLowerCase() : "",
            _browserType,
            /**
            * Returns browser's user agent
            * @return {string}: User agent string
            */
            _getUserAgent = function () {
                return _userAgent;
            },
            /**
            * Determine browser type by useragent
            * @return {enum}: Browser type
            */
            _getBrowserType = function () {
                if (!_browserType) { _setBrowserType(); }

                return _browserType;
            },
            _setBrowserType = function () {
                if (JM.BigIdea.Platform.GetUserAgent().indexOf("chrome") !== -1) {
                    _browserType = JM.BigIdea.Enums.Browsers.Chrome;
                } else if (JM.BigIdea.Platform.GetUserAgent().indexOf("safari") !== -1) {
                    _browserType = JM.BigIdea.Enums.Browsers.Safari;
                } else if (JM.BigIdea.Platform.GetUserAgent().indexOf("firefox") !== -1) {
                    _browserType = JM.BigIdea.Enums.Browsers.Firefox;
                } else if (JM.BigIdea.Platform.GetUserAgent().indexOf("msie") !== -1 ||
                    JM.BigIdea.Platform.GetUserAgent().indexOf("trident") !== -1) {
                    _browserType = JM.BigIdea.Enums.Browsers.InternetExplorer;
                }
            },
            _getOSType = function () {
                var osType = "";
                if (JM.BigIdea.Platform.GetUserAgent().indexOf("windows") !== -1) {
                    osType = JM.BigIdea.Enums.OS.Windows;
                } else if (JM.BigIdea.Platform.GetUserAgent().indexOf("mac") !== -1) {
                    osType = JM.BigIdea.Enums.OS.Mac;
                }
                return osType;
            },

            _isAndroid = function () {
                return JM.BigIdea.Platform.GetUserAgent().match(/Android/i);
            },
            _isBlackberry = function () {
                return JM.BigIdea.Platform.GetUserAgent().match(/BlackBerry/i);
            },
            _isIphone = function () {
                return JM.BigIdea.Platform.GetUserAgent().match(/iPhone/i);
            },
            _isIpad = function () {
                return JM.BigIdea.Platform.GetUserAgent().match(/iPad/i);
            },
            _isIpod = function () {
                return JM.BigIdea.Platform.GetUserAgent().match(/iPod/i);
            },
            _isIos = function () {
                return JM.BigIdea.Platform.GetUserAgent().match(/iPhone|iPad|iPod/i);
            },
            _isWindowsPhone = function () {
                return JM.BigIdea.Platform.GetUserAgent().match(/IEMobile/i);
            },
            _isMobile = function () {
                return (_isAndroid() || _isBlackberry() || _isIos() || _isWindowsPhone());
            },

            _getIEVersion = function () {
                var userAgent = JM.BigIdea.Platform.GetUserAgent();
                var isIE = JM.BigIdea.Platform.GetBrowserType() === JM.BigIdea.Enums.Browsers.InternetExplorer;
                return (isIE) ? parseInt(userAgent.split('msie')[1]) : false;
            },

            _getDownloadHotkey = function () {
                var browser = JM.BigIdea.Platform.GetBrowserType(),
                    os = JM.BigIdea.Platform.GetOSType(),
                    hotkey = '';

                if (os === JM.BigIdea.Enums.OS.Windows) {
                    switch (browser) {
                        case JM.BigIdea.Enums.Browsers.Chrome:
                            hotkey = 'Ctrl + J';
                            break;
                        case JM.BigIdea.Enums.Browsers.Safari:
                            hotkey = 'Ctrl + Alt + L';
                            break;
                        case JM.BigIdea.Enums.Browsers.Firefox:
                            hotkey = 'Ctrl + J';
                            break;
                        case JM.BigIdea.Enums.Browsers.InternetExplorer:
                            hotkey = 'Ctrl + J';
                            break;
                    }
                } else {
                    switch (browser) {
                        case JM.BigIdea.Enums.Browsers.Chrome:
                            hotkey = 'Shift + Cmd + J';
                            break;
                        case JM.BigIdea.Enums.Browsers.Safari:
                            hotkey = 'Alt + Cmd + L';
                            break;
                        case JM.BigIdea.Enums.Browsers.Firefox:
                            hotkey = 'Cmd + J';
                            break;
                    }
                }

                return hotkey;
            };

        return {
            /**
            * Returns browser's user agent
            * @return {string}: User agent string
            */
            GetUserAgent: _getUserAgent,
            /**
            * Determine browser type by useragent
            * @return {enum}: Browser type
            */
            GetBrowserType: _getBrowserType,
            /**
            * Calculates browser type from UA string
            * @return {enum}: Browser type
            */
            SetBrowserType: _setBrowserType,

            /**
            * Calculates operating system type from UA string
            * @return {enum}: OS type
            */
            GetOSType: _getOSType,

            /**
             * Gets download folder hotkey for browser
             * @return {string}: Formatted hotkey
             */
            GetDownloadHotkey: _getDownloadHotkey,

            /**
             * Determines if the browser is mobile from
             * the userAgent
             * @return {boolean}: true=mobile; false=standard
             */
            IsMobileBrowser: _isMobile,

            /**
             * Determines if the browser is IE and returns the verison number, returns false otherwise
             * the userAgent
             * @return {int|boolean}: false=not IE; number=version of IE
             */
            GetIEVersion: _getIEVersion
        };
    })();
}(window));/// <reference path="../../../../Common/Scripts/3rdParty/modernizr.js" />
/// <reference path="../../../Common/Scripts/Platform/Platform.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};

JM.BigIdea.Html5 = (function (modernizr, platform, browserTypes) {
    "use strict";
    var _isHtml5Supported = function () {
        platform.SetBrowserType();

        // IE, older than version 11
        var isOldIe = platform.GetBrowserType() === browserTypes.InternetExplorer &&
            !platform.GetUserAgent().match(/rv:/i);

        return !isOldIe &&
            modernizr.blobconstructor &&
            modernizr.canvas &&
            modernizr.cors &&
            modernizr.csstransforms &&
            modernizr.json &&
            modernizr.localstorage &&
            modernizr.svg &&
            modernizr.websockets;
    };

    return {
        /**
        * Detect if all the features required by the HTML5 client are supported
        * @return {bool}: True if supported
        */

        IsHtml5Supported: _isHtml5Supported,
    };
})(Modernizr, JM.BigIdea.Platform, JM.BigIdea.Enums.Browsers);(function () {
    var browser = getBrowserInfo();
    if ((browser.name === "MSIE" && parseInt(browser.version) < 11) ||
        (browser.name === "Safari" && parseFloat(browser.version) < 8)) {
        window.location.href = "/SuppressBrowser.aspx";
    }

    //source: https://www.gregoryvarghese.com/how-to-get-browser-name-and-version-via-javascript/
    function getBrowserInfo() {
        var ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
        if (/trident/i.test(M[1])) {
            tem = /\brv[ :]+(\d+)/g.exec(ua) || [];
            return { name: 'IE ', version: (tem[1] || '') };
        }
        if (M[1] === 'Chrome') {
            tem = ua.match(/\bOPR\/(\d+)/)
            if (tem != null) { return { name: 'Opera', version: tem[1] }; }
        }
        M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
        if ((tem = ua.match(/version\/(\d+)/i)) != null) { M.splice(1, 1, tem[1]); }
        return {
            name: M[0],
            version: M[1]
        };
    }
})();(function (f) { if (typeof exports === "object" && typeof module !== "undefined") { module.exports = f() } else if (typeof define === "function" && define.amd) { define([], f) } else { var g; if (typeof window !== "undefined") { g = window } else if (typeof global !== "undefined") { g = global } else if (typeof self !== "undefined") { g = self } else { g = this } g.protocolCheck = f() } })(function () {
    var define, module, exports; return (function e(t, n, r) { function s(o, u) { if (!n[o]) { if (!t[o]) { var a = typeof require == "function" && require; if (!u && a) return a(o, !0); if (i) return i(o, !0); var f = new Error("Cannot find module '" + o + "'"); throw f.code = "MODULE_NOT_FOUND", f } var l = n[o] = { exports: {} }; t[o][0].call(l.exports, function (e) { var n = t[o][1][e]; return s(n ? n : e) }, l, l.exports, e, t, n, r) } return n[o].exports } var i = typeof require == "function" && require; for (var o = 0; o < r.length; o++) s(r[o]); return s })({
        1: [function (require, module, exports) {
            function _registerEvent(target, eventType, cb) {
                if (target.addEventListener) {
                    target.addEventListener(eventType, cb);
                    return {
                        remove: function () {
                            target.removeEventListener(eventType, cb);
                        }
                    };
                } else {
                    target.attachEvent(eventType, cb);
                    return {
                        remove: function () {
                            target.detachEvent(eventType, cb);
                        }
                    };
                }
            }

            function _createHiddenIframe(target, uri) {
                var iframe = document.createElement("iframe");
                iframe.src = uri;
                iframe.id = "hiddenIframe";
                iframe.style.display = "none";
                target.appendChild(iframe);

                return iframe;
            }

            function openUriWithHiddenFrame(uri, failCb, successCb) {

                var timeout = setTimeout(function () {
                    failCb();
                    handler.remove();
                }, 1000);

                var iframe = document.querySelector("#hiddenIframe");
                if (!iframe) {
                    iframe = _createHiddenIframe(document.body, "about:blank");
                }

                var handler = _registerEvent(window, "blur", onBlur);

                function onBlur() {
                    clearTimeout(timeout);
                    handler.remove();
                    successCb();
                }

                iframe.contentWindow.location.href = uri;
            }

            function openUriWithTimeoutHack(uri, failCb, successCb) {

                var timeout = setTimeout(function () {
                    failCb();
                    handler.remove();
                }, 1000);

                //handle page running in an iframe (blur must be registered with top level window)
                var target = window;
                while (target != target.parent) {
                    target = target.parent;
                }

                var handler = _registerEvent(target, "blur", onBlur);

                function onBlur() {
                    clearTimeout(timeout);
                    handler.remove();
                    successCb();
                }

                window.location = uri;
            }

            function openUriUsingFirefox(uri, failCb, successCb) {
                var iframe = document.querySelector("#hiddenIframe");

                if (!iframe) {
                    iframe = _createHiddenIframe(document.body, "about:blank");
                }

                try {
                    iframe.contentWindow.location.href = uri;
                    successCb();
                } catch (e) {
                    if (e.name == "NS_ERROR_UNKNOWN_PROTOCOL") {
                        failCb();
                    }
                }
            }

            function openUriUsingIEInOlderWindows(uri, failCb, successCb) {
                if (getInternetExplorerVersion() === 10) {
                    openUriUsingIE10InWindows7(uri, failCb, successCb);
                } else if (getInternetExplorerVersion() === 9 || getInternetExplorerVersion() === 11) {
                    openUriWithHiddenFrame(uri, failCb, successCb);
                } else {
                    openUriInNewWindowHack(uri, failCb, successCb);
                }
            }

            function openUriUsingIE10InWindows7(uri, failCb, successCb) {
                var timeout = setTimeout(failCb, 1000);
                window.addEventListener("blur", function () {
                    clearTimeout(timeout);
                    successCb();
                });

                var iframe = document.querySelector("#hiddenIframe");
                if (!iframe) {
                    iframe = _createHiddenIframe(document.body, "about:blank");
                }
                try {
                    iframe.contentWindow.location.href = uri;
                } catch (e) {
                    failCb();
                    clearTimeout(timeout);
                }
            }

            function openUriInNewWindowHack(uri, failCb, successCb) {
                var myWindow = window.open('', '', 'width=0,height=0');

                myWindow.document.write("<iframe src='" + uri + "'></iframe>");

                setTimeout(function () {
                    try {
                        myWindow.location.href;
                        myWindow.setTimeout("window.close()", 1000);
                        successCb();
                    } catch (e) {
                        myWindow.close();
                        failCb();
                    }
                }, 1000);
            }

            function openUriWithMsLaunchUri(uri, failCb, successCb) {
                navigator.msLaunchUri(uri,
                    successCb,
                    failCb
                );
            }

            function checkBrowser() {
                var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
                return {
                    isOpera: isOpera,
                    isFirefox: typeof InstallTrigger !== 'undefined',
                    isSafari: Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0,
                    isChrome: !!window.chrome && !isOpera,
                    isIE: /*@cc_on!@*/false || !!document.documentMode // At least IE6
                }
            }

            function getInternetExplorerVersion() {
                var rv = -1;
                if (navigator.appName === "Microsoft Internet Explorer") {
                    var ua = navigator.userAgent;
                    var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
                    if (re.exec(ua) != null)
                        rv = parseFloat(RegExp.$1);
                }
                else if (navigator.appName === "Netscape") {
                    var ua = navigator.userAgent;
                    var re = new RegExp("Trident/.*rv:([0-9]{1,}[\.0-9]{0,})");
                    if (re.exec(ua) != null) {
                        rv = parseFloat(RegExp.$1);
                    }
                }
                return rv;
            }

            module.exports = function (uri, failCb, successCb) {
                function failCallback() {
                    failCb && failCb();
                }

                function successCallback() {
                    successCb && successCb();
                }

                if (navigator.msLaunchUri) { //for IE and Edge in Win 8 and Win 10
                    openUriWithMsLaunchUri(uri, failCb, successCb);
                } else {
                    var browser = checkBrowser();

                    if (browser.isFirefox) {
                        openUriUsingFirefox(uri, failCallback, successCallback);
                    } else if (browser.isChrome) {
                        openUriWithTimeoutHack(uri, failCallback, successCallback);
                    } else if (browser.isIE) {
                        openUriUsingIEInOlderWindows(uri, failCallback, successCallback);
                    } else {
                        //not supported, implement please
                    }
                }
            }

        }, {}]
    }, {}, [1])(1)
});/*!
 * jScrollPane - v2.0.0beta12 - 2012-09-27
 * http://jscrollpane.kelvinluck.com/
 *
 * Copyright (c) 2010 Kelvin Luck
 * Dual licensed under the MIT or GPL licenses.
 */

// Script: jScrollPane - cross browser customisable scrollbars
//
// *Version: 2.0.0beta12, Last updated: 2012-09-27*
//
// Project Home - http://jscrollpane.kelvinluck.com/
// GitHub       - http://github.com/vitch/jScrollPane
// Source       - http://github.com/vitch/jScrollPane/raw/master/script/jquery.jscrollpane.js
// (Minified)   - http://github.com/vitch/jScrollPane/raw/master/script/jquery.jscrollpane.min.js
//
// About: License
//
// Copyright (c) 2012 Kelvin Luck
// Dual licensed under the MIT or GPL Version 2 licenses.
// http://jscrollpane.kelvinluck.com/MIT-LICENSE.txt
// http://jscrollpane.kelvinluck.com/GPL-LICENSE.txt
//
// About: Examples
//
// All examples and demos are available through the jScrollPane example site at:
// http://jscrollpane.kelvinluck.com/
//
// About: Support and Testing
//
// This plugin is tested on the browsers below and has been found to work reliably on them. If you run
// into a problem on one of the supported browsers then please visit the support section on the jScrollPane
// website (http://jscrollpane.kelvinluck.com/) for more information on getting support. You are also
// welcome to fork the project on GitHub if you can contribute a fix for a given issue. 
//
// jQuery Versions - tested in 1.4.2+ - reported to work in 1.3.x
// Browsers Tested - Firefox 3.6.8, Safari 5, Opera 10.6, Chrome 5.0, IE 6, 7, 8
//
// About: Release History
//
// 2.0.0beta12 - (2012-09-27) fix for jQuery 1.8+
// 2.0.0beta11 - (2012-05-14)
// 2.0.0beta10 - (2011-04-17) cleaner required size calculation, improved keyboard support, stickToBottom/Left, other small fixes
// 2.0.0beta9 - (2011-01-31) new API methods, bug fixes and correct keyboard support for FF/OSX
// 2.0.0beta8 - (2011-01-29) touchscreen support, improved keyboard support
// 2.0.0beta7 - (2011-01-23) scroll speed consistent (thanks Aivo Paas)
// 2.0.0beta6 - (2010-12-07) scrollToElement horizontal support
// 2.0.0beta5 - (2010-10-18) jQuery 1.4.3 support, various bug fixes
// 2.0.0beta4 - (2010-09-17) clickOnTrack support, bug fixes
// 2.0.0beta3 - (2010-08-27) Horizontal mousewheel, mwheelIntent, keyboard support, bug fixes
// 2.0.0beta2 - (2010-08-21) Bug fixes
// 2.0.0beta1 - (2010-08-17) Rewrite to follow modern best practices and enable horizontal scrolling, initially hidden
//							 elements and dynamically sized elements.
// 1.x - (2006-12-31 - 2010-07-31) Initial version, hosted at googlecode, deprecated

(function($,window,undefined){

    $.fn.jScrollPane = function(settings)
    {
        // JScrollPane "class" - public methods are available through $('selector').data('jsp')
        function JScrollPane(elem, s)
        {
            var settings, jsp = this, pane, paneWidth, paneHeight, container, contentWidth, contentHeight,
                percentInViewH, percentInViewV, isScrollableV, isScrollableH, verticalDrag, dragMaxY,
                verticalDragPosition, horizontalDrag, dragMaxX, horizontalDragPosition,
                verticalBar, verticalTrack, scrollbarWidth, verticalTrackHeight, verticalDragHeight, arrowUp, arrowDown,
                horizontalBar, horizontalTrack, horizontalTrackWidth, horizontalDragWidth, arrowLeft, arrowRight,
                reinitialiseInterval, originalPadding, originalPaddingTotalWidth, previousContentWidth,
                wasAtTop = true, wasAtLeft = true, wasAtBottom = false, wasAtRight = false,
                originalElement = elem.clone(false, false).empty(),
                mwEvent = $.fn.mwheelIntent ? 'mwheelIntent.jsp' : 'mousewheel.jsp';

            originalPadding = elem.css('paddingTop') + ' ' +
                elem.css('paddingRight') + ' ' +
                elem.css('paddingBottom') + ' ' +
                elem.css('paddingLeft');
            originalPaddingTotalWidth = (parseInt(elem.css('paddingLeft'), 10) || 0) +
                (parseInt(elem.css('paddingRight'), 10) || 0);

            function initialise(s)
            {

                var /*firstChild, lastChild, */isMaintainingPositon, lastContentX, lastContentY,
                    hasContainingSpaceChanged, originalScrollTop, originalScrollLeft,
                    maintainAtBottom = false, maintainAtRight = false;

                settings = s;

                if (pane === undefined) {
                    originalScrollTop = elem.scrollTop();
                    originalScrollLeft = elem.scrollLeft();

                    elem.css(
                        {
                            overflow: 'hidden',
                            padding: 0
                        }
                    );
                    // TODO: Deal with where width/ height is 0 as it probably means the element is hidden and we should
                    // come back to it later and check once it is unhidden...
                    paneWidth = elem.innerWidth() + originalPaddingTotalWidth;
                    paneHeight = elem.innerHeight();

                    elem.width(paneWidth);

                    pane = $('<div class="jspPane" />').css('padding', originalPadding).append(elem.children());
                    container = $('<div class="jspContainer" />')
                        .css({
                            'width': paneWidth + 'px',
                            'height': paneHeight + 'px'
                        }
                    ).append(pane).appendTo(elem);

                    /*
                     // Move any margins from the first and last children up to the container so they can still
                     // collapse with neighbouring elements as they would before jScrollPane
                     firstChild = pane.find(':first-child');
                     lastChild = pane.find(':last-child');
                     elem.css(
                     {
                     'margin-top': firstChild.css('margin-top'),
                     'margin-bottom': lastChild.css('margin-bottom')
                     }
                     );
                     firstChild.css('margin-top', 0);
                     lastChild.css('margin-bottom', 0);
                     */
                } else {
                    elem.css('width', '');

                    maintainAtBottom = settings.stickToBottom && isCloseToBottom();
                    maintainAtRight  = settings.stickToRight  && isCloseToRight();

                    hasContainingSpaceChanged = elem.innerWidth() + originalPaddingTotalWidth != paneWidth || elem.outerHeight() != paneHeight;

                    if (hasContainingSpaceChanged) {
                        paneWidth = elem.innerWidth() + originalPaddingTotalWidth;
                        paneHeight = elem.innerHeight();
                        container.css({
                            width: paneWidth + 'px',
                            height: paneHeight + 'px'
                        });
                    }

                    // If nothing changed since last check...
                    if (!hasContainingSpaceChanged && previousContentWidth == contentWidth && pane.outerHeight() == contentHeight) {
                        elem.width(paneWidth);
                        return;
                    }
                    previousContentWidth = contentWidth;

                    pane.css('width', '');
                    elem.width(paneWidth);

                    container.find('>.jspVerticalBar,>.jspHorizontalBar').remove().end();
                }

                pane.css('overflow', 'auto');
                if (s.contentWidth) {
                    contentWidth = s.contentWidth;
                } else {
                    contentWidth = pane[0].scrollWidth;
                }
                contentHeight = pane[0].scrollHeight;
                pane.css('overflow', '');

                percentInViewH = contentWidth / paneWidth;
                percentInViewV = contentHeight / paneHeight;
                isScrollableV = percentInViewV > 1;

                isScrollableH = percentInViewH > 1;

                //console.log(paneWidth, paneHeight, contentWidth, contentHeight, percentInViewH, percentInViewV, isScrollableH, isScrollableV);

                if (!(isScrollableH || isScrollableV)) {
                    elem.removeClass('jspScrollable');
                    pane.css({
                        top: 0,
                        width: container.width() - originalPaddingTotalWidth
                    });
                    removeMousewheel();
                    removeFocusHandler();
                    removeKeyboardNav();
                    removeClickOnTrack();
                } else {
                    elem.addClass('jspScrollable');

                    isMaintainingPositon = settings.maintainPosition && (verticalDragPosition || horizontalDragPosition);
                    if (isMaintainingPositon) {
                        lastContentX = contentPositionX();
                        lastContentY = contentPositionY();
                    }

                    initialiseVerticalScroll();
                    initialiseHorizontalScroll();
                    resizeScrollbars();

                    if (isMaintainingPositon) {
                        scrollToX(maintainAtRight  ? (contentWidth  - paneWidth ) : lastContentX, false);
                        scrollToY(maintainAtBottom ? (contentHeight - paneHeight) : lastContentY, false);
                    }

                    initFocusHandler();
                    initMousewheel();
                    initTouch();

                    if (settings.enableKeyboardNavigation) {
                        initKeyboardNav();
                    }
                    if (settings.clickOnTrack) {
                        initClickOnTrack();
                    }

                    observeHash();
                    if (settings.hijackInternalLinks) {
                        hijackInternalLinks();
                    }
                }

                if (settings.autoReinitialise && !reinitialiseInterval) {
                    reinitialiseInterval = setInterval(
                        function()
                        {
                            initialise(settings);
                        },
                        settings.autoReinitialiseDelay
                    );
                } else if (!settings.autoReinitialise && reinitialiseInterval) {
                    clearInterval(reinitialiseInterval);
                }

                originalScrollTop && elem.scrollTop(0) && scrollToY(originalScrollTop, false);
                originalScrollLeft && elem.scrollLeft(0) && scrollToX(originalScrollLeft, false);

                elem.trigger('jsp-initialised', [isScrollableH || isScrollableV]);
            }

            function initialiseVerticalScroll()
            {
                if (isScrollableV) {

                    container.append(
                        $('<div class="jspVerticalBar" />').append(
                            $('<div class="jspCap jspCapTop" />'),
                            $('<div class="jspTrack" />').append(
                                $('<div class="jspDrag" />').append(
                                    $('<div class="jspDragTop sprt" />'),
                                    $('<div class="jspDragBottom sprt" />')
                                )
                            ),
                            $('<div class="jspCap jspCapBottom" />')
                        )
                    );

                    verticalBar = container.find('>.jspVerticalBar');
                    verticalTrack = verticalBar.find('>.jspTrack');
                    verticalDrag = verticalTrack.find('>.jspDrag');

                    if (settings.showArrows) {
                        arrowUp = $('<a class="jspArrow sprt jspArrowUp" />').bind(
                            'mousedown.jsp', getArrowScroll(0, -1)
                        ).bind('click.jsp', nil);
                        arrowDown = $('<a class="jspArrow sprt jspArrowDown" />').bind(
                            'mousedown.jsp', getArrowScroll(0, 1)
                        ).bind('click.jsp', nil);
                        if (settings.arrowScrollOnHover) {
                            arrowUp.bind('mouseover.jsp', getArrowScroll(0, -1, arrowUp));
                            arrowDown.bind('mouseover.jsp', getArrowScroll(0, 1, arrowDown));
                        }

                        appendArrows(verticalTrack, settings.verticalArrowPositions, arrowUp, arrowDown);
                    }

                    verticalTrackHeight = paneHeight;
                    container.find('>.jspVerticalBar>.jspCap:visible,>.jspVerticalBar>.jspArrow').each(
                        function()
                        {
                            verticalTrackHeight -= $(this).outerHeight();
                        }
                    );


                    verticalDrag.hover(
                        function()
                        {
                            verticalDrag.addClass('jspHover');
                        },
                        function()
                        {
                            verticalDrag.removeClass('jspHover');
                        }
                    ).bind(
                        'mousedown.jsp',
                        function(e)
                        {
                            // Stop IE from allowing text selection
                            $('html').bind('dragstart.jsp selectstart.jsp', nil);

                            verticalDrag.addClass('jspActive');

                            var startY = e.pageY - verticalDrag.position().top;

                            $('html').bind(
                                'mousemove.jsp',
                                function(e)
                                {
                                    positionDragY(e.pageY - startY, false);
                                }
                            ).bind('mouseup.jsp mouseleave.jsp', cancelDrag);
                            return false;
                        }
                    );
                    sizeVerticalScrollbar();
                }
            }

            function sizeVerticalScrollbar()
            {
                verticalTrack.height(verticalTrackHeight + 'px');
                verticalDragPosition = 0;
                scrollbarWidth = settings.verticalGutter + verticalTrack.outerWidth();

                // Make the pane thinner to allow for the vertical scrollbar
                pane.width(paneWidth - scrollbarWidth - originalPaddingTotalWidth);

                // Add margin to the left of the pane if scrollbars are on that side (to position
                // the scrollbar on the left or right set it's left or right property in CSS)
                try {
                    if (verticalBar.position().left === 0) {
                        pane.css('margin-left', scrollbarWidth + 'px');
                    }
                } catch (err) {
                }
            }

            function initialiseHorizontalScroll()
            {
                if (isScrollableH) {

                    container.append(
                        $('<div class="jspHorizontalBar" />').append(
                            $('<div class="jspCap jspCapLeft" />'),
                            $('<div class="jspTrack" />').append(
                                $('<div class="jspDrag" />').append(
                                    $('<div class="jspDragLeft" />'),
                                    $('<div class="jspDragRight" />')
                                )
                            ),
                            $('<div class="jspCap jspCapRight" />')
                        )
                    );

                    horizontalBar = container.find('>.jspHorizontalBar');
                    horizontalTrack = horizontalBar.find('>.jspTrack');
                    horizontalDrag = horizontalTrack.find('>.jspDrag');

                    if (settings.showArrows) {
                        arrowLeft = $('<a class="jspArrow jspArrowLeft" />').bind(
                            'mousedown.jsp', getArrowScroll(-1, 0)
                        ).bind('click.jsp', nil);
                        arrowRight = $('<a class="jspArrow jspArrowRight" />').bind(
                            'mousedown.jsp', getArrowScroll(1, 0)
                        ).bind('click.jsp', nil);
                        if (settings.arrowScrollOnHover) {
                            arrowLeft.bind('mouseover.jsp', getArrowScroll(-1, 0, arrowLeft));
                            arrowRight.bind('mouseover.jsp', getArrowScroll(1, 0, arrowRight));
                        }
                        appendArrows(horizontalTrack, settings.horizontalArrowPositions, arrowLeft, arrowRight);
                    }

                    horizontalDrag.hover(
                        function()
                        {
                            horizontalDrag.addClass('jspHover');
                        },
                        function()
                        {
                            horizontalDrag.removeClass('jspHover');
                        }
                    ).bind(
                        'mousedown.jsp',
                        function(e)
                        {
                            // Stop IE from allowing text selection
                            $('html').bind('dragstart.jsp selectstart.jsp', nil);

                            horizontalDrag.addClass('jspActive');

                            var startX = e.pageX - horizontalDrag.position().left;

                            $('html').bind(
                                'mousemove.jsp',
                                function(e)
                                {
                                    positionDragX(e.pageX - startX, false);
                                }
                            ).bind('mouseup.jsp mouseleave.jsp', cancelDrag);
                            return false;
                        }
                    );
                    horizontalTrackWidth = container.innerWidth();
                    sizeHorizontalScrollbar();
                }
            }

            function sizeHorizontalScrollbar()
            {
                container.find('>.jspHorizontalBar>.jspCap:visible,>.jspHorizontalBar>.jspArrow').each(
                    function()
                    {
                        horizontalTrackWidth -= $(this).outerWidth();
                    }
                );

                horizontalTrack.width(horizontalTrackWidth + 'px');
                horizontalDragPosition = 0;
            }

            function resizeScrollbars()
            {
                if (isScrollableH && isScrollableV) {
                    var horizontalTrackHeight = horizontalTrack.outerHeight(),
                        verticalTrackWidth = verticalTrack.outerWidth();
                    verticalTrackHeight -= horizontalTrackHeight;
                    $(horizontalBar).find('>.jspCap:visible,>.jspArrow').each(
                        function()
                        {
                            horizontalTrackWidth += $(this).outerWidth();
                        }
                    );
                    horizontalTrackWidth -= verticalTrackWidth;
                    paneHeight -= verticalTrackWidth;
                    paneWidth -= horizontalTrackHeight;
                    horizontalTrack.parent().append(
                        $('<div class="jspCorner" />').css('width', horizontalTrackHeight + 'px')
                    );
                    sizeVerticalScrollbar();
                    sizeHorizontalScrollbar();
                }
                // reflow content
                if (isScrollableH) {
                    pane.width((container.outerWidth() - originalPaddingTotalWidth) + 'px');
                }
                contentHeight = pane.outerHeight();
                percentInViewV = contentHeight / paneHeight;

                if (isScrollableH) {
                    horizontalDragWidth = Math.ceil(1 / percentInViewH * horizontalTrackWidth);
                    if (horizontalDragWidth > settings.horizontalDragMaxWidth) {
                        horizontalDragWidth = settings.horizontalDragMaxWidth;
                    } else if (horizontalDragWidth < settings.horizontalDragMinWidth) {
                        horizontalDragWidth = settings.horizontalDragMinWidth;
                    }
                    horizontalDrag.width(horizontalDragWidth + 'px');
                    dragMaxX = horizontalTrackWidth - horizontalDragWidth;
                    _positionDragX(horizontalDragPosition); // To update the state for the arrow buttons
                }
                if (isScrollableV) {
                    verticalDragHeight = Math.ceil(1 / percentInViewV * verticalTrackHeight);
                    if (verticalDragHeight > settings.verticalDragMaxHeight) {
                        verticalDragHeight = settings.verticalDragMaxHeight;
                    } else if (verticalDragHeight < settings.verticalDragMinHeight) {
                        verticalDragHeight = settings.verticalDragMinHeight;
                    }
                    verticalDrag.height(verticalDragHeight + 'px');
                    dragMaxY = verticalTrackHeight - verticalDragHeight;
                    _positionDragY(verticalDragPosition); // To update the state for the arrow buttons
                }
            }

            function appendArrows(ele, p, a1, a2)
            {
                var p1 = "before", p2 = "after", aTemp;

                // Sniff for mac... Is there a better way to determine whether the arrows would naturally appear
                // at the top or the bottom of the bar?
                if (p == "os") {
                    p = /Mac/.test(navigator.platform) ? "after" : "split";
                }
                if (p == p1) {
                    p2 = p;
                } else if (p == p2) {
                    p1 = p;
                    aTemp = a1;
                    a1 = a2;
                    a2 = aTemp;
                }

                ele[p1](a1)[p2](a2);
            }

            function getArrowScroll(dirX, dirY, ele)
            {
                return function()
                {
                    arrowScroll(dirX, dirY, this, ele);
                    this.blur();
                    return false;
                };
            }

            function arrowScroll(dirX, dirY, arrow, ele)
            {
                arrow = $(arrow).addClass('jspActive');

                var eve,
                    scrollTimeout,
                    isFirst = true,
                    doScroll = function()
                    {
                        if (dirX !== 0) {
                            jsp.scrollByX(dirX * settings.arrowButtonSpeed);
                        }
                        if (dirY !== 0) {
                            jsp.scrollByY(dirY * settings.arrowButtonSpeed);
                        }
                        scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.arrowRepeatFreq);
                        isFirst = false;
                    };

                doScroll();

                eve = ele ? 'mouseout.jsp' : 'mouseup.jsp';
                ele = ele || $('html');
                ele.bind(
                    eve,
                    function()
                    {
                        arrow.removeClass('jspActive');
                        scrollTimeout && clearTimeout(scrollTimeout);
                        scrollTimeout = null;
                        ele.unbind(eve);
                    }
                );
            }

            function initClickOnTrack()
            {
                removeClickOnTrack();
                if (isScrollableV) {
                    verticalTrack.bind(
                        'mousedown.jsp',
                        function(e)
                        {
                            if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) {
                                var clickedTrack = $(this),
                                    offset = clickedTrack.offset(),
                                    direction = e.pageY - offset.top - verticalDragPosition,
                                    scrollTimeout,
                                    isFirst = true,
                                    doScroll = function()
                                    {
                                        var offset = clickedTrack.offset(),
                                            pos = e.pageY - offset.top - verticalDragHeight / 2,
                                            contentDragY = paneHeight * settings.scrollPagePercent,
                                            dragY = dragMaxY * contentDragY / (contentHeight - paneHeight);
                                        if (direction < 0) {
                                            if (verticalDragPosition - dragY > pos) {
                                                jsp.scrollByY(-contentDragY);
                                            } else {
                                                positionDragY(pos);
                                            }
                                        } else if (direction > 0) {
                                            if (verticalDragPosition + dragY < pos) {
                                                jsp.scrollByY(contentDragY);
                                            } else {
                                                positionDragY(pos);
                                            }
                                        } else {
                                            cancelClick();
                                            return;
                                        }
                                        scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq);
                                        isFirst = false;
                                    },
                                    cancelClick = function()
                                    {
                                        scrollTimeout && clearTimeout(scrollTimeout);
                                        scrollTimeout = null;
                                        $(document).unbind('mouseup.jsp', cancelClick);
                                    };
                                doScroll();
                                $(document).bind('mouseup.jsp', cancelClick);
                                return false;
                            }
                        }
                    );
                }

                if (isScrollableH) {
                    horizontalTrack.bind(
                        'mousedown.jsp',
                        function(e)
                        {
                            if (e.originalTarget === undefined || e.originalTarget == e.currentTarget) {
                                var clickedTrack = $(this),
                                    offset = clickedTrack.offset(),
                                    direction = e.pageX - offset.left - horizontalDragPosition,
                                    scrollTimeout,
                                    isFirst = true,
                                    doScroll = function()
                                    {
                                        var offset = clickedTrack.offset(),
                                            pos = e.pageX - offset.left - horizontalDragWidth / 2,
                                            contentDragX = paneWidth * settings.scrollPagePercent,
                                            dragX = dragMaxX * contentDragX / (contentWidth - paneWidth);
                                        if (direction < 0) {
                                            if (horizontalDragPosition - dragX > pos) {
                                                jsp.scrollByX(-contentDragX);
                                            } else {
                                                positionDragX(pos);
                                            }
                                        } else if (direction > 0) {
                                            if (horizontalDragPosition + dragX < pos) {
                                                jsp.scrollByX(contentDragX);
                                            } else {
                                                positionDragX(pos);
                                            }
                                        } else {
                                            cancelClick();
                                            return;
                                        }
                                        scrollTimeout = setTimeout(doScroll, isFirst ? settings.initialDelay : settings.trackClickRepeatFreq);
                                        isFirst = false;
                                    },
                                    cancelClick = function()
                                    {
                                        scrollTimeout && clearTimeout(scrollTimeout);
                                        scrollTimeout = null;
                                        $(document).unbind('mouseup.jsp', cancelClick);
                                    };
                                doScroll();
                                $(document).bind('mouseup.jsp', cancelClick);
                                return false;
                            }
                        }
                    );
                }
            }

            function removeClickOnTrack()
            {
                if (horizontalTrack) {
                    horizontalTrack.unbind('mousedown.jsp');
                }
                if (verticalTrack) {
                    verticalTrack.unbind('mousedown.jsp');
                }
            }

            function cancelDrag()
            {
                $('html').unbind('dragstart.jsp selectstart.jsp mousemove.jsp mouseup.jsp mouseleave.jsp');

                if (verticalDrag) {
                    verticalDrag.removeClass('jspActive');
                }
                if (horizontalDrag) {
                    horizontalDrag.removeClass('jspActive');
                }
            }

            function positionDragY(destY, animate)
            {
                if (!isScrollableV) {
                    return;
                }
                if (destY < 0) {
                    destY = 0;
                } else if (destY > dragMaxY) {
                    destY = dragMaxY;
                }

                // can't just check if(animate) because false is a valid value that could be passed in...
                if (animate === undefined) {
                    animate = settings.animateScroll;
                }
                if (animate) {
                    jsp.animate(verticalDrag, 'top', destY,	_positionDragY);
                } else {
                    verticalDrag.css('top', destY);
                    _positionDragY(destY);
                }

            }

            function _positionDragY(destY)
            {
                if (destY === undefined) {
                    destY = verticalDrag.position().top;
                }

                container.scrollTop(0);
                verticalDragPosition = destY;

                var isAtTop = verticalDragPosition === 0,
                    isAtBottom = verticalDragPosition == dragMaxY,
                    percentScrolled = destY/ dragMaxY,
                    destTop = -percentScrolled * (contentHeight - paneHeight);

                if (wasAtTop != isAtTop || wasAtBottom != isAtBottom) {
                    wasAtTop = isAtTop;
                    wasAtBottom = isAtBottom;
                    elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]);
                }

                updateVerticalArrows(isAtTop, isAtBottom);
                pane.css('top', destTop);
                elem.trigger('jsp-scroll-y', [-destTop, isAtTop, isAtBottom]).trigger('scroll');
            }

            function positionDragX(destX, animate)
            {
                if (!isScrollableH) {
                    return;
                }
                if (destX < 0) {
                    destX = 0;
                } else if (destX > dragMaxX) {
                    destX = dragMaxX;
                }

                if (animate === undefined) {
                    animate = settings.animateScroll;
                }
                if (animate) {
                    jsp.animate(horizontalDrag, 'left', destX,	_positionDragX);
                } else {
                    horizontalDrag.css('left', destX);
                    _positionDragX(destX);
                }
            }

            function _positionDragX(destX)
            {
                if (destX === undefined) {
                    destX = horizontalDrag.position().left;
                }

                container.scrollTop(0);
                horizontalDragPosition = destX;

                var isAtLeft = horizontalDragPosition === 0,
                    isAtRight = horizontalDragPosition == dragMaxX,
                    percentScrolled = destX / dragMaxX,
                    destLeft = -percentScrolled * (contentWidth - paneWidth);

                if (wasAtLeft != isAtLeft || wasAtRight != isAtRight) {
                    wasAtLeft = isAtLeft;
                    wasAtRight = isAtRight;
                    elem.trigger('jsp-arrow-change', [wasAtTop, wasAtBottom, wasAtLeft, wasAtRight]);
                }

                updateHorizontalArrows(isAtLeft, isAtRight);
                pane.css('left', destLeft);
                elem.trigger('jsp-scroll-x', [-destLeft, isAtLeft, isAtRight]).trigger('scroll');
            }

            function updateVerticalArrows(isAtTop, isAtBottom)
            {
                if (settings.showArrows) {
                    arrowUp[isAtTop ? 'addClass' : 'removeClass']('jspDisabled');
                    arrowDown[isAtBottom ? 'addClass' : 'removeClass']('jspDisabled');
                }
            }

            function updateHorizontalArrows(isAtLeft, isAtRight)
            {
                if (settings.showArrows) {
                    arrowLeft[isAtLeft ? 'addClass' : 'removeClass']('jspDisabled');
                    arrowRight[isAtRight ? 'addClass' : 'removeClass']('jspDisabled');
                }
            }

            function scrollToY(destY, animate)
            {
                var percentScrolled = destY / (contentHeight - paneHeight);
                positionDragY(percentScrolled * dragMaxY, animate);
            }

            function scrollToX(destX, animate)
            {
                var percentScrolled = destX / (contentWidth - paneWidth);
                positionDragX(percentScrolled * dragMaxX, animate);
            }

            function scrollToElement(ele, stickToTop, animate)
            {
                var e, eleHeight, eleWidth, eleTop = 0, eleLeft = 0, viewportTop, viewportLeft, maxVisibleEleTop, maxVisibleEleLeft, destY, destX;

                // Legal hash values aren't necessarily legal jQuery selectors so we need to catch any
                // errors from the lookup...
                try {
                    e = $(ele);
                } catch (err) {
                    return;
                }
                eleHeight = e.outerHeight();
                eleWidth= e.outerWidth();

                container.scrollTop(0);
                container.scrollLeft(0);

                // loop through parents adding the offset top of any elements that are relatively positioned between
                // the focused element and the jspPane so we can get the true distance from the top
                // of the focused element to the top of the scrollpane...
                while (!e.is('.jspPane')) {
                    eleTop += e.position().top;
                    eleLeft += e.position().left;
                    e = e.offsetParent();
                    if (/^body|html$/i.test(e[0].nodeName)) {
                        // we ended up too high in the document structure. Quit!
                        return;
                    }
                }

                viewportTop = contentPositionY();
                maxVisibleEleTop = viewportTop + paneHeight;
                if (eleTop < viewportTop || stickToTop) { // element is above viewport
                    destY = eleTop - settings.verticalGutter;
                } else if (eleTop + eleHeight > maxVisibleEleTop) { // element is below viewport
                    destY = eleTop - paneHeight + eleHeight + settings.verticalGutter;
                }
                if (destY) {
                    scrollToY(destY, animate);
                }

                viewportLeft = contentPositionX();
                maxVisibleEleLeft = viewportLeft + paneWidth;
                if (eleLeft < viewportLeft || stickToTop) { // element is to the left of viewport
                    destX = eleLeft - settings.horizontalGutter;
                } else if (eleLeft + eleWidth > maxVisibleEleLeft) { // element is to the right viewport
                    destX = eleLeft - paneWidth + eleWidth + settings.horizontalGutter;
                }
                if (destX) {
                    scrollToX(destX, animate);
                }

            }

            function contentPositionX()
            {
                return -pane.position().left;
            }

            function contentPositionY()
            {
                return -pane.position().top;
            }

            function isCloseToBottom()
            {
                var scrollableHeight = contentHeight - paneHeight;
                return (scrollableHeight > 20) && (scrollableHeight - contentPositionY() < 10);
            }

            function isCloseToRight()
            {
                var scrollableWidth = contentWidth - paneWidth;
                return (scrollableWidth > 20) && (scrollableWidth - contentPositionX() < 10);
            }

            function initMousewheel()
            {
                container.unbind(mwEvent).bind(
                    mwEvent,
                    function (event, delta, deltaX, deltaY) {
                        var dX = horizontalDragPosition, dY = verticalDragPosition;
                        jsp.scrollBy(deltaX * settings.mouseWheelSpeed, -deltaY * settings.mouseWheelSpeed, false);
                        // return true if there was no movement so rest of screen can scroll
                        return dX == horizontalDragPosition && dY == verticalDragPosition;
                    }
                );
            }

            function removeMousewheel()
            {
                container.unbind(mwEvent);
            }

            function nil()
            {
                return false;
            }

            function initFocusHandler()
            {
                pane.find(':input,a').unbind('focus.jsp').bind(
                    'focus.jsp',
                    function(e)
                    {
                        scrollToElement(e.target, false);
                    }
                );
            }

            function removeFocusHandler()
            {
                pane.find(':input,a').unbind('focus.jsp');
            }

            function initKeyboardNav()
            {
                var keyDown, elementHasScrolled, validParents = [];
                isScrollableH && validParents.push(horizontalBar[0]);
                isScrollableV && validParents.push(verticalBar[0]);

                // IE also focuses elements that don't have tabindex set.
                pane.focus(
                    function()
                    {
                        elem.focus();
                    }
                );

                elem.attr('tabindex', 0)
                    .unbind('keydown.jsp keypress.jsp')
                    .bind(
                    'keydown.jsp',
                    function(e)
                    {
                        if (e.target !== this && !(validParents.length && $(e.target).closest(validParents).length)){
                            return;
                        }
                        var dX = horizontalDragPosition, dY = verticalDragPosition;
                        switch(e.keyCode) {
                            case 40: // down
                            case 38: // up
                            case 34: // page down
                            case 32: // space
                            case 33: // page up
                            case 39: // right
                            case 37: // left
                                keyDown = e.keyCode;
                                keyDownHandler();
                                break;
                            case 35: // end
                                scrollToY(contentHeight - paneHeight);
                                keyDown = null;
                                break;
                            case 36: // home
                                scrollToY(0);
                                keyDown = null;
                                break;
                        }

                        elementHasScrolled = e.keyCode == keyDown && dX != horizontalDragPosition || dY != verticalDragPosition;
                        return !elementHasScrolled;
                    }
                ).bind(
                    'keypress.jsp', // For FF/ OSX so that we can cancel the repeat key presses if the JSP scrolls...
                    function(e)
                    {
                        if (e.keyCode == keyDown) {
                            keyDownHandler();
                        }
                        return !elementHasScrolled;
                    }
                );

                if (settings.hideFocus) {
                    elem.css('outline', 'none');
                    if ('hideFocus' in container[0]){
                        elem.attr('hideFocus', true);
                    }
                } else {
                    elem.css('outline', '');
                    if ('hideFocus' in container[0]){
                        elem.attr('hideFocus', false);
                    }
                }

                function keyDownHandler()
                {
                    var dX = horizontalDragPosition, dY = verticalDragPosition;
                    switch(keyDown) {
                        case 40: // down
                            jsp.scrollByY(settings.keyboardSpeed, false);
                            break;
                        case 38: // up
                            jsp.scrollByY(-settings.keyboardSpeed, false);
                            break;
                        case 34: // page down
                        case 32: // space
                            jsp.scrollByY(paneHeight * settings.scrollPagePercent, false);
                            break;
                        case 33: // page up
                            jsp.scrollByY(-paneHeight * settings.scrollPagePercent, false);
                            break;
                        case 39: // right
                            jsp.scrollByX(settings.keyboardSpeed, false);
                            break;
                        case 37: // left
                            jsp.scrollByX(-settings.keyboardSpeed, false);
                            break;
                    }

                    elementHasScrolled = dX != horizontalDragPosition || dY != verticalDragPosition;
                    return elementHasScrolled;
                }
            }

            function removeKeyboardNav()
            {
                elem.attr('tabindex', '-1')
                    .removeAttr('tabindex')
                    .unbind('keydown.jsp keypress.jsp');
            }

            function observeHash()
            {
                if (location.hash && location.hash.length > 1) {
                    var e,
                        retryInt,
                        hash = escape(location.hash.substr(1)) // hash must be escaped to prevent XSS
                        ;
                    try {
                        e = $('#' + hash + ', a[name="' + hash + '"]');
                    } catch (err) {
                        return;
                    }

                    if (e.length && pane.find(hash)) {
                        // nasty workaround but it appears to take a little while before the hash has done its thing
                        // to the rendered page so we just wait until the container's scrollTop has been messed up.
                        if (container.scrollTop() === 0) {
                            retryInt = setInterval(
                                function()
                                {
                                    if (container.scrollTop() > 0) {
                                        scrollToElement(e, true);
                                        $(document).scrollTop(container.position().top);
                                        clearInterval(retryInt);
                                    }
                                },
                                50
                            );
                        } else {
                            scrollToElement(e, true);
                            $(document).scrollTop(container.position().top);
                        }
                    }
                }
            }

            function hijackInternalLinks()
            {
                // only register the link handler once
                if ($(document.body).data('jspHijack')) {
                    return;
                }

                // remember that the handler was bound
                $(document.body).data('jspHijack', true);

                // use live handler to also capture newly created links
                $(document.body).delegate('a[href*=#]', 'click', function(event) {
                    // does the link point to the same page?
                    // this also takes care of cases with a <base>-Tag or Links not starting with the hash #
                    // e.g. <a href="index.html#test"> when the current url already is index.html
                    var href = this.href.substr(0, this.href.indexOf('#')),
                        locationHref = location.href,
                        hash,
                        element,
                        container,
                        jsp,
                        scrollTop,
                        elementTop;
                    if (location.href.indexOf('#') !== -1) {
                        locationHref = location.href.substr(0, location.href.indexOf('#'));
                    }
                    if (href !== locationHref) {
                        // the link points to another page
                        return;
                    }

                    // check if jScrollPane should handle this click event
                    hash = escape(this.href.substr(this.href.indexOf('#') + 1));

                    // find the element on the page
                    element;
                    try {
                        element = $('#' + hash + ', a[name="' + hash + '"]');
                    } catch (e) {
                        // hash is not a valid jQuery identifier
                        return;
                    }

                    if (!element.length) {
                        // this link does not point to an element on this page
                        return;
                    }

                    container = element.closest('.jspScrollable');
                    jsp = container.data('jsp');

                    // jsp might be another jsp instance than the one, that bound this event
                    // remember: this event is only bound once for all instances.
                    jsp.scrollToElement(element, true);

                    if (container[0].scrollIntoView) {
                        // also scroll to the top of the container (if it is not visible)
                        scrollTop = $(window).scrollTop();
                        elementTop = element.offset().top;
                        if (elementTop < scrollTop || elementTop > scrollTop + $(window).height()) {
                            container[0].scrollIntoView();
                        }
                    }

                    // jsp handled this event, prevent the browser default (scrolling :P)
                    event.preventDefault();
                });
            }

            // Init touch on iPad, iPhone, iPod, Android
            function initTouch()
            {
                var startX,
                    startY,
                    touchStartX,
                    touchStartY,
                    moved,
                    moving = false;

                container.unbind('touchstart.jsp touchmove.jsp touchend.jsp click.jsp-touchclick').bind(
                    'touchstart.jsp',
                    function(e)
                    {
                        var touch = e.originalEvent.touches[0];
                        startX = contentPositionX();
                        startY = contentPositionY();
                        touchStartX = touch.pageX;
                        touchStartY = touch.pageY;
                        moved = false;
                        moving = true;
                    }
                ).bind(
                    'touchmove.jsp',
                    function(ev)
                    {
                        if(!moving) {
                            return;
                        }

                        var touchPos = ev.originalEvent.touches[0],
                            dX = horizontalDragPosition, dY = verticalDragPosition;

                        jsp.scrollTo(startX + touchStartX - touchPos.pageX, startY + touchStartY - touchPos.pageY);

                        moved = moved || Math.abs(touchStartX - touchPos.pageX) > 5 || Math.abs(touchStartY - touchPos.pageY) > 5;

                        // return true if there was no movement so rest of screen can scroll
                        return dX == horizontalDragPosition && dY == verticalDragPosition;
                    }
                ).bind(
                    'touchend.jsp',
                    function(e)
                    {
                        moving = false;
                        /*if(moved) {
                         return false;
                         }*/
                    }
                ).bind(
                    'click.jsp-touchclick',
                    function(e)
                    {
                        if(moved) {
                            moved = false;
                            return false;
                        }
                    }
                );
            }

            function destroy(){
                var currentY = contentPositionY(),
                    currentX = contentPositionX();
                elem.removeClass('jspScrollable').unbind('.jsp');
                elem.replaceWith(originalElement.append(pane.children()));
                originalElement.scrollTop(currentY);
                originalElement.scrollLeft(currentX);

                // clear reinitialize timer if active
                if (reinitialiseInterval) {
                    clearInterval(reinitialiseInterval);
                }
            }

            // Public API
            $.extend(
                jsp,
                {
                    // Reinitialises the scroll pane (if it's internal dimensions have changed since the last time it
                    // was initialised). The settings object which is passed in will override any settings from the
                    // previous time it was initialised - if you don't pass any settings then the ones from the previous
                    // initialisation will be used.
                    reinitialise: function(s)
                    {
                        s = $.extend({}, settings, s);
                        initialise(s);
                    },
                    // Scrolls the specified element (a jQuery object, DOM node or jQuery selector string) into view so
                    // that it can be seen within the viewport. If stickToTop is true then the element will appear at
                    // the top of the viewport, if it is false then the viewport will scroll as little as possible to
                    // show the element. You can also specify if you want animation to occur. If you don't provide this
                    // argument then the animateScroll value from the settings object is used instead.
                    scrollToElement: function(ele, stickToTop, animate)
                    {
                        scrollToElement(ele, stickToTop, animate);
                    },
                    // Scrolls the pane so that the specified co-ordinates within the content are at the top left
                    // of the viewport. animate is optional and if not passed then the value of animateScroll from
                    // the settings object this jScrollPane was initialised with is used.
                    scrollTo: function(destX, destY, animate)
                    {
                        scrollToX(destX, animate);
                        scrollToY(destY, animate);
                    },
                    // Scrolls the pane so that the specified co-ordinate within the content is at the left of the
                    // viewport. animate is optional and if not passed then the value of animateScroll from the settings
                    // object this jScrollPane was initialised with is used.
                    scrollToX: function(destX, animate)
                    {
                        scrollToX(destX, animate);
                    },
                    // Scrolls the pane so that the specified co-ordinate within the content is at the top of the
                    // viewport. animate is optional and if not passed then the value of animateScroll from the settings
                    // object this jScrollPane was initialised with is used.
                    scrollToY: function(destY, animate)
                    {
                        scrollToY(destY, animate);
                    },
                    // Scrolls the pane to the specified percentage of its maximum horizontal scroll position. animate
                    // is optional and if not passed then the value of animateScroll from the settings object this
                    // jScrollPane was initialised with is used.
                    scrollToPercentX: function(destPercentX, animate)
                    {
                        scrollToX(destPercentX * (contentWidth - paneWidth), animate);
                    },
                    // Scrolls the pane to the specified percentage of its maximum vertical scroll position. animate
                    // is optional and if not passed then the value of animateScroll from the settings object this
                    // jScrollPane was initialised with is used.
                    scrollToPercentY: function(destPercentY, animate)
                    {
                        scrollToY(destPercentY * (contentHeight - paneHeight), animate);
                    },
                    // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
                    // the value of animateScroll from the settings object this jScrollPane was initialised with is used.
                    scrollBy: function(deltaX, deltaY, animate)
                    {
                        jsp.scrollByX(deltaX, animate);
                        jsp.scrollByY(deltaY, animate);
                    },
                    // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
                    // the value of animateScroll from the settings object this jScrollPane was initialised with is used.
                    scrollByX: function(deltaX, animate)
                    {
                        var destX = contentPositionX() + Math[deltaX<0 ? 'floor' : 'ceil'](deltaX),
                            percentScrolled = destX / (contentWidth - paneWidth);
                        positionDragX(percentScrolled * dragMaxX, animate);
                    },
                    // Scrolls the pane by the specified amount of pixels. animate is optional and if not passed then
                    // the value of animateScroll from the settings object this jScrollPane was initialised with is used.
                    scrollByY: function(deltaY, animate)
                    {
                        var destY = contentPositionY() + Math[deltaY<0 ? 'floor' : 'ceil'](deltaY),
                            percentScrolled = destY / (contentHeight - paneHeight);
                        positionDragY(percentScrolled * dragMaxY, animate);
                    },
                    // Positions the horizontal drag at the specified x position (and updates the viewport to reflect
                    // this). animate is optional and if not passed then the value of animateScroll from the settings
                    // object this jScrollPane was initialised with is used.
                    positionDragX: function(x, animate)
                    {
                        positionDragX(x, animate);
                    },
                    // Positions the vertical drag at the specified y position (and updates the viewport to reflect
                    // this). animate is optional and if not passed then the value of animateScroll from the settings
                    // object this jScrollPane was initialised with is used.
                    positionDragY: function(y, animate)
                    {
                        positionDragY(y, animate);
                    },
                    // This method is called when jScrollPane is trying to animate to a new position. You can override
                    // it if you want to provide advanced animation functionality. It is passed the following arguments:
                    //  * ele          - the element whose position is being animated
                    //  * prop         - the property that is being animated
                    //  * value        - the value it's being animated to
                    //  * stepCallback - a function that you must execute each time you update the value of the property
                    // You can use the default implementation (below) as a starting point for your own implementation.
                    animate: function(ele, prop, value, stepCallback)
                    {
                        var params = {};
                        params[prop] = value;
                        ele.animate(
                            params,
                            {
                                'duration'	: settings.animateDuration,
                                'easing'	: settings.animateEase,
                                'queue'		: false,
                                'step'		: stepCallback
                            }
                        );
                    },
                    // Returns the current x position of the viewport with regards to the content pane.
                    getContentPositionX: function()
                    {
                        return contentPositionX();
                    },
                    // Returns the current y position of the viewport with regards to the content pane.
                    getContentPositionY: function()
                    {
                        return contentPositionY();
                    },
                    // Returns the width of the content within the scroll pane.
                    getContentWidth: function()
                    {
                        return contentWidth;
                    },
                    // Returns the height of the content within the scroll pane.
                    getContentHeight: function()
                    {
                        return contentHeight;
                    },
                    // Returns the horizontal position of the viewport within the pane content.
                    getPercentScrolledX: function()
                    {
                        return contentPositionX() / (contentWidth - paneWidth);
                    },
                    // Returns the vertical position of the viewport within the pane content.
                    getPercentScrolledY: function()
                    {
                        return contentPositionY() / (contentHeight - paneHeight);
                    },
                    // Returns whether or not this scrollpane has a horizontal scrollbar.
                    getIsScrollableH: function()
                    {
                        return isScrollableH;
                    },
                    // Returns whether or not this scrollpane has a vertical scrollbar.
                    getIsScrollableV: function()
                    {
                        return isScrollableV;
                    },
                    // Gets a reference to the content pane. It is important that you use this method if you want to
                    // edit the content of your jScrollPane as if you access the element directly then you may have some
                    // problems (as your original element has had additional elements for the scrollbars etc added into
                    // it).
                    getContentPane: function()
                    {
                        return pane;
                    },
                    // Scrolls this jScrollPane down as far as it can currently scroll. If animate isn't passed then the
                    // animateScroll value from settings is used instead.
                    scrollToBottom: function(animate)
                    {
                        positionDragY(dragMaxY, animate);
                    },
                    // Hijacks the links on the page which link to content inside the scrollpane. If you have changed
                    // the content of your page (e.g. via AJAX) and want to make sure any new anchor links to the
                    // contents of your scroll pane will work then call this function.
                    hijackInternalLinks: $.noop,
                    // Removes the jScrollPane and returns the page to the state it was in before jScrollPane was
                    // initialised.
                    destroy: function()
                    {
                        destroy();
                    }
                }
            );

            initialise(s);
        }

        // Pluginifying code...
        settings = $.extend({}, $.fn.jScrollPane.defaults, settings);

        // Apply default speed
        $.each(['mouseWheelSpeed', 'arrowButtonSpeed', 'trackClickSpeed', 'keyboardSpeed'], function() {
            settings[this] = settings[this] || settings.speed;
        });

        return this.each(
            function()
            {
                var elem = $(this), jspApi = elem.data('jsp');
                if (jspApi) {
                    jspApi.reinitialise(settings);
                } else {
                    $("script",elem).filter('[type="text/javascript"],:not([type])').remove();
                    jspApi = new JScrollPane(elem, settings);
                    elem.data('jsp', jspApi);
                }
            }
        );
    };

    $.fn.jScrollPane.defaults = {
        showArrows					: false,
        maintainPosition			: true,
        stickToBottom				: false,
        stickToRight				: false,
        clickOnTrack				: true,
        autoReinitialise			: false,
        autoReinitialiseDelay		: 500,
        verticalDragMinHeight		: 0,
        verticalDragMaxHeight		: 99999,
        horizontalDragMinWidth		: 0,
        horizontalDragMaxWidth		: 99999,
        contentWidth				: undefined,
        animateScroll				: false,
        animateDuration				: 300,
        animateEase					: 'linear',
        hijackInternalLinks			: false,
        verticalGutter				: 4,
        horizontalGutter			: 4,
        mouseWheelSpeed				: 0,
        arrowButtonSpeed			: 0,
        arrowRepeatFreq				: 50,
        arrowScrollOnHover			: false,
        trackClickSpeed				: 0,
        trackClickRepeatFreq		: 70,
        verticalArrowPositions		: 'split',
        horizontalArrowPositions	: 'split',
        enableKeyboardNavigation	: true,
        hideFocus					: false,
        keyboardSpeed				: 0,
        initialDelay                : 300,        // Delay before starting repeating
        speed						: 30,		// Default speed when others falsey
        scrollPagePercent			: .8		// Percent of visible area scrolled when pageUp/Down or track area pressed
    };

})(jQuery,this);

if (typeof Object.create !== 'function') {
    /**
     * Creates a new object with the specified prototype object and properties.
     * @param {Object} val
     */
    Object.create = function (val) {
        function F() {}
        F.prototype = val;
        return new F();
    };
};

/**
 * @namespace LMI
 */
var LMI = LMI || {};

LMI.global = this;

/**
 * Returns true if the specified value is undefined. It's the preferred mode for checking whether the value is defined
 * or not.
 * 
 * @param {*} val The value which will be checked.
 * @return {Boolean} Whether the value is defined.
 * @static
 */
LMI.isUndefined = function(val) {
    return typeof val === 'undefined';
};

/**
 * Returns true if the specified value is |null|
 * @param {*} val Variable which will be tested.
 * @return {boolean} Whether variable is null.
 */
LMI.isNull = function(val) {
    return val === null;
};

/**
 * Returns true if the specified value is |null| or undefined or an empty string
 * @param {*} val Variable which will be tested.
 * @return {boolean} Whether variable is null, undefinen or empty string.
 */
LMI.isNullOrEmptyString = function(val) {
    return LMI.isNull(val) || LMI.isUndefined(val) ||
        (LMI.isString(val) && val == '');
}

/**
 * Returns true if the specified value is a string
 * @param {*} val value will be tested.
 * @return {boolean} true if the value is a string.
 */
LMI.isString = function(val) {
  return typeof val == 'string';
};

/**
 * Returns true if the specified value is a bool.
 * @param {*} val Variable to test.
 * @return {bool} Whether variable is bool.
 */
LMI.isBoolean = function(val) {
  return typeof val == 'boolean';
};

/**
 * Returns true if the specified value is a number
 * @param {*} val Variable to test.
 * @return {bool} Whether variable is a number.
 */
LMI.isNumber = function(val) {
  return typeof val == 'number';
};

/**
 * Returns true if the specified value is a function type.
 * @param {*} val Type to test.
 * @return {bool} Whether Type is a function.
 */
LMI.isFunction = function(val) {
   return LMI.typeOf(val) == 'function';
};

/**
 * Returns true if the specified value is an array type.
 * @param {*} val Type to test.
 * @return {bool} Whether Type is a function.
 */
LMI.isArray = function(val) {
   return LMI.typeOf(val) == 'array';
};

/**
 * Returns true if the specified value is an object type.
 * @param {*} val Type to test.
 * @return {bool} Whether Type is a function.
 */
LMI.isObject = function(val) {
   return LMI.typeOf(val) == 'object';
};

/**
 * Returns the type of the specified value. It differs from the typeof operator usage
 * in such that way that try to hide the weaknesses of javascript.
 * Source: http://javascript.crockford.com/remedial.html and its improvements from google.
 * @param value
 */
LMI.typeOf = function(value) {
var s = typeof value;
  if (s == 'object') {
    if (value) {
      // Check these first, so we can avoid calling Object.prototype.toString if
      // possible.
      //
      // IE improperly marshals tyepof across execution contexts, but a
      // cross-context object will still return false for "instanceof Object".
      if (value instanceof Array) {
        return 'array';
      } else if (value instanceof Object) {
        return s;
      }

      // HACK: In order to use an Object prototype method on the arbitrary
      //   value, the compiler requires the value be cast to type Object,
      //   even though the ECMA spec explicitly allows it.
      var className = Object.prototype.toString.call(
          /** @type {Object} */ (value));
      // In Firefox 3.6, attempting to access iframe window objects' length
      // property throws an NS_ERROR_FAILURE, so we need to special-case it
      // here.
      if (className == '[object Window]') {
        return 'object';
      }

      // We cannot always use constructor == Array or instanceof Array because
      // different frames have different Array objects. In IE6, if the iframe
      // where the array was created is destroyed, the array loses its
      // prototype. Then dereferencing val.splice here throws an exception, so
      // we can't use goog.isFunction. Calling typeof directly returns 'unknown'
      // so that will work. In this case, this function will return false and
      // most array functions will still work because the array is still
      // array-like (supports length and []) even though it has lost its
      // prototype.
      // Mark Miller noticed that Object.prototype.toString
      // allows access to the unforgeable [[Class]] property.
      //  15.2.4.2 Object.prototype.toString ( )
      //  When the toString method is called, the following steps are taken:
      //      1. Get the [[Class]] property of this object.
      //      2. Compute a string value by concatenating the three strings
      //         "[object ", Result(1), and "]".
      //      3. Return Result(2).
      // and this behavior survives the destruction of the execution context.
      if ((className == '[object Array]' ||
           // In IE all non value types are wrapped as objects across window
           // boundaries (not iframe though) so we have to do object detection
           // for this edge case
           typeof value.length == 'number' &&
           typeof value.splice != 'undefined' &&
           typeof value.propertyIsEnumerable != 'undefined' &&
           !value.propertyIsEnumerable('splice')

          )) {
        return 'array';
      }
      // HACK: There is still an array case that fails.
      //     function ArrayImpostor() {}
      //     ArrayImpostor.prototype = [];
      //     var impostor = new ArrayImpostor;
      // this can be fixed by getting rid of the fast path
      // (value instanceof Array) and solely relying on
      // (value && Object.prototype.toString.vall(value) === '[object Array]')
      // but that would require many more function calls and is not warranted
      // unless closure code is receiving objects from untrusted sources.

      // IE in cross-window calls does not correctly marshal the function type
      // (it appears just as an object) so we cannot use just typeof val ==
      // 'function'. However, if the object has a call property, it is a
      // function.
      if ((className == '[object Function]' ||
          typeof value.call != 'undefined' &&
          typeof value.propertyIsEnumerable != 'undefined' &&
          !value.propertyIsEnumerable('call'))) {
        return 'function';
      }


    } else {
      return 'null';
    }

  } else if (s == 'function' && typeof value.call == 'undefined') {
    // In Safari typeof nodeList returns 'function', and on Firefox
    // typeof behaves similarly for HTML{Applet,Embed,Object}Elements
    // and RegExps.  We would like to return object for those and we can
    // detect an invalid function by making sure that the function
    // object has a call method.
    return 'object';
  }
  return s;
};

/**
 * Empty function used for default values of callbacks.
 * @return {void} Nothing.
 */
LMI.emptyFunction = function() {};

/**
 * Writes a text in the console (independently from browsers). If you want to use special console methods (assert,
 * debug, count, group, profile, time...etc), you'll have to use the native console object instead of
 * using LMI.log() function.
 * @param {Object|String} message Text which is displayed in console.
 * @param {String} cat Category to use. Can be info, warn, error. The fallback category is log type
 * @example LMI.log('mymessage');LMI.log('msg2','warn');LMI.log('msg','error');
 */
LMI.log = (function() {

    var printByCategory = function (message, cat) {
            switch (cat) {
                case 'info':
                    window.console['info'](message);
                    break;
                case 'warn':
                    window.console['warn'](message);
                    break;
                case 'error':
                    window.console['error'](message);
                    break;
                default:
                    window.console['log'](message);
                    break;
            }
     };

    return function(message, category) {
        if (window.console && window.console['firebug']) {
            // for firebug
            printByCategory(message, category);
        } else if (window.console) {
            // for ie, chrome, safari
            printByCategory(message, category)
        } else if (window.opera) {
            // for opera
            window.opera['postError'](message);
        } else {
            // any other case.
            // TODO: discuss about Client-side Global Logger.
        }
    }

})();

/**
 * Checks a variable and sets a default value if the variable is undefined.
 * Throws an exception if the value is required.
 * @param {Object} value The value that should be checked
 * @param {Object} settings Settings for default function <br />
 *   settings.message: Message in case of missing mandatory value
 *   settings.defaultValue: Default value returned in case of missing value
 */
LMI.checkDefault = function checkDefault(value, settings) {
    if (typeof settings === 'undefined') {
        settings = {};
    }
    var required = typeof settings.required === 'undefined' ? false : settings.required,
            message = typeof settings.message === 'undefined' ? false : settings.message,
            getDefValue = function() {
                if (required) {
                    throw new TypeError(message);
                } else {
                    return settings.defaultValue;
                }
            };

    return typeof value === 'undefined' ? getDefValue() : value;
};

/**
 * Checks if the given value exists in the given object
 * @param {Object} obj Object to be searched for the value
 * @param { * } val Value to check in obj
 * @return {bool} Whether the value exists in the object or not.
 */
LMI.checkValue = function(obj, val) {
    for (var i in obj){
        if (obj[i] === val)
        return true;
    }
    return false;
};

/**
 * Add # to the value if the specified parameter does not start with #.
 * @param {string} val String to be tested.
 * @return {string} string start with #.
 */
LMI.addHashmark = function(val) {
    if (!LMI.isString(val)) {
        return null;
    };
    var length = val.length, result = val;
    if (length !== 0 && val[0] !== '#') {
        result = '#' + val;
    }
    return result;
};

/**
 * Generates a random unique id. (e.g. toc86x, bc2s7g, 46bld8...etc).
 * @return {string} unique id.
 */
LMI.randomId = function() {
    return Math.floor(Math.random() * 2147483648).toString(36);
};

/**
 * Converts a number to hexadecimal form.
 * @param {Number} val Numeric value to convert
 * @return {string} Hex form of the specified number.
 */
LMI.num2hex = function(val) {
    return Number(val).toString(16);
};
/**
 * Converts a hex number to decimal value.
 * @param {string} val Hex number to convert.
 * @return {Number} Numeric value of the hex number.
 */
LMI.hex2num = function(val) {
    return parseInt(val, 16);
};
/**
 * Prevents event bubbling or propagation. CancelBubble is supported only by IE.
 * e.stopPropagation works only in FireFox.
 * @param {Event} e Event object.
 */
LMI.stopEvent = function(e) {
    if (!e) {
        var e = window.event;   // for IE;
    }

    if (e.stopPropagation) {
        e.stopPropagation();
    } else {
        e.cancelBubble = true;      // e.stopPropagation()
    }

    if (e.preventDefault) {
        e.preventDefault();
    } else {
        e.returnValue = false;      // e.preventDefault()
    }

};
/**
 * Returns the frames count.
 */
LMI.frameLevelCount = function() {
    var _window = window,
        level = 0;
    while (_window.parent.location.href != _window.location.href) {
        _window = _window.parent;
        level++;
    }
    return level;
};

// { "dependsOn": ["/Common/Scripts/LMI/lmi.js"] }
/// <reference path="/Common/Scripts/LMI/lmi.js" />

var LMI = LMI || {};
LMI.Data = LMI.Data || {};

// todo: do we need such a big memory eater...or what?
// todo: pagestate should use this hashtable


/**
 * HashTable Javascript implentation.
 * @class HashTable
 */
LMI.Data.HashTable = function () {

    var _cache = { __count: 0 },
        prop;

    function _add(key, value) {
        if(_containsKey(key))
            throw new Error("[HashTable] The key ['" + key + "'] already exists in this instance!");

        _addOrUpdate(key, value);
    };

    function _addOrUpdate(key, value) {
        if (LMI.isUndefined(key))
            throw new Error('[HashTable] key is required for adding value.');

        if (LMI.isUndefined(value))
            throw new Error('[HashTable] value is required');

        if(!_containsKey(key))
            _cache.__count++;

        _cache[key] = value;
    };

    function _count() {
        return _cache.__count;
    };

    function _clear() {
        for (prop in _cache)
            delete _cache[prop];

        _cache.__count = 0;
    };

    function _containsKey(key) {
        return _cache.hasOwnProperty(key);
    };

    function _containsValue(value) {
        throw new Error('[HashTable] not implemented method: containsValue');
    };

    function _item(key) {
        return _cache[key];
    };

    function _keys() {
        var __keys = [];

        for (prop in _cache)
            if(prop !== '__count' && LMI.typeOf !== 'function')
                __keys.push(prop);

        return __keys;
    };

    function _values() {
        var __values = [],
            i;

        for (prop in _cache)
            if(prop !== '__count' && LMI.typeOf !== 'function'){
                //if the value is an array, then we read out the items of it
                if(LMI.typeOf(_cache[prop]) === 'array'){
                    for(i = 0; i < _cache[prop].length; i++)
                    {
                        __values.push(_cache[prop][i])
                    }
                } else {
                    __values.push(_cache[prop]);
                }
            }
        return __values;
    };

    function _remove(key) {
        if (LMI.isUndefined(key))
            throw new Error('[HashTable] key is required for removing.');

        delete _cache[key];
        _cache.__count--;
    };

    function _val(key, value) {
        if (LMI.typeOf(key) === 'object') {
            for (prop in key) {
                _addOrUpdate(prop, key[prop]);
            }
        }

        if (!LMI.isUndefined(value)) {
            _addOrUpdate(key, value);
        }

        return _item(key);
    };

    return {
        /**
         * Adds key/value pair to the instance.
         * @param key Key to add.
         * @param value Value to add.
         */
        add: _add,
        /**
         * Adds or updates if the key exists
         * @param key Key to add.
         * @param value Value to add.
         */
         addOrUpdate: _addOrUpdate,
        /**
         * Empties the cache.
         */
        clear: _clear,
        /**
         * Returns the stored value for the specified key.
         * @param key Key to search for.
         */
        contains: _containsKey,
         /**
          * Returns the stored value for the specified key.
          * @param key Key to search for.
          * @return {bool} True, if the specified key is available.
          */
        containsKey: _containsKey,
        /**
         * Not Implemented.
         * @param value Value to search for.
         */
        containsValue: _containsValue,
        /**
         * Returns the number of the stored items.
         */
        count: _count,
        /**
         * Gets the value of the specified key.
         * @param key Key to search for.
         * @return The stored value for the specified key.
         */
        item: _item,
        /**
         * Returns an array with the keys.
         * @return new array instance.
         */
        keys: _keys,
        /**
         * Returts an Array with the values
         * @return {Array} array of the values
         */
        values: _values,
        /**
         * Remove the value of the specified key and deletes the key itself as well.
         */
        remove: _remove,
        /**
         * Gets or adds or updates the specified key with the specified value.
         * @param {Object} key Key to search for, to add, or to update
         * @param value Value to add
         * @return The value.
         */
        val: _val
    };
};

var LMI = LMI || {};
LMI.UI = LMI.UI || {};

LMI.UI.RENDERMODE = { 'Central': 0, 'G3': 1, 'MOBILEDEVICE': 2 };
// todo: add proper css classes here
LMI.UI.BUTTONS = { 'Default': '0', 'Yellow': '1', 'Green': '2' };

LMI.UI.PANELS = { 'Default': '0', 'Table': '1' };

LMI.UI.DIALOGS = { 'Default': 'cModal', 'Registration': '', 'CentralDarkBlue': 'dModal' };// { "dependsOn": ["/Common/Scripts/LMI/lmi.js", "/Common/Scripts/LMI/string/string.js"] }
/// <reference path="/Common/Scripts/LMI/lmi.js" />
/// <reference path="/Common/Scripts/LMI/string/string.js" />

var LMI = LMI || {};
LMI.UI = LMI.UI || {};

LMI.UI.DomHelpers = (function() {
    function _cloneAttributes(sourceNode, targetNode, overWriteExistingAttributes) {
        var i, item,
            attr = sourceNode.attributes;

        for (i = attr.length; i--; ) {
            item = attr[i];

            // todo: fix it...
            // ie specific part, just copy the supported attributes, and leave the rest behind (below ie8 the attributes collection contains all supported nodes, nut just the specified ones)
            if (sourceNode.hasAttribute && item.specified && LMI.typeOf(targetNode.attributes[item.name]) !== 'undefined') {
                targetNode[item.name] = item.value;
            }
            else {
                targetNode[item.name] = item.value;
            }
        }
    };

    function _hasClass(element, className) {
        var classAttribute = " " + element.className + " ";
        return classAttribute.indexOf(" " + className + " ") !==  -1;
    };

    function _addClass(element, classNames) {
        var classProp = element.className,
            toBeAdded = classNames.split(' '), arrLength = toBeAdded.length,
            i, result;

        if(!classProp && arrLength === 1)
            element.className = classNames;
        else
        {
            result = " " + classProp + " ";
            for(i = arrLength;i--;)
                if (!~result.indexOf(" " + toBeAdded[i] + " "))
                    result += toBeAdded[i] + " ";

            element.className = LMI.Utils.string.trim(result);
        }
    };

    function _removeClass(element, classNames) {
        var classProp = element.className,
            toBeRemoved = classNames.split(' '), arrLength = toBeRemoved.length,
            i, result;

        if (!classProp && arrLength === 1)
            element.className = '';
        else {
            result = " " + classProp + " ";
            for (i = arrLength; i--;)
                result = result.replace(" " + toBeRemoved[i] + " ", " ");

            element.className = LMI.Utils.string.trim(result);
        }
    };

    function _removeAttributeProperty(elementAttribute, propertyName) {
        if (elementAttribute.removeProperty)
            elementAttribute.removeProperty(propertyName);
        else
            elementAttribute[propertyName] = "";
    };

    function _after(newNode, refNode) {
        var nextSibling = refNode.nextElementSibling || refNode.nextSibling,
            parent = refNode.parentNode;

        if (!parent) {
            parent = document.createDocumentFragment();
            parent.appendChild(refNode);
        }
        
        if (nextSibling) {
            parent.insertBefore(newNode, nextSibling);
            return parent;
        } else {
            parent.appendChild(newNode);
            return parent;
        }
    };

    function _before(newNode, refNode) {
        var parent = refNode.parentNode;

        if (!parent) {
            parent = document.createDocumentFragment();
            parent.appendChild(refNode);
        }

        parent.insertBefore(newNode, refNode);
        return parent;
    };

    function _empty(refNode) {
        var firstChild;

        // prepare for IE, cannot get child element if the GC already freed the element, and throws an access denied
        function _tryGetFirstChild(refNode) {
            try {
                return refNode.firstElementChild || refNode.firstChild;
            }
            catch(e) {
                return false;
            }
        }

        while (firstChild = _tryGetFirstChild(refNode)) {
            if(!LMI.isUndefined(firstChild))
                _empty(firstChild);

            refNode.removeChild(firstChild);
        }
    };

    function _remove(refNode) {
        _empty(refNode);
        if (refNode.parentNode) {
            refNode.parentNode.removeChild(refNode);
        }
    };

    // from john resig: http://johnboxall.github.com/test/getStyle.html
    function _getStyle(elem, name) {
        // J/S Pro Techniques p136
        if (elem.style[name]) {
            return elem.style[name];
        } else if (elem.currentStyle) {
            return elem.currentStyle[name];
        }
        else if (document.defaultView && document.defaultView.getComputedStyle) {
            name = name.replace(/([A-Z])/g, "-$1");
            name = name.toLowerCase();
            s = document.defaultView.getComputedStyle(elem, "");
            return s && s.getPropertyValue(name);
        } else {
            return null;
        }
    };

    // note: we can only detect dimensions for visible elements
    function _width(node, arg) {
        var width = node.offsetWidth;
        switch(LMI.typeOf(arg)) {
            case 'boolean':
                if(!arg)
                    return width;
                else
                    return width + parseInt(_getStyle(node, 'marginLeft'), 10) + parseInt(_getStyle(node, 'marginRight'), 10);

            case 'number':
                node.style.width = arg + 'px';
                break;

            default:
                return width;
        };
    };

    // note: we can only detect dimensions for visible elements
    function _height(node, arg) {
        var height = node.offsetHeight;
        switch(LMI.typeOf(arg)) {
            case 'boolean':
                if(!arg)
                    return height;
                else
                    return height + parseInt(_getStyle(node, 'marginTop'), 10) + parseInt(_getStyle(node, 'marginBottom'), 10);

            case 'number':
                node.style.height = arg + 'px';
                break;

            default:
                return height;
        };
    };

    function _viewPort() {
        var viewPort,
            docEl = document.documentElement,
            body = document.getElementsByTagName('body')[0];

        if(!LMI.isUndefined(window.innerWidth)) {
            viewPort = { width: window.innerWidth, height: window.innerHeight };
        } else if (!LMI.isUndefined(docEl) && !LMI.isUndefined(docEl.clientWidth) && docEl.clientWidth != 0) {
            viewPort = { width: docEl.clientWidth, height: docEl.clientHeight };
        } else {
            viewPort = { width: body.clientWidth, height: body.clientHeight };
        }
        return viewPort;
    }

    function _getElementsByClassName(classname) {
        if (document.getElementsByClassName) { // use native implementation if available
            return document.getElementsByClassName(classname);
        } else {
            var classElements = [],
                els = document.getElementsByTagName("*"),
                elsLen = els.length,
                pattern = new RegExp("(^|\\s)" + classname + "(\\s|$)"),
                i,
                j;

            for (i = 0, j = 0; i < elsLen; i++) {
                if (pattern.test(els[i].className)) {
                    classElements[j] = els[i];
                    j++;
                }
            }
            return classElements;
        }
    };

    return {
        cloneAttributes: _cloneAttributes,
        hasClass : _hasClass,
        addClass: _addClass,
        removeClass: _removeClass,
        removeAttributeProperty: _removeAttributeProperty,
        after: _after,
        before: _before,
        empty: _empty,
        remove: _remove,
        width: _width,
        height: _height,
        getStyle: _getStyle,
        viewPort: _viewPort,
        getElementsByClassName: _getElementsByClassName
    };
}());// { "dependsOn": ["/Common/Scripts/LMI/LMI.js", "/Common/Scripts/LMI/data/hashtable.js"] }
/// <reference path="/Common/Scripts/LMI/lmi.js" />
/// <reference path="/Common/Scripts/LMI/data/hashtable.js" />

var LMI = LMI || {};
LMI.UI = LMI.UI || {};

LMI.UI.Events = (function () {

    var _resizeTimeout,
        _resizeEventsCollection,
        _keys = {
            ALT: 18,
            ARROW_LEFT: 37,
            ARROW_UP: 38,
            ARROW_RIGHT: 39,
            ARROW_DOWN: 40,
            CTRL: 17,
            ENTER: 13,
            END: 35,
            HOME: 36,
            TAB: 9,
            SHIFT: 16,
            PAGE_UP: 33,
            PAGE_DOWN: 34
        };

    function _bind(elem, eventName, delegate) {
        if (LMI.isUndefined(elem))
            throw new Error("[Event - bind] dom element must be defined.");

        if (LMI.isUndefined(eventName))
            throw new Error("[Event - bind] eventName must be defined.");

        if (LMI.isUndefined(delegate))
            throw new Error("[Event - bind] delegate must be defined.");

        if (elem.addEventListener) {
            eventName = /on$/.test(eventName) ? eventName.replace("on", "") : eventName;
            elem.addEventListener(eventName, delegate, false);
        }
        else if (elem.attachEvent) {
            eventName = /on$/.test(eventName) ? eventName : "on" + eventName;
            elem.attachEvent(eventName, delegate);
        }
    };

    function _unbind(elem, eventName, delegate) {
        if (LMI.isUndefined(elem))
            throw new Error("[Event - bind] dom element must be defined.");

        if (LMI.isUndefined(eventName))
            throw new Error("[Event - bind] eventName must be defined.");

        if (LMI.isUndefined(delegate))
            throw new Error("[Event - bind] delegate must be defined.");

        if (elem.detachEvent) {
            eventName = /on$/.test(eventName) ? eventName : "on" + eventName;
            elem.detachEvent(eventName, delegate);
        }
        else {
            eventName = /on$/.test(eventName) ? eventName.replace("on", "") : eventName;
            elem.removeEventListener(eventName, delegate, false);
        }
    };

    function _bindEventCollection(eventCollection) {
        var i,
            item;

        for (i = eventCollection.length; i--; ) {
            item = eventCollection[i];
            _bind(item.element, item.event, item.handler);
        }
    };

    function _unbindEventCollection(eventCollection) {
        var i,
            item;

        for (i = eventCollection.length; i--; ) {
            item = eventCollection[i];
            _unbind(item.element, item.event, item.handler);
            item.handler = null;
            delete eventCollection[i];
        }
    };

    function _getResizeEventsCollection() {
        if (LMI.isUndefined(_resizeEventsCollection))
            return _resizeEventsCollection = new LMI.Data.HashTable();

        return _resizeEventsCollection;
    };

    function _addWindowResize(eventKey, resizeDelegate) {
        _getResizeEventsCollection().add(eventKey, resizeDelegate);
    };

    function _removeWindowResize(eventKey) {
        _getResizeEventsCollection().remove(eventKey);
    };

    function _fireWindowResizeEvents(eventsCollection) {
        var eventKeys = eventsCollection.keys();
        for (i = eventKeys.length; i--; ) {
            eventsCollection.item(eventKeys[i])();
        }
    };

    function _registerWindowResize() {
        window.clearTimeout(_resizeTimeout);
        _resizeTimeout = window.setTimeout(function () {
            _fireWindowResizeEvents(_getResizeEventsCollection());
        }, 50);
    };

    _bind(window, 'resize', _registerWindowResize);

    function _stopPropagation(event) {
        if (!event)
            event = window.event;

        if (event.stopPropagation)
            event.stopPropagation();
        else
            event.cancelBubble = true;
    };

    function _preventDefault(event) {
        if (!event)
            event = window.event;

        if (event.preventDefault)
            event.preventDefault();
        else
            event.returnValue = false;
    };

    function _eventTarget(event) {
        if (!event)
            event = window.event;

        return event.target || event.srcElement;
    };

    return {
        bind: _bind,
        unbind: _unbind,
        bindEvents: _bindEventCollection,
        unbindEvents: _unbindEventCollection,
        addWindowResize: _addWindowResize,
        removeWindowResize: _removeWindowResize,
        stopPropagation: _stopPropagation,
        preventDefault: _preventDefault,
        eventTarget: _eventTarget,
        keys: _keys
    };
} ());// { "dependsOn": ["/Common/Scripts/LMI/lmi.js", "/Common/Scripts/LMI/data/hashtable.js", "/Common/Scripts/LMI/ui/ui.js", "/Common/Scripts/LMI/dom/dom.js"] }
/// <reference path="/Common/Scripts/LMI/lmi.js" />
/// <reference path="/Common/Scripts/LMI/data/hashtable.js" />
/// <reference path="/Common/Scripts/LMI/ui/ui.js" />
/// <reference path="/Common/Scripts/LMI/dom/dom.js" />

/**
 * @namespace
 */
LMI = LMI || {};
/**
 * @namespace
 */

LMI.UI = LMI.UI || {};
/**
 * @namespace
 */
LMI.UI.Controls = LMI.UI.Controls || {};

// TODO: maybe the LMI.UI.Control would be fine???

/**
 * Base class for UI controls.
 * @class
 * @param {Node} element DOMElement to wrap
 * @param {Object} args Arguments
 */
LMI.UI.Controls.Control = function (element, args) {

    if (LMI.isUndefined(element))
        throw new Error('[BaseControl] missing _element.');

    if (LMI.isUndefined(args))
        args = {};

    // properties
    var _element = element,
        _container = null,
        _hashTable,
        _displayStyleKey = '_display',
        _renderMode;

    function _getHashTableInstance() {
        if (LMI.isUndefined(_hashTable))
            return _hashTable = new LMI.Data.HashTable();

        return _hashTable;
    };

    function _hide(_elem) {
        if (LMI.isUndefined(_elem))
            return;

        _getHashTableInstance().val(_displayStyleKey, _elem.style.display);
        _elem.style.display = "none";
    };

    function _dispose(_elem) {
        throw new Error("[BaseControl] _dispose should be implemented in inherited member.");
    };

    // TODO: too long return fnc should be moved here as private impl..

    return {
        /**
         * Gets or sets the RenderMode for the control.
         * @param value Central or G3
         */
        renderMode : function (value) {
            if (!LMI.isUndefined(value))
                return _renderMode = value;

            if (!LMI.isUndefined(_renderMode))
                return _renderMode;

            if (!LMI.isUndefined(args.renderMode))
                return _renderMode = args.renderMode;
            else
                return _renderMode = LMI.UI.RENDERMODE.Central;
        },

        /**
         * Gets or sets the _element of the control.
         * @param value  
         */
        getElement : function (value) {
            if (!LMI.isUndefined(value))
                return _element = value;

            return _element;
        },
        /**
         * Gets or sets the _container element of the control.
         * @param value
         */
        getContainer : function (value) {
            if (!LMI.isUndefined(value))
                return _container = value;

            return _container;
        },
        /**
         * Abstract function. It should be should be implemented in inherited class.
         */
        getValue : function () {
            throw new Error("[BaseControl] getValue should be implemented in inherited member.");
        },
        /**
         * Abstract function. It should be should be implemented in inherited class.
         */
        setValue : function () {
            throw new Error("[BaseControl] setValue should be implemented in inherited member.");
        },
        /**
         * Displays the DOMElement.
         */
        show : function () {
            var _data = _getHashTableInstance(),
                _elem = _container || _element,
                _elemstyle = _elem.style,
                _oldstyle = _data.item[_displayStyleKey];

            if(!LMI.isUndefined(_oldstyle) && _oldstyle !== "") {
                _elemstyle.display = _data.item[_displayStyleKey];
            }
            else
                LMI.UI.DomHelpers.removeAttributeProperty(_elemstyle, "display");
        },
        /**
         * Hides the DOMElement.
         */
        hide : function () {
            _hide(_container || _element);
        },
        /**
         * Enables the control.
         */
        enable : function () {
            var _elem = _container || _element;
            _elem.className = _elem.className.replace("disabled", "");

            _element.removeAttribute('disabled');
        },
        /**
         * Disbles the control.
         */
        disable : function () {
            var _elem = _container || _element;
            if(_elem.className.indexOf("disabled") === -1)
                _elem.className += " disabled";

            _element.setAttribute('disabled', 'true');
        },

        /**
         * Removes the DOMElement, events, and dependencies.
         */
        remove : function () {
            // hide element, before modifying the underlying dom structure to avoid reflows
            var _elem = _container || _element;
            _hide(_elem);
            _dispose(_elem);
            LMI.UI.DomHelpers.remove(_elem);
            _getHashTableInstance().clear();
        },
        /**
         * 
         * @param delegate
         */
        setDisposeDelegate : function (delegate)
        {
            _dispose = delegate;
        },
        /**
         * Abstract function. It should be implemented in inherited class.
         */
        redraw : function () {
            throw new Error("[BaseControl] redraw should be implemented in inherited member.");
        },
        /**
         * Returns the control inner cache instance.
         */
        data: function() {
            return _getHashTableInstance();
        }
    }
};

/**
 * Creates one or more instance of the specified control.
 * @param {Object} _object The control to be instantiated.
 * @param {Object} _args Arguments to be passed to the control.
 * @example
 * var ctl = LMI.UI.Control;
 * var newButton = ctl.create(ctl.Controls.Button, { element: document.getElementById('btn1') }
 * var newButton = ctl.create(ctl.Controls.Button, { element: [document.getElementById('btn1'), ..., jQuery('.class')] }
 */
LMI.UI.Controls.create = function(_object, _args) {
    var _instances = [],
    i, _elemlength = _args.element.length, j;

    if (LMI.isUndefined(_elemlength) || !LMI.isUndefined(_elemlength) && _elemlength === 1)
        return new _object(_args.element, _args.args);

    i = _elemlength;
    while(i--) {
        j = _elemlength - i - 1;
        _instances[j] = new _object(_args.element[j], _args.args);
    }

    return _instances;
};// { "dependsOn": ["/Common/Scripts/LMI/lmi.js", "/Common/Scripts/LMI/dom/dom.js", "/Common/Scripts/LMI/ui/events.js"] }
/// <reference path="/Common/Scripts/LMI/lmi.js" />
/// <reference path="/Common/Scripts/LMI/dom/dom.js" />
/// <reference path="/Common/Scripts/LMI/ui/events.js" />

LMI = LMI || {};
LMI.UI = LMI.UI || {};
LMI.UI.Controls = LMI.UI.Controls || {};

var JM = JM || {};
JM.Scheduler = JM.Scheduler || {};


/**
 * @class
 * @param element
 * @param args
 */
LMI.UI.Controls.Dialog = function(element, args) {

    var
        _random = LMI.randomId(),
        _id = _random + '_dialog',
        _control,
        _dialogContent = document.createElement('div'),
        _box = _dialogContent.cloneNode(false),
        _inr = _dialogContent.cloneNode(false),
        _closingX = _dialogContent.cloneNode(false),
        _eventCollection = [],
        _onHideCallbackFn,
        _elementId = LMI.isUndefined(element) ? _random : element.id,
        _dom = LMI.UI.DomHelpers,
        _events = LMI.UI.Events,
        __showClose;

    if(LMI.isNull(element) || LMI.isUndefined(element)) {
        element = document.createElement('div');
    }

    _control = new LMI.UI.Controls.Control(element, args);

    $("body").append(element);

    function _disposeDelegate() {
        _events.unbindEvents(_eventCollection);
        delete _eventCollection;
    }

    function _showCloseInternal(value) {
        if(value) {
            _closingX.style.display = "block";
            _eventCollection[0] = { element:_closingX, event: "click", handler: _hide};
            _events.bind(_eventCollection[0].element, _eventCollection[0].event, _eventCollection[0].handler);
        } else {
            _closingX.style.display = "none";
            if(!LMI.isUndefined(_eventCollection[0])) {
                _events.unbind(_eventCollection[0].element, _eventCollection[0].event, _eventCollection[0].handler);
                delete _eventCollection[0];
            }
        }
    }

    function _showClose(value) {
        if (!LMI.isUndefined(value)) {
            _showCloseInternal(value);
            return __showClose = value;
        }

        if (!LMI.isUndefined(__showClose))
            return __showClose;

        if (!LMI.isUndefined(args) && !LMI.isUndefined(args.showClose))
            return __showClose = args.showClose;
        else
            return __showClose = true;
    }

    function _redraw() {
        var elem = _control.getElement(),
            container = _dialogContent.cloneNode(false),
            dim = _dialogContent.cloneNode(false),
            oDisplay = elem.style.display;

        // hide original element
        elem.style.display = "none";
        
        container.setAttribute('id', elem.id.length === 0 ? _id : elem.id+'_dialog');

        container.className = "dialog";
        container.style.display = "none";

        _control.getContainer(container);

        dim.className = "dimmer";
        container.appendChild(dim);

        _box.className = "box";
        container.appendChild(_box);

        _inr.className = "inner";
        _box.appendChild(_inr);

        _closingX.className = "sprt modalclosebutton";
        _closingX.innerHTML = '';
        _inr.appendChild(_closingX);

        _inr.appendChild(_dialogContent);

        _dom.after(container, elem);

        // move element to new position, remove hiding
        _dialogContent.appendChild(elem);
        elem.style.display = oDisplay;

        _events.bindEvents(_eventCollection);
        _showCloseInternal(_showClose());
    }

    function _checkVerticalPosition() {
        var viewPort = _dom.viewPort(),
            height = _dom.height(_inr),
            topMargin = parseInt(_dom.getStyle(_box, "marginTop"), 10);

        if(viewPort.height < (height + (topMargin * 2)))
            _dom.addClass(_box, "big");
    }

    function strip(val) {
        var tmp = document.createElement("DIV");
        tmp.innerHTML = val;
        return tmp;
    }

    function _updateContent(content) {
        var c;
        _dom.empty(_dialogContent);
        if (LMI.isString(content)) {
            c = strip(content);
            _dialogContent.appendChild(c);
            return;
        } else if (content.nodeType in [1,3]) {
            c = content;
        } else {
            throw Error('[content] parameter contains incorrect value.');
        }

        _dialogContent.appendChild(c);

        //storing the new id of the content
        _elementId = content.id;

        //updating the dialogContainer's id
        _control.getContainer().id = _elementId + '_dialog';
    }

    function _show() {
        _control.getContainer().style.display = "block";
        _checkVerticalPosition();
    }

    function _hide() {
        if(!LMI.isUndefined(_onHideCallbackFn))
            _onHideCallbackFn.call(_control);
    
        _control.getContainer().style.display = "none";
    }

    function _onHideCallBack(fn){
        if(!LMI.isFunction(fn) || LMI.isUndefined(fn))
            throw Error ('OnHideCallback param should be a function');

        _onHideCallbackFn = fn;
    }

    function _getId(){
        return _elementId;
    }

    // method overrides and extension methods
    _control.redraw = _redraw;
    _control.show = _show;
    _control.hide = _hide;
    _control.updateContent = _updateContent;
    _control.showClose = _showClose;
    _control.onHideCallback = _onHideCallBack;
    _control.getId = _getId;

    _control.setDisposeDelegate(_disposeDelegate);

    // init dom manipulation
    _control.redraw();

    return _control;
};

LMI.UI.Controls.DialogUtils = (function(){
    var
        instance,
        oneButtonMarkup = $('<div id="jmDialogContainer">\
                                    <h2 class="title" />\
                                    <div class="msg" />\
                                    <div class="buttons">\
                                        <button type="button" class="jm" id="first" />\
                                    </div>\
                              </div>');
        //twoButtonMarkup = oneButtonMarkup.find('.buttons').append($('<button type="button" class="jm" id="second" />'));


    /**
     *
     * @param {Object} opts required: title, msg, btntext, btnhandler
     */
    function initOneButtonDialog(opts){
        var
            t = opts.title || 'Deafult Title',
            m = opts.msg || 'Deafult Msg',
            bt = opts.btntext || 'Deafult Button',
            c = opts.context || this;


        var markup = oneButtonMarkup.clone();

            markup
                .find('.title').text(t).end()
                .find('.msg').html(m).end()
                .find('#first')
                    .text(bt)
                    .click(function(){
                    //debugger;
                        try {
                            if (opts.btnhandler) {
                                opts.btnhandler.call(c);
                            }

                            if (!LMI.isUndefined(instance))
                                instance.hide();
                        } catch(e) {
                            throw new Error(e.message);
                        }
                    });

        instance = JM.Scheduler.App.showDialog(markup[0]);
    }

    function initTwoButtonDialog(opts){
        var
            t = opts.title || 'Deafult Title',
            m = opts.msg || '',
            bt1 = opts.btntext1 || 'Deafult Button 1',
            bt2 = opts.btntext2 || 'Deafult Button 2',
            handler1 = opts.btnhandler1,
            handler2 = opts.btnhandler2,
            c = opts.context || this;

        var markup = oneButtonMarkup.clone();
        markup
            .find('.buttons')
            .append($('<button type="button" class="jm" id="second" />'));

        markup
            .find('.title').text(t).end()
            .find('.msg').html(m).end()
            .find('#first')
                .text(bt1)
                .click(function(){
                    //debugger;
                    try {
                        if (handler1) {
                            handler1.call(c);
                        }
                        if (!LMI.isUndefined(instance))
                            instance.hide();
                    } catch(e) {
                        throw new Error(e.message);
                    }
                }).end()
            .find('#second')
                .text(bt2)
                .click(function(){

                    try {
                        if(handler2)
                            handler2.call(c);

                        if (!LMI.isUndefined(instance))
                            instance.hide();
                    } catch(e) {
                        throw new Error(e.message);
                    }
            });

        instance = JM.Scheduler.App.showDialog(markup[0]);
    }



    return {
        getOneButtonDialog: initOneButtonDialog,
        getTwoButtonDialog: initTwoButtonDialog
    }

})();
var LMI = LMI || {};
LMI.Connection = LMI.Connection || {};
LMI.Connection.AjaxRequest = LMI.Connection.AjaxRequest || {};

// Global dialog instance, need to refactor out to App later
var GLOBAL_ajaxRequestDialog = GLOBAL_ajaxRequestDialog || null;

LMI.Connection.AjaxRequest.genericWSCall = function (isAsync, request, onSuccess, onFailed, onComplete, context, url, showWaitAnimationAfter, unformattedResponse, customAnimationMarkup) {
    var serializedRequest2 = JSON.stringify(request);

    // We create callbacks with context so when successCallback will be called it calls onSuccess with context
    var successCallback = onSuccess || null;
    var errorCallback = onFailed || null;
    var msg = "Please wait";

    // Global variable hack for singleton instance
    // This should be refactored to Joinme.App later
    if (LMI.isUndefined(GLOBAL_ajaxRequestDialog) || LMI.isNull(GLOBAL_ajaxRequestDialog)) {
        var defaultMarkup = $('<div class="pleasewait">\
                            <div class="sprt_l iconbutton animIcon loadinganim inliner" id="animIcon_66950">&nbsp;</div>\
                            <div class="inliner txt">' + msg + ' ...</div>\
                            </div>');

        GLOBAL_ajaxRequestDialog = LMI.UI.Controls.Dialog(defaultMarkup[0], { showClose: false });
    }

    var timer = (showWaitAnimationAfter > 0) ? setTimeout(showDialog, showWaitAnimationAfter) : null;

    jQuery.ajax({
        type: "POST",
        contentType: "application/json; charset=utf-8",
        url: url,
        data: serializedRequest2,
        //context: context, context is not needed because context will be passed to onSuccess from successCallback
        dataType: "json",
        async: isAsync,
        success: function (response) {
            // Quick fix for Purchase;
            // Tmp solution for old ASMX webservices which called by new AjaxRequest implementation;
            // Needs to be removed later;
            if (response != null && response.d != null && response.d.iError != null) {
                if (response.d.iError == 2)
                    top.window.location = '/login';
                else if (response.d.iError == 999)
                    window.location = '/ErrorPages/UnknownError.aspx';
            }

            if (onComplete != null) {
                onComplete();
            }

            hideDialog(timer);

            if (successCallback == null) {
                return;
            }

            var responseEmpty = LMI.Connection.AjaxRequest._isEmpty(response);

            if (responseEmpty) {
                successCallback.call(context);
            }
            // For the joinme json.d responses
            else if (unformattedResponse) {
                successCallback.call(context, response);
            } else {
                // This will be the property name in response which contains the result
                var resultPropertyName = url.substr(url.lastIndexOf("/") + 1) + "Result";

                var result = response[resultPropertyName];
                successCallback.call(context, result);
            }
        },
        error: function (message) {
            if (onComplete != null) {
                onComplete();
            }

            hideDialog(timer);

            var error = JSON.parse(message.responseText);
            switch (error.StatusCode) {
                case 1:
                case 100001:
                    jQuery(window).unbind();
                    window.top.location = error.Details;
                    break;
                default:
                    if (errorCallback != null)
                        errorCallback.call(context, error);
                    break;
            }
        }
    });

    function hideDialog(timer) {
        if (LMI.isUndefined(GLOBAL_ajaxRequestDialog) || LMI.isNull(GLOBAL_ajaxRequestDialog))
            LMI.log("Ajax Request Dialog not initialized before called show", 'error');

        clearTimeout(timer);
        GLOBAL_ajaxRequestDialog.hide();
    }

    function showDialog() {
        if (LMI.isUndefined(GLOBAL_ajaxRequestDialog) || LMI.isNull(GLOBAL_ajaxRequestDialog))
            LMI.log("Ajax Request Dialog not initialized before called show", 'error');

        GLOBAL_ajaxRequestDialog.show();
    }
};

LMI.Connection.AjaxRequest._isEmpty = function(object) {
    for (var property in object) {
        if (object.hasOwnProperty(property)) {
            return false;
        }
    }
    return true;
};// References
// #region

/// <reference path="/Common/Scripts/3rdParty/jquery.js" />
/// <reference path="/Common/Scripts/3rdParty/jquery-1.4.1-vsdoc.js" />

// #endregion

// Global variables & functions
// #region



var aLocalizations = {};

var requiredFlashPlayerVersion = '9.0.124';

var b_PurchaseBackEvent = false;

var WebServices =
{
    'Purchase': '/Purchase/PurchaseWS.asmx/',
    'Common': '/Ajax/Backend.asmx/'
}

var PagesWhoRequireCSSFirst = {};

var KeyCodes = {
    '-': '109, 173, 189',
    'Space': '32',
    '.': '190, 46',
    '+': '',
    'x': '',
    '(': '',
    ')': '',
    '[': '91',
    ']': '93',
    'Esc': '27',
    '_':'109, 173, 189',
    'BackSpace':'8',
    'Delete':'46',
    'Enter':'13'
};

var SessionType =
{
    'IE8': 'IE8',
    'IE9': 'IE9',
    'IE11': 'IE11',
    'Firefox': 'FF',
    'Opera': 'Opera',
    'Chrome': 'Chrome',
    'Safari': 'Safari'
};

var PaltformType =
{
    'MAC': 'MAC',
    'Windows': 'Windows'
};

var b_ScrollDragPreventer = false;

var logoutURL = "";
var loginURL = "";

var emailValidatorRegexp = new RegExp("^((?!(.+\\.\\.+))(?!(.+\\.@+))(?!(^\\.))([\\w-+!&*#~%=\\$\\.']+))@((?:[\\w-]+\\.)*\\w[\\w-]*)\\.([a-z]{2,})$", "i");

function focusElement($element)
{
    setTimeout(function () {
        if ($element.offsetWidth && $element.offsetHeight && document.activeElement && document.activeElement != $element) {
            $element.focus();
        }
    });
}

// Obsoluted features
// #region
var c_j_ContentPages =
{
    c_s_Main: '/Main.aspx',
    c_s_MyJoinMe: '/MyJoinMe.aspx'
};

var c_j_BackendPages =
{
    c_s_Download: '/Download.aspx',
    c_s_Refresher: '/Refresher.aspx'
};

var c_j_ErrorPages =
{
    c_s_PageNotFound: '/ErrorPages/PageNotFound.aspx',
    c_s_UnknownError: '/ErrorPages/UnknownError.aspx'
};
// #endregion

// #endregion

// JoinMe namespace
// #region
/// <value name="JoinMe">Common</value>
var JoinMe = new function JoinMe()
{
    this.commonSingletonInstance = null;

    // Get the instance of the class
    // If there's none, instanciate one
    var getInstance = function ()
    {
        if (!this.commonSingletonInstance)
        {
            this.commonSingletonInstance = new createInstance();
        }
        return this.commonSingletonInstance;
    };

    // Create an instance of the Cats class
    var createInstance = function ()
    {
        /// <field name="Browser">Browser information</field>
        var $this = this;

        // Browser
        // #region
        this.Browser = new function ()
        {
            /// <summary>
            /// Browser information
            /// </summary>
            /// <field name="sUserAgent">Browsers user agent in lower case</field>
            /// <field name="bHasUserAgent">Flag indicating whether user agent exists</field>
            /// <field name="bHasDotNet2OrLater">Flag indicating whether user has .NET 2.0 or higher</field>
            /// <field name="bIsIE">Flag indicating whether users browser is IE</field>
            this.sUserAgent = navigator.userAgent ? navigator.userAgent.toLowerCase() : "";
            this.bHasUserAgent = this.sUserAgent != "";
            this.bHasDotNet2OrLater = this.bHasUserAgent ? this.sUserAgent.match(/\.net clr [2-3.]+|.net[4-9]/g) : false;
            this.b_JavaInstalled = false; // EXP-4789
            this.bIsIE = (this.sUserAgent.indexOf("msie") != -1 || this.sUserAgent.indexOf("trident") != -1);

            // SessionType
            // #region
            var sessionType = SessionType.Safari; // 99% of undetected browsers are safari based browsers
            if (this.sUserAgent.indexOf("trident/7") != -1)
                sessionType = SessionType.IE11;
            else if (this.sUserAgent.indexOf("msie 9") != -1)
                sessionType = SessionType.IE9;
            else if (this.sUserAgent.indexOf("msie 8") != -1)
                sessionType = SessionType.IE8;
            else if (this.sUserAgent.indexOf("firefox/") != -1)
                sessionType = SessionType.Firefox;
            else if (this.sUserAgent.indexOf("opera/") != -1)
                sessionType = SessionType.Opera;
            else if (this.sUserAgent.indexOf("chrome/") != -1)
                sessionType = SessionType.Chrome;
            else if (this.sUserAgent.indexOf("safari/") != -1)
                sessionType = SessionType.Safari;
            this.eSessionType = sessionType;
            // #endregion

            // PlatformType
            // #region
            var platformType = PaltformType.Windows;
            if (this.sUserAgent.indexOf("mac os x") != -1)
                platformType = PaltformType.MAC;
            else
                platformType = PaltformType.Windows;
            this.ePlatformType = platformType;
            // #endregion

            //Flash
            // #region
            this.FlashVersion = function ()
            {
                var flashVesrion = swfobject.getFlashPlayerVersion();
                return flashVesrion.major + '.' + flashVesrion.minor;
            };
            this.FullFlashVersion = function ()
            {
                var flashVesrion = swfobject.getFlashPlayerVersion();
                return flashVesrion.major + '.' + flashVesrion.minor + '.' + flashVesrion.release;
            };
            this.bHasRequiredFlashVersion = function () { return swfobject.hasFlashPlayerVersion(requiredFlashPlayerVersion) };
            // #endregion

            this.TimeZone = new function ()
            {

                // Set default values
                this.offset = 0;
                this.isDSTExists = false;
                this.DSTstartDate = new Date(Date.UTC(2000, 1, 0, 0, 0, 0, 0));
                this.DSTendDate = new Date(Date.UTC(2000, 1, 0, 0, 0, 0, 0));

                // http://www.michaelapproved.com/articles/daylight-saving-time-dst-detect/
                function TimezoneDetect()
                {
                    var dtDate = new Date('1/1/' + (new Date()).getUTCFullYear());
                    // Set initial offset high so it is adjusted on the first attempt
                    var intOffset = 10000,
                        intMonth,
                        intHoursUtc,
                        intHours,
                        intDaysMultiplyBy;

                    // Go through each month to find the lowest offset to account for DST
                    for (intMonth = 0; intMonth < 12; intMonth++)
                    {
                        // Go to the next month
                        dtDate.setUTCMonth(dtDate.getUTCMonth() + 1);

                        // To ignore daylight saving time look for the lowest offset.
                        // Since, during DST, the clock moves forward, it'll be a bigger number.
                        if (intOffset > (dtDate.getTimezoneOffset() * (-1)))
                            intOffset = (dtDate.getTimezoneOffset() * (-1));
                    }
                    return intOffset;
                }

                // http://www.michaelapproved.com/articles/timezone-detect-and-ignore-daylight-saving-time-dst/
                // Find start and end of DST
                function DstDetect()
                {
                    var dtDstDetect = new Date(),
                        dtDstStart = '',
                        dtDstEnd = '',
                    // Temp date hold
                        dtDstStartHold = '',
                    // 366 (include leap year) * 2 (for two years)
                        intYearDayCount = 732,
                        intHourOfYear = 1,
                        intDayOfYear,
                    // Custom function. Make sure you include it.
                        intOffset = TimezoneDetect();

                    // Start from a year ago to make sure we include any previously starting DST
                    dtDstDetect = new Date()
                    dtDstDetect.setUTCFullYear(dtDstDetect.getUTCFullYear() - 1);
                    dtDstDetect.setUTCHours(0, 0, 0, 0);

                    // Going hour by hour through the year will detect DST with shorter code but that could result in 8760
                    // FOR loops and several seconds of script execution time. Longer code narrows this down a little.
                    // Go one day at a time and find out approx time of DST and if there even is DST on this computer.
                    // Also need to make sure we catch the most current start and end cycle.
                    for (intDayOfYear = 1; intDayOfYear <= intYearDayCount; intDayOfYear++)
                    {
                        dtDstDetect.setUTCDate(dtDstDetect.getUTCDate() + 1);

                        if ((dtDstDetect.getTimezoneOffset() * (-1)) != intOffset && dtDstStartHold == '')
                            dtDstStartHold = new Date(dtDstDetect);

                        if ((dtDstDetect.getTimezoneOffset() * (-1)) == intOffset && dtDstStartHold != '')
                        {
                            dtDstStart = new Date(dtDstStartHold);
                            dtDstEnd = new Date(dtDstDetect);
                            dtDstStartHold = '';

                            // DST is being used in this timezone. Narrow the time down to the exact hour the change happens
                            // Remove 48 hours (a few extra to be on safe side) from the start/end date and find the exact change point
                            // Go hour by hour until a change in the timezone offset is detected.
                            dtDstStart.setUTCHours(dtDstStart.getUTCHours() - 48);
                            dtDstEnd.setUTCHours(dtDstEnd.getUTCHours() - 48);

                            // First find when DST starts
                            for (intHourOfYear = 1; intHourOfYear <= 48; intHourOfYear++)
                            {
                                dtDstStart.setUTCHours(dtDstStart.getUTCHours() + 1);

                                // If we found it then exit the loop. dtDstStart will have the correct value left in it.
                                if ((dtDstStart.getTimezoneOffset() * (-1)) != intOffset)
                                    break;
                            }

                            // Now find out when DST ends
                            for (intHourOfYear = 1; intHourOfYear <= 48; intHourOfYear++)
                            {
                                dtDstEnd.setUTCHours(dtDstEnd.getUTCHours() + 1);

                                // If we found it then exit the loop. dtDstEnd will have the correct value left in it.
                                if ((dtDstEnd.getTimezoneOffset() * (-1)) != (intOffset + 60))
                                    break;
                            }

                            // Check if DST is currently on for this time frame. If it is then return these values.
                            // If not then keep going. The function will either return the last values collected
                            // or another value that is currently in effect
                            if ((new Date()).getTime() >= dtDstStart.getTime() && (new Date()).getTime() <= dtDstEnd.getTime())
                                return new Array(dtDstStart, dtDstEnd);
                        }
                    }
                    return new Array(dtDstStart, dtDstEnd);
                }
                this.offset = TimezoneDetect();
                this.TimeZoneInfo = DstDetect();

                if (this.TimeZoneInfo[0].toString() != '' && this.TimeZoneInfo[1].toString() != '')
                    this.isDSTExists = true;

                if (this.isDSTExists)
                {
                    this.DSTstartDate = this.TimeZoneInfo[0];
                    this.DSTendDate = this.TimeZoneInfo[1];
                }
            };
        };
        // #endregion

        // Menu
        // #region

        this.topNavigation = new function ()
        {
            var _navigation,
                _menuArea,
                _newsArea,
                _loginArea,
                _naviItems,
                _menuMainItems,
                _menuSubItems,
                _selectedUrl,
                _activeItem,
                _showItemGroup,
                _hideItemGroups,
                _resizeTimer,
                _menuPadding,
                _allowedPadding,
                _footer,

                _initNavigation = function ()
                {
                    _navigation = $('#navigationBar');
                    _footer = $('#footerInner');
                    _menuArea = $('#menuArea');
                    _newsArea = $('#newsArea');
                    _loginArea = $('#loginArea');
                    _naviItems = _menuArea.find('.menuItem');
                    _menuMainItems = _naviItems.find('span:first');
                    _menuSubItems = _navigation.find('.menuSubItems');
                    _menuPadding = 45;

                    _showSubMenu = function (subMenu)
                    {
                        subMenu.addClass('hover');
                    }

                    _hideSubMenu = function ()
                    {
                        _naviItems.removeClass('hover');
                    }

                    $(window).resize(function ()
                    {
                        window.clearTimeout(_resizeTimer);
                        _resizeTimer = window.setTimeout(function ()
                        {
                            _resizeNavi();
                        }, 20);
                    });

                    _naviItems.mouseover(function ()
                    {
                        _showSubMenu($(this));
                    });

                    _naviItems.mouseout(function ()
                    {
                        _hideSubMenu();
                    });

                    _naviItems.mouseup(function ()
                    {
                        _hideSubMenu();
                    });
                    _resizeNavi();
                },

                _setActiveItem = function (url)
                {
                    _selectedUrl = '.selectedUrl-' + url.toLowerCase().replace(/\/|\.aspx|\?|\&|=/g, '');

                    try{
                        _activeItem = _navigation.find(_selectedUrl + ':first');
                    } catch (e){
                        return;
                    }
                    _menuMainItems.removeClass('active');
                    if (_activeItem.parent().hasClass('menuSubItems'))
                    {
                        _activeItem.parent().siblings('span').addClass('active');
                    }
                    else
                    {
                        _activeItem.addClass('active');
                    }
                },

                _resizeNavi = function ()
                {
                    var _footerOffset = $(document).width() - _footer.width();
                    if (_footerOffset > 0)
                    {
                        _footer.css({ left: (_footerOffset / 2) + 5 + 'px' });
                    }
                },

                _showNavigationBar = function (_isAnimated)
                {
                    var _top = '0px';
                    _isAnimated ? _navigation.animate({ top: _top }) : _navigation.css({ top: _top });
                },

                _hideNavigationBar = function (_isAnimated)
                {
                    var _top = -1 * _navigation.height() - 10 + 'px';
                    _isAnimated ? _navigation.animate({ top: _top }) : _navigation.css({ top: _top });
                };

            return {
                setActiveItem: _setActiveItem,
                initNavigation: _initNavigation,
                resizeNavi: _resizeNavi,
                show: _showNavigationBar,
                hide: _hideNavigationBar
            }
        }

        this.Menu = new function ()
        {
            var bIsSticking = false;
            var bIsPageLoading = true;
            var bPageNeedsScrollbar = false;

            this.setContentTopmargin = function () {
                var contentWindowHeight = $('#maincontentwindow').height();
                contentWindowHeight = contentWindowHeight < 400 ? 400 : contentWindowHeight;
                var i_HDiff = parseInt(($(window).height() - contentWindowHeight) / 2);
                var i_Top = i_HDiff;

                if (i_HDiff < 70 && $('.layoutA').length > 0)
                {
                    i_Top = 70;
                }
                else if (i_HDiff < 25)
                {
                    i_Top = 25;
                }

                if (i_HDiff > 235)
                    i_Top = 235;
                else if (i_HDiff < 81)
                    i_Top = 81;

                $('#maincontentwindow').css('margin-top', i_Top + 'px');
            }

            this.setMenuContentPosition = function ()
            {
                var dom_MainWrapper = $('#mainWrapper');
                var dom_MainContent = $('#maincontentwindow');
                var dom_MenuContent = $('#menucontentwindow');
                var dom_SocialIcons = $('#social_icons');

                if (dom_MainWrapper.height() > 640)
                {
                    dom_MenuContent.css('top', dom_MainWrapper.height() - 100 + 'px');
                    // dom_SocialIcons.css('top', dom_MainWrapper.height() - 33 + 'px');
                }
                else if (dom_MainContent.height() < 400)
                {
                    dom_MenuContent.css('top', 530 + 'px');
                    // dom_SocialIcons.css('top', 592 + 'px');
                }

                $(window).bind('resize', $this.Menu.setContentTopmargin);
            };

            this.setNewFeaturesBubble = function ()
            {
                if ($this.Page.fn_o_getCookie('coolnewfeatures') != '1')
                    $('#menucontentwindow').append('<div id="newFeaturesBubble" class="sprt"><b></b></div>');
            }

            $(document).ready(function ()
            {
                //don't ask why, just let it be :) [Ok, so ie8 resize event fires 3 times for a single resize while others fire only once as they supposed to, furthermore
                //other browsers need two invocation of setmenucontentpos because during the first invocation they tend to read the positions of an element inside the
                //method wrong or not the final positions. FF needs the second invocation, unless 1 grey column appears in the place of the scrollbar.
                $(window).bind("resize", function ()
                {
                    $this.Menu.setMenuContentPosition();
                });
                $this.Menu.setMenuContentPosition();
                $this.Menu.setNewFeaturesBubble();
            });
        };
        // #endregion

        // Input limiters
        // #region
        this.InputLimiters = new function ()
        {
            this.fn_b_IsCharacter = function (o_Event)
            {
                return o_Event.keyCode >= 65 &&
                    o_Event.keyCode <= 90;
            };

            this.isBasicFunctionKey = function (oEvent)
            {
                var iCharCode = oEvent.keyCode;
                return (iCharCode == 8 ||    // backspace
                    iCharCode == 27 ||   // esc
                    iCharCode == 9 ||    // tab
                    iCharCode == 46 ||   // delete
                    (iCharCode >= 35 &&   // end, home
                        iCharCode <= 37) ||  // <-
                    iCharCode == 39 ||   // ->
                    iCharCode == 13 ||   // enter
                    iCharCode == 17 ||   // CTRL
                    iCharCode == 116)    // F5
            };
            this.isNumber = function (oEvent)
            {
                var iCharCode = oEvent.keyCode;
                return (!oEvent.shiftKey &&
                    ((iCharCode >= 48 &&     // 0-9
                        iCharCode <= 57) ||
                        (iCharCode >= 96 &&     // Numlock 0-9
                            iCharCode <= 105)))
            };

            this.isSpecificKey = function (oEvent, iKeyCode)
            {
                if (iKeyCode.toString().indexOf(",") == -1)
                {
                    return (oEvent.keyCode == parseInt(iKeyCode));
                }
                else
                {
                    var a_keyCodes = iKeyCode.split(",");
                    for (var i in a_keyCodes) {
                        if (a_keyCodes.hasOwnProperty(i)) {
                            if (parseInt($.trim(a_keyCodes[i])) == oEvent.keyCode) {
                                return true;
                            }
                        }
                    }
                    return false;
                }
            };
            this.isSelectEvent = function (oEvent)
            {
                var iCharCode = oEvent.keyCode;
                if (oEvent.ctrlKey &&
                    iCharCode == 65)            // CTRL + A
                    return true
                else if (oEvent.shiftKey &&
                    (iCharCode == 35 ||     // SHIFT + HOME
                        iCharCode == 36 ||     // SHIFT + END
                        iCharCode == 37 ||     // SHIFT <-
                        iCharCode == 39))      // SHIFT ->
                    return true
                else
                    return false;
            };
            this.isCopy = function (oEvent)
            {
                var iCharCode = oEvent.keyCode;
                if ($this.Browser.ePlatformType == PaltformType.MAC)
                {
                    if (oEvent.metaKey &&
                        iCharCode == 67)       // CMD + C
                        return true;
                    else
                        return false;
                }
                else
                {
                    if (oEvent.ctrlKey &&
                        (iCharCode == 45 ||      // CTRL + INSERT
                            iCharCode == 67))       // CTRL + C
                        return true;
                    else
                        return false;
                }
            };
            this.isPaste = function (oEvent)
            {
                var iCharCode = oEvent.keyCode;
                if ($this.Browser.ePlatformType == PaltformType.MAC)
                {
                    if (oEvent.metaKey &&
                        iCharCode == 86)        // CMD + V
                        return true;
                    else
                        return false;
                }
                else
                {
                    if (oEvent.ctrlKey &&
                        iCharCode == 86)        // CTRL + V
                        return true;
                    else if (oEvent.shiftKey &&
                        iCharCode == 45)    // SHIFT + INSERT
                        return true;
                    else
                        return false;
                }
            };
            this.isCopyPaste = function (oEvent)
            {
                return $this.InputLimiters.isPaste(oEvent) ||
                    $this.InputLimiters.isCopy(oEvent);
            };
        };
        // #endregion

        // Content
        // #region
        this.Content = new function ()
        {
            this.o_resizeHandler = null;
            this.o_attach = null;
            var o_thisContent = this;
            var allowDetectTeamTrialFlowGet = true;

            this.navigateToUrl = function (urlOrHandler)
            {
                var navigateAction = function () {
                    if (typeof urlOrHandler === "string")
                        window.location = urlOrHandler;
                    else
                        urlOrHandler();
                };

                if ((typeof JM !== "undefined") && (typeof JM.Scheduler !== "undefined") && (typeof JM.Scheduler.App !== "undefined"))
                {
                    var currentMeeting = JM.Scheduler.App.getCurrentMeeting();
                    if (currentMeeting != null)
                    {
                        currentMeeting.confirmNavigationIfUnsaved(navigateAction);
                        return false;
                    }
                    else
                    {
                        navigateAction();
                    }
                }
                else
                {
                    navigateAction();
                }
            };

            this.showMainContent = function () {
                window.showLoadingBox = false;
                $("#loading-box").hide();
                $("#maincontentwindow").animate({ opacity: 1 }, 200, function () { });
            }

            this.updateMainContent = function (sPageUrl, jsonParams, oCallBack, skipSchedulerDialog) {

                if ((typeof skipSchedulerDialog === "undefined") || (!skipSchedulerDialog))
                {
                    if ((typeof JM !== "undefined") && (typeof JM.Scheduler !== "undefined") && (typeof JM.Scheduler.App !== "undefined"))
                    {
                        var currentMeeting = JM.Scheduler.App.getCurrentMeeting();
                        if (currentMeeting != null)
                        {
                            currentMeeting.confirmNavigationIfUnsaved(function () { o_thisContent.updateMainContent(sPageUrl, jsonParams, oCallBack, true); });
                            if ($this.Content.setActiveItemCallback) {
                                $this.Content.setActiveItemCallback(sPageUrl);
                            }
                            $this.Content.showMainContent();
                            return;
                        }
                    }
                }

                $this.topNavigation.setActiveItem(sPageUrl);

                try { parent._gaq.push(['_trackPageview', sPageUrl]) } catch (e) { ; };

                try { $this.Page.fn_v_hideTooltip() } catch (e) { ; }

                $this.Page.Threads.fn_v_StopAllThreads();
                $this.Page.markActualCSSForDelete();
                //$this.Page.removePageResourceFiles("css");
                $this.Page.ClearValidators();
                $this.CustomControls.fn_v_ClearControls();
                $(document).unbind("click");
                $this.Page.b_AutoFocus = true;

                if (jsonParams == undefined ||
                    !jsonParams["b_DontEmptyHash"])
                {
                    $this.Page.fn_v_LoadHashParameter("");
                }
                if ($this.Page.bIsUserLoggedIn && sPageUrl == c_j_ContentPages.c_s_Main)
                {
                    sPageUrl = c_j_ContentPages.c_s_MyJoinMe;
                }
                sPageUrl = sPageUrl.toLowerCase();

                if (PagesWhoRequireCSSFirst[sPageUrl] != undefined)
                {
                    for (var i in PagesWhoRequireCSSFirst[sPageUrl.toLowerCase()]) {
                        if (PagesWhoRequireCSSFirst[sPageUrl.toLowerCase()].hasOwnProperty(i)) {
                            $this.Page.addCSSLink(PagesWhoRequireCSSFirst[sPageUrl.toLowerCase()][i]);
                        }
                    }
                }

                if (sPageUrl == "/main.aspx")
                {
                    if (!jsonParams)
                        jsonParams = { 'iThemeId': $this.Page.iThemeId };
                    else if (!jsonParams.iThemeId)
                        jsonParams["iThemeId"] = $this.Page.iThemeId;
                }

                if (jsonParams && jsonParams.loadtoiframe === true)
                {
                    $("#jm_maincontent").load(sPageUrl, jsonParams, function ()
                    {
                        if ($this.Content.setActiveItemCallback) {
                            $this.Content.setActiveItemCallback(sPageUrl);
                        }
                        $this.Content.showMainContent();
                    });
                    return;
                }

                $("#jm_maincontent").parent().load(sPageUrl, jsonParams, function ()
                {
                    window.setTimeout(function ()
                    {
                        $this.Page.fn_v_initSessionTimer();

                        $this.topNavigation.resizeNavi();

                        if (jsonParams != undefined && jsonParams["b_DoubleClickable"] === true)
                        {
                            $("#X").show();
                        }
                        else
                        {
                            $("#X").hide();
                        }
                           //debugger;
                        //setMenuContentPosition() method is invoked twice, because of browser rendering anomalies
                        //some browser needs only just the first invocation, attachscrollbar() won't make any layout difference
                        //others needs a second setMenuContentPosition()
                        //note: only the second invocation is not enough (Chrome)
                        //$this.Menu.setMenuContentPosition();
                            $this.Content.attachScrollbar(sPageUrl);
                            $this.Menu.setMenuContentPosition();
                            $this.Content.setTronText();

                        $this.Page.sLastPage = sPageUrl;

                        //mysterious bugfix for ie8, when menu text shifts up and down 1px according to whether a scrollbar is present in maincontent or not
                        //unfortunately this bug could not be fixed by css only
                        if (sPageUrl.indexOf("legalese.aspx") == -1 && $this.Browser.eSessionType == SessionType.IE8)
                        {
                            $("table.menucontent td.content").css("padding-bottom", "1").css("height", "29px");
                        }

                        var oMainContent = $("#jm_maincontent");
                        var iNewContentWidth = oMainContent.find("table>tbody>tr.white_middle>td.white_middle>div").width();
                        oMainContent.parents("table:first").width(iNewContentWidth + 70);

                        $this.Page.removePageResourceFiles();

                        if (oCallBack)
                            oCallBack();

                        // online help
                        if ($this.Page.Tooltip) $this.Page.Tooltip.discoverHelpNodes();

                        if ($this.Content.setActiveItemCallback) {
                            $this.Content.setActiveItemCallback(sPageUrl);
                        }
                        $this.Content.showMainContent();
                    }, 0);
                });
            };

            this.setTronText = function ()
            {
                var dom_TronMain = $('#maincontentwindow .tron:first .tr:first');
                var dom_TrontextMain = $('#maincontentwindow .trontext:first');
                var dom_TronMenu = $('#menucontentwindow .tron:first .tr:first');
                var dom_TrontextMenu = $('#menucontentwindow .trontext:first');
                var i_CloseWidth = 0;
                var dom_XButton = $('#X');

                /* [EXP-5713]
                if (dom_XButton.css('display') == 'none')
                {
                dom_TrontextMain.css('margin-right', '0px');
                }
                else
                {
                dom_TrontextMain.css('margin-right', '27px');
                i_CloseWidth = 30;
                }
                */
                dom_TronMain.css('width', dom_TrontextMain.width() + (dom_TrontextMain.width() % 2) + i_CloseWidth + 'px');
                dom_TronMenu.css('width', dom_TrontextMenu.width() + (dom_TrontextMenu.width() % 2) + 'px');
            }

            this.attachScrollbar = function (s_PageUrl)
            {
                var dom_ScrollableMainContent = $("#jm_scrollablemaincontent");

                var fn_v_Scroll = function ()
                {
                    // Hide last opened combotextbox panel
                    if ($this.CustomControls.o_LastComboTextBox() != null)
                    {
                        $this.CustomControls.o_LastComboTextBox().fn_v_ShowOrHidePanel(false);
                        $this.CustomControls.o_LastComboTextBox().HTML().blur();
                    }
                    // Hide last opened bubble
                    if ($this.CustomControls.o_LastBubble() != null)
                        $this.CustomControls.o_LastBubble().Hide();
                }
                dom_ScrollableMainContent.unbind("scroll");
                dom_ScrollableMainContent.scroll(fn_v_Scroll);

                var fn_v_attach = function (b_BlockResizeObserving)
                {

                    if (b_BlockResizeObserving)
                    {
                        if ($this.Browser.bIsIE) {
                            dom_ResizeObserver.unbind("resize", fn_v_ContentResizeHandler);
                        } else {
                            dom_ResizeObserver.unbind('DOMSubtreeModified', fn_v_ContentResizeHandler);
                        }
                    }

                    if(dom_ScrollableMainContent.length > 0){
                    dom_ScrollableMainContent.jScrollPane({ showArrows: true, scrollbarWidth: 8 });
                    dom_ScrollableMainContent.css("overflow", "visible");
                    // This div is created after the scrollbar has been attached
                    var dom_JScrollPaneContainer = dom_ScrollableMainContent.parent();
                    //dom_JScrollPaneContainer.css("width", dom_JScrollPaneContainer.parent().width() + "px");
                    }
                    var fn_v_GenericClick = function (oEvent)
                    {
                        oEvent.stopPropagation();
                    }

                    /* ie scrolldrag mouseup preventer */
                    if ($this.Browser.bIsIE)
                    {
                        var dom_ScrolldDrag = $(".jScrollPaneDrag");

                        dom_ScrolldDrag.mousedown(function () { $(document).bind('mouseup', fn_v_SetDocumentMouseup); b_ScrollDragPreventer = true; });

                        var fn_v_SetDocumentMouseup = function ()
                        {

                            $(document).unbind('mouseup', fn_v_SetDocumentMouseup);
                            window.setTimeout(function () { b_ScrollDragPreventer = false; }, 0);
                        }

                        $(document).click(function (event) { if (b_ScrollDragPreventer) event.stopImmediatePropagation(); });
                    }

                    $(".jScrollPaneTrack")
                        .unbind("click", fn_v_GenericClick)
                        .click(fn_v_GenericClick);
                    $(".jScrollCap")
                        .unbind("click", fn_v_GenericClick)
                        .click(fn_v_GenericClick);
                    $(".jScrollArrowUp")
                        .unbind("click", fn_v_GenericClick)
                        .click(fn_v_GenericClick)
                        .addClass('sprt');
                    $(".jScrollArrowDown")
                        .unbind("click", fn_v_GenericClick)
                        .click(fn_v_GenericClick)
                        .addClass('sprt');
                    $(".jScrollPaneDragTop")
                        .addClass('sprt');
                    $(".jScrollPaneDragBottom")
                        .addClass('sprt');

                    if (b_BlockResizeObserving)
                    {
                        if ($this.Browser.bIsIE) {
                            dom_ResizeObserver.bind("resize", fn_v_ContentResizeHandler);
                        } else {
                            dom_ResizeObserver.bind("DOMSubtreeModified", fn_v_ContentResizeHandler);
                        }
                    }
                };

                fn_v_attach(false);

                var dom_ResizeObserver = dom_ScrollableMainContent;
                var i_PreviousHeight = 0;
                var b_IsAttachInProgress = false;
                var o_TryEnter = null;
                var fn_v_ContentResizeHandler = function ()
                {
                    if (i_PreviousHeight != dom_ResizeObserver.height())
                    {
                        if (b_IsAttachInProgress == false)
                        {
                            b_IsAttachInProgress = true;
                            fn_v_attach(true);
                            i_PreviousHeight = dom_ResizeObserver.height();
                            if (o_TryEnter != null)
                            {
                                clearTimeout(o_TryEnter);
                                o_TryEnter = null;
                            }
                            if ($this.Browser.eSessionType == SessionType.Firefox)
                                fn_v_attach(true);
                            b_IsAttachInProgress = false;
                        }
                        else
                        {
                            if (o_TryEnter != null)
                            {
                                clearTimeout(o_TryEnter);
                                o_TryEnter = null;
                            }
                            o_TryEnter = setTimeout(fn_v_ContentResizeHandler, 10);
                        }
                    }
                };

                o_thisContent.o_resizeHandler = fn_v_ContentResizeHandler;
                o_thisContent.o_attach = fn_v_attach;

                if ($this.Browser.bIsIE) {
                    dom_ResizeObserver.unbind("resize", fn_v_ContentResizeHandler);
                    dom_ResizeObserver.bind("resize", fn_v_ContentResizeHandler);
                }
                else {
                    dom_ResizeObserver.unbind('DOMSubtreeModified', fn_v_ContentResizeHandler);
                    dom_ResizeObserver.bind('DOMSubtreeModified', fn_v_ContentResizeHandler);
                }
            };

            $(document).ready(function ()
            {
                $this.Content.attachScrollbar("");
            });
        };
        // #endregion

        // Page
        // #region
        this.Page = new function ()
        {
            this.o_backgroundImage = null;
            this.iThemeId = -1;
            this.bIsUserLoggedIn = false;
            this.bIsFirstLogin = false;
            this.b_IsRememberMe = false;
            this.b_IsUserLoggedInByEncryptedCookie = false;
            this.b_IsUserLoggedInByOneTimeTicket = false;
            this.b_IsUserCommesFromHost = false;
            var aResourceFiles = {};
            var aValidators = {};
            var sLastPage = "";
            this.sCurrentLanguage = "en";
            this.Object = null;
            this.ModalDialog = null;
            this.ValidatorControl = null;
            this.ValidatorControlPosition = null;
            var sInValidControl = null;
            this.s_RedirectTo = "";
            this.sTicket = "";
            this.sInvitationCode = "";
            this.iProfileID = -1;
            this.b_AutoFocus = true;
            this.s_Location = "";
            this.s_SecureLocation = "";
            this.s_FactoryBg = "";
            this.s_UserBg = "";
            this.a_BackgroundPreview;
            var a_HashParameters = {};
            this.FocusedElement = null;
            this.j_RedirectPageParameters = null;
            this.b_SaveConfirmed = true;
            var b_IsPageLoaded = false;
            var i_bgImageWidth = 0;
            var i_bgImageHeight = 0;
            var d_bgImageRatio = 0;
            this.b_LockValidationForComboSelect = false;
            this.b_AllowSystemDialog = true;
            this.b_Register = false;
            this.b_LogoutTriggered = false;
            

            var _debugMode = false;

            this.b_IsPageLoaded = function (o_PageHasBeenLoaded)
            {
                if (o_PageHasBeenLoaded === undefined)
                    return b_IsPageLoaded;
                else if (typeof (o_PageHasBeenLoaded) === "boolean")
                    b_IsPageLoaded = o_PageHasBeenLoaded;
            };
            var a_JsFilesToLoad = [];

            this.fn_v_LoadHashParameter = function (s_Hash)
            {
                a_HashParameters = {};

                var a_SplittedHash = s_Hash.split("&&");

                for (var i in a_SplittedHash)
                {
                    if (a_SplittedHash.hasOwnProperty(i)) {
                        var s_Item = a_SplittedHash[i];
                        var i_IndexOfParameter = s_Item.indexOf("=");
                        a_HashParameters[s_Item.substring(0, i_IndexOfParameter)] = $.trim(s_Item.substr(i_IndexOfParameter + 1));
                    }
                }
            };

            this.fn_s_GetParameterInHash = function (s_ParameterName)
            {
                var s_HashValue = a_HashParameters[s_ParameterName];

                if (s_HashValue)
                {
                    return s_HashValue;
                }

                return null;
            };

            // #region DefaultControl
            this.AddDefaultControlToForm = function (o_Control)
            {
                if (!o_Control ||
                    !o_Control.HTML().hasClass("button"))
                    return;

                var dom_Form = o_Control.HTML().parents("form");
                if (dom_Form.length == 1)
                {
                    var o_KeyDown = function (o_Event)
                    {
                        if (o_Event.keyCode == 13 && !$this.Page.b_LockValidationForComboSelect)
                        {
                            $this.Page.Threads.fn_v_StartThread("form_Submit",
                                function () { o_Control.HTML().click(); },
                                20,
                                true);
                        }
                    };

                    dom_Form.unbind("keydown", o_KeyDown).keydown(o_KeyDown);
                }
            }
            // #endregion

            // Threads
            // #region
            this.Threads = new function ()
            {
                /// <summary>
                /// Manage Threads started by this Page
                /// </summary>

                var o_this = this;

                var a_Intervals = {};
                /// <summary>
                /// Collection of Threads started by setInterval
                /// </summary>
                var a_Timeouts = {};
                /// <summary>
                /// Collection of Threads started by setTimeout
                /// </summary>

                var a_Intervals_framework = {};
                var a_Timeouts_framework = {};

                var i_QuicklyStartedThreadCounter = 0;
                /// <summary>
                /// This incremental number is the part of quickly started thread IDs
                /// </summary>
                var b_StoreThreadReferences = true;

                // fn_v_StopAllThreads
                // #region
                this.fn_v_StopAllThreads = function ()
                {
                    /// <summary>
                    /// Stop all threads started by this page
                    /// </summary>

                    for (var sKey in a_Intervals)
                    {
                        if (a_Intervals.hasOwnProperty(sKey)) {
                            clearInterval(a_Intervals[sKey]);
                        }
                    }
                    a_Intervals = {};

                    for (var sKey in a_Timeouts)
                    {
                        if (a_Timeouts.hasOwnProperty(sKey)) {
                            clearTimeout(a_Timeouts[sKey]);
                        }
                    }
                    a_Timeouts = {};
                };
                // #endregion

                // fn_b_StopThread
                // #region
                this.fn_b_StopThread = function (s_ThreadID)
                {
                    /// <summary>
                    /// Stop Thread by ID
                    /// </summary>
                    /// <field name="s_ThreadID">Identifier of a Thread</field>

                    var b_Succed = true;
                    if (b_StoreThreadReferences)
                    {
                        if (a_Intervals[s_ThreadID])
                            clearInterval(a_Intervals[s_ThreadID]);
                        else if (a_Timeouts[s_ThreadID])
                            clearTimeout(a_Timeouts[s_ThreadID]);
                        else
                            b_Succed = false;
                    }
                    else
                    {
                        if (a_Intervals_framework[s_ThreadID])
                            clearInterval(a_Intervals_framework[s_ThreadID]);
                        else if (a_Timeouts_framework[s_ThreadID])
                            clearTimeout(a_Timeouts_framework[s_ThreadID]);
                        else
                            b_Succed = false;
                    }
                    return b_Succed;
                };
                // #endregion

                // fn_v_StartThread
                // #region
                this.fn_v_StartThread = function (s_ThreadID, fn_v_CodeFunct, i_Delay, b_RunOnce)
                {
                    /// <summary>
                    /// Start a new Thread
                    /// </summary>
                    /// <field name="s_ThreadID">Identifier of a Thread</field>
                    /// <field name="fn_v_CodeFunct">Represent a function to execute</field>
                    /// <field name="i_Delay">Delay time between interations</field>
                    /// <field name="b_RunOnce">Execute code once or execute it periodically</field>

                    this.fn_b_StopThread(s_ThreadID);
                    if (b_RunOnce)
                    {
                        var o_ThreadReference = setTimeout(function ()
                        {
                            fn_v_CodeFunct();
                            o_this.fn_b_StopThread(s_ThreadID);
                        }, i_Delay);

                        if (b_StoreThreadReferences)
                            a_Timeouts[s_ThreadID] = o_ThreadReference;
                        else
                            a_Timeouts_framework[s_ThreadID] = o_ThreadReference;
                    }
                    else
                    {
                        var o_ThreadReference = setInterval(fn_v_CodeFunct, i_Delay);
                        if (b_StoreThreadReferences)
                            a_Intervals[s_ThreadID] = o_ThreadReference;
                        else
                            a_Intervals_framework[s_ThreadID] = o_ThreadReference;
                    }
                };
                // #endregion

                // fn_v_StartThreadNow
                // #region
                this.fn_v_StartThreadNow = function (fn_v_CodeFunct)
                {
                    /// <summary>
                    /// Quickly start a new Thread right now
                    /// </summary>
                    /// <field name="fn_v_CodeFunct">Represent a function to execute</field>

                    this.fn_v_StartThread("QuicklyStartedThread_" + i_QuicklyStartedThreadCounter, fn_v_CodeFunct, 0, true);
                    i_QuicklyStartedThreadCounter++;
                };
                // #endregion


                // fn_v_AllowThreadReferencesStore
                // #region
                this.fn_v_AllowThreadReferencesStore = function (b_AllowThreadReferencesStore)
                {
                    /// <summary>
                    /// Allow or disallow thread references store
                    /// </summary>
                    /// <field name="b_AllowThreadReferencesStore">Allow or disallow thread references store</field>

                    b_StoreThreadReferences = b_AllowThreadReferencesStore;
                };
                // #endregion
            }
            // #endregion

            // Validation
            // #region
            this.fn_s_InValidControlId = function ()
            {
                return sInValidControl;
            };

            this.ClearValidators = function ()
            {
                $this.Page.ValidatorControl = null;
                aValidators = {};
            };

            var fn_HideValidator = function (o_Control)
            {
                $this.Page.ValidatorControl.Hide();
                o_Control.removeClass("error");
                if (o_Control.hasClass("combotextbox"))
                {
                    var o_ComboTextBox = $this.CustomControls.ComboTextBox.get(o_Control.attr("id"));
                    o_ComboTextBox.HTMLButton().removeClass("error");
                    o_ComboTextBox.HTMLButton_p().removeClass("error");
                }
                sInValidControl = null;
            };

            var fn_ShowValidator = function (o_Control, b_PositionError)
            {
                var s_ControlId = o_Control.attr("id");
                if (b_PositionError)
                {
                    var offset;
                    if (o_Control.attr('type') == 'password')
                        offset = o_Control.parent().position();
                    else
                        offset = o_Control.position();
                    var left = offset.left;
                    var top = offset.top;
                    if ($this.Browser.bIsIE)
                    {
                        left += 2;
                    }
                    if ($this.Page.ValidatorControl.Arrow().hasClass("left"))
                    {
                        left += o_Control.width() + 9;
                        top -= 11;
                    }
                    else if ($this.Page.ValidatorControl.Arrow().hasClass("bottom"))
                    {
                        left -= 19;
                        top -= $this.Page.ValidatorControl.HTML().height() + 2;
                    }
                    if (o_Control.hasClass("checkbox"))
                    {
                        top += 20;
                        left += 15;
                    }
                    if (o_Control.hasClass("combotextbox"))
                    {
                        left += 22;
                    }

                    $this.Page.ValidatorControl.Show(left, top);
                }
                else
                {
                    if ($this.Page.ValidatorControlPosition)
                    {
                        $this.Page.ValidatorControl.Show($this.Page.ValidatorControlPosition.X, $this.Page.ValidatorControlPosition.Y);
                        $this.Page.ValidatorControlPosition = null;
                    }
                    else
                        $this.Page.ValidatorControl.Show();
                }
                o_Control.addClass("error");
                if (o_Control.hasClass("combotextbox"))
                {
                    var o_ComboTextBox = $this.CustomControls.ComboTextBox.get(s_ControlId);
                    o_ComboTextBox.HTMLButton().addClass("error");
                    o_ComboTextBox.HTMLButton_p().addClass("error");
                }
                sInValidControl = s_ControlId;
            };

            this.AddValidator = function (sControlId, oIsValidFunction, bLiveValidation, bPositionError, bForceValidation)
            {
                aValidators[sControlId] = { fn_Callback: oIsValidFunction, b_PositionError: bPositionError };
                var oControl = $('#' + sControlId);

                if ($this.Page.ValidatorControl == null)
                {
                    //$("form:first").append($("<a id='error'></a>"));
                    if (oControl.attr('type') == 'password')
                        oControl.parent().parent().append($("<a id='error'></a>"));
                    else
                        oControl.parent().append($("<a id='error'></a>"));

                    $this.Page.ValidatorControl = new $this.CustomControls.Bubble({ "id": "error" });
                }

                if (bLiveValidation)
                {
                    if (oControl.hasClass("combotextbox"))
                    {
                        var oCombo = $this.CustomControls.ComboTextBox.get(sControlId);
                        oCombo.HTMLReadOnlyDiv().click(function ()
                        {
                            if (sInValidControl == sControlId)
                            {
                                fn_HideValidator(oControl);
                            }
                        });
                        oCombo.HTMLButton().click(function ()
                        {
                            if (sInValidControl == sControlId)
                            {
                                fn_HideValidator(oControl);
                            }
                        });
                    }
                    if (oControl.hasClass("textbox"))
                    {
                        oControl.blur(function ()
                        {
                            var sControlVal = oControl.val();
                            if (sControlVal != '' && sControlVal != $this.CustomControls.TextBox.get(oControl.attr('id')).sWatermark)
                            {
                                if ((!$this.Page.ValidatorControl.IsVisible() ||
                                    bForceValidation) &&
                                    (typeof (oIsValidFunction) == "string" ? !eval(oIsValidFunction) : !oIsValidFunction()))
                                {
                                    fn_ShowValidator(oControl, bPositionError);
                                }
                            }
                        });
                        oControl.focus(function ()
                        {
                            if (sInValidControl == sControlId)
                            {
                                fn_HideValidator(oControl);
                            }
                        });
                    }
                    else if (oControl.hasClass("checkbox"))
                    {
                        $this.CustomControls.CheckBox.get(sControlId).OnCheck(function ()
                        {
                            if (!$this.Page.ValidatorControl.IsVisible() ||
                                sInValidControl == sControlId)
                            {
                                if (typeof (oIsValidFunction) == "string" ? !eval(oIsValidFunction) : !oIsValidFunction())
                                {
                                    fn_ShowValidator(oControl, bPositionError);
                                }
                                else
                                {
                                    fn_HideValidator(oControl);
                                }
                            }
                        }, "validation");
                    }
                }
            };

            this.ValidateControl = function (b_ShowBubble, s_ControlId)
            {
                if (!s_ControlId)
                    s_ControlId = sInValidControl;

                if (aValidators[s_ControlId])
                {
                    if (typeof (aValidators[s_ControlId].fn_Callback) == "string" ? !eval(aValidators[s_ControlId].fn_Callback) : !aValidators[s_ControlId].fn_Callback())
                    {
                        if (b_ShowBubble)
                        {
                            var o_Control = $("#" + s_ControlId);
                            fn_ShowValidator(o_Control, aValidators[s_ControlId].b_PositionError);
                        }
                        return false;
                    }
                    else
                    {
                        var o_Control = $("#" + s_ControlId);
                        if (sInValidControl == s_ControlId)
                        {
                            fn_HideValidator(o_Control);
                        }
                    }
                }
                return true;
            }

            this.IsPageValid = function (b_ShowBubbles)
            {
                if ($this.Page.ValidatorControl != null && $this.Page.ValidatorControl.IsVisible())
                    return false;

                for (var control in aValidators)
                {
                    if (aValidators.hasOwnProperty(control)) {
                        if (typeof (aValidators[control].fn_Callback) == "string" ? !eval(aValidators[control].fn_Callback) : !aValidators[control].fn_Callback()) {
                            if (b_ShowBubbles == undefined)
                                b_ShowBubbles = true;

                            if (b_ShowBubbles) {
                                var o_Control = $("#" + control);
                                fn_ShowValidator(o_Control, aValidators[control].b_PositionError);
                            }

                            return false;
                        }
                    }
                }

                return true;
            };
            // #endregion

            // Script & CSS stuff
            // #region
            this.markActualCSSForDelete = function ()
            {
                //debugger;
                for (var sKey in aResourceFiles)
                {
                    if (aResourceFiles.hasOwnProperty(sKey) && aResourceFiles[sKey] == true)
                    {
                        var links = jQuery('link'),
                            i = links.length;
                        while (i--) {
                            if (links[i].href.indexOf(sKey) != -1) {
                                jQuery(links[i]).addClass('remove_css');
                                break;
                            }
                        }
                    }
                }
            };

            this.removePageResourceFiles = function ()
            {
                setTimeout(function ()
                {
                    $('link.remove_css').remove();
                }, 0);
            };

            this.addCSSLink = function (sCSSFile)
            {
                var oNode = document.createElement("link");
                oNode.type = "text/css";
                oNode.rel = "Stylesheet";
                oNode.href = sCSSFile;
                document.getElementsByTagName("HEAD")[0].appendChild(oNode);
                setTimeout(function ()
                {
                    aResourceFiles[sCSSFile] = true;
                }, 0);
            };
            // #endregion

            // Ajax Setup & Communication
            // #region
            $.ajaxSetup(
                { cache: false, error: function (j_Response, s_TextStatus, s_ErrorThrown)
                {
                    if (j_Response.status == 0)
                    {
                        // TODO
                    }
                    else if (j_Response.status == 404)
                    {
                        window.location = c_j_ErrorPages.c_s_PageNotFound;
                    }
                    else if (j_Response.status == 500)
                    {
                        if (_debugMode)
                        {
                            window.top.document.documentElement.innerHTML = j_Response.response;
                            return;
                        }
                        else
                        {
                            window.location = c_j_ErrorPages.c_s_UnknownError;
                        }
                    }
                    else if (s_ErrorThrown == 'parsererror')
                    {
                        window.location = c_j_ErrorPages.c_s_UnknownError;
                    }
                    else if (s_ErrorThrown == 'timeout')
                    {
                        window.location = c_j_ErrorPages.c_s_UnknownError;
                    }
                    else
                    {
                        window.location = c_j_ErrorPages.c_s_UnknownError;
                    }
                }
                });

            // fn_v_SendAjax
            // #region
            // fn_v_SendAjax
            // #region
            this.fn_v_SendAjax = function (s_WebService, s_MethodToCall, o_Data, fn_v_RetFunc)
            {
                $.ajax(
                    {
                        type: "POST",
                        url: s_WebService + s_MethodToCall,
                        cache: false,
                        data: o_Data == undefined ? null : (typeof (o_Data) == "string" ? o_Data : $.jsonToString(o_Data)),
                        contentType: "application/json; charset=utf-8",
                        dataType: "json",
                        success: function (j_Response)
                        {
                            $this.Page.fn_v_initSessionTimer();
                            if (j_Response.d.iError == 2)
                            {
                                window.top.location = loginURL;
                            }
                            else if (j_Response.d.iError == 4)
                            {
                                window.location.reload();
                            }
                            else if (j_Response.d.iError == 999)
                            {
                                window.location = c_j_ErrorPages.c_s_UnknownError;
                            }
                            else
                            {
                                if (fn_v_RetFunc)
                                    fn_v_RetFunc(j_Response.d);
                            }
                        }
                    });
            };
            // #endregion

            // fn_v_SendAjax_v2
            // #region
            this.fn_v_SendAjax_v2 = function (s_WebService, s_MethodToCall, j_Data, fn_v_RetFunc)
            {
                // Validate j_Data
                var s_Data = null;
                if (j_Data == undefined)
                    s_Data = null;
                else if (typeof (j_Data) == "string")
                    s_Data = null;
                else
                    s_Data = $.jsonToString_v2(j_Data);

                $.ajax(
                {
                    type: "POST",
                    url: s_WebService + s_MethodToCall,
                    cache: false,
                    data: s_Data,
                    contentType: "application/json; charset=utf-8",
                    dataType: "json",
                    success: function (j_Response)
                    {
                        $this.Page.fn_v_initSessionTimer();
                        if (j_Response.d.iError == 2)
                        {
                            window.top.location = loginURL;
                        }
                        else if (j_Response.d.iError == 4)
                        {
                            window.location.reload();
                        }
                        else if (j_Response.d.iError == 999)
                        {
                            window.location = c_j_ErrorPages.c_s_UnknownError;
                        }
                        else
                        {
                            if (fn_v_RetFunc)
                                fn_v_RetFunc(j_Response.d);
                        }
                    }
                });
            };
            // #endregion

            // #endregion

            // Cookie manipulation
            // #region
            this.fn_v_setCookie = function (name, value, expire_day)
            {
                var expiredate = new Date();
                if (!expire_day)
                    expiredate.setTime(expiredate.getTime() + (365 * 24 * 3600 * 1000));
                else
                    expiredate.setTime(expiredate.getTime() + (expire_day * 24 * 3600 * 1000));

                document.cookie = name + "=" + escape(value) +
                    "; expires=" + expiredate.toGMTString() + "; secure";
            };

            this.fn_o_getCookie = function (name)
            {
                // Search after the first one
                var prefix = " " + name + "=";
                var begin = document.cookie.indexOf(prefix);

                // If not found search of all
                if (begin == -1)
                {
                    prefix = name + "=";
                    begin = document.cookie.indexOf(prefix);
                }
                // We need this if a cookie's name is at the end of another's one!

                if (begin == -1) return null;
                var end = document.cookie.indexOf(";", begin);
                if (end == -1) end = document.cookie.length;
                return unescape(document.cookie.substring(begin + prefix.length, end));
            };

            this.fn_v_loginByOneTimeCodeFromHost = function (loginURL)
            {
                window.top.location.href = loginURL;
                return;
            };
            // #endregion

            // Setup Modal Dialog
            // #region
            function o_ModalDialog()
            {
                var o_Bubble = new $this.CustomControls.Bubble({
                    id: 'modalDialog',
                    s_css: 'modalDialog',
                    color: 'white',
                    arrow: 'none'
                });

                o_Bubble.HTML().css("z-index", "-1");
                //o_Bubble.HTML().css("opacity", "0");
                o_Bubble.HTML().css('top', '-100000px');
                o_Bubble.HTML().css('display', 'block');

                var closeModalDialog = function ()
                {
                    $this.Page.b_AllowSystemDialog = true;
                    $this.Page.ModalDialog.Hide();
                };

                var closeModalDialogOnEsc = function (o_Event)
                {
                    if ($this.InputLimiters.isSpecificKey(o_Event, KeyCodes.Esc))
                    {
                        $this.Page.b_AllowSystemDialog = true;
                        $this.Page.ModalDialog.Hide();
                    }
                }

                // Create backshadow
                var dom_HTMLDialogBackground = $("<div></div>")
                    .addClass("bubbleback")
                    .css("z-index", "-1")
                    .click(function (o_Event)
                    {
                        o_Event.stopPropagation();
                    });

                dom_HTMLDialogBackground.hide();

                // Create close button
                var dom_NewMainContent = $("<div></div>")
                    .css("padding-top", "10px");
                var dom_CloseIconButton = $('<div></div>')
                    .addClass('sprt modalclosebutton')
                    .click(closeModalDialog);
                var dom_CloseIconButton_p = $('<div></div>')
                    .addClass('joci_rel')
                    .addClass('modalclosebutton_p');

                var dom_NewContent = $("<div></div>");

                dom_NewMainContent.append(dom_CloseIconButton);
                dom_NewMainContent.append(dom_NewContent);
                dom_NewMainContent.append("<a id='closeButton'></a>");
                $("body").append(dom_HTMLDialogBackground);

                o_Bubble.Content(dom_NewMainContent);

                var dom_CloseButton = new $this.CustomControls.Button({
                    's_Id': 'closeButton',
                    's_Text': "close",
                    'i_TabIndex': 9999,
                    'b_KeepInMemory': true,
                    's_Style': 'margin-top: 10px;'
                }); 

                dom_CloseButton.OnClick(closeModalDialog, "close");

                this.Content = function (o_content)
                {
                    if (o_content)
                    {
                        dom_NewContent.html("");
                        $this.Content.attachScrollbar("");
                        dom_NewContent.html(o_content);
                    }
                    else
                    {
                        return dom_NewContent.html();
                    }
                }

                this.Show = function ()
                {
                    var oHTML = o_Bubble.HTML();
                    var dom_Table = oHTML.children(':first');
                    oHTML.css('margin-left', -1 * Math.round(dom_Table.outerWidth() / 2) + 'px');
                    oHTML.css('margin-top', -1 * Math.round(dom_Table.outerHeight() / 2) + 'px');
                    oHTML.css('left', '50%');
                    oHTML.css('top', '50%');

                    dom_CloseButton.HTML().focus().focus();
                    $(document).one("keydown", closeModalDialogOnEsc);
                    o_Bubble.HTML().css("z-index", "100001");
                    o_Bubble.HTML().css("top", "50%");
                    dom_HTMLDialogBackground.css("z-index", "10000");
                }

                this.Hide = function ()
                {
                    $(document).unbind("click", closeModalDialogOnEsc);
                    o_Bubble.HTML().css("z-index", "-1");
                    o_Bubble.HTML().css("top", "-100000px");
                    dom_HTMLDialogBackground.css("z-index", "-1");
                }

                this.b_ShowCloseButton = function (b_Visible)
                {
                    if (b_Visible)
                        dom_CloseButton.HTML().show();
                    else
                        dom_CloseButton.HTML().hide();
                }

                this.showCloseIcon = function (visible)
                {
                    if (visible)
                        dom_CloseIconButton.show();
                    else
                        dom_CloseIconButton.hide();
                }

                this.HTML = function ()
                {
                    return o_Bubble.HTML();
                }

                return this;
            };

            this.fn_v_setupModalDialog = function ()
            {
                if (this.ModalDialog == null)
                {
                    var dom_ModalDialogReplacement = $('<a></a>')
                        .attr('id', 'modalDialog');
                    $('body').append(dom_ModalDialogReplacement);
                    this.ModalDialog = new o_ModalDialog();
                }
            }
            // #endregion

            // Setup Tooltip
            // #region
            function o_Tooltip()
            {
                var o_Bubble = new $this.CustomControls.Bubble({
                    id: 'tooltip',
                    s_css: 'modalDialog',
                    color: 'white',
                    arrow: 'bottom'
                });

                o_Bubble.HTML().mouseover(function () { $(this).css('display', 'block') });
                o_Bubble.HTML().mouseout(function () { $(this).css('display', 'none') });

                this.HTML = function ()
                {
                    return o_Bubble.HTML();
                }

                this.discoverHelpNodes = function ()
                {
                    $('.olh').mouseover(function () { show(this) }).mouseout(hide);
                    $('.olh:not(.inline)').append('<b class="qm sprt"></b>');
                    $('.olh.inline').css('padding-right', '0px');
                }

                var show = function (o_node)
                {
                    o_Bubble.Content('<div id="tooltipcontent">' + $(o_node).attr('alt') + '</div>');

                    var i_nodeLeft = $(o_node).offset().left;

                    var i_nodeWidth = $(o_node).width();
                    if ($(o_node).hasClass('inline'))
                        i_nodeWidth = (i_nodeWidth - 30) / 2;

                    var i_contextLeft = i_nodeLeft - (($('body').width() - 715) / 2);
                    var i_bubbleWidth = o_Bubble.HTML().width();

                    if (i_bubbleWidth > 300)
                    {
                        o_Bubble.HTML().css('width', '300px');
                    }
                    if (i_bubbleWidth <= 200)
                    {
                        $('#tooltipcontent').css('width', '140px');
                    }

                    i_bubbleWidth = o_Bubble.HTML().width();

                    var i_bubbleHeight = o_Bubble.HTML().height();

                    if (i_bubbleWidth + i_contextLeft < 715)
                    {
                        i_computedLeft = i_nodeLeft - 48;
                        o_Bubble.Arrow().css('left', '30px');
                        i_computedLeft += i_nodeWidth + 27;
                    }
                    else if (i_bubbleWidth + i_contextLeft > 715 && i_contextLeft > 443)
                    {
                        i_computedLeft = i_nodeLeft - i_bubbleWidth + 70;
                        o_Bubble.Arrow().css('left', o_Bubble.HTML().width() - 50 + 'px');
                        i_computedLeft += i_nodeWidth - 11;
                    }
                    else
                    {
                        i_computedLeft = i_nodeLeft - i_bubbleWidth / 2 + 20;
                        o_Bubble.Arrow().css('left', o_Bubble.HTML().width() / 2 - 28 + 'px');
                        i_computedLeft += i_nodeWidth;
                    }
                    var top = $(o_node).offset().top - i_bubbleHeight;

                    var i_delay = $(o_node).hasClass('inline') ? 500 : 0;

                    $this.Page.Threads.fn_v_StartThread('showTooltipBubble', function ()
                    {
                        o_Bubble.HTML().css({ 'display': 'block', 'left': i_computedLeft + 'px', 'top': top - 7 + 'px' });
                    }, i_delay, true);
                }
                var hide = function ()
                {
                    o_Bubble.Content('');
                    o_Bubble.HTML().css({ 'display': 'none' });
                    $this.Page.Threads.fn_b_StopThread('showTooltipBubble');
                }
                this.hide = function ()
                {
                    hide();
                }
                return this;
            };

            this.fn_v_setupTooltip = function ()
            {
                if (this.tooltip == null)
                {
                    var dom_TooltipReplacement = $('<a></a>').attr('id', 'tooltip');
                    $('body').append(dom_TooltipReplacement);
                    this.Tooltip = new o_Tooltip();
                    $this.Page.fn_v_hideTooltip = this.Tooltip.hide;
                }
            }

            // #endregion

            // Init Session Timer
            // #region
            this.fn_v_initSessionTimer = function ()
            {
                if ($this.Page.bIsUserLoggedIn && ($this.Page.b_IsRememberMe || $this.Page.b_IsUserLoggedInByEncryptedCookie))
                {
                    $this.Page.Threads.fn_v_AllowThreadReferencesStore(false);
                    $this.Page.Threads.fn_v_StartThread('KeepSessionAlive', function ()
                    {
                        if ($this.Page.bIsUserLoggedIn)
                        {
                            $.get(c_j_BackendPages.c_s_Refresher + "?randomkey=" + Math.random());
                            $this.Page.fn_v_initSessionTimer();
                        }
                    }, ($this.Page.i_SessionTimeout - 2) * 60 * 1000, true)
                    $this.Page.Threads.fn_v_AllowThreadReferencesStore(true);
                }
            }
            // #endregion

            // Set Background Image
            // #region
            this.setBackgroundImage = function (s_imageUrl) {
                    var dom_bgImage = new Image();
                    var container = $('#ImgBg');
                    dom_bgImage.src = s_imageUrl;
                    dom_bgImage.id = 'backgroundimage';
                    container.html('').append(dom_bgImage);
                    if (container.data('grayscale')) {
                        dom_bgImage.className = 'grayscale';
                    }
                    dom_bgImage.onload = function ()
                    {
                        // setTimeout is for Microsoft Edge hack - sometimes the image is not actually loaded/visible in the "onload" event
                        setTimeout(function () {
                            $this.Page.o_backgroundImage = $('#backgroundimage');
                            $this.Page.i_bgImageWidth = $this.Page.o_backgroundImage.width();
                            $this.Page.i_bgImageHeight = $this.Page.o_backgroundImage.height();
                            $this.Page.d_bgImageRatio = $this.Page.i_bgImageWidth / $this.Page.i_bgImageHeight;

                            $this.Page.fitBackgroundImage();

                            container.css('opacity', 1);

                        }, 0);
                    }

                if (s_imageUrl != this.s_FactoryBg)
                    this.s_UserBg = s_imageUrl;
            }

            this.refreshPersonalBackgroundImage = function (hasPersonalBackground) {
                if ($this && $this.Page && $this.Page.o_backgroundImage && $this.Page.s_UserBg) {
                    var imgUri = $this.Page.o_backgroundImage.attr("src");
                    if (hasPersonalBackground && imgUri !== $this.Page.s_UserBg)
                        $this.Page.setBackgroundImage($this.Page.s_UserBg);
                }
            }

            this.setFactoryBg = function (s_imageUrl)
            {
                this.s_FactoryBg = s_imageUrl;
            }

            this.setDebugMode = function (mode) 
			{
                _debugMode = mode;
            }
            
            this.DebugMode = function () {
                return _debugMode;
            }

            // #endregion

            // Fit Background Image
            // #region
            this.fitBackgroundImage = function ()
            {
                if (!$this.Page.o_backgroundImage) {
                    return false;
                }

                var o_bgImg = $this.Page.o_backgroundImage;
                var d_bgImageRatio = $this.Page.d_bgImageRatio;
                var i_windowWidth = $(window).width();
                var i_windowHeight = $(window).height();
                var i_scaledWidth = 0;
                var i_scaledHeight = 0;

                if (d_bgImageRatio <= (i_windowWidth / i_windowHeight))
                {
                    i_scaledWidth = i_windowWidth;
                    i_scaledHeight = i_scaledWidth / d_bgImageRatio;
                }
                else
                {
                    i_scaledHeight = i_windowHeight;
                    i_scaledWidth = i_scaledHeight * d_bgImageRatio;
                }

                o_bgImg.css('width', i_scaledWidth + 'px');
                o_bgImg.css('height', i_scaledHeight + 'px');

                if (i_scaledWidth > i_windowWidth) {
                    o_bgImg.css('left', -1 * (i_scaledWidth - i_windowWidth) / 2 + 'px');
                }

                if (i_scaledHeight > i_windowHeight) {
                    o_bgImg.css('top', -1 * (i_scaledHeight - i_windowHeight) / 2 + 'px');
                }
            };

            // Fit Thumbnail Image
            // #region
            this.fitThumbImage = function (dom_Image)
            {
                if (dom_Image.width() == 0)
                    return;
                if (dom_Image.width() >= 115)
                {
                    dom_Image.css({ 'position': 'relative', 'left': -1 * (dom_Image.width() - 115) / 2 + 'px' });
                }
                else
                {
                    var d_ratio = dom_Image.width() / dom_Image.height();
                    dom_Image.css({ 'position': 'relative', 'width': '115px', 'height': 115 / d_ratio + 'px' });
                }
            }
            // #endregion

            $(document).ready(function ()
            {
                $(window).bind("resize", function () { $this.Page.fitBackgroundImage(); });
            });
        };
        // #endregion

        // CustomControls
        // #region
        this.CustomControls = new function ()
        {
            $(document).keydown(function (o_Event)
            {
                if (o_Event.shiftKey &&
                    o_Event.keyCode == 9)
                    b_IsShiftTab = true;
                else
                    b_IsShiftTab = false;
            });

            this.fn_v_ClearControls = function ()
            {
                a_Callbacks = {};
                aTextBoxes = {};
                aBubbles = {};
                aComboTextBoxes = {};
                aIconButtons = {};
                aButtons = {};
                aCheckBoxes = {};
                aValidators = {};
                aRadios = {};
                o_LastComboTextBox = null;
                o_LastBubble = null;
                i_MinTabIndex = 9999999;
                b_IsShiftTab = false;
            };

            // Helper functions
            // #region
            // Calbacks
            // #region
            var a_Callbacks = {};
            var a_PermenentCallbacks = {};
            var c_a_ControlType = { 'Button': 'button', 'Radio': 'radio', 'CheckBox': 'checkbox', 'TextBox': 'textbox', 'Bubble': 'bubble', 'IconButton': 'iconbutton', 'ComboTextBox': 'combotextbox' };
            var fn_v_AddCallback = function (c_a_ControlType, s_ControlId, s_fnCallback, s_cbName, b_KeepInMemory)
            {
                if (!b_KeepInMemory)
                {
                    if (!a_Callbacks[c_a_ControlType])
                        a_Callbacks[c_a_ControlType] = {};

                    if (!a_Callbacks[c_a_ControlType][s_ControlId])
                        a_Callbacks[c_a_ControlType][s_ControlId] = {};

                    a_Callbacks[c_a_ControlType][s_ControlId][s_cbName] = s_fnCallback;
                }
                else
                {
                    if (!a_PermenentCallbacks[c_a_ControlType])
                        a_PermenentCallbacks[c_a_ControlType] = {};

                    if (!a_PermenentCallbacks[c_a_ControlType][s_ControlId])
                        a_PermenentCallbacks[c_a_ControlType][s_ControlId] = {};

                    a_PermenentCallbacks[c_a_ControlType][s_ControlId][s_cbName] = s_fnCallback;
                }
            };

            var fn_v_RemoveCallback = function (c_a_ControlType, s_ControlId, s_cbName, b_KeepInMemory)
            {
                if (!b_KeepInMemory)
                    delete a_Callbacks[c_a_ControlType][s_ControlId][s_cbName];
                else
                    delete a_PermenentCallbacks[c_a_ControlType][s_ControlId][s_cbName];
            };

            var fn_v_callback = function (c_a_ControlType, s_ControlId, o_Event, o_Element)
            {
                if (a_Callbacks[c_a_ControlType])
                {
                    if (a_Callbacks[c_a_ControlType][s_ControlId])
                    {
                        for (var i in a_Callbacks[c_a_ControlType][s_ControlId])
                        {
                            if (a_Callbacks[c_a_ControlType][s_ControlId].hasOwnProperty(i)) {
                                var s_cbType = typeof (a_Callbacks[c_a_ControlType][s_ControlId][i]);
                                if (s_cbType == 'string') {
                                    eval(a_Callbacks[c_a_ControlType][s_ControlId][i] + '()');
                                }
                                else if (s_cbType == 'function') {
                                    a_Callbacks[c_a_ControlType][s_ControlId][i](o_Event, o_Element);
                                }
                            }
                        }
                    }
                }

                if (a_PermenentCallbacks[c_a_ControlType])
                {
                    if (a_PermenentCallbacks[c_a_ControlType][s_ControlId])
                    {
                        for (var i in a_PermenentCallbacks[c_a_ControlType][s_ControlId])
                        {
                            if (a_PermenentCallbacks[c_a_ControlType][s_ControlId].hasOwnProperty(i)) {
                                var s_cbType = typeof (a_PermenentCallbacks[c_a_ControlType][s_ControlId][i]);
                                if (s_cbType == 'string')
                                {
                                    eval(a_PermenentCallbacks[c_a_ControlType][s_ControlId][i] + '()');
                                }
                                if (s_cbType == 'function')
                                {
                                    a_PermenentCallbacks[c_a_ControlType][s_ControlId][i](o_Event, o_Element);
                                }
                            }
                        }
                    }
                }

                return false;
            };
            // #endregion

            var roundTable = function (sCssType, sCssColor)
            {
                return $("<table cellspacing='0' cellpadding='0'></table>").
                    addClass(sCssType).
                    addClass(sCssColor).
                    append(
                    $("<tbody></tbody>").
                        append(
                        $("<tr></tr>").
                            addClass(sCssColor + "_top").
                            append(
                            $("<td></td>").
                                addClass("sprt " + sCssColor + "_left")
                        ).
                            append(
                            $("<td></td>").
                                addClass("sprt_x " + sCssColor + "_middle")
                        ).
                            append(
                            $("<td></td>").
                                addClass("sprt " + sCssColor + "_right")
                        )
                    ).
                        append(
                        $("<tr></tr>").
                            addClass(sCssColor + "_middle").
                            append(
                            $("<td></td>").
                                addClass("sprt_y " + sCssColor + "_left")
                        ).
                            append(
                            $("<td></td>").
                                addClass(sCssColor + "_middle")
                        ).
                            append(
                            $("<td></td>").
                                addClass("sprt_y " + sCssColor + "_right")
                        )
                    ).
                        append(
                        $("<tr></tr>").
                            addClass(sCssColor + "_bottom").
                            append(
                            $("<td></td>").
                                addClass("sprt " + sCssColor + "_left")
                        ).
                            append(
                            $("<td></td>").
                                addClass("sprt_x " + sCssColor + "_middle")
                        ).
                            append(
                            $("<td></td>").
                                addClass("sprt " + sCssColor + "_right")
                        )
                    )
                );
            };
            // #endregion

            // Constructor
            // #region
            var aTextBoxes = {};
            var aBubbles = {};
            var aComboTextBoxes = {};
            var aIconButtons = {};
            var aButtons = {};
            var aCheckBoxes = {};
            var aValidators = {};
            var aRadios = {};
            var o_LastComboTextBox = null;
            this.o_LastComboTextBox = function ()
            {
                return o_LastComboTextBox;
            }
            var o_LastBubble = null;
            this.o_LastBubble = function ()
            {
                return o_LastBubble;
            }
            var i_MinTabIndex = 9999999;
            var b_IsShiftTab = false;
            // #endregion

            // Activators
            // #region
            var Activators = new function ()
            {
                // Constructor
                // #region
                var sWatermarkClassName = "watermark";
                var sOnFocusClassName = "focus";
                // #endregion

                // TextBox Activator
                // #region
                this.ActivateTextBox = function (oControl)
                {
                    if (typeof (oControl.HTML().dom_WaterField) != 'undefined')
                    {
                        var fn_v_PswdHideWatermark = function ()
                        {
                            oControl.HTML().css('background', '#fff');
                            oControl.HTML().unbind('keydown', fn_v_PswdHideWatermark);
                            oControl.HTML().unbind('mousedown', fn_v_PswdHideWatermark);
                        }

                        var fn_v_OnFocus = function ()
                        {
                            oControl.HTML().addClass('focus');
                            if (oControl.b_FirstSelected)
                            {
                                oControl.HTML().bind('keydown', fn_v_PswdHideWatermark);
                                oControl.HTML().bind('mousedown', fn_v_PswdHideWatermark);
                            }
                            else
                            {
                                oControl.HTML().css('background', '#fff');
                            }

                            $this.Page.FocusedElement = oControl;
                        }

                        var fn_v_OnBlur = function ()
                        {
                            if (oControl.HTML().val() === '')
                            {
                                oControl.HTML().css('background', 'url(/Common/Images/blank.gif)');
                                oControl.HTML().removeClass('focus');
                            }
                            else
                            {
                                oControl.HTML().removeClass('focus');
                            }
                            oControl.b_FirstSelected = false;
                        }
                        oControl.HTML().focus(fn_v_OnFocus);
                        oControl.HTML().blur(fn_v_OnBlur);
                    }

                    else
                    {
                        var fn_v_HideWatermark = function ()
                        {
                            if (oControl.HTML().hasClass(sWatermarkClassName))
                            {
                                oControl.HTML().removeClass(sWatermarkClassName);
                                oControl.HTML().val("");
                            }
                            oControl.HTML().unbind('keydown', fn_v_HideWatermark);
                            oControl.HTML().unbind('mousedown', fn_v_HideWatermark);
                        }

                        var oOnFocus = function (o_Event)
                        {
                            oControl.HTML().addClass(sOnFocusClassName);
                            if (oControl.b_FirstSelected)
                            {
                                if (this.setSelectionRange) this.setSelectionRange(0, 0);
                                oControl.HTML().bind('keydown', fn_v_HideWatermark);
                                oControl.HTML().bind('mousedown', fn_v_HideWatermark);
                            }
                            else
                            {
                                if (oControl.HTML().hasClass(sWatermarkClassName))
                                {
                                    oControl.HTML().removeClass(sWatermarkClassName);
                                    oControl.HTML().val("");
                                }
                            }

                            $this.Page.FocusedElement = oControl;
                        };

                        var oOnBlur = function ()
                        {
                            oControl.HTML().removeClass(sOnFocusClassName);
                            if (oControl.HTML().val() == "" && oControl.sWatermark != "")
                            {
                                oControl.HTML().addClass(sWatermarkClassName);
                                oControl.HTML().val(oControl.sWatermark);
                            }
                            oControl.b_FirstSelected = false;
                        };

                        if (oControl.bIsTextArea)
                        {
                            oControl.HTML().keydown(function (o_Event)
                            {
                                if (o_Event.keyCode == 13)
                                    o_Event.stopPropagation();
                            });
                        }

                        oControl.HTML().focus(oOnFocus).blur(oOnBlur);
                    }

                };
                // #endregion

            };
            // #endregion

            // Bubble
            // #region
            this.Bubble = function (options)
            {
                ///<summary>
                /// Bubble
                ///</summary>
                ///<param name="options">
                /// color - the color of the bubble - default red, possible values red, white &#13;
                /// id - id of the bubble &#13;
                /// arrow - position of the arrow - default left, possible values left, right, top, bottom, none &#13;
                /// style - bubbles inline style &#13;
                /// s_css - CSS class name &#13;
                /// b_hideOnDocumentClick - Hide bubble on document click - default false &#13;
                /// b_WrapControlIntoDiv - Wrap elements into a container div - default false &#13;
                ///</param>
                var default_args = {
                    'color': 'red',
                    'id': undefined,
                    'arrow': 'left',
                    'style': undefined,
                    's_css': undefined,
                    'b_hideOnDocumentClick': false,
                    'b_WrapControlIntoDiv': false
                };

                if (!options)
                {
                    options = default_args;
                }

                for (var index in default_args)
                {
                    if (default_args.hasOwnProperty(index) && typeof options[index] == "undefined") {
                        options[index] = default_args[index];
                    }
                }

                if (options.color == "white")
                    options.color = "white_bubble";

                var oHTML = $("<div></div>").
                    addClass("bubble").
                    addClass(options.color).
                    append(roundTable("round", options.color));
                if (options.style)
                    oHTML.attr("style", options.style);
                if (options.s_css)
                    oHTML.addClass(options.s_css);

                var oArrow = null;
                if (options.arrow != 'none')
                {
                    oArrow = $("<div class='joci_rel'><div class='sprt joci_abs'></div></div>")
                        .addClass(options.arrow);
                    if (options.arrow == "bottom")
                        oHTML.append(oArrow);
                    else
                        oHTML.prepend(oArrow);
                }
                oHTML.attr("id", options.id);
                var oContentHTML = oHTML.find("tr." + options.color + "_middle td." + options.color + "_middle:first");

                var dom_Replacement = $("#" + options.id);
                var dom_ParentContainer = null;
                if (options.b_WrapControlIntoDiv)
                {
                    // Create Control Container
                    dom_ParentContainer = $("<div></div>")
                        .addClass("joci_rel")
                        .addClass("bubble_container");
                    dom_Replacement.wrap(dom_ParentContainer);
                }
                var oHtmlToBeReplaced = dom_Replacement.replaceWith(oHTML);

                aBubbles[options.id] = { "html": oHTML, "contentHtml": oContentHTML, "arrow": oArrow, "a_Options": options };

                return new $this.CustomControls.Bubble.fn.init(oHTML, oContentHTML, oArrow, options);
            };

            this.Bubble.get = function (sId)
            {
                var arrayItem = aBubbles[sId];
                if (!arrayItem)
                    arrayItem = { "html": null, "contentHtml": null, "arrow": null, "dom_ParentContainer": null, "a_Options": {} };

                return new $this.CustomControls.Bubble.fn.init(arrayItem.html, arrayItem.contentHtml, arrayItem.arrow, arrayItem.a_Options);
            };

            this.Bubble.fn = this.Bubble.prototype = {
                init: function (html, contentHtml, arrow, a_Options)
                {
                    this.b_Exists = true;
                    if (!html)
                        this.b_Exists = false;

                    var oHTML = html;
                    var oContentHTML = contentHtml;
                    var oArrow = arrow;
                    var bNeedsArrowPositioning = oArrow ? oArrow.hasClass("right") : false;
                    var bIsVisible = false;
                    var b_HideOnDocumentClick = a_Options.b_hideOnDocumentClick;
                    var fn_v_HideCallBackFunct = false;

                    this.HideOnDocumentClick = function (b_value)
                    {
                        if (b_value == undefined)
                            return b_HideOnDocumentClick;
                        else
                            b_HideOnDocumentClick = b_value;
                    };

                    this.Content = function (oContent)
                    {
                        ///<summary>
                        /// Content
                        ///</summary>
                        if (oContent)
                        {
                            oContentHTML.html(oContent);
                            if (oArrow && oArrow.hasClass("right"))
                                bNeedsArrowPositioning = true;
                            if (bIsVisible)
                                this.Show();
                        }
                        else
                        {
                            return oContentHTML.html();
                        }
                    };

                    this.HTML = function ()
                    {
                        return oHTML;
                    };

                    this.Arrow = function ()
                    {
                        return oArrow;
                    };
                    this.Show = function (x, y)
                    {
                        // Hide last opened combotextbox panel
                        if (o_LastComboTextBox != null)
                        {
                            o_LastComboTextBox.fn_v_ShowOrHidePanel(false);
                            o_LastComboTextBox.HTML().blur();
                        }
                        if (o_LastBubble != null)
                        {
                            o_LastBubble.Hide();
                            o_LastBubble = null;
                        }
                        if (a_Options.id != 'modalDialog')
                            o_LastBubble = this;

                        oHTML.show();

                        bIsVisible = true;

                        this.updatePosition(x, y);

                        if (bNeedsArrowPositioning)
                        {
                            oArrow.css("left", oContentHTML.parents("table").width() - 13);
                        }
                        if (b_HideOnDocumentClick)
                        {
                            $(document).one("click", this, function (o_Event)
                            {
                                if (o_Event.data.IsVisible())
                                    o_Event.data.Hide();
                                o_LastBubble = null;
                            });
                        }
                    };

                    this.updatePosition = function (x, y)
                    {
                        if (x != null && y != null)
                        {
                            if (a_Options.b_WrapControlIntoDiv)
                            {
                                oHTML.parent().css("top", y);
                                oHTML.parent().css("left", x);
                            }
                            else
                            {
                                oHTML.css("top", y);
                                oHTML.css("left", x);
                            }
                        }
                    };

                    this.fn_v_HideCallBack = function (fn_v_HideCallBackFunction)
                    {
                        ///<summary>
                        /// Content
                        ///</summary>
                        if (fn_v_HideCallBackFunction)
                            fn_v_HideCallBackFunct = fn_v_HideCallBackFunction;
                        else
                            return fn_v_HideCallBackFunct;
                    };

                    this.Hide = function ()
                    {
                        if (typeof fn_v_HideCallBackFunct == 'function')
                            fn_v_HideCallBackFunct();
                        if(oHTML)
                            oHTML.hide();
                        if (a_Options.b_modalDialog)
                            oHTMLDialogBackground.hide();
                        bIsVisible = false;
                    };
                    this.IsVisible = function ()
                    {
                        return bIsVisible;
                    };
                }
            }

            // #endregion

            // TextBox Control
            // #region
            /// <field name="TextBox">Creates a textbox</field>
            /// <value name="TextBox">Common</value>
            this.TextBox = function (sId, sWatermark, sCSS, sStyle, sValue, iTabIndex, iMaxLength, bIsReadOnly, bIsTextArea, bIsPassword)
            {
                // Constructor
                // #region
                var oHTML = null;
                var b_Disabled = bIsReadOnly;
                if (bIsTextArea)
                {
                    this.bIsTextArea = true;
                    oHTML = $("<textarea />");
                }
                else
                {
                    this.bIsTextArea = false;
                    oHTML = $("<input />");
                    if (bIsPassword)
                        oHTML.attr("type", "password");
                    else
                        oHTML.attr("type", "text");
                }
                oHTML.addClass("textbox");
                if (sId)
                    oHTML.attr("id", sId);
                if (sCSS)
                    oHTML.addClass(sCSS);
                if (sValue)
                    oHTML.attr("value", sValue);
                if (sStyle)
                    oHTML.attr("style", sStyle);
                if (sValue)
                    oHTML.attr("value", sValue);
                if (iTabIndex)
                    oHTML.attr("tabindex", iTabIndex);
                if (iMaxLength)
                    oHTML.attr("maxlength", iMaxLength);
                if (bIsReadOnly)
                    oHTML.attr("readonly", true);

                this.sWatermark = (!sWatermark) ? "" : sWatermark;
                if (!bIsPassword)
                {
                    $("#" + sId).replaceWith(oHTML);
                }
                else
                {

                    var dom_BoxClone = $('<input/>')
                        .attr('type', 'text')
                        .addClass('textbox')
                        .addClass('watermark')
                        .addClass(sCSS)
                        .attr('style', sStyle)
                        .attr('readonly', true)
                        .css({ 'position': 'absolute', 'top': '0px', 'left': '0', 'border': 'none' })
                        .val(this.sWatermark);
                    dom_container = $('<span></span>')
                        .css({ 'position': 'relative', 'display': 'inline-block' })
                        .append(dom_BoxClone)
                        .append(oHTML);

                    oHTML.css({ 'position': 'relative' });
                    oHTML.dom_WaterField = dom_BoxClone;
                    dom_BoxClone.dom_SendField = oHTML;
                    $("#" + sId).replaceWith(dom_container);
                }


                this.HTML = function () { return oHTML; }
                aTextBoxes[oHTML.attr("id")] = this;
                // #endregion

                this.Disable = function (b_Value)
                {
                    if (b_Value == undefined)
                        return b_Disabled;

                    if (b_Disabled === b_Value)
                        return;

                    if (b_Value)
                        oHTML.attr("readonly", true);
                    else
                        oHTML.removeAttr("readonly");

                    b_Disabled = b_Value;
                };

                this.Value = function (s_Value)
                {
                    if (s_Value == undefined)
                    {
                        if (bIsPassword)
                        {
                            if (oHTML.val() == "")
                                return null;
                            else
                                return oHTML.val();
                        }
                        else
                        {
                            if (oHTML.val() != sWatermark)
                                return oHTML.val();
                            else
                                return null;
                        }
                    }
                    else
                    {
                        oHTML.val(s_Value);
                    }
                };

                Activators.ActivateTextBox(this);
                oHTML.blur();
                this.b_FirstSelected = false;
                this.fn_v_SetFirstSelected = function ()
                {
                    this.b_FirstSelected = true;
                    oHTML.focus().focus();
                }

                if ($this.Page.b_AutoFocus &&
                    iTabIndex &&
                    iTabIndex < i_MinTabIndex)
                {
                    i_MinTabIndex = iTabIndex;
                    setTimeout(function () {
                        if (oHTML.offsetWidth && oHTML.offsetHeight && document.activeElement && document.activeElement != oHTML) {
                                oHTML.focus();
                            }
                        });
                }

                this.SelectedText = function ()
                {
                    // IE support
                    if ($this.Browser.bIsIE)
                    {
                        if (document.selection)
                        {

                            var TextRange = document.selection.createRange();
                            if (TextRange.parentElement().id == sId)
                            {
                                return TextRange.text;
                            }
                        }

                        return null;
                    }
                    // Firefox support
                    else
                    {
                        if ((oHTML[0].selectionStart || oHTML[0].selectionStart == "0") &&
                            oHTML[0].selectionStart != oHTML[0].selectionEnd)
                        {
                            return oHTML.val().substring(oHTML[0].selectionStart, oHTML[0].selectionEnd);
                        }

                        return null;
                    }
                }
            };


            // External Methods
            // #region
            this.TextBox.get = function (sId)
            {
                return aTextBoxes[sId];
            }
            this.TextBox.getCaretPosition = function (oObject)
            {
                var CaretPos = 0; // IE Support
                if ($this.Browser.bIsIE)
                {
                    var selection = document.selection
                    oObject.focus();
                    var Sel = selection.createRange();
                    Sel.moveStart('character', -oObject.value.length);
                    CaretPos = Sel.text.length;
                }
                // Firefox support
                else if (oObject.selectionStart || oObject.selectionStart == '0')
                {
                    CaretPos = oObject.selectionStart;
                }
                return (CaretPos);
            }
            this.TextBox.setCaretPosition = function (oObject, iPosition)
            {
                // Firefox support
                if (oObject.setSelectionRange)
                {
                    oObject.focus();
                    oObject.setSelectionRange(iPosition, iPosition);
                }
                // IE support
                else if (oObject.createTextRange)
                {
                    var range = oObject.createTextRange();
                    range.collapse(true);
                    range.moveEnd('character', iPosition);
                    range.moveStart('character', iPosition);
                    try
                    {
                        range.select();
                    } catch (e)
                    {

                    }
                }
            }
            // #endregion

            // #endregion

            // ComboTextBox Control
            // #region
            this.ComboTextBox = function ()
            {
                // Properties
                // #region

                var _this = this;
                var i_DefaultRowHeight = 20;
                var s_CurrentTheme = "";
                var b_IsVisible = true;
                var s_SelectedValue = '';
                var b_ComboIsOpen = false;
                var i_ArrayLength = 0;
                var b_CommesFromBeforeArrowClick = false;
                var b_DisableSecondClick = false;
                var b_CommesFromKeyboard = false;
                var dom_CurrentTr = null;
                this.iPanelOffsetLeft = 0;
                this.iPanelOffsetTop = 0;
                this.EnterByKeyBoard = false;
                // Properties for TextBox Component
                this.sId = null;
                this.sWatermark = null;
                this.iWidth = null;
                this.sCSS = null;
                this.sStyle = null;
                this.sValue = null;
                this.sHiddenValue = null;
                this.iTabIndex = null;
                this.iMaxLength = null;
                this.bIsReadOnly = null;
                this.iKeyNaviPosition = false;

                // Properties for ArrowButton Component
                this.sId_Arrow = null;
                this.sId_p_Arrow = null;
                this.sCSS_Arrow = null;
                this.sCSS_p_Arrow = null;

                // Properties for Panel Component
                this.sId_Panel = null;
                this.sId_p_Panel = null;
                this.sCSS_Panel = null;
                this.sCSS_p_Panel = null;

                // Properties for list Theme
                this.iWidth_Panel = null;
                this.iRows_Panel = null;
                this.aContentItems = null;
                this.onSelected = null;
                this.onAfterSelected = null;
                this.bAutoSelect = null;
                this.bDeleteColumn = null;
                this.onItemDelete = null;

                // Other properties for Control
                this.onBeforeArrowClick = null;
                this.bCanAlignCenter = null;
                this.bDisableBlurApply = null;
                this.bDisableArrowHover = null;
                this.bNoAutoSelectOnArrowClick = null;
                this.iPanelTopPosOffset = null;
                this.bDisableReadOnlyDiv = null;
                this.bDisableAutoLastControlHide = null;
                this.bDisableAutoItemSelect = null;
                this.bDisableAutoLastControlHide2 = null;

                this.iSelectedKey = null;
                this.iSelectedValue = null;
                // #endregion

                // fn_v_ApplyChanges
                // #region
                this.fn_v_ApplyChanges = function (i_SelectedKey, s_SelectedText, o_Event, o_ComboTextBox)
                {
                    s_SelectedValue = s_SelectedText;
                    var bAllowChanges = true;
                    if (i_SelectedKey == -1)
                        bAllowChanges = (s_SelectedText != (_this.sValue ? _this.sValue : ""));
                    _this.iSelectedKey = i_SelectedKey;
                    _this.sSelectedText = s_SelectedText;
                    if (_this.onSelected && bAllowChanges)
                        _this.onSelected(_this.sSelectedText, _this.iSelectedKey);
                    fn_v_callback(c_a_ControlType.ComboTextBox, _this.sId, o_Event, o_ComboTextBox);
                }
                // #endregion

                // fn_v_FocusOrBlur
                // #region
                this.fn_v_FocusOrBlur = function (o_Control, b_Focus)
                {
                    if (b_Focus)
                    {
                        o_Control.HTML().addClass("focus");
                        if (_this.bDisableArrowHover == null || !_this.bDisableArrowHover)
                        {
                            o_Control.HTMLButton().addClass("focus");
                            o_Control.HTMLButton_p().addClass("focus");
                        }
                        o_Control.HTML().bind('keydown', function (o_event)
                        {
                            if (o_event.keyCode == 40 && !b_ComboIsOpen)
                            {
                                o_Control.HTMLButton().trigger('click');
                                if (!_this.onBeforeArrowClick)
                                    _this.fn_v_InitKeyNaviPosition(true);
                                _this.fn_v_OnControlKeyDown(o_event);
                                $this.Page.b_LockValidationForComboSelect = true;
                                return false;
                            }
                        });
                    }
                    else
                    {
                        o_Control.HTML().removeClass("focus");
                        o_Control.HTMLButton().removeClass("focus");
                        o_Control.HTMLButton_p().removeClass("focus");
                    }
                }
                // #endregion

                // fn_v_ShowOrHidePanel
                // #region
                this.fn_v_ShowOrHidePanel = function (b_Show)
                {
                    b_ComboIsOpen = b_Show;
                    if (b_Show)
                    {
                        this.HTMLPanel_p().show();
                        if ($this.Browser.ePlatformType == PaltformType.MAC && $this.Browser.eSessionType == SessionType.Firefox)
                        {
                            $(document).unbind('keyup', _this.fn_v_OnControlKeyDown);
                            $(document).keyup(_this.fn_v_OnControlKeyDown);
                        }
                        else
                        {
                            $(document).unbind('keydown', _this.fn_v_OnControlKeyDown);
                            $(document).keydown(_this.fn_v_OnControlKeyDown);
                        }
                    }
                    else
                    {
                        this.HTMLPanel_p().hide();
                        if ($this.Browser.ePlatformType == PaltformType.MAC && $this.Browser.eSessionType == SessionType.Firefox)
                            $(document).unbind('keyup', _this.fn_v_OnControlKeyDown);
                        else
                            $(document).unbind('keydown', _this.fn_v_OnControlKeyDown);
                    }
                }
                // #endregion

                // fn_v_ShowOrHideArrow
                // #region
                this.fn_v_ShowOrHideArrow = function (b_Show)
                {
                    if (b_Show)
                    {
                        if (!this.HTMLButton_p().isVisible())
                        {
                            this.HTMLButton_p().show();
                            if (this.iWidth)
                                this.HTML().css("width", this.iWidth + "px");
                        }
                    }
                    else
                    {
                        if (this.HTMLButton_p().isVisible())
                        {
                            this.HTMLButton_p().hide();
                            if (this.iWidth)
                            {
                                var i_NewWidth = this.iWidth + 21;
                                if (!$this.Browser.bIsIE)
                                    i_NewWidth++;
                                this.HTML().css("width", i_NewWidth + "px");
                            }
                        }
                    }
                }
                // #endregion


                // fn_v_RemoveItem
                // #region
                this.fn_v_RemoveItem = function (s_ItemKey)
                {
                    this.HTMLPanel().find("#combotextbox_tr_" + this.sId + "_" + s_ItemKey).remove();
                }
                // #endregion

                // fn_v_InitKeyNaviPosition
                // #region
                this.fn_v_InitKeyNaviPosition = function (b_ReInit)
                {
                    if (!_this.iKeyNaviPosition || b_ReInit)
                    {
                        var i_TopMin = parseInt(Math.abs(_this.HTMLTable().parent()[0].offsetTop) / 20) - 1;
                        if (i_TopMin < 0)
                            i_TopMin = 0;
                        _this.iKeyNaviPosition = _this.HTMLTable().find('tr:eq(' + i_TopMin + ')');
                        _this.iKeyNaviPosition.addClass('hover');
                    }
                }
                // #endregion

                // fn_v_OnControlKeyDown
                // #region
                this.fn_v_OnControlKeyDown = function (o_Event)
                {
                    if (s_CurrentTheme != "list")
                        return true;
                    if (o_Event.keyCode == 9)
                        return false;
                    if ((o_Event.keyCode < 37 || o_Event.keyCode > 40) && o_Event.keyCode != 13 && o_Event.keyCode != 27)
                    {
                        if (_this.bIsReadOnly != null && _this.bIsReadOnly)
                        {
                            if (_this.bDisableAutoItemSelect == null || !_this.bDisableAutoItemSelect)
                            {
                                var i_ScrollAmount = 0;
                                var s_ExpectedKey = '';
                                var s_PressedKey = String.fromCharCode(o_Event.keyCode).toLowerCase();
                                if (o_Event.keyCode >= 96 && o_Event.keyCode <= 105)
                                    s_PressedKey = o_Event.keyCode - 96;
                                for (var s_Key in _this.aContentItems)
                                {
                                    i_ScrollAmount += i_DefaultRowHeight;
                                    if (_this.aContentItems.hasOwnProperty(s_Key) && _this.aContentItems[s_Key].substring(0, 1).toLowerCase() == s_PressedKey)
                                    {
                                        s_ExpectedKey = s_Key;
                                        break;
                                    }
                                }
                                if (i_ScrollAmount != null && s_ExpectedKey != '')
                                {
                                    if (i_ScrollAmount - i_DefaultRowHeight >= 2 * i_DefaultRowHeight)
                                        _this.HTMLTable_p()[0].scrollTo(i_ScrollAmount - 3 * i_DefaultRowHeight);
                                    else if (i_ScrollAmount - i_DefaultRowHeight >= i_DefaultRowHeight)
                                        _this.HTMLTable_p()[0].scrollTo(i_ScrollAmount - 2 * i_DefaultRowHeight);
                                    else
                                        _this.HTMLTable_p()[0].scrollTo(i_ScrollAmount - i_DefaultRowHeight);
                                    _this.HTMLTable().children().children().removeClass('selected');
                                    var obj_Tr = $('#combotextbox_tr_' + _this.sId + '_' + s_ExpectedKey);
                                    obj_Tr.addClass('selected');
                                    _this.iKeyNaviPosition = obj_Tr;
                                }
                            }
                        }
                    }
                    else if (o_Event.keyCode == 38 || o_Event.keyCode == 40)
                    {
                        var fn_v_KeyNaviScroll = function (o_Item)
                        {
                            var dom_ScrollContainer = _this.HTMLTable().parent();
                            if (dom_ScrollContainer.parent().hasClass('jScrollPaneContainer'))
                            {
                                var dom_ScrollPane = dom_ScrollContainer.parent();
                                var i_TopMin = (Math.abs(dom_ScrollContainer[0].offsetTop));
                                var i_TopMax = (Math.abs(dom_ScrollContainer[0].offsetTop) + dom_ScrollPane.height());
                                var i_Offset = dom_ScrollContainer[0].offsetTop % 20;
                                if ((i_Offset != 0) && (o_Item[0].offsetTop < i_TopMin || o_Item[0].offsetTop >= i_TopMax))
                                    dom_ScrollContainer[0].scrollBy((20 + i_Offset));
                                if (o_Item[0].offsetTop < i_TopMin)
                                    dom_ScrollContainer[0].scrollBy(-20);
                                if (o_Item[0].offsetTop >= i_TopMax)
                                    dom_ScrollContainer[0].scrollBy(20);
                            }
                        }
                        if (!_this.onBeforeArrowClick)
                            _this.fn_v_InitKeyNaviPosition(false);
                        if (_this.iKeyNaviPosition)
                        {
                            if (o_Event.keyCode == 40)
                            {
                                _this.iKeyNaviPosition.removeClass('hover');
                                if (_this.iKeyNaviPosition.next('tr').length > 0)
                                    _this.iKeyNaviPosition = _this.iKeyNaviPosition.next('tr');
                                fn_v_KeyNaviScroll(_this.iKeyNaviPosition);
                            }
                            else if (o_Event.keyCode == 38)
                            {
                                _this.iKeyNaviPosition.removeClass('hover');
                                if (_this.iKeyNaviPosition.prev('tr').length > 0)
                                    _this.iKeyNaviPosition = _this.iKeyNaviPosition.prev('tr');
                                fn_v_KeyNaviScroll(_this.iKeyNaviPosition);
                            }
                            _this.iKeyNaviPosition.addClass('hover');
                        }
                    }
                    else if (o_Event.keyCode == 13)
                    {
                        _this.EnterByKeyBoard = true;
                        _this.iKeyNaviPosition.find('td:first').trigger('click');
                        _this.HTML().focus();
                        _this.HTML().select();
                    }
                    else if (o_Event.keyCode == 27)
                    {
                        _this.fn_v_ShowOrHidePanel(false);
                        $this.Page.b_LockValidationForComboSelect = false;
                    }
                }
                // #endregion

                // fn_v_InitControl
                // #region
                this.fn_v_InitControl = function (s_ThemeId)
                {
                    if (this.bCanAlignCenter != null && this.bCanAlignCenter && this.sId && this.iWidth)
                    {
                        // Create Control Container
                        var dom_ParentContainer = $("<div></div>")
                            .addClass("combotextbox_parentcontainer")
                            .css("width", this.iWidth + 33 + "px")
                            .css("margin-left", -(this.iWidth + 33) / 2 + "px");
                        $("#" + this.sId).wrap(dom_ParentContainer);
                    }
                    if (this.iWidth)
                    {
                        if (!this.sStyle)
                            this.sStyle = "";
                        this.sStyle += "; width: " + this.iWidth + "px;";
                    }

                    // Create TextBox
                    // remember previous minTabIndex
                    var tempTabIndex = i_MinTabIndex;
                    var oTextBox = new $this.CustomControls.TextBox(this.sId, this.sWatermark, this.sCSS, this.sStyle, this.sValue, this.iTabIndex, this.iMaxLength, this.bIsReadOnly, false, false);
                    oTextBox.HTML().blur();
                    i_MinTabIndex = tempTabIndex;

                    // Inherited from TextBox
                    var dom_HTML = oTextBox.HTML().addClass("combotextbox");

                    if (_this.bIsReadOnly != null && _this.bIsReadOnly)
                        dom_HTML.addClass("readonly");

                    // Create ArrowButton + ArrowButton Container
                    var i_ArrowButtonLeft = this.iWidth + 9;
                    if (!$this.Browser.bIsIE)
                        i_ArrowButtonLeft--;

                    var dom_ArrowButtonComponent = $("<div>&nbsp;</div>")
                        .addClass("joci_abs")
                        .addClass("sprt combotextbox_arrow")
                        .css("left", i_ArrowButtonLeft + "px");

                    var dom_ArrowButtonComponent_p = $("<div></div>")
                        .addClass("joci_rel")
                        .addClass("combotextbox_arrow_p");
                    if (this.sId_Arrow)
                        dom_ArrowButtonComponent.attr("id", this.sId_Arrow);
                    if (this.sId_p_Arrow)
                        dom_ArrowButtonComponent_p.attr("id", this.sId_p_Arrow);
                    if (this.sCSS_Arrow)
                        dom_ArrowButtonComponent.addClass(this.sCSS_Arrow);
                    if (this.sCSS_p_Arrow)
                        dom_ArrowButtonComponent_p.addClass(this.sCSS_p_Arrow);
                    dom_ArrowButtonComponent_p.append(dom_ArrowButtonComponent);
                    dom_HTML.after(dom_ArrowButtonComponent_p);

                    // Create Panel + Panel Container
                    var dom_PanelComponent = $("<div></div>")
                        .addClass("joci_abs")
                        .addClass("combotextbox_panel");

                    var dom_PanelComponent_p = $("<div></div>")
                        .addClass("joci_rel")
                        .addClass("combotextbox_panel_p");
                    if (this.sId_Panel)
                        dom_PanelComponent.attr("id", this.sId_Panel);
                    if (this.sId_p_Panel)
                        dom_PanelComponent_p.attr("id", this.sId_p_Panel);
                    if (this.sCSS_Panel)
                        dom_PanelComponent.addClass(this.sCSS_Panel);
                    if (this.sCSS_p_Panel)
                        dom_PanelComponent_p.addClass(this.sCSS_p_Panel);
                    dom_PanelComponent_p.append(dom_PanelComponent);

                    var dom_CombotextboxPanelContainer = $("#combotextbox_panel_container");

                    // ReadOnly Div
                    if (_this.bIsReadOnly != null && _this.bIsReadOnly)
                    {
                        var dom_ReadOnlyDiv = $("<div>&nbsp;</div>")
                            .addClass("joci_abs")
                            .addClass("combotextbox_readonly");

                        var dom_ReadOnlyDiv_p = $("<div></div>")
                            .addClass("joci_rel")
                            .addClass("combotextbox_readonly_p");

                        dom_ReadOnlyDiv_p.append(dom_ReadOnlyDiv);
                        dom_HTML.after(dom_ReadOnlyDiv_p);
                    }

                    // TextBox
                    this.HTML = function ()
                    {
                        return dom_HTML;
                    }

                    // ArrowBox
                    this.HTMLButton = function ()
                    {
                        return dom_ArrowButtonComponent;
                    }

                    // ArrowBox Container
                    this.HTMLButton_p = function ()
                    {
                        return dom_ArrowButtonComponent_p;
                    }

                    // Panel
                    this.HTMLPanel = function ()
                    {
                        return dom_PanelComponent;
                    }

                    // Panel Container
                    this.HTMLPanel_p = function ()
                    {
                        return dom_PanelComponent_p;
                    }

                    // ReadOnly Div
                    if (_this.bIsReadOnly != null && _this.bIsReadOnly)
                    {
                        this.HTMLReadOnlyDiv = function ()
                        {
                            return dom_ReadOnlyDiv;
                        }
                        this.HTMLReadOnlyDiv_p = function ()
                        {
                            return dom_ReadOnlyDiv_p;
                        }
                    }

                    // Store instance of this atomic control
                    if (this.sId)
                        aComboTextBoxes[this.sId] = this;

                    // TextBox onFocus
                    var fn_v_OnFocus = function ()
                    {
                        _this.fn_v_FocusOrBlur(_this, true);
                    };

                    // ReadOnlyDiv onClick
                    var fn_v_OnReadOnlyDivClick = function (o_Event)
                    {
                        if (_this.bIsReadOnly != null && _this.bIsReadOnly)
                        {
                            _this.HTMLButton().click();
                            o_Event.stopPropagation();
                        }
                    };

                    // TextBox onBlur
                    var fn_v_OnBlur = function (o_Event)
                    {
                        _this.fn_v_FocusOrBlur(_this, false);
                        if (_this.bDisableBlurApply == null || !_this.bDisableBlurApply)
                        {
                            if (_this.bIsReadOnly == null || !_this.bIsReadOnly)
                                _this.fn_v_ApplyChanges(-1, _this.HTML().val(), o_Event, _this);
                        }
                    };

                    // TextBox onKeyUp
                    var fn_v_OnKeyUp = function (o_Event)
                    {
                        if (o_Event.keyCode == 13)
                            _this.fn_v_ApplyChanges(-1, _this.HTML().val(), o_Event, _this);
                    };

                    // Document onClick
                    var fn_v_OnDocClick = function ()
                    {
                        $(document).unbind("click", fn_v_OnDocClick);
                        _this.fn_v_ShowOrHidePanel(false);
                        o_LastComboTextBox = null;
                        _this.HTML().blur();
                        dom_CombotextboxPanelContainer.hide();
                        $this.Page.b_LockValidationForComboSelect = false;
                    };

                    // Button onClick
                    var fn_v_ButtonOnClick = function ()
                    {
                        if (b_ComboIsOpen && !b_CommesFromKeyboard && (b_CommesFromBeforeArrowClick == undefined || (b_CommesFromBeforeArrowClick != undefined && !b_CommesFromBeforeArrowClick)))
                        {
                            b_DisableSecondClick = true;
                            fn_v_OnDocClick();
                        }
                        else
                        {
                            if (o_LastComboTextBox != null)
                            {
                                _this.fn_v_FocusOrBlur(o_LastComboTextBox, false);
                                o_LastComboTextBox.fn_v_ShowOrHidePanel(false);
                                o_LastComboTextBox = null;
                            }
                            _this.fn_v_FocusOrBlur(_this, true);
                            if (_this.bNoAutoSelectOnArrowClick == null || !_this.bNoAutoSelectOnArrowClick)
                            {
                                if (_this.bIsReadOnly == null || !_this.bIsReadOnly)
                                {
                                    _this.HTML().focus();
                                    _this.HTML().select();
                                }
                            }
                            _this.fn_v_ShowOrHidePanel(true);
                            $(document).unbind("click", fn_v_OnDocClick);
                            $(document).click(fn_v_OnDocClick);

                            if (_this.bDisableAutoLastControlHide2 == null || !_this.bDisableAutoLastControlHide2)
                                o_LastComboTextBox = _this;

                            dom_CombotextboxPanelContainer.html(dom_PanelComponent_p);
                            var dom_JmMainContent = $("#jm_maincontent");

                            if (_this.iPanelOffsetLeft == 0)
                                dom_CombotextboxPanelContainer.css("left", _this.HTML().offset().left - dom_JmMainContent.offset().left + "px");
                            else
                                dom_CombotextboxPanelContainer.css("left", _this.HTML().offset().left + _this.iPanelOffsetLeft + "px");

                            if (_this.iPanelOffsetTop == 0)
                                dom_CombotextboxPanelContainer.css("top", _this.HTML().offset().top - dom_JmMainContent.offset().top + 27 + "px");
                            else
                                dom_CombotextboxPanelContainer.css("top", _this.HTML().offset().top + _this.iPanelOffsetTop + "px");

                            dom_CombotextboxPanelContainer.show();

                            if (_this.bDisableAutoLastControlHide == null || !_this.bDisableAutoLastControlHide)
                            {
                                if (o_LastBubble != null)
                                    o_LastBubble.Hide();
                            }
                        }
                    };

                    // Panel onMouseUp
                    var fn_v_PanelOnMouseUp = function ()
                    {
                        _this.fn_v_FocusOrBlur(_this, true);
                    };

                    // Generic onClick
                    var fn_v_OnGenericClick = function (o_Event)
                    {
                        o_Event.stopPropagation();
                    };

                    // Ondemand callbacks
                    this.OnSelect = function (s_fnCallback, s_cbName)
                    {
                        fn_v_AddCallback(c_a_ControlType.ComboTextBox, _this.sId, s_fnCallback, s_cbName);
                    };

                    this.RemoveOnSelect = function (s_cbName)
                    {
                        fn_v_RemoveCallback(c_a_ControlType.ComboTextBox, _this.sId, s_cbName);
                    };

                    // Bind multiple events
                    this.HTML().unbind("focus", fn_v_OnFocus)
                        .focus(fn_v_OnFocus)
                        .unbind("blur", fn_v_OnBlur)
                        .blur(fn_v_OnBlur);

                    this.HTMLButton().unbind("click", fn_v_ButtonOnClick)
                        .click(fn_v_ButtonOnClick)
                        .unbind("click", fn_v_OnGenericClick)
                        .click(fn_v_OnGenericClick);

                    this.HTMLPanel_p().unbind("mouseup", fn_v_PanelOnMouseUp)
                        .mouseup(fn_v_PanelOnMouseUp)
                        .unbind("click", fn_v_OnGenericClick)
                        .click(fn_v_OnGenericClick);

                    if (_this.bIsReadOnly != null && _this.bIsReadOnly)
                    {
                        this.HTMLReadOnlyDiv().unbind("click", fn_v_OnReadOnlyDivClick)
                            .click(fn_v_OnReadOnlyDivClick);
                    }

                    this.fn_v_ShowOrHidePanel(false);

                    if (this.bIsReadOnly == null || !this.bIsReadOnly)
                    {
                        this.HTML().unbind("keyup", fn_v_OnKeyUp)
                            .keyup(fn_v_OnKeyUp);
                    }
                    s_CurrentTheme = s_ThemeId;

                    if (_this.bIsReadOnly != null && _this.bIsReadOnly)
                    {
                        if (_this.bDisableReadOnlyDiv != null && _this.bDisableReadOnlyDiv)
                            this.HTMLReadOnlyDiv().css("width", "0px");
                        else
                            this.HTMLReadOnlyDiv().css("width", this.iWidth + 8 + "px");
                    }

                    if ($this.Page.b_AutoFocus &&
                        _this.iTabIndex &&
                        _this.iTabIndex < i_MinTabIndex)
                    {
                        i_MinTabIndex = _this.iTabIndex;
                        _this.HTML().focus();
                    }

                    // 1st Theme: empty panel
                    if (s_ThemeId == "")
                    {
                        // TODO
                    }
                    // 2nd Theme: list
                    else if (s_ThemeId == "list")
                    {
                        if (!this.iWidth_Panel && this.iWidth)
                            this.iWidth_Panel = this.iWidth + 29;

                        if (this.iWidth_Panel && this.iRows_Panel && this.aContentItems)
                        {
                            this.HTMLPanel().css("width", (this.iWidth_Panel - ($this.Browser.bIsIE ? 0 : 1)) + "px");
                            this.HTMLPanel().css("height", (this.iRows_Panel * i_DefaultRowHeight) + "px");
                            b_CommesFromBeforeArrowClick = false;

                            s_SelectedValue = _this.HTML().val();
                            if (_this.sHiddenValue)
                                s_SelectedValue = _this.sHiddenValue;

                            // Find selected item before arrow clicked
                            for (var s_Key in this.aContentItems)
                            {
                                if (this.aContentItems.hasOwnProperty(s_Key) && this.aContentItems[s_Key] == s_SelectedValue)
                                {
                                    this.iSelectedKey = s_Key;
                                    this.sSelectedText = s_SelectedValue;
                                    break;
                                }
                            }

                            this.HTMLButton().click(function ()
                            {
                                if (b_DisableSecondClick)
                                {
                                    b_DisableSecondClick = false;
                                    return;
                                }

                                if (_this.sHiddenValue)
                                    s_SelectedValue = _this.sHiddenValue;

                                _this.HTMLPanel().css("width", (_this.iWidth_Panel - ($this.Browser.bIsIE ? 0 : 1)) + "px");
                                // Count items
                                i_ArrayLength = 0;
                                for (var s_Key in _this.aContentItems)
                                {
                                    if (_this.aContentItems.hasOwnProperty(s_Key)) {
                                        i_ArrayLength++;
                                    }
                                }
                                if (_this.onBeforeArrowClick)
                                {
                                    if (b_CommesFromBeforeArrowClick)
                                    {
                                        b_CommesFromBeforeArrowClick = false;
                                    }
                                    else
                                    {
                                        _this.aContentItems = new Array();
                                        i_ArrayLength = 0;
                                    }
                                }

                                if (i_ArrayLength == 0)
                                {
                                    // No Items
                                    if (_this.onBeforeArrowClick)
                                    {
                                        b_CommesFromBeforeArrowClick = true;
                                        _this.onBeforeArrowClick();
                                        return;
                                    }
                                    _this.fn_v_ShowOrHideArrow(false);
                                }
                                else
                                {
                                    _this.fn_v_ShowOrHideArrow(true);
                                }

                                // Create Table Container
                                var i_TableParentWidth = (_this.iWidth_Panel - ($this.Browser.bIsIE ? 2 : 3) + (i_ArrayLength <= _this.iRows_Panel ? 2 : 0));

                                var dom_Table_p = $("<div></div>")
                                    .css("width", i_TableParentWidth + "px")
                                    .css("height", (_this.iRows_Panel * i_DefaultRowHeight) + "px")
                                    .addClass("combotextbox_table_p");

                                var i_TableWidth = _this.iWidth_Panel - ($this.Browser.bIsIE ? 12 : 13);

                                if (i_ArrayLength <= _this.iRows_Panel)
                                {
                                    i_TableWidth += 20;
                                    if (_this.bDeleteColumn != null && _this.bDeleteColumn)
                                        i_TableWidth -= 8;
                                }

                                // Create Table
                                var dom_Table = $("<table></table>")
                                    .attr("cellpadding", "0")
                                    .attr("cellspacing", "0")
                                    .css("width", i_TableWidth + "px")
                                    .addClass("combotextbox_table");

                                // Table for list theme
                                _this.HTMLTable = function ()
                                {
                                    return dom_Table;
                                }
                                _this.HTMLTable_p = function ()
                                {
                                    return dom_Table_p;
                                }

                                var i_ScrollAmount = 0;
                                var i_RowsHeight = 0;

                                dom_CurrentTr = null;

                                var fn_v_setKeyNaviPosition = function (dom_Tr)
                                {
                                    dom_Tr.parent().find('tr').removeClass('hover');
                                    _this.iKeyNaviPosition = dom_Tr;
                                }

                                // Process content items
                                for (var s_Key in _this.aContentItems)
                                {
                                    if (_this.aContentItems.hasOwnProperty(s_Key)) {
                                        // Create Row
                                        var dom_Tr = $("<tr></tr>")
                                            .addClass("combotextbox_tr")
                                            .attr("id", "combotextbox_tr_" + _this.sId + "_" + s_Key);

                                        dom_Tr.mouseover(function () {
                                            fn_v_setKeyNaviPosition($(this));
                                            $(this).addClass('hover');
                                        });

                                        var dom_Td = $("<td></td>")
                                            .append(_this.aContentItems[s_Key])
                                            .addClass("combotextbox_td")
                                            .attr("id", "combotextbox_td" + _this.sId + "_" + s_Key);
                                        dom_Tr.append(dom_Td);



                                        // Create Delete Column
                                        if (_this.bDeleteColumn != null && _this.bDeleteColumn) {
                                            var dom_IconRep = $("<a></a>")
                                                .attr("id", "comboicon_delete_" + _this.sId + "_" + s_Key);
                                            var dom_Td_delete = $("<td></td>")
                                                .addClass("combotextbox_td_delete")
                                                .append(dom_IconRep);
                                            dom_Tr.append(dom_Td_delete);
                                        }

                                        _this.HTMLTable().append(dom_Tr);
                                        i_RowsHeight += i_DefaultRowHeight;

                                        // Find selected item and highlight
                                        if (_this.aContentItems[s_Key] == s_SelectedValue) {
                                            if (_this.bDisableAutoItemSelect == null || !_this.bDisableAutoItemSelect) {
                                                i_ScrollAmount = i_RowsHeight;
                                                dom_Tr.addClass("selected");
                                                _this.iSelectedKey = s_Key;
                                                _this.sSelectedText = s_SelectedValue;
                                            }
                                        }
                                        dom_Td.click(function (o_Event) {
                                            // Select an item
                                            _this.fn_v_ApplyChanges($(this).attr("id").replace("combotextbox_td" + _this.sId + "_", ""),
                                                $(this).html(),
                                                o_Event,
                                                _this);
                                            if (_this.bAutoSelect != null && _this.bAutoSelect) {
                                                _this.HTML().val(_this.sSelectedText.replace(/&amp;/g, '&'));
                                                _this.HTML().blur();
                                                _this.fn_v_ShowOrHidePanel(false);
                                            }
                                            dom_CombotextboxPanelContainer.hide();
                                            if (_this.onAfterSelected)
                                                _this.onAfterSelected();
                                            $this.Page.b_LockValidationForComboSelect = false;
                                            o_Event.stopPropagation();
                                        });
                                    }
                                }

                                _this.HTMLTable_p().append(_this.HTMLTable());
                                _this.HTMLPanel().html(_this.HTMLTable_p());

                                if (_this.bDeleteColumn != null && _this.bDeleteColumn && _this.onItemDelete)
                                {
                                    // Create Delete Icons
                                    for (var s_Key in _this.aContentItems) {
                                        if (_this.aContentItems.hasOwnProperty(s_Key)) {
                                            var o_DeleteIcon = new $this.CustomControls.IconButton();
                                            o_DeleteIcon.s_Id = "comboicon_delete_" + _this.sId + "_" + s_Key;
                                            o_DeleteIcon.s_CSS = "comboicon_delete";
                                            o_DeleteIcon.s_Title = "delete";
                                            o_DeleteIcon.b_DisableAutoComboClose = true;
                                            o_DeleteIcon.fn_v_onClick = function (sControlID) {
                                                var sCurrentKey = sControlID.replace("comboicon_delete_" + _this.sId + "_", "");
                                                _this.onItemDelete(_this.aContentItems[sCurrentKey], sCurrentKey);
                                                _this.fn_v_RemoveItem(sCurrentKey);
                                                _this.i_CountOfAvailableItems = Math.round(_this.HTMLTable().height() / i_DefaultRowHeight);
                                            }
                                            o_DeleteIcon.fn_v_InitControl("delete");
                                        }
                                    }
                                }

                                // Attach scrollpane
                                _this.HTMLTable_p().jScrollPane({ showArrows: true, scrollbarWidth: 8 });

                                // Fix for ScrollPaneDrag
                                var dom_DragItem = _this.HTMLPanel().find(".jScrollPaneDrag");
                                if (dom_DragItem.height() < 8)
                                    dom_DragItem.css("height", "8px");

                                var fn_v_GenericClick = function (oEvent)
                                {
                                    oEvent.stopPropagation();
                                }

                                /* ie scrolldrag mouseup preventer */
                                if ($this.Browser.bIsIE)
                                {
                                    var dom_ScrolldDrag = $(".jScrollPaneDrag");

                                    dom_ScrolldDrag.mousedown(function () { $(document).bind('mouseup', fn_v_SetDocumentMouseup); b_ScrollDragPreventer = true; });

                                    var fn_v_SetDocumentMouseup = function ()
                                    {

                                        $(document).unbind('mouseup', fn_v_SetDocumentMouseup);
                                        window.setTimeout(function () { b_ScrollDragPreventer = false; }, 0);
                                    }

                                    $(document).click(function (event) { if (b_ScrollDragPreventer) event.stopImmediatePropagation(); });
                                }

                                $(".jScrollArrowUp").addClass('sprt');
                                $(".jScrollArrowDown").addClass('sprt');
                                $(".jScrollPaneDragTop").addClass('sprt');
                                $(".jScrollPaneDragBottom").addClass('sprt');

                                _this.HTMLPanel().find(".jScrollPaneTrack").unbind("mouseup", fn_v_PanelOnMouseUp)
                                    .mouseup(fn_v_PanelOnMouseUp)
                                    .unbind("click", fn_v_GenericClick)
                                    .click(fn_v_GenericClick);

                                _this.HTMLPanel().find(".jScrollCap").unbind("mouseup", fn_v_PanelOnMouseUp)
                                    .mouseup(fn_v_PanelOnMouseUp)
                                    .unbind("click", fn_v_GenericClick)
                                    .click(fn_v_GenericClick);

                                _this.HTMLPanel().find(".jScrollArrowUp").unbind("mouseup", fn_v_PanelOnMouseUp)
                                    .mouseup(fn_v_PanelOnMouseUp)
                                    .unbind("click", fn_v_GenericClick)
                                    .click(fn_v_GenericClick);

                                _this.HTMLPanel().find(".jScrollArrowDown").unbind("mouseup", fn_v_PanelOnMouseUp)
                                    .mouseup(fn_v_PanelOnMouseUp)
                                    .unbind("click", fn_v_GenericClick)
                                    .click(fn_v_GenericClick);
                                _this.fn_v_InitKeyNaviPosition(true);
                            });
                        }
                    }
                }
                // #endregion
            };

            // External Methods
            // #region
            this.ComboTextBox.get = function (sId)
            {
                return aComboTextBoxes[sId];
            }
            // #endregion

            // #endregion

            // IconButton Control
            // #region
            this.IconButton = function ()
            {
                // Properties
                // #region

                var _this = this;
                this.s_Id = null;
                this.s_CSS = null;
                this.s_Style = null;
                this.s_Title = null;
                this.fn_v_onClick = null;
                this.b_DisableAutoComboClose = null;

                // #endregion


                // fn_v_ShowOrHide
                // #region
                this.fn_v_ShowOrHide = function (b_show)
                {
                    if (b_show)
                        this.HTML().show();
                    else
                        this.HTML().hide();
                }
                // #endregion

                // fn_v_InitControl
                // #region
                this.fn_v_InitControl = function (s_ThemeId)
                {
                    // Create IconButton
                    var dom_IconButtonComponent = $("<div>&nbsp;</div>")
                        .addClass("sprt_l iconbutton");
                    if (this.s_Id)
                        dom_IconButtonComponent.attr("id", this.s_Id);
                    if (this.s_CSS)
                        dom_IconButtonComponent.addClass(this.s_CSS);
                    if (this.s_Style)
                        dom_IconButtonComponent.attr("style", this.s_Style);
                    if (this.s_Title)
                        dom_IconButtonComponent.attr("title", this.s_Title);

                    var dom_HTML = dom_IconButtonComponent;
                    $("#" + this.s_Id).replaceWith(dom_HTML);

                    this.HTML = function ()
                    {
                        return dom_HTML;
                    }
                    // Store instance of this atomic control
                    if (this.s_Id)
                        aIconButtons[this.s_Id] = this;

                    // onClick
                    var fn_v_OnClick = function (o_Event)
                    {
                        if (_this.fn_v_onClick)
                            _this.fn_v_onClick(_this.s_Id);
                        if (o_LastComboTextBox != null)
                        {
                            if (_this.b_DisableAutoComboClose == null || !_this.b_DisableAutoComboClose)
                            {
                                o_LastComboTextBox.fn_v_ShowOrHidePanel(false);
                                o_LastComboTextBox.fn_v_FocusOrBlur(o_LastComboTextBox, false);
                            }
                        }
                        o_Event.stopPropagation();
                    };

                    // Bind multiple events
                    this.HTML().unbind("click", fn_v_OnClick)
                        .click(fn_v_OnClick);

                    switch (s_ThemeId)
                    {
                        case "delete":
                            this.HTML().addClass("delete");
                            break;
                        case "email":
                            this.HTML().addClass("email");
                            break;
                        case "check":
                            this.HTML().addClass("check");
                            break;
                        case "globe":
                            this.HTML().addClass("globe");
                            break;
                        case "head":
                            this.HTML().addClass("head");
                            break;
                        case "clipboard":
                            this.HTML().addClass("clipboard");
                            break;
                        case "copy":
                            this.HTML().addClass("copy");
                            break;
                        case "loadinganim":
                            this.HTML().addClass("loadinganim");
                            break;
                        case "loadinganimbig":
                            this.HTML().addClass("loadinganimbig");
                            break;
                    }
                }
                // #endregion
            };

            // External Methods
            // #region
            this.IconButton.get = function (sId)
            {
                return aIconButtons[sId];
            }
            // #endregion

            // #endregion

            // Button Control
            // #region
            this.Button = function (options)
            {
                ///<summary>
                /// Button control
                ///</summary>
                ///<param name="options">
                /// s_Id - id of the button &#13;
                /// s_CSS - css class to be applied on the button &#13;
                /// s_Text - buttons text &#13;
                /// i_TabIndex - tab index, default 1 &#13;
                /// s_Style - inline style to be applied &#13;
                /// s_Theme - theme, possible values: big, small, default value big &#13;
                /// s_Color - buttons color, default orange &#13;
                /// b_KeepInMemory - wether button should be kept over page reloads &#13;
                /// b_DefaultButton - the button event which performs a form submit, default false &#13;
                ///</param>
                ///<return>
                /// Button object
                ///</return>
                if (!options["s_Id"])
                    return new $this.CustomControls.Button.fn.init(null, null);

                var default_args = {
                    's_CSS': false,
                    's_Text': "",
                    'i_TabIndex': 1,
                    's_Style': "",
                    's_Theme': 'big',
                    's_Color': 'orange',
                    'b_KeepInMemory': false,
                    'b_DefaultButton': false
                };

                for (var index in default_args)
                {
                    if (default_args.hasOwnProperty(index) && typeof options[index] == "undefined") {
                        options[index] = default_args[index];
                    }
                }

                var oHTML = $("<table></table>")
                    .addClass("button")
                    .addClass(options.s_Color)
                    .attr("cellpadding", "0")
                    .attr("cellspacing", "0")
                    .attr("id", options.s_Id)
                    .attr("style", options.s_Style)
                if (options.s_Theme == "small")
                    oHTML.addClass('small');
                if (options.s_CSS)
                    oHTML.addClass(options.s_CSS);

                var oLabel = $("<span></span>");
                if (options.i_TabIndex)
                    oLabel.attr("tabindex", options.i_TabIndex);
                if (options.s_Text)
                    oLabel.append(options.s_Text);
                var oTdLeft = $("<td></td>")
                    .attr("class", "sprt buttonleft");
                var oTdMiddle = $("<td></td>")
                    .attr("class", "sprt_x buttonmiddle")
                    .append(oLabel);
                var oTdRight = $("<td></td>")
                    .attr("class", "sprt buttonright");
                var oTr = $("<tr></tr>")
                    .append(oTdLeft)
                    .append(oTdMiddle)
                    .append(oTdRight);
                var oTbody = $("<tbody></tbody>")
                    .append(oTr);
                oHTML.append(oTbody);
                $("#" + options.s_Id).replaceWith(oHTML);

                var fn_v_OnMouseClick = function (o_Event)
                {
                    if (oHTML.hasClass("disabled"))
                        return false;

                    fn_v_callback(c_a_ControlType.Button, options.s_Id, o_Event, $this.CustomControls.Button.get(options.s_Id));

                    if (options['b_DefaultButton'])
                        o_Event.stopPropagation();
                }

                var fn_v_OnKeyDown = function (o_Event)
                {
                    if (o_Event.keyCode == 13 ||
                        $this.InputLimiters.isSpecificKey(o_Event, KeyCodes.Space))
                        fn_v_OnMouseClick(o_Event);
                }

                aButtons[options.s_Id] = { "html": oHTML, "label": oLabel, "keepInMemory": options.b_KeepInMemory };
                oHTML.keydown(fn_v_OnKeyDown);
                oHTML.click(fn_v_OnMouseClick);

                if ($this.Page.b_AutoFocus &&
                    options["i_TabIndex"] &&
                    options["i_TabIndex"] < i_MinTabIndex)
                {
                    i_MinTabIndex = options["i_TabIndex"];
                    focusElement(oHTML);
                }

                var o_ButtonWrapper = new $this.CustomControls.Button.fn.init(oHTML, oLabel, options.b_KeepInMemory);

                if (options.b_DefaultButton)
                    $this.Page.AddDefaultControlToForm(o_ButtonWrapper);
                return o_ButtonWrapper;
            };

            // External Methods
            // #region
            this.Button.get = function (sId)
            {
                var j_ArrayItem = aButtons[sId];
                if (!j_ArrayItem)
                    j_ArrayItem = { "html": null, "label": null, "keepInMemory": false };

                return new $this.CustomControls.Button.fn.init(j_ArrayItem.html, j_ArrayItem.label, j_ArrayItem.keepInMemory);
            }
            // #endregion

            this.Button.fn = this.Button.prototype = {
                init: function (html, label, keepInMemory)
                {
                    this.b_Exists = true;
                    if (!html)
                        this.b_Exists = false;

                    var o_HTML = html;
                    var s_Id = html ? html.attr("id") : '';
                    var o_Label = label;
                    this.HTML = function () { return o_HTML; }
                    this.Label = function () { return o_Label; }
                    var bDisabled = o_HTML ? o_HTML.hasClass("disabled") : false;

                    this.Disabled = function (bDisableButton)
                    {
                        if (bDisableButton != undefined && o_HTML)
                        {
                            bDisabled = bDisableButton;
                            if (bDisableButton)
                                o_HTML.addClass("disabled");
                            else
                                o_HTML.removeClass("disabled");
                        }
                        else
                        {
                            return bDisabled;
                        }
                    };

                    // Ondemand callbacks
                    this.OnClick = function (s_fnCallback, s_cbName)
                    {
                        fn_v_AddCallback(c_a_ControlType.Button, s_Id, s_fnCallback, s_cbName, keepInMemory);
                    };
                }
            };

            // #endregion

            // CheckBox Control
            // #region
            this.CheckBox = function (o_options)
            {
                // Set defaults
                if (!o_options['s_id']) return false;

                var default_args = {
                    's_CSS': false,
                    's_style': false,
                    's_text': false,
                    's_HTMLrId': false,
                    'i_tabIndex': false,
                    'o_fnCallBack': []
                };

                var o_this = this;
                var b_Checked = false;
                var b_Disabled = false;

                for (var i in default_args)
                {
                    if (default_args.hasOwnProperty(i) && typeof o_options[i] == "undefined") {
                        o_options[i] = default_args[i];
                    }
                }

                // Create html
                var dom_Checkbox = $('<span></span>')
                    .addClass("checkbox")
                    .attr('id', o_options['s_id']);

                var dom_SpriteArea = $('<span></span>');
                dom_SpriteArea.addClass('sprt sprite');
                dom_Checkbox.append(dom_SpriteArea);

                var dom_ContextArea = $('<span></span>');

                if (o_options['s_HTMLrId'])
                {
                    var o_template = $('#' + o_options['s_HTMLrId']);
                    dom_ContextArea.append(o_template.clone());
                    o_template.remove();
                }
                else if (o_options['s_text'])
                {
                    dom_ContextArea.append(o_options['s_text']);
                }

                dom_Checkbox.append(dom_ContextArea);

                if (o_options['i_tabIndex'])
                    dom_Checkbox.attr("tabindex", o_options['i_tabIndex']);

                if (o_options['s_CSS'])
                    dom_Checkbox.addClass(o_options['s_CSS']);

                if (o_options['s_style'])
                    dom_Checkbox.attr('style', o_options['s_style']);

                $('#' + o_options['s_id']).replaceWith(dom_Checkbox);

                // Add to custom control array
                aCheckBoxes[o_options['s_id']] = { 'html': dom_Checkbox, 'b_Checked': b_Checked, 'b_Disabled': b_Disabled };

                var o_Wrapper = $this.CustomControls.CheckBox.fn.init(dom_Checkbox);

                // Private methods
                var fn_v_Toggle = function (o_Event)
                {
                    if (aCheckBoxes[o_options['s_id']].b_Disabled)
                        return false;

                    var o_CheckBox = $this.CustomControls.CheckBox.get(o_options.s_id);
                    o_CheckBox.Checked(!o_CheckBox.Checked());
                    fn_v_callback(c_a_ControlType.CheckBox, o_options.s_id, o_Event, o_CheckBox);
                };

                // Event binding
                var fn_v_OnKeyDown = function (o_Event)
                {
                    if (o_Event.keyCode == 32)
                        fn_v_Toggle(o_Event);
                };

                dom_Checkbox.keydown(fn_v_OnKeyDown);

                dom_Checkbox.click(fn_v_Toggle);

                dom_Checkbox.mousedown(function () { return false });

                if ($this.Page.b_AutoFocus &&
                    o_options["i_tabIndex"] &&
                    o_options["i_tabIndex"] < i_MinTabIndex)
                {
                    i_MinTabIndex = o_options["i_tabIndex"];
                    focusElement(dom_ContextArea);
                }

                return o_Wrapper;
            };

            this.CheckBox.fn = this.CheckBox.prototype =
            {
                init: function (dom_HTML)
                {
                    this.b_Exists = true;
                    if (!dom_HTML)
                        this.b_Exists = false;

                    var dom_Checkbox = dom_HTML;
                    var dom_ContextArea = dom_HTML ? dom_HTML.find("label:first") : '';
                    var s_Id = dom_HTML ? dom_HTML.attr('id') : '';

                    var fn_v_Check = function ()
                    {
                        if (aCheckBoxes[s_Id].b_Disabled) return false;
                        aCheckBoxes[s_Id].b_Checked = true;
                        dom_Checkbox.addClass('checked');
                    };

                    var fn_v_UnCheck = function ()
                    {
                        if (aCheckBoxes[s_Id].b_Disabled) return false;
                        aCheckBoxes[s_Id].b_Checked = false;
                        dom_Checkbox.removeClass('checked')
                    };

                    var fn_v_SetDisabled = function ()
                    {
                        aCheckBoxes[s_Id].b_Disabled = true;
                        dom_Checkbox.addClass('disabled');
                    };

                    var fn_v_SetUnDisabled = function ()
                    {
                        aCheckBoxes[s_Id].b_Disabled = false;
                        dom_Checkbox.removeClass('disabled');
                    };

                    this.HTML = function () { return dom_Checkbox; };

                    this.Focus = function ()
                    {
                        dom_ContextArea.focus();
                    };

                    // Public methods
                    this.Checked = function ()
                    {
                        var a_args = arguments;
                        if (a_args.length === 0)
                        {
                            return aCheckBoxes[s_Id].b_Checked;
                        }
                        else
                        {
                            if (a_args[0] === true) fn_v_Check();
                            if (a_args[0] === false) fn_v_UnCheck();
                        }
                    };

                    this.Disabled = function ()
                    {
                        var a_args = arguments;
                        if (a_args.length === 0)
                        {
                            return aCheckBoxes[s_Id].b_Disabled;
                        }
                        else
                        {
                            if (a_args[0] === true) fn_v_SetDisabled();
                            if (a_args[0] === false) fn_v_SetUnDisabled();
                        }
                    };

                    // Ondemand callbacks
                    this.OnCheck = function (s_fnCallback, s_cbName)
                    {
                        fn_v_AddCallback(c_a_ControlType.CheckBox, s_Id, s_fnCallback, s_cbName);
                    };
                }
            }

            // External Methods
            // #region
            this.CheckBox.get = function (sId)
            {
                var j_ArrayItem = aCheckBoxes[sId];
                if (!j_ArrayItem)
                    j_ArrayItem = { "html": null };

                return new $this.CustomControls.CheckBox.fn.init(j_ArrayItem.html);
            };
            // #endregion

            // #endregion

            // Radio Control
            // #region
            this.Radio = function (o_options)
            {
                // Set defaults
                if (!o_options['s_id'] ||
                    !o_options['s_name'])
                    return new $this.CustomControls.Radio.fn.init(null);

                var default_args = {
                    's_value': false,
                    's_CSS': false,
                    's_style': false,
                    's_text': false,
                    's_HTMLrId': false,
                    'i_tabIndex': false,
                    'o_fnCallBack': []
                };

                var o_this = this;
                var b_Selected = false;
                var b_Disabled = false;

                for (var i in default_args)
                {
                    if (default_args.hasOwnProperty(i) && typeof o_options[i] == "undefined") {
                        o_options[i] = default_args[i];
                    }
                }

                o_this.value = o_options['s_value'];

                // Create html
                var dom_Radio = $('<span></span>')
                    .addClass("radio")
                    .addClass("unselected")
                    .attr('id', o_options['s_id'])
                    .attr('name', o_options['s_name']);

                var dom_SpriteArea = $('<span></span>');
                dom_SpriteArea.addClass('sprt sprite');
                dom_Radio.append(dom_SpriteArea);

                var dom_ContextArea = $('<span></span>');

                if (o_options['s_HTMLrId'])
                {
                    var o_template = $('#' + o_options['s_HTMLrId']);
                    dom_ContextArea.append(o_template.clone());
                    o_template.remove();
                }
                else if (o_options['s_text'])
                {
                    dom_ContextArea.append(o_options['s_text']);
                }

                dom_Radio.append(dom_ContextArea);

                if (o_options['i_tabIndex'])
                    dom_Radio.attr("tabindex", o_options['i_tabIndex']);

                if (o_options['s_CSS'])
                    dom_Radio.addClass(o_options['s_CSS']);

                if (o_options['s_style'])
                    dom_Radio.attr('style', o_options['s_style']);

                $('#' + o_options['s_id']).replaceWith(dom_Radio);

                // Add to custom control array
                if (typeof (aRadios[o_options['s_name']]) == 'undefined')
                    aRadios[o_options['s_name']] = [];

                var o_Wrapper = new $this.CustomControls.Radio.fn.init(dom_Radio);

                var fn_v_OnSelectedByUser = function (o_Event)
                {
                    var o_Radio = $this.CustomControls.Radio.get(o_options.s_id);
                    if (!o_Radio.Selected())
                    {
                        o_Radio.Selected(true);
                        fn_v_callback(c_a_ControlType.Radio, o_options.s_id, o_Event, o_Radio);
                    }
                };

                // Event binding
                var fn_v_OnKeyDown = function (o_Event)
                {
                    if (o_Event.keyCode == 32)
                        fn_v_OnSelectedByUser(o_Event);
                };

                dom_Radio.keydown(fn_v_OnKeyDown);

                dom_Radio.click(fn_v_OnSelectedByUser);

                aRadios[o_options['s_name']][o_options['s_id']] = { 'html': dom_Radio, 'value': o_options['s_value'] };

                if ($this.Page.b_AutoFocus &&
                    o_options["i_tabIndex"] &&
                    o_options["i_tabIndex"] < i_MinTabIndex)
                {
                    i_MinTabIndex = o_options["i_tabIndex"];
                    focusElement(dom_ContextArea);
                }

                return o_Wrapper;
            };

            this.Radio.fn = this.Radio.prototype =
            {
                init: function (dom_HTML, s_value)
                {
                    this.b_Exists = true;
                    if (!dom_HTML)
                        this.b_Exists = false;

                    var dom_Radio = dom_HTML;
                    //var b_Selected = dom_Radio ? dom_Radio.hasClass('selected') : false;
                    var b_Disabled = dom_Radio ? dom_Radio.hasClass('disabled') : false;
                    var s_Id = dom_Radio ? dom_Radio.attr('id') : '';
                    var s_Name = dom_Radio ? dom_Radio.attr('name') : '';
                    var s_Value = s_value;

                    // Private methods
                    var fn_v_SetDisabled = function ()
                    {
                        b_Disabled = true;
                        dom_Radio.addClass('disabled');
                    };

                    var fn_v_SetUnDisabled = function ()
                    {
                        b_Disabled = false;
                        dom_Radio.removeClass('disabled');
                    };

                    // Public methods
                    this.Selected = function ()
                    {
                        var a_args = arguments;
                        if (a_args.length === 0)
                        {
                            return b_Selected = dom_Radio ? dom_Radio.hasClass('selected') : false;
                        }
                        else if (a_args.length === 1)
                        {
                            if (a_args[0] === true)
                            {
                                if (b_Disabled)
                                    return false;
                                if (dom_Radio ? dom_Radio.hasClass('selected') : false)
                                    return false;
                                for (var i in aRadios[s_Name])
                                {
                                    if (aRadios[s_Name].hasOwnProperty(i)) {
                                        $this.CustomControls.Radio.get(i).Selected(false);
                                    }
                                }
                                //b_Selected = true;
                                dom_Radio
                                    .addClass('selected');
                            }
                            if (a_args[0] === false)
                            {
                                //b_Selected = false;
                                dom_Radio
                                    .removeClass('selected');
                            }
                        }
                    };

                    this.HTML = function ()
                    {
                        return dom_HTML;
                    };

                    this.Value = function ()
                    {
                        return s_Value;
                    };

                    this.Disable = function ()
                    {
                        var a_args = arguments;
                        if (a_args.length === 0)
                        {
                            return b_Disabled;
                        }
                        else
                        {
                            if (a_args[0] === true) fn_v_SetDisabled();
                            if (a_args[0] === false) fn_v_SetUnDisabled();
                        }
                    };

                    // Ondemand callbacks
                    this.OnSelect = function (s_fnCallback, s_cbName)
                    {
                        fn_v_AddCallback(c_a_ControlType.Radio, s_Id, s_fnCallback, s_cbName);
                    };

                    this.RemoveOnSelect = function (s_cbName)
                    {
                        fn_v_RemoveCallback(c_a_ControlType.Radio, s_Id, s_cbName);
                    };
                }
            };

            // External Methods
            this.Radio.get = function (sId)
            {
                for (var i in aRadios)
                {
                    if (aRadios.hasOwnProperty(i) && aRadios[i][sId]) {
                        return new $this.CustomControls.Radio.fn.init(aRadios[i][sId].html, aRadios[i][sId].value);
                    }
                }

                return new $this.CustomControls.Radio.fn.init(null);
            };

            this.Radio.Selected = function () // name , value
            {
                var a_args = arguments;

                if (a_args.length === 0)
                {
                    return false;
                }
                else if (a_args.length === 1)
                {
                    for (var i in aRadios[a_args[0]])
                    {
                        if (aRadios[a_args[0]].hasOwnProperty(i)) {
                            var o_Radio = $this.CustomControls.Radio.get(i);
                            if (o_Radio.Selected() === true) {
                                return o_Radio.Value();
                                break;
                            }
                        }
                    }
                    return false;
                }
                else
                {
                    for (var i in aRadios[a_args[0]])
                    {
                        if (aRadios[a_args[0]].hasOwnProperty(i) && aRadios[a_args[0]][i].value === a_args[1])
                        {
                            $this.CustomControls.Radio.get(i).Selected(true);
                            return true;
                            break;
                        }
                    }
                    return false;
                }

            };

            this.Radio.Disable = function () // name
            {
                var a_args = arguments;
                if (a_args.length === 0)
                {
                    return false;
                }
                if (a_args.length === 1)
                {
                    for (var i in aRadios[a_args[0]])
                    {
                        if (aRadios[a_args[0]].hasOwnProperty(i) && aRadios[a_args[0]][i].Disable() === false)
                        {
                            return false;
                            break;
                        }
                    }
                    return true;
                }
                else
                {
                    for (var i in aRadios[a_args[0]])
                    {
                        if (aRadios[a_args[0]].hasOwnProperty(i)) {
                            if (a_args[1] === true) aRadios[a_args[0]][i].Disable(true);
                            if (a_args[1] === false) aRadios[a_args[0]][i].Disable(false);
                        }
                    }
                }
            }

            // #endregion

        }
        // #endregion

        // Validation
        // #region
        this.Validation = new function ()
        {
            this.bIsStringEmpty = function (sString)
            {
                if (sString &&
                    $.trim(sString) != "")
                    return false;

                return true;
            }
            this.fn_b_IsNumber = function (sNumber)
            {
                if (!$this.Validation.bIsStringEmpty(sNumber) &&
                    /^[0-9]+$/.test(sNumber))
                {
                    return true;
                }
                return false;
            }
            this.bIsEmailValid = function (sEmail) {
                if (!this.bIsStringEmpty(sEmail) &&
                    emailValidatorRegexp.test(sEmail) &&
                    !(/\.\.|^\.|\.@/.test(sEmail)))
                    return true;

                return false;
            };
            this.fn_b_IsThereValidEmail = function (s_Emails)
            {
                var a_Emails = $this.Helpers.fn_a_GetEmails(s_Emails);

                if (a_Emails.length > 0)
                    return true;

                return false;
            };
            this.bIsPhoneNumber = function (sPhoneNumber)
            {
                // en.wikipedia.org/wiki/Local_conventions_for_writing_telephone_numbers
                var sValidChars = " 1234567890-+()[]./";
                var sPhone = $.trim(sPhoneNumber);
                if (sPhone.length == 0)
                {
                    return false;
                }
                for (var iCharPos = 0; iCharPos < sPhone.length; iCharPos++)
                {
                    if (sValidChars.indexOf(sPhone.charAt(iCharPos)) == -1)
                    {
                        return false;
                    }
                }
                return true;
            };
            this.bIsUrlString = function (sUrlString)
            {
                var sValidChars = "abcdefghijklmnopqrstuvwzxyABCDEFGHIJKLMNOPQRSTUVWZXY0123456789-_.";
                if (sUrlString.length == 0)
                {
                    return false;
                }

                for (var iCharPos = 0; iCharPos < sUrlString.length; iCharPos++)
                {
                    if (sValidChars.indexOf(sUrlString.charAt(iCharPos)) == -1)
                    {
                        return false;
                    }
                }
                return true;
            };
        };
        // #endregion

        // Helpers
        // #region
        this.Helpers = new function ()
        {
            // fn_s_EscapeJSONItem
            // #region
            this.fn_s_EscapeJSONItem = function (s_Item)
            {
                var s_BackSlash = String.fromCharCode(92);
                return s_Item
                    .replace(/\\/g, '&#92;')
                    .replace(/"/g, '&#34;');
            }
            // #endregion

            // fn_a_GetEmails
            // #region
            this.fn_a_GetEmails = function (s_Emails)
            {
                var s_Emails = s_Emails.replace(/;/g, ' ').replace(/,/g, ' ').split(' '),
                    a_Result = [],
                    validEmails = [],
                    matches = null,
                    s_Email = null;

                for (var i in s_Emails) {
                    if (s_Emails.hasOwnProperty(i)) {
                        if (!s_Emails[i] ||
                            $.trim(s_Emails[i]) == "" ||
                            /\.\.|^\.|\.@/.test($.trim(s_Emails[i])) ||
                            s_Emails[i].split('@').length != 2)
                            continue;

                        if (!$this.Validation.bIsEmailValid(s_Emails[i]))
                            continue;

                        s_Email = s_Emails[i];

                        if (s_Email.length > 70)
                            continue;

                        a_Result.push(s_Email);
                    }
                }

                return a_Result;
            }
            // #endregion
        };
        // #endregion

        $(document).ready(function ()
        {
            $this.Content.o_attach(true);
            $this.Page.fn_v_setupModalDialog();
            $this.Page.fn_v_setupTooltip();
            $this.Page.fn_v_initSessionTimer();
            $this.Page.Tooltip.discoverHelpNodes();
        });
    };

    return getInstance();
};
// #endregion

function MainPage()
{
    var instance = function () {
        var $this = this,
            error = JoinMe.CustomControls.Bubble.get("global"),
            joinButton = $("div.join:first"),
            dom_Share = $(".sharebutton"),
            dom_Join = $(".join"),
            s_LastCode = "",
            b_IsPersonalUrl = false;

        setTimeout(function () {
            var i_ErrorCode = window.FlashClientError;
            if ((!JoinMe.Browser.bHasRequiredFlashVersion() && !JM.BigIdea.Html5.IsHtml5Supported())
                || i_ErrorCode === "2")
            {
                setTimeout(function ()
                {
                    if (!error.b_Exists)
                        error = JoinMe.CustomControls.Bubble.get("global");
                    if (!error.b_Exists)
                        return;
                    error.Content("You do not have Flash{2}installed. To view a screen,{2}install {0}Flash Player{1} first."
                        .replace("{0}", "<a style='color: #FFFFFF; cursor: pointer;' onclick='JoinMe.Page.Object.HideValidator();' href=\"javascript:top ? (top.window.location.href = 'http://www.adobe.com/go/getflash/') : (window.location.href = 'http://www.adobe.com/go/getflash/');\">")
                        .replace("{1}", "</a>")
                        .replace(/\{2\}/g, "<br />"));
                    var offset = joinButton.position();
                    if (offset != null)
                    {
                        var left = offset.left - 19;
                        var top = offset.top - error.HTML().height() + 2;
                        if (JoinMe.Browser.eSessionType == SessionType.IE8)
                        {
                            top += 3;
                        }
                        error.Show(left, top);
                        $(document).unbind("click", $this.HideValidator);
                        $(document).click($this.HideValidator);
                    }
                }, 5);
            }
            else if (i_ErrorCode)
            {
                $this.ShowValidatorByErrorCode(i_ErrorCode);
            }
        }, 0);

        var bIsSelectEvent = false;
        var i_beamPos = 0;
       
        this.HideValidator = function ()
        {
            error.Hide();
        };

        this.downloadHost = function () 
        {
            var b_dl2frame = false; // download to iframe
            if ($('#downloadToIframe').length > 0)
            {
                b_dl2frame = true;
                var s_clientTicket = $('#clientTicket').val();
                var s_swapMode = $('#swapMode').val();
                var s_viewerName = encodeURI(encodeURIComponent($('#viewerName').val()));
                var s_audioPanel = $('#audioPanel').val();
            }

            var j_Data = { url: null };
            if (JoinMe.Page.bIsUserLoggedIn)
            {
                if (JoinMe.CustomControls.Radio.Selected("share_options") == "personal")
                {
                    j_Data.url = $("#own_url").val();
                }
                else
                {
                    j_Data.url = "";
                }
            }

            var downloadRequestResponse = function (data)
            {
                if (b_dl2frame || data.iError == 0) {
                    var s_QueryString = "";

                    if (!b_dl2frame && JoinMe.Page.bIsUserLoggedIn) {
                        s_QueryString = "?" + "code=" + data.i_ViewerCode + "&ticket=" + data.i_Ticket + "&oneTimeLoginTicket=" + data.OneTimeLoginTicket;
                    }

                    if (JoinMe.Browser.ePlatformType == PaltformType.MAC &&
                        JoinMe.Browser.b_JavaInstalled) {
                        if (b_dl2frame)
                            s_QueryString = "?clientTicket=" + s_clientTicket;

                        $('applet').remove();
                        error.Content('<b>' + "Please wait while starting Java." + '</b>');
                        var offset = $('div.share:last').position();
                        var left = offset.left - 19;
                        var top = offset.top - error.HTML().height() + 2;
                        error.Show(left, top);
                        $(document).unbind("click", $this.HideValidator).click($this.HideValidator);
                        setTimeout(function() {
                            error.Content("{0}Download didn\u0027t start?{1}"
                                .replace('{0}', "<a style='color: #FFFFFF; cursor: pointer;' onclick='JoinMe.Page.Object.HideValidator();' href='javascript:changeUtilityFrameLocation(\"/Download.aspx" + s_QueryString + "\");'>")
                                .replace('{1}', '</a>'));
                            error.Show(left, top);
                            $(document).unbind("click", $this.HideValidator).click($this.HideValidator);
                        }, 10 * 1000);
                        setTimeout(function() {
                            $this.HideValidator();
                        }, 30 * 1000);
                        // Download with Java Web Start downloader
                        $('body').append('<applet code="com.logmein.joinme.JoinMeJApplet.class" archive="' + JoinMe.Page.s_SecureLocation + 'java/join.me.jar, ' + JoinMe.Page.s_SecureLocation + 'java/lib/swing-layout-1.0.4.jar" width="1" height="1"><param name="downloadUrl" value="' + JoinMe.Page.s_SecureLocation + '/Download.aspx' + s_QueryString + (s_QueryString.length == 0 ? '?' : '&') + 'prepareforjava=true"></applet>');
                    } else {
                        // Download without Java Web Start downloader
                        if (b_dl2frame) {
                            s_QueryString = "?clientTicket=" + s_clientTicket + "&swapMode=" + s_swapMode + "&viewerName=" + s_viewerName;
                            if (s_audioPanel != '')
                                s_QueryString += "&audioPanel=1";
                            changeUtilityFrameLocation('/Download.aspx' + s_QueryString);
                        } else {
                            changeUtilityFrameLocation('/Download.aspx' + s_QueryString);
                        }
                    }
                }
            }
            if (b_dl2frame)
            {
                downloadRequestResponse({});
            }
            else
            {
                $.ajaxSetup({ async: false });
                JoinMe.Page.fn_v_SendAjax(WebServices.Common, "hostDownloadRequested", j_Data, downloadRequestResponse);
                $.ajaxSetup({ async: true });
            }
        };

        var fn_v_ShareClicked = function (o_Event)
        {
            if (!stickyApp.isHostPreferred())
            {
                try { parent._gaq.push(['_trackEvent', 'Buttons', 'Click', 'JoinMe-Share']) } catch (e) { ; };
                JM.ICallQueue.getInstance().addQueueItem('/googleconversion.html');

                if (JoinMe.Browser.bIsIE)
                    $this.downloadHost();
                else
                    JM.ICallQueue.getInstance().addQueueItem($this.downloadHost);    
            }
            else
            {
                var url = '';

                if ((JoinMe.Page.bIsUserLoggedIn) && (JoinMe.CustomControls.Radio.Selected("share_options") == "personal")) {
                    url = $("#own_url").val();
                }

                stickyApp.setErrorBubble(error);
                stickyApp.startStickyShare(url, $('div.sharebutton:last'), fn_v_ShareClicked);
            }
        };

        var shareWithTrackingClicked = function (o_Event)
        {
            if (JoinMe.Browser.bIsIE) {
                JM.MediaPlex.getInstance().track(JM.MediaPlexActions.ShareButtonClick);
                fn_v_ShareClicked();
            } else {
                JM.MediaPlex.getInstance().track(JM.MediaPlexActions.ShareButtonClick, null, fn_v_ShareClicked);
            }
        };

        var fn_v_JoinClicked = function (o_Event)
        {
            if (o_Event)
                o_Event.stopImmediatePropagation();

            JM.MediaPlex.getInstance().track(JM.MediaPlexActions.JoinButtonClick, null, function () {
                $this.startViewer();
                try { parent._gaq.push(['_trackEvent', 'Buttons', 'Click', 'JoinMe-Join']) } catch (e) { ; };
            });
        };

        var formatCodeAreaText = function (oObject, bIsDelete, bIsBackSpace)
        {
            if (!b_IsPersonalUrl)
            {
                var iCursorPos = JoinMe.CustomControls.TextBox.getCaretPosition(oObject[0]);
                var sOldValue = oObject.val().replace(/-/g, "");
                var sNewValue = "";
                for (var j = 0; j < sOldValue.length; j++)
                {
                    sNewValue += sOldValue.charAt(j);
                    if (j == 2 || j == 5)
                    {
                        sNewValue += "-";
                    }
                }

                if (bIsBackSpace)
                {
                    if (sNewValue.charAt(sNewValue.length - 1) == "-")
                        sNewValue = sNewValue.substring(0, sNewValue.length - 1);
                    oObject.val(sNewValue);
                    if (iCursorPos == 4 || iCursorPos == 8)
                        JoinMe.CustomControls.TextBox.setCaretPosition(oObject[0], iCursorPos - 1);
                    else
                        JoinMe.CustomControls.TextBox.setCaretPosition(oObject[0], iCursorPos);
                }
                else if (bIsDelete)
                {
                    if (sNewValue.charAt(sNewValue.length - 1) == "-")
                        sNewValue = sNewValue.substring(0, sNewValue.length - 1);
                    oObject.val(sNewValue);
                    JoinMe.CustomControls.TextBox.setCaretPosition(oObject[0], iCursorPos);
                }
                else
                {
                    if (sNewValue.charAt(sNewValue.length - 1) == "-" ||
                sNewValue.charAt(iCursorPos - 1) == "-" ||
                sNewValue.charAt(iCursorPos) == "-")
                        iCursorPos++;
                    if (sNewValue.charAt(sNewValue.length - 1) == "-" &&
                iCursorPos < sNewValue.length)
                    {
                        sNewValue = sNewValue.substring(0, sNewValue.length - 1);
                    }
                    oObject.val(sNewValue);
                    JoinMe.CustomControls.TextBox.setCaretPosition(oObject[0], iCursorPos);
                }
            }
        };

        this.showMaintenanceMessage = function () {
            var 
                isduring = window.IS_DURING_MAINTENANCE,
                audioonly = window.IS_AUDIO_ONLY,
                title = isduring ? "System Maintenance" :
                    audioonly ? "Planned Audio System Maintenance" : "Planned System Maintenance",
                text = audioonly ? "During this time you may experience a short disruption (5 mins) of our conference call service.\u003cbr /\u003ejoin.me will continue to function without interruption.".replace("{0}", "<br/>") :
                    "During this time you will not be able to start new meetings.".replace("{0}", "<br/>");

            var content_ = '<div id="maintenancePopupMessage">\
                             <h1>' + title + '</h1>\
                              <table>\
                                <tr>\
                                    <td class="start">' + "Start:" + '</td>\
                                    <td class="start-content">\
                                        <div>\
                                            <div class="date">' + window.MAINTENANCE_MESSAGE_EST_DATE + '</div>\
                                            <div class="est">' + window.MAINTENANCE_MESSAGE_EST_TIME + '<span class="light"> ' + "(Eastern Standard Time)" + '</span></div>\
                                            <div class="cet">' + window.MAINTENANCE_MESSAGE_CET_TIME + '<span class="light"> ' + "(Central European Time)" + '</span></div>\
                                        </div>\
                                    </td>\
                                </tr>\
                                <tr>\
                                    <td class="duartion">' + "Duration:" + '</td>\
                                    <td class="duartion-content">\
                                        <div>' + window.MAINTENANCE_DURATION + ' ' + "minutes" + '</div>\
                                    </td>\
                                </tr>\
                                <tr>\
                                    <td class="during" colspan="2">' + text + '</td>\
                                </tr>\
                              </table>\
                          </div>';

            JoinMe.Page.ModalDialog.Content(content_);
            JoinMe.Page.ModalDialog.b_ShowCloseButton(false);
            JoinMe.Page.ModalDialog.Show();
        };
    }

    if (!this.instance)
        this.instance = new instance();

    return this.instance;
}

jQuery.fn.extend(
{
    isVisible: function ()
    {
        return (this.css("display") != "none");
    },
    jm_hide: function ()
    {
        this.hide();
        JoinMe.Menu.setMenuContentPosition();
    },
    jm_show: function ()
    {
        this.show();
        JoinMe.Menu.setMenuContentPosition();
    }
});

jQuery.jsonToString = function (json)
{
    var sJSON = '{"';
    var bIsFirst = true;
    for (var item in json)
    {
        if (json.hasOwnProperty(item)) {
            if (!bIsFirst)
                sJSON += ',"';
            else
                bIsFirst = false;
            sJSON += item + '":';
            if (json[item] == null ||
                json[undefined])
                sJSON += null;
            else if (typeof (json[item]) == "string")
                sJSON += '"' + json[item] + '"';
            else
                sJSON += json[item];
        }
    }
    if (sJSON == '{"')
        return '{}';
    else
        return sJSON + "}";
};

jQuery.jsonToString_v2 = function (j_Data)
{
    var s_JSON = '{"';
    var b_IsFirst = true;
    for (var s_Item in j_Data)
    {
        if (j_Data.hasOwnProperty(s_Item)) {
            if (!b_IsFirst)
                s_JSON += ',"';
            else
                b_IsFirst = false;
            s_JSON += JoinMe.Helpers.fn_s_EscapeJSONItem(s_Item) + '": ';
            if (j_Data[s_Item] == null || j_Data[undefined])
                s_JSON += null;
            else if (typeof (j_Data[s_Item]) == "string")
                s_JSON += '"' + JoinMe.Helpers.fn_s_EscapeJSONItem(j_Data[s_Item]) + '"';
            else
                s_JSON += j_Data[s_Item];
        }
    }
    if (s_JSON == '{"')
        return '{}';
    else
        return s_JSON + "}";
};

jQuery.inherit = function (subClass, baseClass)
{
    if (arguments.length > 2)
    {
        subClass.prototype = new baseClass(Array.prototype.slice.call(arguments, 2));
        subClass.prototype.constructor = subClass;

        subClass.base = new baseClass(Array.prototype.slice.call(arguments, 2));

        baseClass.call(subClass, Array.prototype.slice.call(arguments, 2));
    }
    else
    {
        subClass.prototype = new baseClass();
        subClass.prototype.constructor = subClass;

        subClass.base = new baseClass();

        baseClass.call(subClass);
    }
};

function fn_v_GoTo(s_location)
{
    top ? (top.window.location.href = s_location) : (window.location.href = s_location);
    return true;
}

function InternalTracking (userType, trackEvent, integration)
{
    var isLoggedIn = JoinMe.Page.bIsUserLoggedIn;
    var segmentIntegration = integration || 0;
    var properties = JSON.stringify({}); //dummy until we transition to jmtracking.js

    var sendRequest = function (trackEvent)
    {
        $.ajaxSetup({ async: false });
        JoinMe.Page.fn_v_SendAjax_v2(
            WebServices.Common,
            'TrackUserInteractionsExtended',
            { ActionName: trackEvent, ActionProperties: properties, Integration: segmentIntegration },
            false
        );
        $.ajaxSetup({ async: true });
    }

    switch (userType)
    {
        case 'all':
            sendRequest(trackEvent);
            break;
        case 'free':
            if (!isLoggedIn) sendRequest(trackEvent);
            break;
        case 'pro':
            if (isLoggedIn) sendRequest(trackEvent);
            break;
    }
}

function changeUtilityFrameLocation(location) {
    if (JoinMe.Browser.eSessionType == SessionType.Chrome) {
        window.top.location.href = location;
        return;
    }
    
    var utilityFrame = document.getElementById('utilityFrame');
    if (utilityFrame) {
        // Utility iframe exists so we can change the location
        utilityFrame.src = location;
    } else {
        // Utility iframe does not exists so first we have to create a new instance
        var newUtilityFrame = $('<iframe></iframe>')
            .attr('id', 'utilityFrame')
            // Do not use CSS class because we don't know the exact CSS file where to put the class definition
            .css('position', 'absolute')
            .css('left', '-20000px')
            .css('top', '-20000px')
            .css('visibility', 'hidden')
            .attr('src', location);
        $('body').append(newUtilityFrame);
    }
}

var stickyApp = new (function () {
    
    var errorBubble = null;
    var $attachErrorBubbleTo = null;

    var getShareStickyUrl = function (personalUrl, callback) {
        var data = { personalUrl: personalUrl, autoScreenSharing: null, autoAudioConnecting: null, autoVideoConnecting: null };
        
        $.ajaxSetup({ async: false });
        JoinMe.Page.fn_v_SendAjax(WebServices.Common, "GetShareStickyAppUrl", data, callback);
        $.ajaxSetup({ async: true });
    };

    var getJoinStickyUrl = function (accessCodeOrPersonalUrl, callback) {
        var data = { accessCodeOrPersonalUrl: accessCodeOrPersonalUrl };
        
        $.ajaxSetup({ async: false });
        JoinMe.Page.fn_v_SendAjax(WebServices.Common, "GetJoinStickyAppUrl", data, callback);
        $.ajaxSetup({ async: true });
    };

    var getPresenterSwapStickyUrl = function (clientTicket, swapMode, viewerName, audioPanel, callback) {
        var data = { clientTicket: clientTicket, swapMode: swapMode, viewerName: viewerName, audioPanel: audioPanel };
        
        $.ajaxSetup({ async: false });
        JoinMe.Page.fn_v_SendAjax(WebServices.Common, "GetSwapPresenterStickyAppUrl", data, callback);
        $.ajaxSetup({ async: true });
    };

    var tryStartStickyApp = function (data, $errorMessageControl, startWithoutStickyFunction, bubbleMessageHiddenCallback) {
        if (isErrorResponse(data)) {
            showError(data, $errorMessageControl);
            return;
        }

        stickyApp.tryStartStickyUrl($errorMessageControl, startWithoutStickyFunction, data.Url, bubbleMessageHiddenCallback);
    };

    var isErrorResponse = function (data) {
        return ((typeof(data.ErrorCode) == 'undefined') || (data.ErrorCode != 0));
    };

    var showError = function (data, $errorMessageControl) {
        if (typeof(data.ErrorCode) == 'undefined')
        {
            var errorMessage = "Server is down!";

            showBubbleMessage(errorMessage, $errorMessageControl, 0.5, 10);
            return;
        }

        var isShare = false;
        if (data.IsShare)
            isShare = data.IsShare;

        if (data.ErrorCode != 0)
        {
            var errorList = {
                "error1": "Invalid personal url or access code.",
                "error3": "Please enter a viewer code",
                "error4": "9-digit number required.",
                "error5": "Invalid name",
            };
            var errorMessage = errorList['error' + data.ErrorCode];

            showBubbleMessage(errorMessage, $errorMessageControl, 0.5, 10);
            return;
        }
    };

    var showBubbleMessage = function (message, $controlToAttachTo, showSeconds, hideSeconds, clickFunction, bubbleMessageHiddenCallback) {
        $attachErrorBubbleTo = $controlToAttachTo;

        setTimeout(function () {
                errorBubble.Content(message);

                var offset = $controlToAttachTo.position();
                var left = offset.left - 19,
                    top = offset.top - errorBubble.HTML().height() + 2;

                errorBubble.Show(left, top);

                if (clickFunction) {
                    errorBubble.HTML().find('td.red_middle').unbind();
                    errorBubble.HTML().find('td.red_middle').click(clickFunction);
                }                    

                $('#mainWrapper').addClass('validatorActive');
            }, showSeconds * 1000);

        setTimeout(function () {
                hideBubbleMessage();
                $('#mainWrapper').removeClass('validatorActive');
                if (typeof(bubbleMessageHiddenCallback) == 'function')
                    bubbleMessageHiddenCallback();
            }, hideSeconds * 1000);
    };

    var hideBubbleMessage = function () {
        errorBubble.Hide();
    };

    this.setErrorBubble = function (newBubble) {
        errorBubble = newBubble;
    };

    this.isHostPreferred = function() {
        if (JoinMe.Browser.bIsIE)
            return false;

        return ((typeof (isHostPreferred) != 'undefined') && (isHostPreferred));
    };

    this.preferHtml5 = function () {
        var cookieName = "PreferredViewer";
        var expires = 'Thu, 01-Jan-2050 00:00:01 GMT';

        var hostName = window.location.hostname;
        var joinMeDomain = ".join.me";
        var isHostInJoinMeDomain = hostName.substring(hostName.length - joinMeDomain.length, hostName.length) === joinMeDomain;

        var domainSegment = isHostInJoinMeDomain ? ";domain=.join.me" : "";

        document.cookie = cookieName + "=HTML5" + ";expires=" + expires + ";path=/" + domainSegment;
    };

    this.startStickyShare = function (personalUrl, $errorMessageControl, startWithoutStickyFunction) {
        $attachErrorBubbleTo = $errorMessageControl;

            if ($('#downloadToIframe').length > 0) {
                var s_clientTicket = $('#clientTicket').val(),
                    s_swapMode = $('#swapMode').val(),
                    s_viewerName = $('#viewerName').val(),
                    s_audioPanel = $('#audioPanel').val(),
                    $errorMessageControl = $('.sharebutton'),
                    bubbleMessageHiddenCallback = function () {
                        hideDownloadDialog();
                    };

            stickyApp.startStickyPresenterSwap(s_clientTicket, s_swapMode, s_viewerName, s_audioPanel, $errorMessageControl, startWithoutStickyFunction, bubbleMessageHiddenCallback);
            return;
        }

        getShareStickyUrl(personalUrl, function (data) {
                tryStartStickyApp(data, $errorMessageControl, startWithoutStickyFunction);
            });
    };

    this.startStickyJoin = function (accessCodeOrPersonalUrl, $errorMessageControl, startWithoutStickyFunction) {
        $attachErrorBubbleTo = $errorMessageControl;

        getJoinStickyUrl(accessCodeOrPersonalUrl, function (data) {
                tryStartStickyApp(data, $errorMessageControl, startWithoutStickyFunction);
            });
    };

    this.startStickyPresenterSwap = function (clientTicket, swapMode, viewerName, audioPanel, $errorMessageControl, startWithoutStickyFunction, bubbleMessageHiddenCallback) {
        $attachErrorBubbleTo = $errorMessageControl;

        getPresenterSwapStickyUrl(clientTicket, swapMode, viewerName, audioPanel, function (data) {
                tryStartStickyApp(data, $errorMessageControl, startWithoutStickyFunction, bubbleMessageHiddenCallback);
            });
    };

    this.tryStartStickyUrl = function ($errorMessageControl, startWithoutStickyFunction, url, bubbleMessageHiddenCallback) {
        var didNotStartMessage = "App didn\u0027t start?";
        showBubbleMessage(didNotStartMessage, $errorMessageControl, 6, 18, function () {
                isHostPreferred = false;
                stickyApp.preferHtml5();
                startWithoutStickyFunction();
            }, bubbleMessageHiddenCallback);

        changeUtilityFrameLocation(url);
    };

})();
/*!	SWFObject v2.2 <http://code.google.com/p/swfobject/> 
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php> 
*/

var swfobject = function ()
{

    var UNDEF = "undefined",
		OBJECT = "object",
		SHOCKWAVE_FLASH = "Shockwave Flash",
		SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
		FLASH_MIME_TYPE = "application/x-shockwave-flash",
		EXPRESS_INSTALL_ID = "SWFObjectExprInst",
		ON_READY_STATE_CHANGE = "onreadystatechange",

		win = window,
		doc = document,
		nav = navigator,

		plugin = false,
		domLoadFnArr = [main],
		regObjArr = [],
		objIdArr = [],
		listenersArr = [],
		storedAltContent,
		storedAltContentId,
		storedCallbackFn,
		storedCallbackObj,
		isDomLoaded = false,
		isExpressInstallActive = false,
		dynamicStylesheet,
		dynamicStylesheetMedia,
		autoHideShow = true,

    /* Centralized function for browser feature detection
    - User agent string detection is only used when no good alternative is possible
    - Is executed directly for optimal performance
    */
	ua = function ()
	{
	    var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
			u = nav.userAgent.toLowerCase(),
			p = nav.platform.toLowerCase(),
			windows = p ? /win/.test(p) : /win/.test(u),
			mac = p ? /mac/.test(p) : /mac/.test(u),
			webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
			ie = ! +"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
			playerVersion = [0, 0, 0],
			d = null;
	    if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT)
	    {
	        d = nav.plugins[SHOCKWAVE_FLASH].description;
	        if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin))
	        { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
	            plugin = true;
	            ie = false; // cascaded feature detection for Internet Explorer
	            d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
	            playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
	            playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
	            playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
	        }
	    }
	    else if (typeof win.ActiveXObject != UNDEF)
	    {
	        try
	        {
	            var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
	            if (a)
	            { // a will return null when ActiveX is disabled
	                d = a.GetVariable("$version");
	                if (d)
	                {
	                    ie = true; // cascaded feature detection for Internet Explorer
	                    d = d.split(" ")[1].split(",");
	                    playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
	                }
	            }
	        }
	        catch (e) { }
	    }
	    return { w3: w3cdom, pv: playerVersion, wk: webkit, ie: ie, win: windows, mac: mac };
	} (),

    /* Cross-browser onDomLoad
    - Will fire an event as soon as the DOM of a web page is loaded
    - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
    - Regular onload serves as fallback
    */
	onDomLoad = function ()
	{
	    if (!ua.w3) { return; }
	    if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body)))
	    { // function is fired after onload, e.g. when script is inserted dynamically 
	        callDomLoadFunctions();
	    }
	    if (!isDomLoaded)
	    {
	        if (typeof doc.addEventListener != UNDEF)
	        {
	            doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
	        }
	        if (ua.ie && ua.win)
	        {
	            doc.attachEvent(ON_READY_STATE_CHANGE, function ()
	            {
	                if (doc.readyState == "complete")
	                {
	                    doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
	                    callDomLoadFunctions();
	                }
	            });
	            if (win == top)
	            { // if not inside an iframe
	                (function ()
	                {
	                    if (isDomLoaded) { return; }
	                    try
	                    {
	                        doc.documentElement.doScroll("left");
	                    }
	                    catch (e)
	                    {
	                        setTimeout(arguments.callee, 0);
	                        return;
	                    }
	                    callDomLoadFunctions();
	                })();
	            }
	        }
	        if (ua.wk)
	        {
	            (function ()
	            {
	                if (isDomLoaded) { return; }
	                if (!/loaded|complete/.test(doc.readyState))
	                {
	                    setTimeout(arguments.callee, 0);
	                    return;
	                }
	                callDomLoadFunctions();
	            })();
	        }
	        addLoadEvent(callDomLoadFunctions);
	    }
	} ();

    function callDomLoadFunctions()
    {
        if (isDomLoaded) { return; }
        try
        { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
            var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
            t.parentNode.removeChild(t);
        }
        catch (e) { return; }
        isDomLoaded = true;
        var dl = domLoadFnArr.length;
        for (var i = 0; i < dl; i++)
        {
            domLoadFnArr[i]();
        }
    }

    function addDomLoadEvent(fn)
    {
        if (isDomLoaded)
        {
            fn();
        }
        else
        {
            domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
        }
    }

    /* Cross-browser onload
    - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
    - Will fire an event as soon as a web page including all of its assets are loaded 
    */
    function addLoadEvent(fn)
    {
        if (typeof win.addEventListener != UNDEF)
        {
            win.addEventListener("load", fn, false);
        }
        else if (typeof doc.addEventListener != UNDEF)
        {
            doc.addEventListener("load", fn, false);
        }
        else if (typeof win.attachEvent != UNDEF)
        {
            addListener(win, "onload", fn);
        }
        else if (typeof win.onload == "function")
        {
            var fnOld = win.onload;
            win.onload = function ()
            {
                fnOld();
                fn();
            };
        }
        else
        {
            win.onload = fn;
        }
    }

    /* Main function
    - Will preferably execute onDomLoad, otherwise onload (as a fallback)
    */
    function main()
    {
        if (plugin)
        {
            testPlayerVersion();
        }
        else
        {
            matchVersions();
        }
    }

    /* Detect the Flash Player version for non-Internet Explorer browsers
    - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
    a. Both release and build numbers can be detected
    b. Avoid wrong descriptions by corrupt installers provided by Adobe
    c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
    - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
    */
    function testPlayerVersion()
    {
        //we need to write new code for flash version detecting because the original one appended a flash object just for fun to body, read its version and then removed from dom
        //that caused safari under windows to display a nice grey box on page refresh
        var words = navigator.plugins["Shockwave Flash"].description.split(" ");
        var mainversion = words[2].split(".")[0];
        var subversion = words[2].split(".")[1];
        var r = parseInt(words[3].replace(/r/g, ""), 10);
        ua.pv = [parseInt(mainversion, 10), parseInt(subversion, 10), parseInt(r, 10)];
        matchVersions();
    }

    /* Perform Flash Player and SWF version matching; static publishing only
    */
    function matchVersions()
    {
        var rl = regObjArr.length;
        if (rl > 0)
        {
            for (var i = 0; i < rl; i++)
            { // for each registered object element
                var id = regObjArr[i].id;
                var cb = regObjArr[i].callbackFn;
                var cbObj = { success: false, id: id };
                if (ua.pv[0] > 0)
                {
                    var obj = getElementById(id);
                    if (obj)
                    {
                        if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312))
                        { // Flash Player version >= published SWF version: Houston, we have a match!
                            setVisibility(id, true);
                            if (cb)
                            {
                                cbObj.success = true;
                                cbObj.ref = getObjectById(id);
                                cb(cbObj);
                            }
                        }
                        else if (regObjArr[i].expressInstall && canExpressInstall())
                        { // show the Adobe Express Install dialog if set by the web page author and if supported
                            var att = {};
                            att.data = regObjArr[i].expressInstall;
                            att.width = obj.getAttribute("width") || "0";
                            att.height = obj.getAttribute("height") || "0";
                            if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
                            if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
                            // parse HTML object param element's name-value pairs
                            var par = {};
                            var p = obj.getElementsByTagName("param");
                            var pl = p.length;
                            for (var j = 0; j < pl; j++)
                            {
                                if (p[j].getAttribute("name").toLowerCase() != "movie")
                                {
                                    par[p[j].getAttribute("name")] = p[j].getAttribute("value");
                                }
                            }
                            showExpressInstall(att, par, id, cb);
                        }
                        else
                        { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
                            displayAltContent(obj);
                            if (cb) { cb(cbObj); }
                        }
                    }
                }
                else
                {	// if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
                    setVisibility(id, true);
                    if (cb)
                    {
                        var o = getObjectById(id); // test whether there is an HTML object element or not
                        if (o && typeof o.SetVariable != UNDEF)
                        {
                            cbObj.success = true;
                            cbObj.ref = o;
                        }
                        cb(cbObj);
                    }
                }
            }
        }
    }

    function getObjectById(objectIdStr)
    {
        var r = null;
        var o = getElementById(objectIdStr);
        if (o && o.nodeName == "OBJECT")
        {
            if (typeof o.SetVariable != UNDEF)
            {
                r = o;
            }
            else
            {
                var n = o.getElementsByTagName(OBJECT)[0];
                if (n)
                {
                    r = n;
                }
            }
        }
        return r;
    }

    /* Requirements for Adobe Express Install
    - only one instance can be active at a time
    - fp 6.0.65 or higher
    - Win/Mac OS only
    - no Webkit engines older than version 312
    */
    function canExpressInstall()
    {
        return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
    }

    /* Show the Adobe Express Install dialog
    - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
    */
    function showExpressInstall(att, par, replaceElemIdStr, callbackFn)
    {
        isExpressInstallActive = true;
        storedCallbackFn = callbackFn || null;
        storedCallbackObj = { success: false, id: replaceElemIdStr };
        var obj = getElementById(replaceElemIdStr);
        if (obj)
        {
            if (obj.nodeName == "OBJECT")
            { // static publishing
                storedAltContent = abstractAltContent(obj);
                storedAltContentId = null;
            }
            else
            { // dynamic publishing
                storedAltContent = obj;
                storedAltContentId = replaceElemIdStr;
            }
            att.id = EXPRESS_INSTALL_ID;
            if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
            if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
            doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
            var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
				fv = "MMredirectURL=" + win.location.toString().replace(/&/g, "%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
            if (typeof par.flashvars != UNDEF)
            {
                par.flashvars += "&" + fv;
            }
            else
            {
                par.flashvars = fv;
            }
            // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
            // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
            if (ua.ie && ua.win && obj.readyState != 4)
            {
                var newObj = createElement("div");
                replaceElemIdStr += "SWFObjectNew";
                newObj.setAttribute("id", replaceElemIdStr);
                obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
                obj.style.display = "none";
                (function ()
                {
                    if (obj.readyState == 4)
                    {
                        obj.parentNode.removeChild(obj);
                    }
                    else
                    {
                        setTimeout(arguments.callee, 10);
                    }
                })();
            }
            createSWF(att, par, replaceElemIdStr);
        }
    }

    /* Functions to abstract and display alternative content
    */
    function displayAltContent(obj)
    {
        if (ua.ie && ua.win && obj.readyState != 4)
        {
            // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
            // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
            var el = createElement("div");
            obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
            el.parentNode.replaceChild(abstractAltContent(obj), el);
            obj.style.display = "none";
            (function ()
            {
                if (obj.readyState == 4)
                {
                    obj.parentNode.removeChild(obj);
                }
                else
                {
                    setTimeout(arguments.callee, 10);
                }
            })();
        }
        else
        {
            obj.parentNode.replaceChild(abstractAltContent(obj), obj);
        }
    }

    function abstractAltContent(obj)
    {
        var ac = createElement("div");
        if (ua.win && ua.ie)
        {
            ac.innerHTML = obj.innerHTML;
        }
        else
        {
            var nestedObj = obj.getElementsByTagName(OBJECT)[0];
            if (nestedObj)
            {
                var c = nestedObj.childNodes;
                if (c)
                {
                    var cl = c.length;
                    for (var i = 0; i < cl; i++)
                    {
                        if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8))
                        {
                            ac.appendChild(c[i].cloneNode(true));
                        }
                    }
                }
            }
        }
        return ac;
    }

    /* Cross-browser dynamic SWF creation
    */
    function createSWF(attObj, parObj, id)
    {
        var r, el = getElementById(id);
        if (ua.wk && ua.wk < 312) { return r; }
        if (el)
        {
            if (typeof attObj.id == UNDEF)
            { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
                attObj.id = id;
            }
            if (ua.ie && ua.win)
            { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
                var att = "";
                for (var i in attObj)
                {
                    if (attObj[i] != Object.prototype[i])
                    { // filter out prototype additions from other potential libraries
                        if (i.toLowerCase() == "data")
                        {
                            parObj.movie = attObj[i];
                        }
                        else if (i.toLowerCase() == "styleclass")
                        { // 'class' is an ECMA4 reserved keyword
                            att += ' class="' + attObj[i] + '"';
                        }
                        else if (i.toLowerCase() != "classid")
                        {
                            att += ' ' + i + '="' + attObj[i] + '"';
                        }
                    }
                }
                var par = "";
                for (var j in parObj)
                {
                    if (parObj[j] != Object.prototype[j])
                    { // filter out prototype additions from other potential libraries
                        par += '<param name="' + j + '" value="' + parObj[j] + '" />';
                    }
                }
                el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
                objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
                r = getElementById(attObj.id);
            }
            else
            { // well-behaving browsers
                var o = createElement(OBJECT);
                o.setAttribute("type", FLASH_MIME_TYPE);
                for (var m in attObj)
                {
                    if (attObj[m] != Object.prototype[m])
                    { // filter out prototype additions from other potential libraries
                        if (m.toLowerCase() == "styleclass")
                        { // 'class' is an ECMA4 reserved keyword
                            o.setAttribute("class", attObj[m]);
                        }
                        else if (m.toLowerCase() != "classid")
                        { // filter out IE specific attribute
                            o.setAttribute(m, attObj[m]);
                        }
                    }
                }
                for (var n in parObj)
                {
                    if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie")
                    { // filter out prototype additions from other potential libraries and IE specific param element
                        createObjParam(o, n, parObj[n]);
                    }
                }
                el.parentNode.replaceChild(o, el);
                r = o;
            }
        }
        return r;
    }

    function createObjParam(el, pName, pValue)
    {
        var p = createElement("param");
        p.setAttribute("name", pName);
        p.setAttribute("value", pValue);
        el.appendChild(p);
    }

    /* Cross-browser SWF removal
    - Especially needed to safely and completely remove a SWF in Internet Explorer
    */
    function removeSWF(id)
    {
        var obj = getElementById(id);
        if (obj && obj.nodeName == "OBJECT")
        {
            if (ua.ie && ua.win)
            {
                obj.style.display = "none";
                (function ()
                {
                    if (obj.readyState == 4)
                    {
                        removeObjectInIE(id);
                    }
                    else
                    {
                        setTimeout(arguments.callee, 10);
                    }
                })();
            }
            else
            {
                obj.parentNode.removeChild(obj);
            }
        }
    }

    function removeObjectInIE(id)
    {
        var obj = getElementById(id);
        if (obj)
        {
            for (var i in obj)
            {
                if (typeof obj[i] == "function")
                {
                    obj[i] = null;
                }
            }
            obj.parentNode.removeChild(obj);
        }
    }

    /* Functions to optimize JavaScript compression
    */
    function getElementById(id)
    {
        var el = null;
        try
        {
            el = doc.getElementById(id);
        }
        catch (e) { }
        return el;
    }

    function createElement(el)
    {
        return doc.createElement(el);
    }

    /* Updated attachEvent function for Internet Explorer
    - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
    */
    function addListener(target, eventType, fn)
    {
        target.attachEvent(eventType, fn);
        listenersArr[listenersArr.length] = [target, eventType, fn];
    }

    /* Flash Player and SWF content version matching
    */
    function hasPlayerVersion(rv)
    {
        var pv = ua.pv, v = rv.split(".");
        v[0] = parseInt(v[0], 10);
        v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
        v[2] = parseInt(v[2], 10) || 0;
        return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
    }

    /* Cross-browser dynamic CSS creation
    - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
    */
    function createCSS(sel, decl, media, newStyle)
    {
        if (ua.ie && ua.mac) { return; }
        var h = doc.getElementsByTagName("head")[0];
        if (!h) { return; } // to also support badly authored HTML pages that lack a head element
        var m = (media && typeof media == "string") ? media : "screen";
        if (newStyle)
        {
            dynamicStylesheet = null;
            dynamicStylesheetMedia = null;
        }
        if (!dynamicStylesheet || dynamicStylesheetMedia != m)
        {
            // create dynamic stylesheet + get a global reference to it
            var s = createElement("style");
            s.setAttribute("type", "text/css");
            s.setAttribute("media", m);
            dynamicStylesheet = h.appendChild(s);
            if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0)
            {
                dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
            }
            dynamicStylesheetMedia = m;
        }
        // add style rule
        if (ua.ie && ua.win)
        {
            if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT)
            {
                dynamicStylesheet.addRule(sel, decl);
            }
        }
        else
        {
            if (dynamicStylesheet && typeof doc.createTextNode != UNDEF)
            {
                dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
            }
        }
    }

    function setVisibility(id, isVisible)
    {
        if (!autoHideShow) { return; }
        var v = isVisible ? "visible" : "hidden";
        if (isDomLoaded && getElementById(id))
        {
            getElementById(id).style.visibility = v;
        }
        else
        {
            createCSS("#" + id, "visibility:" + v);
        }
    }

    /* Filter to avoid XSS attacks
    */
    function urlEncodeIfNecessary(s)
    {
        var regex = /[\\\"<>\.;]/;
        var hasBadChars = regex.exec(s) != null;
        return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
    }

    /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
    */
    var cleanup = function ()
    {
        if (ua.ie && ua.win)
        {
            window.attachEvent("onunload", function ()
            {
                // remove listeners to avoid memory leaks
                var ll = listenersArr.length;
                for (var i = 0; i < ll; i++)
                {
                    listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
                }
                // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
                var il = objIdArr.length;
                for (var j = 0; j < il; j++)
                {
                    removeSWF(objIdArr[j]);
                }
                // cleanup library's main closures to avoid memory leaks
                for (var k in ua)
                {
                    ua[k] = null;
                }
                ua = null;
                for (var l in swfobject)
                {
                    swfobject[l] = null;
                }
                swfobject = null;
            });
        }
    } ();

    return {
        /* Public API
        - Reference: http://code.google.com/p/swfobject/wiki/documentation
        */
        registerObject: function (objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn)
        {
            if (ua.w3 && objectIdStr && swfVersionStr)
            {
                var regObj = {};
                regObj.id = objectIdStr;
                regObj.swfVersion = swfVersionStr;
                regObj.expressInstall = xiSwfUrlStr;
                regObj.callbackFn = callbackFn;
                regObjArr[regObjArr.length] = regObj;
                setVisibility(objectIdStr, false);
            }
            else if (callbackFn)
            {
                callbackFn({ success: false, id: objectIdStr });
            }
        },

        getObjectById: function (objectIdStr)
        {
            if (ua.w3)
            {
                return getObjectById(objectIdStr);
            }
        },

        embedSWF: function (swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn)
        {
            var callbackObj = { success: false, id: replaceElemIdStr };
            if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr)
            {
                setVisibility(replaceElemIdStr, false);
                addDomLoadEvent(function ()
                {
                    widthStr += ""; // auto-convert to string
                    heightStr += "";
                    var att = {};
                    if (attObj && typeof attObj === OBJECT)
                    {
                        for (var i in attObj)
                        { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
                            att[i] = attObj[i];
                        }
                    }
                    att.data = swfUrlStr;
                    att.width = widthStr;
                    att.height = heightStr;
                    var par = {};
                    if (parObj && typeof parObj === OBJECT)
                    {
                        for (var j in parObj)
                        { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
                            par[j] = parObj[j];
                        }
                    }
                    if (flashvarsObj && typeof flashvarsObj === OBJECT)
                    {
                        for (var k in flashvarsObj)
                        { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
                            if (typeof par.flashvars != UNDEF)
                            {
                                par.flashvars += "&" + k + "=" + flashvarsObj[k];
                            }
                            else
                            {
                                par.flashvars = k + "=" + flashvarsObj[k];
                            }
                        }
                    }
                    if (hasPlayerVersion(swfVersionStr))
                    { // create SWF
                        var obj = createSWF(att, par, replaceElemIdStr);
                        if (att.id == replaceElemIdStr)
                        {
                            setVisibility(replaceElemIdStr, true);
                        }
                        callbackObj.success = true;
                        callbackObj.ref = obj;
                    }
                    else if (xiSwfUrlStr && canExpressInstall())
                    { // show Adobe Express Install
                        att.data = xiSwfUrlStr;
                        showExpressInstall(att, par, replaceElemIdStr, callbackFn);
                        return;
                    }
                    else
                    { // show alternative content
                        setVisibility(replaceElemIdStr, true);
                    }
                    if (callbackFn) { callbackFn(callbackObj); }
                });
            }
            else if (callbackFn) { callbackFn(callbackObj); }
        },

        switchOffAutoHideShow: function ()
        {
            autoHideShow = false;
        },

        ua: ua,

        getFlashPlayerVersion: function ()
        {
            return { major: ua.pv[0], minor: ua.pv[1], release: ua.pv[2] };
        },

        hasFlashPlayerVersion: hasPlayerVersion,

        createSWF: function (attObj, parObj, replaceElemIdStr)
        {
            if (ua.w3)
            {
                return createSWF(attObj, parObj, replaceElemIdStr);
            }
            else
            {
                return undefined;
            }
        },

        showExpressInstall: function (att, par, replaceElemIdStr, callbackFn)
        {
            if (ua.w3 && canExpressInstall())
            {
                showExpressInstall(att, par, replaceElemIdStr, callbackFn);
            }
        },

        removeSWF: function (objElemIdStr)
        {
            if (ua.w3)
            {
                removeSWF(objElemIdStr);
            }
        },

        createCSS: function (selStr, declStr, mediaStr, newStyleBoolean)
        {
            if (ua.w3)
            {
                createCSS(selStr, declStr, mediaStr, newStyleBoolean);
            }
        },

        addDomLoadEvent: addDomLoadEvent,

        addLoadEvent: addLoadEvent,

        getQueryParamValue: function (param)
        {
            var q = doc.location.search || doc.location.hash;
            if (q)
            {
                if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
                if (param == null)
                {
                    return urlEncodeIfNecessary(q);
                }
                var pairs = q.split("&");
                for (var i = 0; i < pairs.length; i++)
                {
                    if (pairs[i].substring(0, pairs[i].indexOf("=")) == param)
                    {
                        return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
                    }
                }
            }
            return "";
        },

        // For internal usage only
        expressInstallCallback: function ()
        {
            if (isExpressInstallActive)
            {
                var obj = getElementById(EXPRESS_INSTALL_ID);
                if (obj && storedAltContent)
                {
                    obj.parentNode.replaceChild(storedAltContent, obj);
                    if (storedAltContentId)
                    {
                        setVisibility(storedAltContentId, true);
                        if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
                    }
                    if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
                }
                isExpressInstallActive = false;
            }
        }
    };
} ();
var LMI = LMI || {};

LMI.FileLoader = (function () {
    var JS_SCRIPT_TYPE = 'text/javascript',
        CSS_SCRIPT_TYPE = 'text/css';

    var _load = function (scriptUri, scriptType) {
        var scriptelement,
            targetElement,
            current;

        if (scriptType === CSS_SCRIPT_TYPE) {
            scriptelement = document.createElement('link');
            scriptelement.rel = "stylesheet";
            targetElement = document.getElementsByTagName('link')[0];
        } else {
            //We assume javascript if no type specified
            scriptelement = document.createElement('script');
            targetElement = document.getElementsByTagName('script')[0];
        }

        current = scriptUri;

        if (typeof scriptUri !== "string") {
            current = scriptUri.shift();
        }

        if (scriptType === CSS_SCRIPT_TYPE) {
            scriptelement.href = current;
        } else {
            scriptelement.src = current;
        }
        scriptelement.type = scriptType;

        if (typeof scriptUri !== "string" && scriptUri.length > 0) {
            //IE support?
            scriptelement.onload = function () {
                _load(scriptUri, scriptType);
            };
        }

        if (targetElement) {
            targetElement.parentNode.insertBefore(scriptelement, targetElement);
        } else {
            document.getElementsByTagName("head")[0].appendChild(scriptelement);
        }
    },

    fileImpl = function (scriptUri, scriptType) {
        if (typeof scriptUri === "string") {
            _load(scriptUri, scriptType);
            return;
        }

        if (typeof scriptUri === "object" && scriptUri instanceof Array) {
            if (scriptUri.length == 0)
                throw Error("Invalid scriptUri, empty array");

            _load(scriptUri, scriptType);
            return;
        }

        throw Error("Invalid scriptUri type, 'string' and 'Array' is supported only.");
    },

    scriptImpl = function(scriptUri) {
        fileImpl(scriptUri, JS_SCRIPT_TYPE);
    },

    styleImpl = function (scriptUri) {
        fileImpl(scriptUri, CSS_SCRIPT_TYPE);
    };

    return {
        /**
         * Loads a script(s) after the first script tag into the dom
         * @param scriptUri {string|Array} the script uri to load, also can be an array of strings
         * @param options {Object} you can specify if the scripts are needed to be loaded after each other with "options.parallel = false"
         */
        script: scriptImpl,
        style: styleImpl
    };
})();/// <reference path="../../../../Common/Scripts/3rdParty/swfobject.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};

(function (swfobject) {
    "use strict";

    JM.BigIdea.FlashVersion = (function () {
        var minFlashVersion = "9.0.124",
            _getVersionNumber = function () {
                 var flashVersion = swfobject.getFlashPlayerVersion();
                return flashVersion.major + '.' + flashVersion.minor;
            },
            _isFlashSupported = function () {
                return swfobject.hasFlashPlayerVersion(minFlashVersion);
            };
       
        return {
            /**
            * Determine the version number of currently installed flash player
            * @return {number}: Flash player version
            */
            GetVersionNumber: _getVersionNumber,

            /**
            * Detect if currently installed flash player version is higher than min supported flash version
            * @return {bool}: True if supported
            */
            IsFlashSupported: _isFlashSupported
        };
    })();
}(swfobject));var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};

JM.BigIdea.Validator = function (rules) {
    "use strict";

    var _rules,

        _validateRegExp = function (input, rule) {
            if (!rule.test(input))
                return false;
            return true;
        },

        _validate = function (input) {
            var prop, result = true;

            for (prop in _rules) {
                if (!result || !_rules[prop]) {
                    break;
                }

                if (_rules[prop] instanceof RegExp) {
                    result = _validateRegExp(input, _rules[prop]);
                }
                else if (typeof _rules[prop] === 'function') {
                    result = _rules[prop](input);
                }
            }
            return result;
        }

    _rules = rules;

    return {
        Validate: _validate
    };
};var JM = JM || {};
JM.BigIdea = JM.BigIdea || {};
JM.BigIdea.AjaxRequest = JM.BigIdea.AjaxRequest || {};

/**
* Send out ajax request to a REST or WCF endpoint and handle errors
* @param {JSON} params: Contains parameters required to send out ajax request [mandatory]
* @param {string} params.url: Endpoint url [mandatory]
* @param {string} params.method: HTTP method what endpoint supports [optional, defaulting to "POST"]
* @param {bool} params.isAsync: Async or not [optional, defaulting to "true"]
* @param {JSON} params.requestBody: Request body in JSON format [optional, defaulting to "{}"]
* @param {obj} params.onSuccess: Success call back function [optional]
* @param {obj} params.onError: Error call back function [optional]
* @param {obj} params.onComplete: Complete call back function [optional]
*/
JM.BigIdea.AjaxRequest.Call = function(params) {
    var _unknownErrorPageUrl = "/ErrorPages/UnknownError.aspx";

    if (!params)
        throw new Error("params is mandatory");

    if (!params.url)
        throw new Error("params.url is mandatory");

    if (typeof params.isAsync === "undefined")
        params.isAsync = true;

    if (window.ActiveXObject) {
        $.ajaxPrefilter(function (options) {
            if (/^patch$/i.test(options.type)) {
                options.xhr = function () {
                    return new window.ActiveXObject("Microsoft.XMLHTTP");
                };
            }
        });
    }
    
    jQuery.ajax({
        type: params.method || "POST",
        contentType: "application/json; charset=utf-8",
        url: params.url,
        data: JSON.stringify(params.requestBody || {}),
        statusCode : {
            401: function () {
                // REST API logged in session lost detected
                $.publish("/track/ajaxRequest/userExitedFlowByGettingLoggedInSessionLost");
                JM.BigIdea.Utils.SetTopWindowLocation('/');
            },
            403: function () {
                // REST API forbidden detected
                $.publish("/track/ajaxRequest/userExitedFlowByGettingForbidden");
                JM.BigIdea.Utils.SetTopWindowLocation(_unknownErrorPageUrl);
            },
            500: function(response) {
                var responseObj = JSON.parse(response.responseText);
                if (responseObj.ReferenceNumber) {
                    // REST API derror detected
                    $.publish("/track/ajaxRequest/userExitedFlowByGettingRestApiUnknownError");
                    JM.BigIdea.Utils.SetTopWindowLocation(_unknownErrorPageUrl + "?lmierrorticket=" + responseObj.ReferenceNumber);
                }
            },
        },
        dataType: "json",
        async: !!params.isAsync,
        success: function (response) {
            if (!params.onSuccess)
                return;
            
            params.onSuccess.call(null, response);
        },
        error: function (response) {
            if (params.onError) {
                params.onError.call(null, response);
                return;
            }

            if (!responseObj || !responseObj.responseText) {
                return;
            }

            var responseObj =  JSON.parse(response.responseText);

            switch (responseObj.StatusCode) {
                case 1:
                case 100001:
                    // WCF derror detected
                    $.publish("/track/ajaxRequest/userExitedFlowByGettingWcfUnknownError");
                    JM.BigIdea.Utils.SetTopWindowLocation(responseObj.Details);
                    break;
                default:
                    // ASMX, WCF or REST API unknown error without error ticket
                    $.publish("/track/ajaxRequest/userExitedFlowByGettingGenericUnknownError");
                    JM.BigIdea.Utils.SetTopWindowLocation(_unknownErrorPageUrl);
                    break;
            }
        },
        complete: function(response) {
            if (!params.onComplete)
                return;

            params.onComplete.call(null, response);
        }
    });
};

/**
* Send out ajax request to a REST or WCF endpoint without error handling
* @param {JSON} params: Contains parameters required to send out ajax request [mandatory]
* @param {string} params.url: Endpoint url [mandatory]
* @param {bool} params.isAsync: Async or not [optional, defaulting to "true"]
* @param {JSON} params.requestBody: Request body in JSON format [optional, defaulting to "{}"]
*/
JM.BigIdea.AjaxRequest.MinCall = function (params) {
    if (!params)
        throw new Error("params is mandatory");

    if (!params.url)
        throw new Error("params.url is mandatory");

    if (typeof params.isAsync === "undefined")
        params.isAsync = true;

    jQuery.ajax({
        type: "POST",
        contentType: "application/json; charset=utf-8",
        url: params.url,
        data: JSON.stringify(params.requestBody || {}),
        dataType: "json",
        async: !!params.isAsync,
        error: function() {
            // Intentionally empty; error callback must be defined to silently handle error
        }
    });
};var JM = window.JM || {};

JM.WebsiteTracking = (function (AjaxRequest) {
    "use strict";

    var Integrations = {
        GA: 0,
        ALL: 1,
        MIXPANEL: 2
    };

    var trackGenericEvent = function trackGenericEvent(eventName, properties, integration, callback) {
        var props = properties || {};
        var segmentIntegration = (isValidIntegration(integration) ? integration : Integrations.GA);
        if (!props.hasOwnProperty("category")) {
            props.category = "join.me Website Tracking";
        }

        var data = {
            eventName: eventName,
            properties: JSON.stringify(props),
            integration: segmentIntegration
        };

        AjaxRequest.Call({
            url: "/Ajax/Tracking.svc/TrackEvent",
            isAsync: true,
            requestBody: {
                trackParams: data
            },
            onSuccess: function() {
                if (callback) callback();
            }
        });
    };

    var trackUserAction = function trackUserAction(actionName, properties, integration, callback) {
        var props = properties || {};
        var segmentIntegration = (isValidIntegration(integration) ? integration : Integrations.GA);
        if (!props.hasOwnProperty("category")) {
            props.category = "join.me Website Tracking";
        }

        AjaxRequest.Call({
            url: "/Ajax/Backend.asmx/TrackUserInteractionsExtended",
            isAsync: true,
            requestBody: {
                ActionName: actionName,
                ActionProperties: JSON.stringify(props),
                Integration: segmentIntegration
            },
            onSuccess: function () {
                if (callback) callback();
            }
        });
    };

    var isValidIntegration = function isValidIntegration(integration) {
        if (integration === undefined || integration === null)
            return false;

        for (var key in Integrations) {
            if (integration === Integrations[key])
                return true;
        }
        return false;
    };

    return {
        SegmentIntegrations: Integrations,
        Track: trackGenericEvent,
        TrackEvent: trackGenericEvent,
        TrackUserAction: trackUserAction
    };

}(JM.BigIdea.AjaxRequest));/// <reference path="../../../Common/Scripts/3rdParty/jquery-1.8.3.js" />
/// <reference path="../../Common/Scripts/Platform/Platform.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};
JM.BigIdea.Enums = JM.BigIdea.Enums || {};

JM.BigIdea.Utils = (function ($, Platform, BrowserTypes, window) {

    /**
    * Gets the current window location
    * @return {string}: Lowercase location
    */
    var _getCurrentWindowLocation = function () {
        return window.location.href.toLowerCase();
    },

        /**
    * Sets the current window location
    * @param location {string}: New location
    */
        _setCurrentWindowLocation = function (location) {
            if (!location) {
                throw new Error("Missing current location");
            }
            window.location.href = location;
        },

        /**
    * Gets the top window location
    * @return {string}: Lowercase location
    */
        _getTopWindowLocation = function () {
            return window.top.location.href.toLowerCase();
        },
        /**
    * Automatically creates a hidden iframe and load an url;
    * Typically used to download EXE or other downloadable files;
    * @param location {string}: New location
    */
        _executeFileDownload = function (location) {
            if (!location)
                throw new Error("Missing download location");

            // Search for existing utility iframe
            $('#utilityFrame').remove();

            // Utility iframe does not exists so first we have to create a new instance
            var $newUtilityFrame = $('<iframe></iframe>')
                .attr('id', 'utilityFrame')
                // Do not use CSS class because we don't know the exact CSS file where to put the class definition
                .css('position', 'absolute')
                .css('left', '-20000px')
                .css('top', '-20000px')
                .css('visibility', 'hidden')
                .attr('src', location);

            $('body').append($newUtilityFrame);
        },
        /**
    * Sets the top window location
    * @param location {string}: New location
    */
        _setTopWindowLocation = function (location) {
            if (!location)
                throw new Error("Missing top location");

            window.top.location.href = location;
        },

        _setParentFrameLocation = function (location) {
            if (!location)
                throw new Error("Missing top location");
            parent.document.location.href = location;
        },
        _addCookie = function (cookieKey, value, expireDays) {
            if (!cookieKey)
                throw new Error("cookiekey is required");
            var _expires = 'Thu, 01-Jan-2050 00:00:01 GMT';

            if (!isNaN(expireDays)) {
                var _expDays = parseInt(expireDays, 10);

                if (_expDays > 0) {
                    var _d = new Date();
                    _d.setDate(_d.getDate() + _expDays);
                    _expires = _d.toUTCString();
                }
            }

            var cookieToAdd = cookieKey + "=" + (value || '') + ";expires=" + _expires + ";path=/;";

            var hostName = window.location.hostname;
            var joinMeDomain = ".join.me";
            var isHostInJoinMeDomain =
                hostName === "join.me" ||
                hostName.substring(hostName.length - joinMeDomain.length, hostName.length) === joinMeDomain;

            // Workaround - Cookies are now set to .join.me instead of secure.join.me so we need to make sure cookies get added properly
            if ((cookieKey === 'HostInstalled' || cookieKey === 'PreferredViewer') && isHostInJoinMeDomain) {
                cookieToAdd += "domain=.join.me;";
            }

            window.document.cookie = cookieToAdd;
        },
        _getCookie = function (cookieKey, defaultValue) {
            if (!cookieKey)
                throw new Error("cookiekey is required");

            var value = "; " + document.cookie;
            var parts = value.split("; " + cookieKey + "=");
            if (parts.length === 2) {
                return parts.pop().split(";").shift();
            }
            return defaultValue;
        },

        _removeCookie = function (cookieKey) {
            if (!cookieKey)
                throw new Error("cookiekey is required");

            var cookieRemove = cookieKey + "=;expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/;";

            window.document.cookie = cookieRemove;
        },
        _isCookieSet = function (cookieKey, value) {
            if (!cookieKey)
                throw new Error("cookiekey is required");
            return window.document.cookie.indexOf(cookieKey + "=" + (value || '')) > -1;
        },
        _getQueryStringParameter = function (queryString, parameterName) {
            parameterName = parameterName.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
            var regex = new RegExp("[\\?&]" + parameterName + "=([^&#]*)"),
                results = regex.exec(queryString);
            return results == null ? "" : window.decodeURIComponent(results[1].replace(/\+/g, " "));
        },
        _getCurrentDateTimeStamp = function () {
            return Date.now ? Date.now() : +new Date;
        },
        _updateQueryStringParameter = function(uri, key, value) {
        var re = new RegExp("([?&])" + key + "=.*?(&|$)", "i");
        var separator = uri.indexOf('?') !== -1 ? "&" : "?";
        if (uri.match(re)) {
            return uri.replace(re, '$1' + key + "=" + value + '$2');
        }
        else {
            return uri + separator + key + "=" + value;
        }
    };

    _getCaretPosition = function(oObject) {
        var CaretPos = 0;
        // IE, older than version 11
        if (Platform.GetBrowserType() === BrowserTypes.InternetExplorer && !Platform.GetUserAgent().match(/rv:/i)) {
            var selection = document.selection;
            oObject.focus();
            var Sel = selection.createRange();
            Sel.moveStart('character', -oObject.value.length);
            CaretPos = Sel.text.length;
        }
        // Other browsers
        else if (oObject.selectionStart || oObject.selectionStart == '0') {
            CaretPos = oObject.selectionStart;
        }
        return (CaretPos);
    };

    _setCaretPosition = function (oObject, iPosition) {
        // Other browsers
        if (oObject.setSelectionRange) {
            oObject.focus();
            oObject.setSelectionRange(iPosition, iPosition);
        }
        // IE
        else if (oObject.createTextRange) {
            var range = oObject.createTextRange();
            range.collapse(true);
            range.moveEnd('character', iPosition);
            range.moveStart('character', iPosition);
            try {
                range.select();
            } catch (e) {

            }
        }
    };

    return {
        /**
        * Gets the current window location
        * @return {string}: Lowercase location
        */
        GetCurrentWindowLocation: _getCurrentWindowLocation,

        /**
        * Sets the current window location
        * @param location {string}: New location
        */
        SetCurrentWindowLocation: _setCurrentWindowLocation,

        /**
        * Gets the top window location
        * @return {string}: Lowercase location
        */
        GetTopWindowLocation: _getTopWindowLocation,

        /**
        * Sets the top window location
        * @param location {string}: New location
        */
        SetTopWindowLocation: _setTopWindowLocation,

        /**
         * Sets the parent frame location
         * @param location {string}: New location
         */
        SetParentFrameLocation: _setParentFrameLocation,


        /**
        * Sets the top window location
        * Automatically creates a hidden iframe and load an url;
        * Typically used to download EXE or other downloadable files;
        * @param location {string}: New location
        */
        ExecuteFileDownload: _executeFileDownload,

        /**
        * Sets a cookie
        * @param cookiekey {string}
        * @param value [string='']: value for the cookie
        * @param expireDays [int]: (optional) number of days from now until cookie expires. If not set expiration will be Thu, 01-Jan-2050 00:00:01 GMT
        */
        AddCookie: _addCookie,

        /**
         * Gets a cookie's value, if the cookie exists.
         * If the cookie does not exist, returns undefined
         * @param cookiekey {string}
         * @param defaultValue: (optional) when provided, value to return instead of undefined if the cookie does not exist.
         */
        GetCookie: _getCookie,

        /**
        * Deletes a cookie
        * @param cookiekey {string}
        */
        RemoveCookie: _removeCookie,

        /**
        * Is Cookie key set
        * @param cookiekey {string}
        */
        IsCookieSet: _isCookieSet,

        /**
        * Gets the current date's timeStamp
        * returns int
        */
        GetCurrentDateTimeStamp: _getCurrentDateTimeStamp,

        /**
         * Gets the named parameter from the query string
         * @param queryString {string}: The query string
         * @param parameterName {string}: The name of the parameter to retrieve
         * @return {string}: The extracted parameter value from the query string or an empty string if not exists
         */
        GetQueryStringParameter: _getQueryStringParameter,

        /**
         * Appends or updates a query string parameter on the specified uri
         * @param uri {string}: The uri we'd like to modify
         * @param key {string}: The name of the parameter to append/modify
         * @param value {string}: The value to set the parameter to
         * @return {string}: The updated uri
         */
        UpdateQueryStringParameter: _updateQueryStringParameter,

        /**
         * Gets the caret position from a dom element
         * @param oObject: dom element
         * @return {int} the current caret position
         */
        GetCaretPosition: _getCaretPosition,

        /**
         * Sets the caret position fom a dom element
         * @param oObject: dom element
         * @param iPosition: the position to set
         */
        SetCaretPosition: _setCaretPosition
    };
})(jQuery,
JM.BigIdea.Platform,
JM.BigIdea.Enums.Browsers,
window);
/// <reference path="../../Common/Scripts/Utils/Utils.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};
JM.BigIdea.Enums = JM.BigIdea.Enums || {};

JM.BigIdea.Enums.CallContext = {
    // Called from unknown page
    Unknown: 0,

    // Called from StartTab
    StartTab: 1,

    // Called from MainPage
    HomePage: 2,

    //Called from Sticky join page
    StickyJoin: 3
};

JM.BigIdea.CallContext = (function (utils) {
    "use strict";
    var _getContext = function () {
        var url = utils.GetCurrentWindowLocation();
        if (url.match(/ShareJoin.aspx/i)) {
            return JM.BigIdea.Enums.CallContext.HomePage;
        }
        else if (url.match(/Default.aspx/i)) {
            return JM.BigIdea.Enums.CallContext.StartTab;
        }
        else if (url.match(/JmClient/i)) {
            return JM.BigIdea.Enums.CallContext.StickyJoin;
        }
        else {
            return JM.BigIdea.Enums.CallContext.Unknown;
        }
    };

    return {
        /*
        Returns the origin of the running BigIdea script
        */
        GetContext: _getContext
    };
})(JM.BigIdea.Utils);var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};

JM.BigIdea.HostPaths = (function () {
    "use strict";
    var _nativePath = '/Download.aspx',
        _sharePath = '/start',

        _getNativeAppPath = function(additionalQueryString) {
            return !!additionalQueryString ? _nativePath + '?' + _cleanPath(additionalQueryString) : _nativePath;
        },

        _getSharePath = function (personalUrl) {
            return _sharePath + '/' + personalUrl;
        },

        _cleanPath = function(queryString) {
            if (queryString) {
                var leadingChar = queryString.substring(0, 1);
                if (leadingChar === '?' || leadingChar === '&')
                    return queryString.substring(1, queryString.length);
                else
                    return queryString;
            }
            return "";
        },
        _getPath = function (queryString) {
            return _getNativeAppPath(queryString);
        },
        _getInstallerPath = function(platform) {
            return _nativePath + '?installer=' + platform;
        };

    return {
        GetNativeAppPath: _getNativeAppPath,
        GetPath: _getPath,
        GetSharePath: _getSharePath,
        GetInstallerPath: _getInstallerPath
    };
}());/// <reference path="../Platform/Platform.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};
JM.BigIdea.Enums = JM.BigIdea.Enums || {};

JM.BigIdea.Detector = (function(Platform, BrowserTypes) {
    "use strict";

    /**
    * Attempt to init. active x control (IE only sticky detector)
    * The presence of the control which is part of the launcher
    * indicates that join.me is installed
    * @return {bool}: True if join.me installed. False if not installed or can not determine.
    */
    var _isJoinMeInstalledIE = function() {
        try {
            var detector = new ActiveXObject("joinme.Detector");
            return !!detector;
        } catch (exception) {
            return false;
        }
    };

    /**
     * Determine using detector if join.me is installed.
     * @returns {bool}: True if join.me is installed. False if not installed or can not determine.
     */
    var _isJoinMeInstalled = function() {
        if (Platform.GetBrowserType() === BrowserTypes.InternetExplorer) {
            return _isJoinMeInstalledIE();
        }

        return false;
    };

    return {
        IsJoinMeInstalled: _isJoinMeInstalled
    }
}(JM.BigIdea.Platform, JM.BigIdea.Enums.Browsers));/// <reference path="../../../Common/Scripts/3rdParty/jquery-1.8.3.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};
JM.BigIdea.Enums = JM.BigIdea.Enums || {};

JM.BigIdea.Enums.ViewerCodeCheckResults = {
    // Everything fine page will be redirected to the flash container page
    Ok: 0,

    // Viewercode or Purl not found
    NotFoundCodeOrPurl: 1,

    // Error bubble must be displayed as user provide nothing
    MissingCodeOrPurl: 2,

    // No flash support
    NoFlash: 3,

    // Invalid code
    InvalidCode: 4,

    // Invalid purl
    InvalidPurl: 5,

    //Unknown Error
    UnknwonError: 6
};

JM.BigIdea.Enums.OperationErrorCode = {
    InvalidPersonalUrl: 4,
    InvalidAuthCode: 5
};

JM.BigIdea.ViewerCodeHandler = (function ($, Enums, AjaxRequest, Utils) {
    "use strict";

    var _settings,

        _check = function() {
            if (!_settings)
                throw new Error("ViewerCodehandler is not initialized!");

            var codeOrPurl = _getFormattedCodeOrPurl();
            if (!codeOrPurl) {
                // Nothing provided
                return Enums.ViewerCodeCheckResults.MissingCodeOrPurl;
            }

            codeOrPurl = codeOrPurl.replace(/ /g, "");

            if ($.isNumeric(codeOrPurl[0])) {
                // 9 digit code
                codeOrPurl = codeOrPurl.replace(/-/g, "");

                if (!$.isNumeric(codeOrPurl)) {
                    // After remove dash it mmust be a number
                    return Enums.ViewerCodeCheckResults.InvalidCode;
                }

                if (codeOrPurl.length !== 9) {
                    // 9 digit code length must be 9 always
                    return Enums.ViewerCodeCheckResults.InvalidCode;
                }

            } else {
                // Purl
                if (codeOrPurl.length < 9) {
                    // Purl minium 9 character length
                    return Enums.ViewerCodeCheckResults.InvalidPurl;
                }
            }

            return Enums.ViewerCodeCheckResults.Ok;
        },

        _handleMeetingCodeChange = function (keyCode) {
            var $codeBox = $(_settings.codeSelector),
            codeBoxVal = $codeBox.val();
            if (keyCode) {

                if (keyCode === 8 || //backspace
                    keyCode === 46 || //delete
                    keyCode === 38 || //up
                    keyCode === 17 || //ctrl
                    keyCode === 40 || //down
                    keyCode === 37 || //left
                    keyCode === 39 || //right
                    keyCode === 33 || //page up
                    keyCode === 34 || //page down
                    keyCode === 36 || //home
                    keyCode === 35) { //end
                    return;
                }
            }
            if (!codeBoxVal)
                return;

            _formatCodeOrPurl();
        },

        _getFormattedCodeOrPurl = function() {
            if (!_settings)
                throw new Error("ViewerCodehandler is not initialized!");

            _formatCodeOrPurl();
            return $(_settings.codeSelector).val();
        },

        _formatCodeOrPurl = function() {
            if (!_settings)
                throw new Error("ViewerCodehandler is not initialized!");

            var $codeElement = $(_settings.codeSelector),
                codeBoxVal = $codeElement.val(),
                _isCode9Dig,
                cleanedCode = "",
                formattedCode = "",
                formatOffset = 0,
                caretPosition = Utils.GetCaretPosition($codeElement[0]);

            if ($.isNumeric(codeBoxVal[0])) {
                // 9 digit code
                $codeElement.attr('maxlength', '11');
                _isCode9Dig = true;
            } else {
                // Purl
                $codeElement.attr('maxlength', '128');
                _isCode9Dig = false;
            }

            if (_isCode9Dig) {
                cleanedCode = codeBoxVal.replace(/\D+/g, '');
                for (var index = 0; index < cleanedCode.length; index++) {
                    formattedCode += cleanedCode.charAt(index);
                    if (index === 2 || index === 5) {
                        formattedCode += "-";
                    }
                }
                if (formattedCode.charAt(caretPosition) === "-" || codeBoxVal.charAt(caretPosition) === "-") {
                    formatOffset++;
                }
            } else {
                formattedCode = codeBoxVal.replace(/[^a-zA-Z0-9_\-\.]/g, "");
                formatOffset = ('' + formattedCode).length - ('' + codeBoxVal).length;
            }

            $codeElement.val(formattedCode);
            Utils.SetCaretPosition($codeElement[0], caretPosition + formatOffset);
        },

        _init = function(settings) {
            if (!settings.codeSelector)
                throw new Error("CodeSelector is mandatory!");

            settings = $.extend({}, settings, {
                // Add default settings here
            });

            _settings = settings;
            var suppressKeyup;

            $(settings.codeSelector)
                .on("keydown", function(event) {
                    suppressKeyup = event.ctrlKey || event.metaKey || event.shiftKey;
                })
                .on("keypress", function(event) {
                    if (event.keyCode === 13) { //enter
                        suppressKeyup = true;
                        if (settings.onEnterKeyCallback && typeof settings.onEnterKeyCallback === "function")
                            settings.onEnterKeyCallback.call(event);
                        event.preventDefault();
                    }
                })
                .on("keyup change", function(event) {
                    if (!suppressKeyup) {
                        _handleMeetingCodeChange(event.keyCode);
                        if (settings.onKeyUpChangeCallback && typeof settings.onKeyUpChangeCallback === "function")
                            settings.onKeyUpChangeCallback.call(event);
                    }
                })
                .on("paste", function() {
                    setTimeout(function() {
                        _formatCodeOrPurl();
                        if (settings.onPasteCallback && typeof settings.onPasteCallback === "function")
                            settings.onPasteCallback.call(event);
                    }, 0);
                });
        },

        _isJoinable = function (viewercodeOrPurl) {
            var error = null;
            //check if this is a joinable viewercode or purl
            AjaxRequest.Call({
                url: "/Ajax/Backend.asmx/CheckViewercodeOrPurlJoinable",
                requestBody: { viewercodeOrPurl: viewercodeOrPurl },
                isAsync: false,
                onSuccess: function(response) {
                    if (response.d.iError === JM.BigIdea.Enums.OperationErrorCode.InvalidPersonalUrl) {
                        error = JM.BigIdea.Enums.ViewerCodeCheckResults.InvalidPurl;
                    } else if (response.d.iError === JM.BigIdea.Enums.OperationErrorCode.InvalidAuthCode) {
                        error = JM.BigIdea.Enums.ViewerCodeCheckResults.InvalidCode;
                    }
                }
            });
            
            return error;
        };

    return {
        GetFormattedCodeOrPurl: _getFormattedCodeOrPurl,
        Check: _check,
        Format : _formatCodeOrPurl,
        Init: _init,
        IsJoinable: _isJoinable
    };
}(jQuery, JM.BigIdea.Enums, JM.BigIdea.AjaxRequest, JM.BigIdea.Utils));/// <reference path="../FlashVersion/FlashVersion.js" />
/// <reference path="../Html5/Html5.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};
JM.BigIdea.Enums = JM.BigIdea.Enums || {};

JM.BigIdea.Enums.ClientResult = {
    // Should redirect to main page
    None: 0,

    // Should redirect to Flash viewer
    Flash: 1,

    // Should redirect to HTML5 viewer
    Html5: 2
};

JM.BigIdea.ClientSelector = (function (flashVersion, html5, platform, utils) {
    "use strict";

    var _getClientResult = function () {
        var searchPart = '';
        try {
            searchPart = window.top.location.search;
        }
        catch (e) {
            //Can't reach top location in CMS
        }

        platform.SetBrowserType();

        var preferredViewer = utils.GetQueryStringParameter(searchPart, 'viewer'),
            isSafari = platform.GetBrowserType() === JM.BigIdea.Enums.Browsers.Safari;

        switch (preferredViewer.toLowerCase()) {
            case 'html5':
                return JM.BigIdea.Enums.ClientResult.Html5;
            case 'flash':
                return JM.BigIdea.Enums.ClientResult.Flash;
            case '':
                break;
            default:
                return JM.BigIdea.Enums.ClientResult.None;
        }

        var flashSupported = flashVersion.IsFlashSupported(),
            html5Supported = html5.IsHtml5Supported();

        if (html5Supported) {
            return JM.BigIdea.Enums.ClientResult.Html5;
        }
        else if (flashSupported) {
            return JM.BigIdea.Enums.ClientResult.Flash;
        }
        else {
            return JM.BigIdea.Enums.ClientResult.None;
        }
    };

    return {
        /**
        * Determine which client to initiate based on Flash availability and browser capabilities
        * @return {enum}: The client to start
        */
        GetClientResult: _getClientResult
    };
}(JM.BigIdea.FlashVersion, JM.BigIdea.Html5, JM.BigIdea.Platform, JM.BigIdea.Utils));/// <reference path="../../../../Common/Scripts/3rdParty/jquery.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};

JM.BigIdea.Html5Loader = (function ($) {
    "use strict";
    var _init = function (config) {
        var $template = $('<div id="overloader" class="over-loader">\
        <div class="over-loader-content">\
            <div class="loader simple"></div>\
        </div>\
    </div>');
        $template.appendTo($(config.parent));
        return $template;
    };
    
    return {
        Init: _init
    };
})(jQuery);/// <reference path="../../../../Common/Scripts/3rdParty/jquery.js" />
/// <reference path="~/BigIdea/Common/Scripts/Tracking/WebsiteTracking.js" />
var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};

JM.BigIdea.MeetingEndCap = (function ($, WebsiteTracking, Platform, Validator, AjaxRequest) {
    "use strict";

    var $target;

    var trackingProperties = { category: "join.me HTML5 Client" };

    function _verticalCenter(target) {
        target.css("top", ($(window).height() / 2) - (target.outerHeight() / 2));
    }

    function trackEndcapEvent(event) {
        WebsiteTracking.Track(event, trackingProperties, JM.WebsiteTracking.SegmentIntegrations.ALL);
    }

    var _registerAndLogin = '/Ajax/Backend.asmx/RegisterAndLogin';


    /*
     * Shows endcap to try and get the user to give use some info about themselves.
     * Used for user not logged in
     */

    function _showRegistrationEndcap(config) {
        $target.find('.inner.endcapIncentive').show();
        $target.find('.inner.endcap').hide();

        // show the endcap message
        $target.find("h1.ridiculouslySimple, h3.createFreeAccount").show();

        //setup form events
        var form = $target.find('.encapIncentiveForm');
        var loading = $target.find('.endcapIncentiveWaiting');
        var nameInputContainer = $target.find('#endcapUserName');
        var firstNameInput = $target.find('#endcapFirstName');
        var lastNameInput = $target.find('#endcapLastName');
        var emailInput = $target.find('[name="registerEmail"]');
        var initialFocus = emailInput;
        var passwordInput = $target.find('[name="registerPassword"]');
        var optOutProductInfoInput = $target.find('[name="emailProductInfo"]');
        var submitButton = $target.find("#endcapIncentiveSubmit");
        var emailValid = false;
        var passwordValid = false;
        var FORM_VALID_CLASS = "primary";

        var setInputPass = function(input) {
            input.removeClass('inputFail').addClass('inputPass');
        };

        var setInputFail = function (input) {
            input.removeClass('inputPass').addClass('inputFail');
        };

        var removeInputValidIndicator = function(input) {
            input.removeClass('inputFail');
            input.removeClass('inputPass');
        };

        var runFormCheck = function () {
            if (emailValid && passwordValid) {
                submitButton.addClass(FORM_VALID_CLASS);
            } else {
                submitButton.removeClass(FORM_VALID_CLASS);
            }
        }

        var showErrorMessage = function (selector, message) {
            $(".errorMessage").hide();
            $(selector).html(message).show();
        };
        
        var endCapCompleted = function(redirectPath) {
            trackEndcapEvent("MEETING_ENDCAP_REGISTER_COMPLETE");
            window.top.location.href = redirectPath;
        };

        var emailInputKeyupHandler = function(e) {
            var emailVal = $.trim(emailInput.val());
            if (!emailVal || !JoinMe.Validation.bIsEmailValid(emailVal)) {
                showErrorMessage(".emailError", "Invalid email address");
                emailValid = false;
                setInputFail(emailInput);
                runFormCheck();
                return;
            }
            emailValid = true;
            $(".emailError").hide();
            setInputPass(emailInput);
            runFormCheck();
        };

        emailInput.unbind('keyup', emailInputKeyupHandler);
        emailInput.bind('keyup', emailInputKeyupHandler);

        var passwordInputKeyupHandler = function (e) {
            var passVal = passwordInput.val();
            if (passVal.length < 6 || passVal.length > 255) {
                showErrorMessage(".passwordError", "Invalid password length");
                passwordValid = false;
                setInputFail(passwordInput);
                runFormCheck();
            } else {
                passwordValid = true;
                $(".passwordError").hide();
                setInputPass(passwordInput);
                runFormCheck();
            }
        };

        passwordInput.unbind('keyup', passwordInputKeyupHandler);
        passwordInput.bind('keyup', passwordInputKeyupHandler);

        var submitButtonClickHandler = function (e) {
            if (!submitButton.hasClass(FORM_VALID_CLASS)) {
                e.preventDefault();
                return false;
            }

            form.hide();
            loading.show();

            AjaxRequest.Call({
                url: _registerAndLogin,
                requestBody: {
                    email: $.trim(emailInput.val()),
                    password: $.trim(passwordInput.val()),
                    sendProductInfo: (optOutProductInfoInput.prop('checked')) ? "false" : "true",
                    firstName: firstNameInput.val(),
                    lastName: lastNameInput.val()
                },
                onSuccess: function(data) {
                    if (data.d.iError == 0) {
                        endCapCompleted(data.d.RedirectPath);
                        return;
                    } else if (data.d.iError == 2) {
                        showErrorMessage(".passwordError", "Invalid password length");
                        setInputFail(passwordInput);
                        passwordValid = false;
                    } else if (data.d.iError == 3) {
                        showErrorMessage(".passwordError", "Unable to login");
                        passwordValid = false;
                    } else if (data.d.iError == 4) {
                        showErrorMessage(".emailError", "Email already exists");
                        setInputFail(emailInput);
                        emailValid = false;
                    } else if (data.d.iError == 5) {
                        showErrorMessage(".emailError", "Federated email. Please contact administrator");
                        setInputFail(emailInput);
                        emailValid = false;
                    } else if (data.d.iError == 6) {
                        showErrorMessage(".emailError", "Invalid email address");
                        setInputFail(emailInput);
                        emailValid = false;
                    } else {
                        showErrorMessage(".emailError", "An unknown error occured");
                        emailValid = false;
                    }
                    form.show();
                    loading.hide();
                    runFormCheck();
                }
            });
        };

        submitButton.unbind('click', submitButtonClickHandler);
        submitButton.bind('click', submitButtonClickHandler);

        $target.find(".purlChooseOtherwise").click(function () {
            $target.find(".endcapSendProductInfo").show();
        });

        if (!firstNameInput.val() && !lastNameInput.val()) {
            nameInputContainer.css('display', 'none');
        }

        trackEndcapEvent("MEETING_ENDCAP_REGISTER_VIEWED");
        $target.show();
        initialFocus.focus();
    }

    function _showDownload() {
        $target.find("#DownloadHostCTA").on("click", function () {
            trackEndcapEvent("MEETING_ENDCAP_DOWNLOAD_CLICKED");

            var downloadLink = "/Download.aspx";
            if (Platform.GetOSType() !== JM.BigIdea.Enums.OS.Mac) {
                // force MSI download if we're not on a mac because the installer won't launch join.me
                downloadLink += "?installer=true";
            }
            document.location.href = downloadLink;
            return false;
        });

        trackEndcapEvent("MEETING_ENDCAP_DOWNLOAD_VIEWED");
        $target.show();
    }

    function showWithRouting(config) {
        $target = $(config.target);
        if ($target && $target.is(":visible")) {
            // Existing target is already visible can't show more than 1 endcap at once
            return;
        }

        trackEndcapEvent("MEETING_ENDCAP_VIEWED");

        if (!config.isUserLoggedIn) {
            _showRegistrationEndcap(config);
        } else {
            _showDownload();
        }

        _verticalCenter($target);
        $(window).resize(function() {
            _verticalCenter($target);
        });
    }

    function hide() {
        if ($target) {
            $target.hide();
        }
    }

    return {
        Show: showWithRouting,
        Hide: hide
    };
})(jQuery, JM.WebsiteTracking, JM.BigIdea.Platform, JM.BigIdea.Validator, JM.BigIdea.AjaxRequest);
/// <reference path="../../../../Common/Scripts/3rdParty/jquery-1.8.3.js" />

/*
 * jQuery Tiny Pub/Sub
 * https://github.com/cowboy/jquery-tiny-pubsub
 *
 * Copyright (c) 2013 "Cowboy" Ben Alman
 * Licensed under the MIT license.
 */

(function($) {

  var o = $({});

  $.subscribe = function() {
    o.on.apply(o, arguments);
  };

  $.unsubscribe = function() {
    o.off.apply(o, arguments);
  };

  $.publish = function() {
    o.trigger.apply(o, arguments);
  };

}(jQuery));var JM = JM || {};
JM.Tracking = JM.Tracking || {};

JM.Tracking.SegmentTracking = (function (window) {
    "use strict";

    var _SEGMENT_IO_KEY = "{{SEGMENTIOAPIKEY}}";

    var _init = function () {
        window.analytics = window.analytics || [], window.analytics.methods = ["identify", "group", "track", "page", "pageview", "alias", "ready", "on", "once", "off", "trackLink", "trackForm", "trackClick", "trackSubmit"], window.analytics.factory = function (t) { return function () { var a = Array.prototype.slice.call(arguments); return a.unshift(t), window.analytics.push(a), window.analytics } }; for (var i = 0; i < window.analytics.methods.length; i++) { var key = window.analytics.methods[i]; window.analytics[key] = window.analytics.factory(key) } window.analytics.load = function (t) { if (!document.getElementById("analytics-js")) { var a = document.createElement("script"); a.type = "text/javascript", a.id = "analytics-js", a.async = !0, a.src = "https://cdn.segment.io/analytics.js/v1/" + t + "/analytics.min.js"; var n = document.getElementsByTagName("script")[0]; n.parentNode.insertBefore(a, n) } }, window.analytics.SNIPPET_VERSION = "2.0.9";
        window.analytics.load(_SEGMENT_IO_KEY);
    };

    var _page = function (page) {
        if (!window.analytics) {
            _init();
        }

        if (page) {
            window.analytics.page(document.location.host);
        } else {
            window.analytics.page();
        }
    }

    var _track = function (event, properties, options) {
        if (!window.analytics) {
            _init();
        }

        var props = properties || {};
        var opts = options || {};
        if (!props.hasOwnProperty("category")) {
            props.category = "join.me Tracking";
        }
        if (!opts.hasOwnProperty("integrations")) {
            opts.integrations = {
                'All': true,
                'Mixpanel': false,
            }   
        }
        props.label = document.location.hostname;
        props.pathname = document.location.pathname;
        analytics.track(event, props, opts);
    }

    return {
        Init: _init,
        Page: _page,
        Track: _track
    }
}(window));/// <reference path="../../../../Common/Scripts/3rdParty/jquery-1.8.3.js" />
/// <reference path="../PubSub/PubSub.js" />
/// <reference path="../../../../Common/Scripts/joinme/iframeCallQueue/iframeCallQueue.js" />
/// <reference path="../Services/AjaxRequest.ml.js" />
/// <reference path="../Tracking/WebsiteTracking.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};

JM.Tracking = (function (AjaxRequest, WebsiteTracking) {
    
    var googleAnaliticsTracking = function (kind, activity, action) {
        try {
            if (!kind)
                throw new Error("kind is not provided");
            if (!activity)
                throw new Error("activity is not provided");
            if (!action)
                throw new Error("action is not provided");

            if(_gaq)
                _gaq.push(['_trackEvent', kind, activity, "BI-" + action]);
        } catch (exception) {
            // Do not break the main flow if a tracking engine is down
            window.console && console.error("Unknown error, GoogleAnaliticsTracking failed on action " + action + "; Message: " + exception.message);
        }
    },

    googleTagManagerRegisterEvent = function (event, gaCategory, gaId, ignoreDuplicates, gateKeeper) {
        if (typeof (dataLayer) != "undefined") {

            if (ignoreDuplicates === true) {
                // Check for duplicate
                for (var i = 0; i != dataLayer.length; i++) {
                    if (dataLayer[i]['event'] === event
                        && dataLayer[i]['gtm.element.dataset.gacat'] === gaCategory
                        && dataLayer[i]['gtm.element.dataset.gaid'] === gaId) {
                        return;
                    }
                }
            }

            dataLayer.push({
                'event': event,
                'gtm.element.dataset.gacat': gaCategory,
                'gtm.element.dataset.gaid': gaId,
                'gateKeeper': gateKeeper
            });
        }
    },
    
    googleConversionTracking = function () {
        try {
            JM.ICallQueue.getInstance().addQueueItem('/googleconversion.html');
        } catch (exception) {
            // Do not break the main flow if a tracking engine is down
            window.console && console.error("Unknown error, GoogleConversionTracking failed; Message: " + exception.message);
        }
    },

    mediaPlexTracking = function (action) {
        try {
            if (!action)
                throw new Error("action is not provided");
            JM.MediaPlex.getInstance().track(action, null, null);
        } catch (exception) {
            // Do not break the main flow if a tracking engine is down
            window.console && console.error("Unknown error, MediaPlexTracking failed on action " + action + "; Message: " + exception.message);
        }
    },
    
    internalTracking = function (action, isAsync) {
        /* InternalTracking is deprecated. Please use JM.WebsiteTracking */
        try {
            if (!action)
                throw new Error("action is not provided");

            AjaxRequest.MinCall({
                url: "/Ajax/Backend.asmx/TrackUserInteractions", // TODO: Use REST endpoint instead
                isAsync: isAsync,
                requestBody: { ActionName: action }
            });

        } catch (exception) {
            // Do not break the main flow if a tracking engine is down
            window.console && console.error("Unknown error, InternalTracking failed on action " + action + "; Message: " + exception.message);
        }
    },
        
    gaPageTracking = function(key, page, title) {
        var gaSnippet = function (i, s, o, g, r, a, m) {
            i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
                (i[r].q = i[r].q || []).push(arguments)
            }, i[r].l = 1 * new Date(); a = s.createElement(o),
            m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m)
        };

        gaSnippet(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
        ga('create', key, 'auto', { 'name': 'jmPageviews' });
        try {
            ga('set', 'userId', JM.BigIdea.Utils.GetCookie('LuluId'));
        } catch (exception) {
            // do not break the basic ga page tracking if something is wrong with reading the luluid cookie
        }
        ga('jmPageviews.send', 'pageview', {
            'page': page,
            'title': title
        });
    };
    
    $(function () {
        $.subscribe('/track/join/click', function () {
            JM.Tracking.GoogleAnaliticsTracking('Buttons', 'Click', 'JoinMe-Join');
            JM.Tracking.MediaPlexTracking('joinme_joinbutton&Join.Me_JoinButton=1');
            WebsiteTracking.Track('FREE_USER_PUSHED_VIEW_BUTTON_ON_HOME_PAGE');
            WebsiteTracking.Track('JmBiUserClickedJoinMeetingOnHomePage');
        });

        $.subscribe('/track/join/click/homepage', function () {
            JM.Tracking.GoogleAnaliticsTracking('Buttons', 'Click', 'JoinMe-Join');
            JM.Tracking.MediaPlexTracking('joinme_joinbutton&Join.Me_JoinButton=1');
            WebsiteTracking.Track('FREE_USER_PUSHED_VIEW_BUTTON_ON_HOME_PAGE');
            WebsiteTracking.Track('JmBiUserClickedJoinMeetingOnHomePage');
        });

        $.subscribe('/track/join/click/starttab', function () {
            JM.Tracking.GoogleAnaliticsTracking('Buttons', 'Click', 'JoinMe-Join');
            JM.Tracking.MediaPlexTracking('joinme_joinbutton&Join.Me_JoinButton=1');
            WebsiteTracking.Track('PRO_USER_PUSHED_VIEW_BUTTON_ON_START_TAB');
            WebsiteTracking.Track('JmBiUserClickedJoinMeetingOnStartTab');
            WebsiteTracking.Track('StampViewStartTabJoinInitialized', { category: 'StampView' }, JM.WebsiteTracking.SegmentIntegrations.ALL);
        });

        $.subscribe('/track/share/click', function () {
            JM.Tracking.GoogleAnaliticsTracking('Buttons', 'Click', 'JoinMe-Share');
            JM.Tracking.GoogleConversionTracking();
            JM.Tracking.MediaPlexTracking('joinme_sharebutton&Join.Me_ShareButton=1');
            WebsiteTracking.Track('FREE_USER_PUSHED_SHARE_BUTTON_ON_HOME_PAGE', { "category": "join.me Landing Page" }, WebsiteTracking.SegmentIntegrations.ALL);
            WebsiteTracking.Track('JmBiUserClickedStartMeetingOnHomePage', { "category": "join.me Landing Page" }, WebsiteTracking.SegmentIntegrations.ALL);
        });

        $.subscribe('/track/share/click/homepage', function () {
            JM.Tracking.GoogleAnaliticsTracking('Buttons', 'Click', 'JoinMe-Share');
            JM.Tracking.GoogleConversionTracking();
            JM.Tracking.MediaPlexTracking('joinme_sharebutton&Join.Me_ShareButton=1');
            WebsiteTracking.Track('FREE_USER_PUSHED_SHARE_BUTTON_ON_HOME_PAGE', { "category": "join.me Landing Page" }, WebsiteTracking.SegmentIntegrations.ALL);
            WebsiteTracking.Track('JmBiUserClickedStartMeetingOnHomePage', { "category": "join.me Landing Page" }, WebsiteTracking.SegmentIntegrations.ALL);
        });

        $.subscribe('/track/share/click/starttab', function () {
            JM.Tracking.GoogleAnaliticsTracking('Buttons', 'Click', 'JoinMe-Share');
            JM.Tracking.GoogleConversionTracking();
            JM.Tracking.MediaPlexTracking('joinme_sharebutton&Join.Me_ShareButton=1');
            WebsiteTracking.Track('PRO_USER_PUSHED_SHARE_BUTTON_ON_START_TAB');
            WebsiteTracking.Track('JmBiUserClickedStartMeetingOnStartTab');
            WebsiteTracking.Track('StampViewStartTabStartInitialized', { category: 'StampView' }, JM.WebsiteTracking.SegmentIntegrations.ALL);
        });

        $.subscribe('/track/intphone/showNumbersClick', function () {
            WebsiteTracking.Track('JmBiUserClickedOnShowNumbersButtonOnIntPhonePage');
        });

        $.subscribe('/track/intphone/helpLinkClick', function () {
            WebsiteTracking.Track('JmBiUserClickedHelpLinkOnIntPhonePage');
        });

        $.subscribe('/track/intphone/missingCodeOrPurlError', function () {
            WebsiteTracking.Track('JmBiUserGetMissingCodeOrPurlErrorOnIntPhonePage');
        });

        $.subscribe('/track/intphone/invalidCodeError', function () {
            WebsiteTracking.Track('JmBiUserGetInvalidCodeErrorOnIntPhonePage');
        });

        $.subscribe('/track/intphone/invalidPurlError', function () {
            WebsiteTracking.Track('JmBiUserGetInvalidPurlErrorOnIntPhonePage');
        });

        $.subscribe('/track/intphone/notFoundCodeOrPurlError', function () {
            WebsiteTracking.Track('JmBiUserGetNotFoundCodeOrPurlErrorOnIntPhonePage');
        });

        // JM.BigIdea.AjaxRequest.Call
        // Error handling
        $.subscribe('/track/ajaxRequest/userExitedFlowByGettingLoggedInSessionLost', function () {
            WebsiteTracking.Track('AjaxRequestUserExitedFlowByGettingLoggedInSessionLost');
        });
        $.subscribe('/track/ajaxRequest/userExitedFlowByGettingForbidden', function () {
            WebsiteTracking.Track('AjaxRequestUserExitedFlowByGettingForbidden');
        });
        $.subscribe('/track/ajaxRequest/userExitedFlowByGettingRestApiUnknownError', function () {
            WebsiteTracking.Track('AjaxRequestUserExitedFlowByGettingRestApiUnknownError');
        });
        $.subscribe('/track/ajaxRequest/userExitedFlowByGettingWcfUnknownError', function () {
            WebsiteTracking.Track('AjaxRequestUserExitedFlowByGettingWcfUnknownError');
        });
        $.subscribe('/track/ajaxRequest/userExitedFlowByGettingGenericUnknownError', function () {
            WebsiteTracking.Track('AjaxRequestUserExitedFlowByGettingGenericUnknownError');
        });

        // AudioSettings page
        // Page load
        $.subscribe('/track/audioSettings/pageLoaded', function () {
            WebsiteTracking.Track('AudioSettingsPageLoaded');
        });
        $.subscribe('/track/audioSettings/pageLoadedFirstTimeInSession', function () {
            WebsiteTracking.Track('AudioSettingsPageLoadedFirstTimeInSession');
        });
        $.subscribe('/track/audioSettings/pageLoadedCreateViewerCodeConferenceBridge', function () {
            WebsiteTracking.Track('AudioSettingsPageLoadedCreateViewerCodeConferenceBridge');
        });

        // Save
        $.subscribe('/track/audioSettings/saveConfirmationDialogCameUp', function () {
            WebsiteTracking.Track('AudioSettingsSaveConfirmationDialogCameUp');
        });
        $.subscribe('/track/audioSettings/saveConfirmationDialogCanceled', function () {
            WebsiteTracking.Track('AudioSettingsSaveConfirmationDialogCanceled');
        });
        $.subscribe('/track/audioSettings/savedWithoutUpdatedInvitations', function () {
            WebsiteTracking.Track('AudioSettingsSavedWithoutUpdatedInvitations');
        });
        $.subscribe('/track/audioSettings/savedWithUpdatedInvitations', function () {
            WebsiteTracking.Track('AudioSettingsSavedWithUpdatedInvitations');
        });

        // Form elements
        $.subscribe('/track/audioSettings/jmConfLineSelected', function () {
            WebsiteTracking.Track('AudioSettingsJmConfLineSelected');
        });
        $.subscribe('/track/audioSettings/intPhoneLinkClicked', function () {
            WebsiteTracking.Track('AudioSettingsIntPhoneLinkClicked');
        });
        $.subscribe('/track/audioSettings/organizerCodeChanged', function () {
            WebsiteTracking.Track('AudioSettingsOrganizerCodeChanged');
        });
        $.subscribe('/track/audioSettings/organizerCommandsLinkClicked', function () {
            WebsiteTracking.Track('AudioSettingsOrganizerCommandsLinkClicked');
        });

        $.subscribe('/track/audioSettings/confModeEveryoneSelected', function () {
            WebsiteTracking.Track('AudioSettingsConfModeEveryoneSelected');
        });
        $.subscribe('/track/audioSettings/confModeOrganizerOnlySelected', function () {
            WebsiteTracking.Track('AudioSettingsConfModeOrganizerOnlySelected');
        });

        $.subscribe('/track/audioSettings/mediaModeAudioOnVideoOnSelected', function () {
            WebsiteTracking.Track('AudioSettingsMediaModeAudioOnVideoOnSelected');
        });
        $.subscribe('/track/audioSettings/mediaModeAudioOnVideoOffSelected', function () {
            WebsiteTracking.Track('AudioSettingsMediaModeAudioOnVideoOffSelected');
        });
        $.subscribe('/track/audioSettings/mediaModeAudioOffVideoOffSelected', function () {
            WebsiteTracking.Track('AudioSettingsMediaModeAudioOffVideoOffSelected');
        });

        $.subscribe('/track/audioSettings/startCallOrganizerJoinsSelected', function () {
            WebsiteTracking.Track('AudioSettingsStartCallOrganizerJoinsSelected');
        });
        $.subscribe('/track/audioSettings/holdMusicBeepSelected', function () {
            WebsiteTracking.Track('AudioSettingsHoldMusicBeepSelected');
        });
        $.subscribe('/track/audioSettings/holdMusicMusicSelected', function () {
            WebsiteTracking.Track('AudioSettingsHoldMusicMusicSelected');
        });
        $.subscribe('/track/audioSettings/holdMusicMusic2Selected', function () {
            WebsiteTracking.Track('AudioSettingsHoldMusicMusic2Selected');
        });
        $.subscribe('/track/audioSettings/holdMusicMusic3Selected', function () {
            WebsiteTracking.Track('AudioSettingsHoldMusicMusic3Selected');
        });
        $.subscribe('/track/audioSettings/holdMusicMusic4Selected', function () {
            WebsiteTracking.Track('AudioSettingsHoldMusicMusic4Selected');
        });
        $.subscribe('/track/audioSettings/holdMusicMusic5Selected', function () {
            WebsiteTracking.Track('AudioSettingsHoldMusicMusic5Selected');
        });
        $.subscribe('/track/audioSettings/chimesChecked', function () {
            WebsiteTracking.Track('AudioSettingsChimesChecked');
        });
        $.subscribe('/track/audioSettings/chimesUnChecked', function () {
            WebsiteTracking.Track('AudioSettingsChimesUnChecked');
        });
        $.subscribe('/track/audioSettings/ownConfLineSelected', function () {
            WebsiteTracking.Track('AudioSettingsOwnConfLineSelected');
        });
        $.subscribe('/track/audioSettings/dialInNumberChanged', function () {
            WebsiteTracking.Track('AudioSettingsDialInNumberChanged');
        });
        $.subscribe('/track/audioSettings/accessCodeChanged', function () {
            WebsiteTracking.Track('AudioSettingsAccessCodeChanged');
        });

        $.subscribe('/track/stickyjoin/downloadapp', function () {
            JM.Tracking.GoogleAnaliticsTracking('Buttons', 'Click', 'StikcyJoin-DownloadApp');
            WebsiteTracking.Track('StickyJoinDownloadAppClicked');
        });

        $.subscribe('/track/stickyjoin/joinviabrowser', function () {
            JM.Tracking.GoogleAnaliticsTracking('Buttons', 'Click', 'StikcyJoin-JoinViaBrowser');
            WebsiteTracking.Track('StickyJoinJoinViaBrowserClicked');
        });

        $.subscribe('/track/stickyjoin/flashrequired', function () {
            JM.Tracking.GoogleAnaliticsTracking('Buttons', 'Click', 'StikcyJoin-FlashRequired');
            WebsiteTracking.Track('StickyJoinFlashRequiredClicked');
        });

        // AndroidLaunch
        $.subscribe('/track/androidLaunch/pageLoaded', function () {
            WebsiteTracking.Track('AndroidLaunchPageLoaded');
        });

        $.subscribe('/track/androidLaunch/joinFired', function () {
            WebsiteTracking.Track('AndroidLaunchJoinFired');
        });
    });

    return {
        GoogleAnaliticsTracking: googleAnaliticsTracking,
        GoogleConversionTracking: googleConversionTracking,
        GoogleTagManagerRegisterEvent: googleTagManagerRegisterEvent,
        MediaPlexTracking: mediaPlexTracking,
        InternalTracking: internalTracking,
        GAPageTracking: gaPageTracking
    };

})(JM.BigIdea.AjaxRequest, JM.WebsiteTracking);
/// <reference path="../Services/AjaxRequest.ml.js" />
/// <reference path="../Detector/Detector.js" />
/// <reference path="../Platform/Platform.js" />
/// <reference path="../Utils/Utils.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};
JM.BigIdea.Detector = JM.BigIdea.Detector || {};
JM.BigIdea.Enums = JM.BigIdea.Enums || {};

JM.BigIdea.Sticky = (function (AjaxRequest, Platform, BrowserTypes, Utils, Detector) {
    "use strict";

    var _cookieKeyHostInstalled = "HostInstalled",
        _cookieKeyPreferredViewer = "PreferredViewer",
        _hostInstalledTrue = "True",
        _preferredViewerHost = "Host",
        _preferredViewerHtml5 = "HTML5",
        _isEnabledInSession,
        /**
        * Turn on sticky by creating sticky cookie and setting the preferred viewer cookie to 'Host'
        */
        _enableSticky = function () {
            _isEnabledInSession = true;

            Utils.AddCookie(_cookieKeyHostInstalled, _hostInstalledTrue);
            Utils.AddCookie(_cookieKeyPreferredViewer, _preferredViewerHost, 7);
        },

        _setHostInstalledCookie = function () {
            Utils.AddCookie(_cookieKeyHostInstalled, _hostInstalledTrue);
        },

        _removeHostInstalledCookie = function() {
            Utils.RemoveCookie(_cookieKeyHostInstalled);
        },

        /**
        * Turn off sticky by setting the preferred viewer cookie to 'HTML5'
        */
        _disableSticky = function () {
            _isEnabledInSession = false;

            Utils.AddCookie(_cookieKeyPreferredViewer, _preferredViewerHtml5, 7);
        },

        /**
        * Determine if sticky start is suppressed
        * @return {bool}: True if suppressed
        */
        _isStickySuppressed = function () {
            return !!Utils.GetQueryStringParameter(location.search, "suppressSticky");
        },

        /**
        * Determine if the sticky and the preferred viewer cookies allow the sticky start or not and if sticky start is suppressed
        * @return {bool}: True if available
        */
        _isStickyEnabled = function () {
            if (_isStickySuppressed())
                return false;

            if (Detector.IsJoinMeInstalled())
                return true;

            if (_isEnabledInSession)
                return _isEnabledInSession;

            return Utils.GetCookie(_cookieKeyPreferredViewer) === _preferredViewerHost || (!Utils.GetCookie(_cookieKeyPreferredViewer) && Utils.IsCookieSet(_cookieKeyHostInstalled));
        },

        /**
        * Determine if the host is installed by checking the sticky cookie
        * @return {bool}: True if host is installed
        */
        _isHostInstalled = function () {
            if (_isStickySuppressed())
                return false;

            if (Detector.IsJoinMeInstalled())
                return true;

            if (_isEnabledInSession)
                return _isEnabledInSession;

            return Utils.GetCookie(_cookieKeyHostInstalled) === _hostInstalledTrue;
        },

        /**
        * Executes sticky presenter swap flow
        * @param clientTicket {string}: The generated ticket for presenter swap
        * @param swapMode {Number}: Swap mode: 1 - requested by self, 2- offered by current presenter
        * @param viewerName {string}: Name to join the session with
        * @return {bool}: True if sticky process succeed;
        */
        _stickyJoinPresenter = function (clientTicket, swapMode, viewerName) {
            var succeed = true;

            AjaxRequest.Call({
                url: "/Ajax/Backend.asmx/GetSwapPresenterStickyAppUrl",
                isAsync: false,
                requestBody: {
                    clientTicket: clientTicket,
                    swapMode: swapMode,
                    viewerName: viewerName
                },
                onSuccess: function (json) {
                    if (!json.d.Url)
                        succeed = false;
                    else {
                        // Open the custom protocol link in an iframe instead of setting top window location (which causes an error in FF and Safari if the app is not installed)
                        // The HTML5 viewer's UI handles the possible cookie-app states well at this point.
                        // If there is HostInstalled cookie, but there is no app installed, the UI suggests to download the app.
                        // If there is HostInstalled cookie and the app is installed, the UI suggest to "Feel free to close this page, your meeting is running in the join.me desktop app"
                        $('<iframe src="' + json.d.Url + '" style="display: none"></iframe>').appendTo('body');
                    }
                }
            });
            return succeed;
        },

        _buttonStickyJoin = function (codeOrPurl) {
            var succeed = true;
            AjaxRequest.Call({
                url: "/Ajax/Backend.asmx/GetJoinStickyAppUrl",
                isAsync: false,
                requestBody: { accessCodeOrPersonalUrl: codeOrPurl },
                onSuccess: function (response) {
                    if (!response.d.Url)
                        succeed = false;
                    else {
                        Utils.ExecuteFileDownload(response.d.Url);
                    }
                }
            });

            return succeed;
        },

        /**
        * Executes sticky share flow
        */
        _stickyShare = function (personalUrl) {
            var succeed = true;

            AjaxRequest.Call({
                url: "/Ajax/Backend.asmx/GetShareStickyAppUrl",
                isAsync: false,
                // personalUrl parameter is mandatory in this ASMX web service but not used when user is not logged in. The whole webservice call should be refactored to WCF and sliced into separate flows
                requestBody: {
                    personalUrl: personalUrl,
                    autoScreenSharing: null,
                    autoAudioConnecting: null,
                    autoVideoConnecting: null
                },
                onSuccess: function (response) {
                    if (!response.d.Url)
                        succeed = false;
                    else
                        Utils.ExecuteFileDownload(response.d.Url);
                }
            });

            return succeed;
        },

        _buttonStickyShare = function (params) {
            var meetingId = -1;
            var downloadLink = '';

            if (!params) {
                params = {};
            }

            AjaxRequest.Call({
                url: "/ajax/JoinMeButton.svc/GetShareStickyAppUrl",
                isAsync: false,
                requestBody: params,
                onSuccess: function (response) {
                    if (response.GetShareStickyAppUrlResult.Url) {
                        Utils.ExecuteFileDownload(response.GetShareStickyAppUrlResult.Url);
                        meetingId = response.GetShareStickyAppUrlResult.MeetingId;
                        downloadLink = response.GetShareStickyAppUrlResult.DownloadLink;
                    }

                }
            });

            return {
                meetingId: meetingId,
                downloadLink: downloadLink
            };
        },

        _isHtml5ViewerPreferred = function () {
            var preferedCookie = Utils.GetCookie(_cookieKeyPreferredViewer);
            return preferedCookie && preferedCookie === _preferredViewerHtml5;
        };

    return {
        /**
        * Turn on sticky by creating sticky cookie
        */
        EnableSticky: _enableSticky,

        /**
        * Turn off sticky by expiring sticky cookie
        */
        DisableSticky: _disableSticky,

        /**
        * Determine if sticky cookie available or not
        * @return {bool}: True if available
        */
        IsStickyEnabled: _isStickyEnabled,

        /**
        * Determine if the host is installed by checking the sticky cookie
        * @return {bool}: True if host is installed
        */
        IsHostInstalled: _isHostInstalled,

        /**
         * Sets the HostInstalled cookie
         */
        SetHostInstalledCookie: _setHostInstalledCookie,

        RemoveHostInstalledCookie: _removeHostInstalledCookie,

        /**
        * Executes sticky presenter swap flow
        * @param clientTicket {string}: The generated ticket for presenter swap
        * @param swapMode {Number}: Swap mode: 1 - requested by self, 2- offered by current presenter
        * @param viewerName {string}: Name to join the session with
        * @return {bool}: True if sticky process succeed;
        */
        StickyJoinPresenter: _stickyJoinPresenter,

        /**
        * Executes sticky join flow for the Button
        */
        ButtonStickyJoin: _buttonStickyJoin,

        /**
        * Executes sticky share flow
        * @personalUrl {string}: PersonalUrl to use. If not filled, OTC flow is used.
        */
        StickyShare: _stickyShare,

        /**
         * Executes sticky share flow for The Button
         */
        ButtonStickyShare: _buttonStickyShare,

        IsHtml5ViewerPrefered: _isHtml5ViewerPreferred
};

}(JM.BigIdea.AjaxRequest, JM.BigIdea.Platform, JM.BigIdea.Enums.Browsers, JM.BigIdea.Utils, JM.BigIdea.Detector));
/// <reference path="../../../../Common/Scripts/3rdParty/jquery-1.8.3.js" />
/// <reference path="../../../Common/Scripts/Utils/Utils.js" />
/// <reference path="../../../Common/Scripts/CallContext/CallContext.js" />
/// <reference path="../../../Common/Scripts/Sticky/Sticky.js" />
/// <reference path="../../../Common/Scripts/ClientSelector/ClientSelector.js" />
/// <reference path="../../../Common/Scripts/ClientTicketGenerator/ClientTicketGenerator.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};
JM.BigIdea.Enums = JM.BigIdea.Enums || {};

JM.BigIdea.Enums.JoinResults = {
    // Everything fine page will be redirected to the flash container page
    Ok: 0,

    // Sticky fallback bubble must be displayed as sticky may not started
    OkButStickyBubbleNeededWithForceJoin: 1,

    // Error bubble must be displayed as user provide nothing
    MissingCodeOrPurl: 2,

    // Currently installed flash player is not supported
    FlashNotSupported: 3,

    // Invalid code
    InvalidCode: 4,

    // Invalid purl
    InvalidPurl: 5,

    // Service error
    ServiceError: 6
};

JM.BigIdea.Join = (function ($, Utils, ClientSelector, Sticky, HostPaths, CallContext, AjaxRequest) {
    "use strict";

    var _getClientTicket = function (sessionId) {
        var ticket = '';
        LMI.Connection.AjaxRequest.genericWSCall(false, { sessionId: sessionId }, function (json) {
            if (!json.d.iError)
                ticket = json.d.Ticket;
        }, null, null, null, "/Ajax/Backend.asmx/GenerateClientTicket", 800, true);
        return ticket;
    },
        _doJoin = function (codeOrPurl, isForceOperation) {
            var clientResult = ClientSelector.GetClientResult(),
                isStickyEnabled = Sticky.IsStickyEnabled(),
                caller = CallContext.GetContext();

            switch (caller) {
                case JM.BigIdea.Enums.CallContext.HomePage:
                    $.publish("/track/join/click/homepage");
                    break;
                case JM.BigIdea.Enums.CallContext.StartTab:
                    $.publish("/track/join/click/starttab");
                    break;
                case JM.BigIdea.Enums.CallContext.StickyJoin:
                    $.publish("/track/stickyjoin/joinviabrowser");
                    break;
                default:
                    $.publish("/track/join/click");
                    break;
            }

            if (isStickyEnabled && isForceOperation) {
                // Turn off sticky forever as user decided to not use
                isStickyEnabled = false;
                Sticky.DisableSticky();
            }

            if (!isStickyEnabled && clientResult === JM.BigIdea.Enums.ClientResult.None) {
                Utils.SetTopWindowLocation("/" + codeOrPurl);
                return JM.BigIdea.Enums.JoinResults.FlashNotSupported;
            }

            if (isStickyEnabled) {
                // Sticky detected
                Utils.SetTopWindowLocation("/" + codeOrPurl);
                return JM.BigIdea.Enums.JoinResults.Ok;
            }
            // Normal way to join
            Utils.SetTopWindowLocation("/" + codeOrPurl);
            return JM.BigIdea.Enums.JoinResults.Ok;
        },
        _doJoinFromClient = function (codeOrPurl, sessionId, presenterSwap, voipConnected, isMuted, channel) {
            var queryString = '&clientTicket=' + JM.BigIdea.Join.GetClientTicket(sessionId) +
                '&swapMode=' + presenterSwap +
                '&AutoAudioConnecting=' + voipConnected +
                '&isMuted=' + isMuted || '' +
                '&channel=' + channel || '';
            Utils.ExecuteFileDownload(HostPaths.GetPath(queryString));
            return JM.BigIdea.Enums.JoinResults.Ok;
        },
        _doLaunchFromClient = function (sessionId, swapMode) {
            var clientTicket = JM.BigIdea.Join.GetClientTicket(sessionId);
            Sticky.StickyJoinPresenter(clientTicket, swapMode, null);
            return JM.BigIdea.Enums.JoinResults.Ok;
        };

    return {
        /**
        * Try to join to a meeting using multiple ways
        * @param codeOrPurl {string}: Contains 9 digit code or personal url
        * @param isForceOperation {bool}: When true only normal join will be executed without sticky check
        * @return {enum}: Execution status defined in JM.BigIdea.Enums.JoinResults
        */
        DoJoin: _doJoin,

        DoJoinFromClient: _doJoinFromClient,

        DoLaunchFromClient: _doLaunchFromClient,

        GetClientTicket: _getClientTicket
    };
}(jQuery, JM.BigIdea.Utils, JM.BigIdea.ClientSelector, JM.BigIdea.Sticky, JM.BigIdea.HostPaths, JM.BigIdea.CallContext, JM.BigIdea.AjaxRequest));/// <reference path="../../../Common/Scripts/3rdParty/jquery-1.8.3.js" />
/// <reference path="../Services/AjaxRequest.ml.js" />
/// <reference path="../../Common/Scripts/Platform/Platform.js" />
/// <reference path="../../Common/Scripts/Html5/DownloadHotkeyGenerator.js" />
/// <reference path="../../Common/Scripts/Html5/DownloadHotkeyGenerator.js" />
/// <reference path="Join.js" />
/// <reference path="../../Common/Scripts/Utils/Utils.js" />
/// <reference path="../../Common/Scripts/HostPaths/HostPaths.js" />
/// <reference path="../../Common/Scripts/Sticky/Sticky.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};
JM.HTML5 = JM.HTML5 || {};

var aLocalizations = [];


// TODO The whole implementation can be refactored to properly use css class, and server side rendering
JM.BigIdea.JoinPage = (function ($, Platform, Join, Utils, HostPath, Sticky, ClientSelector, SegmentTracking) {
    "use strict";

    var _trackingProperties = { category: "join.me Interstitial" },
        _suppressBrowser = Utils.GetQueryStringParameter(location.search, 'suppressBrowser');

    var $titleText,
        $subTitleText,
        $descriptionText,
        $descriptionSecondaryText,
        $loader,
        $loaderContainer,
        $loaderSuccess,
        _joinViaBrowserSelector,
        _code,
        _client = ClientSelector.GetClientResult(),
        _setupButtons = function (joinViaBrowserAction) {

            if (_client !== JM.BigIdea.Enums.ClientResult.None && !_suppressBrowser) {
                $(document).off("click", _joinViaBrowserSelector).on("click", _joinViaBrowserSelector, function () {
                    JM.HTML5.joinViaBrowserClickedAt = performance.now();
                    //Force stickyless Join
                    if (joinViaBrowserAction) {
                        Sticky.DisableSticky();
                        $.publish("/track/stickyjoin/joinviabrowser");
                        joinViaBrowserAction();
                    } else {
                        Join.DoJoin(_code, true);
                    }
                    return false;
                });
            } else {
                $(_joinViaBrowserSelector).html('<a id="downloadApp" href="javascript:void(0);">' + "Download the desktop app" + '</a>');

                $(document).off("click", "#downloadApp").on("click", "#downloadApp", function () {
                    JM.WebsiteTracking.Track('INTERSTITIAL_JOIN_FORCE_DOWNLOAD_CLICKED', _trackingProperties, JM.WebsiteTracking.SegmentIntegrations.ALL);

                    $.publish("/track/stickyjoin/flashrequired");

                    //Native download
                    $.publish("/track/stickyjoin/downloadapp");
                    var path = HostPath.GetPath('codeOrPurl=' + _code);
                    Utils.SetTopWindowLocation(path);
                    return false;
                });

                $(document).off("click", "#downloadChrome").on("click", "#downloadChrome", function () {
                    var win = window.open('https://www.google.com/chrome/browser/desktop/index.html', '_blank');
                    win.focus();
                });
            }
        },

        _writeDownloadMessage = function () {
            if (_client === JM.BigIdea.Enums.ClientResult.None || _suppressBrowser) {
                $loaderContainer.hide();

                $titleText.text("To join the meeting...");
                $subTitleText.text("To join, please download the join.me desktop app or use Chrome browser.");
                $descriptionText.html('<a id="downloadApp" class="downloadButton" href="javascript:void(0);">' + "Download the desktop app" + '</a>');
                $descriptionSecondaryText.html('<a id="downloadChrome" class="downloadButtonChrome" href="javascript:void(0);">' + "Download Chrome" + '</a>');

                JM.WebsiteTracking.Track('INTERSTITIAL_JOIN_UNSUPPORTED_VIEWER_PAGE_SHOWN', _trackingProperties, JM.WebsiteTracking.SegmentIntegrations.ALL);
            } else {
                $titleText.html("Looks like you already have the join.me app.");
                $subTitleText.html("The desktop app should appear on your screen in a few seconds.");
                $descriptionText.html("The join.me app didn\u0027t start? {0}Join with your browser{1}."
                    .replace(/\{0\}/g, '<span id="joinViaBrowser">')
                    .replace(/\{1\}/g, '</span>'));

                setTimeout(function () {
                    $loader.fadeOut(500, function () {
                        $loaderSuccess.fadeIn(500);
                    });
                }, 6000);

                JM.WebsiteTracking.Track('INTERSTITIAL_JOIN_LAUNCH_PAGE_SHOWN', _trackingProperties, JM.WebsiteTracking.SegmentIntegrations.ALL);
            }  
        },
        _getJoinCode = function () {
            var viewerCodeMatch, purlMatch;

            if (location.search.indexOf("code") > 0) {
                return Utils.GetQueryStringParameter(location.search, 'code');
            } else if (location.search.indexOf("personalurl") > 0) {
                return Utils.GetQueryStringParameter(location.search, 'personalurl');
            } else if (location.pathname.indexOf("join") > 0) {
                return location.pathname.substring(location.pathname.lastIndexOf("/") + 1, location.pathname.length);
            }
            //when the JmClient page is displayed normally and not in an iframe we have to parse the viewerCode differently
            //ex: /123-456-789?suppressSticky=true -> we have to capture only the viewerCode part
            else if ((viewerCodeMatch = /^\/(\d[\w-]*)\??(.*)?/g.exec(location.pathname))) {
                return viewerCodeMatch[1];
            }
            //similar to the viewerCodeMatch above, but for purl
            else if ((purlMatch = /^\/([A-Za-z][\w-\.]{8,})\??(.*)?/g.exec(location.pathname))) {
                return purlMatch[1];
            }
            return '';
        },
        _init = function (config) {
            _joinViaBrowserSelector = config.joinViaBrowser;
            $titleText = $(config.titleText);
            $subTitleText = $(config.subTitleText);
            $descriptionText = $(config.descriptionText);
            $descriptionSecondaryText = $(config.descriptionSecondaryText);
            $loader = $(config.loader);
            $loaderContainer = $(config.loaderContainer);
            $loaderSuccess = $(config.loaderSuccess);

            _code = JM.BigIdea.JoinPage.GetJoinCode();

            _writeDownloadMessage();
            _setupButtons(config.joinViaBrowserAction);
        };

    return {
        Init: _init,
        GetJoinCode: _getJoinCode
    };
}(jQuery,
    JM.BigIdea.Platform,
    JM.BigIdea.Join,
    JM.BigIdea.Utils,
    JM.BigIdea.HostPaths,
    JM.BigIdea.Sticky,
    JM.BigIdea.ClientSelector,
    JM.Tracking.SegmentTracking));/// <reference path="../../../../Common/Scripts/3rdParty/jquery.js" />
/// <reference path="../../ShareJoin/Scripts/Join/Join.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};

JM.BigIdea.Html5Subscriber = (function (Utils, Join, Sticky, Platform) {
    "use strict";

    var aLocalizations = [];

    var rootScope,
        MEETING_IS_FULL = 7,


    _initiateDialog = function (config) {
        var $primaryButton = $('#modalPrimaryButton'),
            $cancelButton = $('#modalCancelButton'),
            $dialogWrapper = $('.modalDialogWrapper'),
            $dialog = $('#modalDialogDownload'),
            $overlay = $('#modalOverlay'),
            $loader = $('#modalLoaderContainer'),
            $closeButton = $('#modalClose'),
            $callByPhone = $('#modalCallByPhone'),
            $callByPhoneLine = $('#modalLine'),
            $modalLaunchSection = $('#modalLaunchSection'),
            $modalLaunchLink = $('#modalLaunchLink'),
            $modalDownloadSection = $('#modalDownloadSection'),
            $modalDownloadLink = $('#modalDownloadLink');
    
        $dialogWrapper.removeClass('downloadContent launchContent annotateContent audioContent videoContent no-footer');
    
        if (config.autoDownload) {
            if (config.downloadCallback) {
                _setDownloadInProgress(config.retryCallback, config.cancelDownloadCallback);
                config.downloadCallback();
                $dialogWrapper.addClass('downloadContent');
            }
            return false;
        }
    
        if (config.launch) {
            $dialogWrapper.addClass('launchContent');
        }
    
        if (config.annotate) {
            $dialogWrapper.addClass('annotateContent');
        }
    
        if (config.audio) {
            $dialogWrapper.addClass('audioContent');
        }
    
        if (config.video) {
            $dialogWrapper.addClass('videoContent');
        }
    
        $overlay.show();
        $primaryButton.off('click').on('click', function (e) {
            if (config.downloadCallback) {
                _setDownloadInProgress(config.retryCallback, config.cancelDownloadCallback);
                config.downloadCallback();
                $dialogWrapper.addClass('downloadContent');
            }
            return false;
        });
    
        $cancelButton.text("Cancel");
    
        $modalLaunchLink.off('click').on('click', function (e) {
            if (config.launchCallback) {
                config.launchCallback();
            }
            return false;
        });
    
        $modalDownloadLink.off('click').on('click', function (e) {
            if (config.downloadCallback) {
                _setDownloadInProgress(config.retryCallback, config.cancelDownloadCallback);
                config.downloadCallback();
                $dialogWrapper.addClass('downloadContent');
            }
            return false;
        });
    
        rootScope.$on('MainController.ExitMeeting', function () {
            $overlay.hide();
            $dialog.hide();
        });
    
        if (config.showChrome) {
            $cancelButton.text("Download Chrome");
            $cancelButton.off('click').on('click', function (e) {
                if (config.chromeCallback) {
                    config.chromeCallback();
                }
                var win = window.open('https://www.google.com/chrome/browser/desktop/index.html', '_blank');
                win.focus();
    
            });
        } else {
            $cancelButton.text("Cancel");
            $cancelButton.off('click').on('click', function (e) {
                $overlay.hide();
                $dialog.hide();
                if (config.cancelCallback) {
                    config.cancelCallback();
                }
                return false;
            });
        }
    
        $closeButton.off('click').on('click', function () {
            $overlay.hide();
            $dialog.hide();
            if (config.cancelCallback) {
                config.cancelCallback();
            }
            return false;
        });
    
        $('#modalTitle').text(config.title);
        $('#modalText').html(config.content);
        if (config.showDownload) {
            $primaryButton.show();
        } else {
            $primaryButton.hide();
        }
    
        if (config.showCancel) {
            $cancelButton.show();
        } else {
            $primaryButton.addClass('singleButton');
            $cancelButton.hide();
        }
    
        if (config.showClose) {
            $closeButton.show();
        } else {
            $closeButton.hide();
        }
    
        if (config.showLoader) {
            $loader.show();
        } else {
            $loader.hide();
        }
    
        if (config.showModalLaunchLink) {
            $modalLaunchSection.show();
        } else {
            $modalLaunchSection.hide();
        }
    
        if (config.showModalDownloadLink) {
            $modalDownloadSection.show();
        } else {
            $modalDownloadSection.hide();
        }
    
        if (config.retryFunction) {
            $('#modalRestartDownload').off('click').on('click', config.retryFunction);
        }
    
        if (config.showCallByPhone) {
            $callByPhoneLine.show();
            $callByPhone.off('click').on('click', function () {
                $overlay.hide();
                $dialog.hide();
                config.callByPhoneCallback();
            });
        } else {
            $callByPhoneLine.hide();
        }
    
        if (!config.showModalLaunchLink && !config.showModalDownloadLink && !config.showCallByPhone) {
            $dialogWrapper.addClass('no-footer');
        }
    
        $dialog.show();
    },
    
    _downloadAnnotate = function (viewerCode, params, channel) {
        var config = {
            title: "Take collaboration to the next level",
            content: "Download the desktop app to start annotating.",
            downloadCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'AnnotateDownload');
                Join.DoJoinFromClient(viewerCode, rootScope.clientTicket, false, params.voipConnected, params.isMuted, channel);
            },
            cancelCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'AnnotateCancel');
                rootScope.$broadcast('Host.Download.Cancel');
            },
            retryCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'AnnotateDownloadingRetry');
                Join.DoJoinFromClient(viewerCode, rootScope.clientTicket, false, params.voipConnected);
            },
            cancelDownloadCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'AnnotateDownloadingCancel');
                rootScope.$broadcast('Host.Download.Cancel');
            },
            launchCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'AnnotatePresenterLaunch', params.source);
                Sticky.EnableSticky();
                location.reload();
                return false;
            },
            showDownload: true,
            showClose: true,
            showModalLine: true,
            showModalLaunchLink: true,
            annotate: true
        };
    
        _initiateDialog(config);
    },
    
    _downloadAudio = function (viewerCode, isFreeMeeting, autoDownload) {
        var config = {
            title: "Uh-oh. Internet calling isn’t supported on this browser.",
            content: "To call via internet, you need to download the join.me desktop app or join the meeting from Chrome or Firefox browsers.",
            downloadCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'AudioDownload');
                Join.DoJoinFromClient(viewerCode, rootScope.clientTicket, false);
            },
            cancelCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'AudioCancel');
                rootScope.$broadcast('Host.Download.Cancel');
            },
            retryCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'AudioDownloadingRetry');
                Join.DoJoinFromClient(viewerCode, rootScope.clientTicket, false);
            },
            cancelDownloadCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'AudioDownloadingCancel');
                rootScope.$broadcast('Host.Download.Cancel');
            },
            chromeCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'AudioDownloadChrome');
            },
            callByPhoneCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'AudioCallByPhone');
                rootScope.$broadcast('Host.OpenPSTN');
            },
            showDownload: true,
            showClose: true,
            showChrome: true,
            showCallByPhone: !isFreeMeeting,
            autoDownload: autoDownload,
            audio: true
        };
    
        _initiateDialog(config);
    },
    
    _downloadVideo = function (viewerCode, isFreeMeeting, autoDownload) {
        var config = {
            title: "Oops. Video isn’t supported on this browser.",
            content: "To use video, you need to download the join.me desktop app or join the meeting from the Google Chrome browser.",
            downloadCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'VideoDownload');
                Join.DoJoinFromClient(viewerCode, rootScope.clientTicket, false);
            },
            cancelCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'VideoCancel');
                rootScope.$broadcast('Host.CancelVideoDownload');
                rootScope.$broadcast('Host.Download.Cancel');
            },
            retryCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'VideoDownloadingRetry');
                Join.DoJoinFromClient(viewerCode, rootScope.clientTicket, false);
            },
            cancelDownloadCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'VideoDownloadingCancel');
                rootScope.$broadcast('Host.Download.Cancel');
            },
            chromeCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'VideoDownloadChrome');
            },
            callByPhoneCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'VideoCallByPhone');
                rootScope.$broadcast('Host.OpenPSTN');
            },
            showDownload: true,
            showClose: true,
            showCallByPhone: !isFreeMeeting,
            showChrome: true,
            autoDownload: autoDownload,
            video: true
        };
    
        _initiateDialog(config);
    },
    
    _launchPresenter = function (viewerCode, channel, params) {
        var config = {
            title: "Launching the join.me desktop app...",
            content: "To share your screen, you need the desktop app. Looks like you already have it...",
            downloadCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'PresenterLaunchDownload', params.source);
                Join.DoJoinFromClient(viewerCode, rootScope.clientTicket, params.swapMode, params.voipConnected, params.isMuted, channel);
            },
            cancelCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'PresenterLaunchDownloadCancel', params.source);
                rootScope.$broadcast('Host.Download.Cancel');
            },
            retryCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'PresenterLaunchDownloadingRetry', params.source, params.voipConnected, params.isMuted, channel);
                Join.DoJoinFromClient(viewerCode, rootScope.clientTicket, false, params.isMuted);
            },
            cancelDownloadCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'PresenterLaunchDownloadingCancel', params.source);
                rootScope.$broadcast('Host.Download.Cancel');
            },
            showDownload: false,
            showClose: true,
            showCancel: false,
            showLoader: true,
            showModalLine: true,
            showModalLaunchLink: false,
            showModalDownloadLink: true,
            launch: true
        };
    
        _initiateDialog(config);
    
        rootScope.$broadcast('Host.Tracking', 'PresenterAutoLaunch', params.source);
        Join.DoLaunchFromClient(rootScope.clientTicket, params.swapMode);
    },
    
    _downloadPresenter = function (viewerCode, channel, params) {
        var config = {
            title: "To share your screen...",
            content: "You need to download the desktop app.",
            downloadCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'PresenterDownload', params.source);
                Join.DoJoinFromClient(viewerCode, rootScope.clientTicket, params.swapMode, params.voipConnected, params.isMuted, channel);
            },
            cancelCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'PresenterCancel', params.source);
                rootScope.$broadcast('Host.Download.Cancel');
            },
            retryCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'PresenterDownloadingRetry', params.source, params.voipConnected, params.isMuted, channel);
                Join.DoJoinFromClient(viewerCode, rootScope.clientTicket, false, params.isMuted);
            },
            cancelDownloadCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'PresenterDownloadingCancel', params.source);
                rootScope.$broadcast('Host.Download.Cancel');
            },
            launchCallback: function () {
                rootScope.$broadcast('Host.Tracking', 'PresenterLaunch', params.source);
                Join.DoLaunchFromClient(rootScope.clientTicket, params.swapMode);
            },
            showDownload: true,
            showClose: true,
            showCancel: false,
            showModalLine: true,
            showModalLaunchLink: true
        };
    
        _initiateDialog(config);
    },
    
    _isPURLorOTC = function () {
        if (location.search.indexOf("code") > 0)
            return 'otc';
        else if (location.search.indexOf("personalurl") > 0)
            return 'purl';
    },
    
    _trackMeetingExit = function (event, source) {
        JM.WebsiteTracking.Track(event, {
            category: 'join.me HTML5 Client',
            buttonLocation: source,
            type: _isPURLorOTC()
        },
            JM.WebsiteTracking.SegmentIntegrations.ALL
        );
    },
    
    _trackViewerLoad = function () {
        JM.WebsiteTracking.Track('HTML5_VIEWER_LOAD', {
            category: 'join.me HTML5 Client',
            type: _isPURLorOTC()
        },
            JM.WebsiteTracking.SegmentIntegrations.ALL
        );
    },
    
    _setDownloadInProgress = function (retryFunction, cancelFunction) {
        var key = Platform.GetDownloadHotkey(),
            modalHotkeyText = $('#modalHotkeyText'),
            $cancelButton = $('#modalCancelButton');
    
    
        if (key) {
            modalHotkeyText.html("Once downloaded, open join.me. Press {0} to find recent downloads."
                .replace(/\{0\}/g, '<b>' + key + '</b>'));
        } else {
            modalHotkeyText.html("Once downloaded, open join.me. Press {0} to find recent downloads.");
        }
    
        var config = {
            title: "Downloading the desktop app...",
            content: $('#desktopAppDownloadingMessage').html(),
            showDownload: false,
            showClose: true,
            retryFunction: retryFunction,
            cancelCallback: cancelFunction
        };
        _initiateDialog(config);
        $cancelButton.text("Close");
    },
    _subscribeToEvents = function (viewerCode, theScope, channel) {
        rootScope = theScope;

        rootScope.$on('StartFailed', function (event, errorCode) {
            Utils.SetCurrentWindowLocation('/JmClient/Start?startFailed=true&html5ErrorCode=' + errorCode.value);
        });
    
        rootScope.$on('JoinFailed', function (event, errorCode) {
            if (errorCode.value !== MEETING_IS_FULL) {
                Utils.SetCurrentWindowLocation('/JmClient/Join?joinFailed=true&viewerCode=' + viewerCode + '&html5ErrorCode=' + errorCode.value);
            }
        });
    
        //Subscribe to tracking events (exit stage left)
        rootScope.$on('exitMeeting', function (event, message, source) {
            switch (message) {
                case ('userInitiated'): //User indicated that they would like to exit meeting
                    _trackMeetingExit('HTML5_VIEWER_EXIT_CLICK', source);
                    break;
                case ('meetingExiting'): //User confirmed that they would like to exit meeting
                    _trackMeetingExit('HTML5_VIEWER_EXIT_CLICK_CONFIRM', source);
                    break;
            }
        });
    
        //If we are being asked to subscribe to events, that means the HTML5 viewer has loaded so we should track that it's loaded
        _trackViewerLoad();
    
        //Subscribe to host download events
        rootScope.$on('Host.Download', function (event, data, params, autoDownload) {
            switch (data) {
                case 'Audio':
                    _downloadAudio(viewerCode, params, autoDownload);
                    break;
                case 'Annotate':
                    _downloadAnnotate(viewerCode, params, channel);
                    break;
                case 'PresenterSwitch':
                    var isHostInstalled = Sticky.IsHostInstalled();
    
                    if (isHostInstalled) {
                        _launchPresenter(viewerCode, channel, params);
                    } else {
                        _downloadPresenter(viewerCode, channel, params);
                    }
                    break;
                case 'PresenterSwitchCancel':
                    $('#modalOverlay').hide();
                    break;
                case 'Video':
                    _downloadVideo(viewerCode, params, autoDownload);
                    break;
            }
        });
        //Subscribe to exit meeting and host download,
        //we can add the redirect logic here later.
        rootScope.$on('Host.ExitSession', function (event, data) {
            Utils.SetTopWindowLocation('/');
        });
    
        //Subscribe to native viewer started event
        rootScope.$on('Host.SwitchedToNative', function (event, data) {
            $('#modalOverlay').hide();
        });
    };

    return {
        /**
        * Subscribe to the angular JS PubSub messages
        * @param rootScope {object}: The Angular $rootScope service from the HTML5 viewer
        * @return {void}
        */
        SubscribeToEvents: _subscribeToEvents
    };
})(JM.BigIdea.Utils, JM.BigIdea.Join, JM.BigIdea.Sticky, JM.BigIdea.Platform);
var JM = JM || {};

/**
 * Toast notification library
 * 
 * Create an element in your html which will become template for the toast notification.
 * 
 * (optional) Use the functions to get or set the following values:
 * JM.ToastNotify.offsetPixels([val]) -> default is 20. This is the distance toasts appear apart from each other,
 *                                and the distance they appear away from the side of the browser window
 * JM.ToastNotify.stackOrigin([val]) -> default is 'bottom'. This is the side of the window that toasts stack away from. For example
 *                               if you use 'bottom', the first toast appears near the bottom edge of the screen and additional toasts
 *                               stack upwards on the screen on top of the toast in question. Other valid settings could are 'left', 'right', 'top'.
 * JM.ToastNotify.anchorOrigin([val]) -> default is 'right'. This is the side of the window that toasts all align with. If you want your toasts to appear along
 *                                the left side of the screen, set this to left. The top, set this to top... NOTE!!! When stackOrigin equals 'top' or 
 *                                'bottom', anchor origin MUST equal 'left' or 'right' and vice versa.
 *                               
 * Call:
 * var _myToast = JM.ToastNotify.toast({
 *     templateSelector: "#yourTemplate",
 *     dictionary: {
 *         valueToReplace: "Replacement value 1",
 *         anotherToReplace: "This is kind of cool"
 *     }
 * })
 * 
 * The library will hide and clone the element jquery retrieves using your passed in selector. All text in the element which matches an item in the
 * dictionary in the format __propertyName__  will be replaced by the text passed in. Passing in dictionary is optional and only needs to be done
 * if you have some values you want to replace. This is useful if you will have multiple toasts on one page sharing the same template with different
 * messages.
 * 
 * To show the toast: _myToast.show()
 * To hide the toast: _myToast.hide()
 */
JM.ToastNotify = (function () {
    var _toastCount=0;
    var _defaultId = "jmtoast";
    var _toastOffsetPixels = 20;
    var _visibleToasts = [];
    var _toastStackOrigin = 'bottom';
    var _toastAnchorOrigin = 'right';
    
    function _log(msg) {
        if (window.console && window.console.log) {
            window.console.log("ToastNotify: " + msg);
        }
    }

    function _error(msg) {
        if (window.console && window.console.error) {
            window.console.error("ToastNotify: " + msg);
        }
    }

    function _removeExactElementAt(ele, idx) {
        if (_visibleToasts[idx] && _visibleToasts[idx] === ele) {
            _visibleToasts.splice(idx, 1);
        }
    }

    // Stacks the visible toasts on the screen
    function _cascadeToasts() {
        if (_toastStackOrigin != 'bottom' && _toastStackOrigin != 'top' && _toastStackOrigin != 'left' && _toastStackOrigin != 'right') {
            _error("Invalid value for stackOrigin: " + _toastStackOrigin + ". Valid values are top, bottom, left, right");
            return;
        }        
        var _offset = _toastOffsetPixels;
        var _nonVisibleToast;
        var _nonVisibleToastIndex;

        $(_visibleToasts).each(function(idx, ele) {
            // make sure this toast is actually visible
            if (!ele.getElement().is(":visible")) {
                // This will happen if the toast was forcibly removed from the DOM without calling toast.hide()
                // to recover from this, we should call the toast.hide() method to remove and restart the cascade
                _nonVisibleToast = ele;
                _nonVisibleToastIndex = idx;
                return false;
            }
            return true;
        });

        if (_nonVisibleToast) {
            _removeExactElementAt(_nonVisibleToast, _nonVisibleToastIndex);            
            _nonVisibleToast.hide();            
            return;
        }

        $(_visibleToasts).each(function(idx, ele) {
            ele.getElement().css(_toastStackOrigin, _offset + 'px');
            _offset += _toastOffsetPixels;
            if (_toastStackOrigin == 'bottom' || _toastStackOrigin == 'top') {
                _offset += ele.getElement().height();
            } else {
                _offset += ele.getElement().width();
            }                                   
            return true;
        });
    }
    
    /*
    Create a new toast notification.

    data.templateSelector - CSS selector for element to be used as a template to create the notification
    data.dictionary - mapping of values to replace in the template
    */
    function _toast(data) {
        if (!data) {
            _error("Bad arguments");
            return null;
        }
        if (!data.templateSelector) {
            _error("Argument templateSelector must be set.");
            return null;
        }
        var _template = $(data.templateSelector);
        if (_template.length == 0) {
            _error('Could not find element using selector: ' + data.templateSelector);
            return null;
        }
        if (_template.length != 1) {
            _error('Template selector should return a single element but returned ' + _template.length + ': ' + data.templateSelector);
            return null;
        }
        if (_toastAnchorOrigin != 'bottom' && _toastAnchorOrigin != 'top' && _toastAnchorOrigin != 'left' && _toastAnchorOrigin != 'right') {
            _error("Invalid value for anchorOrigin: " + _toastAnchorOrigin + ". Valid values are top, bottom, left, right");
            return null;
        }
        if (_toastStackOrigin != 'bottom' && _toastStackOrigin != 'top' && _toastStackOrigin != 'left' && _toastStackOrigin != 'right') {
            _error("Invalid value for stackOrigin: " + _toastStackOrigin + ". Valid values are top, bottom, left, right");
            return null;
        }
        if ((_toastAnchorOrigin == 'left' || _toastAnchorOrigin == 'right') && (_toastStackOrigin == 'left' || _toastStackOrigin == 'right')) {
            _error("When anchorOrigin is equal to 'left' or 'right', stackOrigin must equal 'top' or 'bottom'");
            return null;
        }
        if ((_toastAnchorOrigin == 'top' || _toastAnchorOrigin == 'bottom') && (_toastStackOrigin == 'top' || _toastStackOrigin == 'bottom')) {
            _error("When anchorOrigin is equal to 'top' or 'bottom', stackOrigin must equal 'left' or 'right'");
            return null;
        }
        // ensure original template is hidden
        _template.hide();
        var _clone = _template.clone();

        // Increment the id suffix by 1 for each toast
        if (_clone.attr('id') == '') _clone.attr('id', _defaultId);
        _clone.attr('id', _clone.attr('id') + _toastCount++);
        _clone.addClass('jm-toast');
        _clone.css(_toastAnchorOrigin, _toastOffsetPixels + 'px');

        // Replace values in template with filled in properties
        $(_clone).insertAfter(_template);
        if (data.dictionary) {
            var _cloneHtml = _clone.html();
            for (var _prop in data.dictionary) {
                if (data.dictionary.hasOwnProperty(_prop)) {
                    // Look for __<propertyname>__ in html and replace all with property value
                    _cloneHtml = _cloneHtml.replace(new RegExp("__[\\s]*" + _prop + "[\\s]*__", "g"), data.dictionary[_prop]);
                }
            }
            _clone.html(_cloneHtml);
        }

        var _toastIndex = null;

        return {
            show: function () {
                if (_toastIndex == null) {
                    _toastIndex = _visibleToasts.push(this) - 1;
                    $(_clone).show();
                }
                _cascadeToasts();
                return this;
            },
            hide: function () {
                if (_toastIndex != null) {
                    $(_clone).hide();
                    _removeExactElementAt(this, _toastIndex);
                    _toastIndex = null;
                }
                _cascadeToasts();
                return this;
            },
            getElement: function () {
                return _clone;
            },
        }
    }

    return {
        toast: _toast,
        offsetPixels: function (pixels) {
            if (typeof pixels == 'number' && isFinite(pixels) && pixels % 1 === 0) {
                _toastOffsetPixels = pixels;
            }

            return _toastOffsetPixels;
        },
        stackOrigin: function (origin) {
            if (origin && (origin == 'top' || origin == 'bottom' || origin == 'left' || origin == 'right')) {
                _toastStackOrigin = origin;
            }

            return _toastStackOrigin;
        },
        anchorOrigin: function(origin) {
            if (origin && (origin == 'top' || origin == 'bottom' || origin == 'left' || origin == 'right')) {
                _toastAnchorOrigin = origin;
            }

            return _toastAnchorOrigin;
        }
    };
}());/// <reference path="../../../../Common/Scripts/3rdParty/jquery.js" />
/// <reference path="/Client/ToastNotify/toastnotify.js" />
/// <reference path="../../ShareJoin/Scripts/Join/Join.js" />
/// <reference path="../Utils/Utils.js" />
/// <reference path="/Common/Scripts/LMI/fileloader/FileLoader.js" />

var JM = window.JM || {};
JM.BigIdea = JM.BigIdea || {};

var aLocalizations = [];

JM.BigIdea.ViewerToast = (function (toast, join, utils, window) {
    "use strict";
    var toastShownCookie = 'viewertoastshown',
        svc,
        isUserLoggedIn;

    var _init = function (config) {
        var viewerToast = toast.toast(
                    {
                        templateSelector: '#toastTemplate',
                        dictionary:
                        {
                            message: "Download the desktop app for the best experience, it\u0027s free!",
                            buttonText: "Use the app"
                        }
                    });

        $(viewerToast.getElement()).find('.share button').click(function () {
            window.analytics.track("HTML5_CLIENT_DOWNLOAD_INVITE_CLICKED", {
                category: "join.me Viewer Prompts"
            }, {
                integrations: {
                    'All': true,
                    'Mixpanel': false,
                }
            });
            join.DoJoinFromClient(config.codeOrPurl, config.clientTicket, false);
            viewerToast.hide();
            _setShownCookie();
        });
        $(viewerToast.getElement()).find('.jm-toast-close a').click(function () {
            viewerToast.hide();
            _setShownCookie();
        });

        return viewerToast;
    },
        _shouldShow = function (isUserLoggedIn, isPresenterPro) {
            return !utils.IsCookieSet(toastShownCookie) && (isUserLoggedIn || isPresenterPro);
        },

        _setShownCookie = function () {
            utils.AddCookie(toastShownCookie, "yes", 1);
        };

    return {
        Init: _init,
        ShouldShow: _shouldShow,
        SetShownCookie: _setShownCookie
    };
})(JM.ToastNotify, JM.BigIdea.Join, JM.BigIdea.Utils, window);
var sentryUrl = JM.HTML5.initParams.environment === "live"
    ? "https://bb783d4a0b91492ea0560b7e50c3a841@sentry.jmawsmonitor.net/6"
    : "https://b999027d581f4a8680dfaf1daef4b165@sentry.jmawsmonitor.net/34";
window.Raven && window.Raven.config(sentryUrl, {}).install();

var initialize = function (svc) {
    //Constants
    var isUserLoggedIn = window.JM.HTML5.initParams.isUserLoggedIn,
        channel = "", //we use only the default channel in the HTML5 viewer
        meetingIsOverState = 11,
        switchedToNativeState = 12;

    //Vars requiring loaded page
    var viewerCode = JM.HTML5.initParams.code,
        personalUrl = JM.HTML5.initParams.personalUrl,
        codeOrPurl = viewerCode || personalUrl,
        meetingEndcapShown = false,
        switchedToNative = false,
        meetingIsFull = false,
        pageUnloaded = false;

    window.JM = JM || {};
    window.JM.HTML5 = JM.HTML5 || {};
    (function subscribeToHtml5AppEvent() {
        var downloadHostToast = JM.BigIdea.ViewerToast.Init({
            codeOrPurl: codeOrPurl,
            clientTicket: svc.clientTicket
        });

        JM.BigIdea.Html5Subscriber.SubscribeToEvents(codeOrPurl, svc, channel);

        svc.$on('PeerService.PageUnload',
            function () {
                pageUnloaded = true;
            });

        svc.$on('JoinService.MeetingIsFull',
            function () {
                meetingIsFull = true;
            });

        svc.$on('exitMeeting',
            function (event, message, source, peer) {
                if (message === 'meetingExiting') {
                    if (event.currentScope.isPresenterMode === true) {
                        JM.BigIdea.Utils.SetTopWindowLocation('/');
                    } else {
                        $('#meetingEndCap #endcapFirstName').val(peer.firstName || '');
                        $('#meetingEndCap #endcapLastName').val(peer.lastName || '');
                        JM.BigIdea.MeetingEndCap.Show({
                            target: '#meetingEndCap',
                            isUserLoggedIn: isUserLoggedIn
                        });
                    }
                }
            });

        svc.$on('CentralLogic.UIStateChange',
            function (event, uiState, isHeadlessViewer) {
                if (isHeadlessViewer) {
                    return;
                }

                switchedToNative = switchedToNative || uiState.ViewerState.value === switchedToNativeState;

                switch (uiState.ViewerState.value) {
                    case meetingIsOverState:
                        if (event.currentScope.isPresenterMode === true) {
                            JM.BigIdea.Utils.SetTopWindowLocation('/');
                        } else {
                            if (!meetingEndcapShown && !switchedToNative && !meetingIsFull && !pageUnloaded) {
                                //AB test GT-1183
                                JM.BigIdea.MeetingEndCap.Show({
                                    target: '#meetingEndCap',
                                    isUserLoggedIn: isUserLoggedIn
                                });
                                meetingEndcapShown = true;
                            }
                        }
                        break;
                    default:
                        var toastElement = downloadHostToast.getElement();
                        if (toastElement && toastElement.is(':visible')) {
                            downloadHostToast.hide();
                        }
                        JM.BigIdea.MeetingEndCap.Hide();
                        break;
                }
            });
    })();
};

window.JM.HTML5.initializeOuterScripts = function (svc) {
    var partialViews = JM.HTML5.partialViews;
    if (partialViews && partialViews.length) {
        var partialViewRequests = partialViews.map(function (partial) {
            return $.ajax({
                type: "GET",
                url: partial.trim(),
                success: function (html) {
                    $('body').append(html);
                }
            });
        });
        //when all templates loaded to the DOM start the initialization
        $.when.apply($, partialViewRequests).done(function() {
            initialize(svc);
        });
    } else {
        initialize(svc);
    }
};

