/* Copyright 2023 Ellucian Company L.P. and its affiliates. */
// - Modified and minified to use resx JS variables for day-of-week names and abbreviations

var Ellucian = Ellucian || {};
Ellucian.DaysOfWeek = Ellucian.DaysOfWeek || {};
Ellucian.DaysOfWeek.Resources = Ellucian.DaysOfWeek.Resources || {};

//! moment.js
//! version : 2.5.1
//! authors : Tim Wood, Iskren Chernev, Moment.js contributors
//! license : MIT
//! momentjs.com
!function (e, t) { "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : e.moment = t() }(this, function () {
    "use strict"; function e() { return J.apply(null, arguments) } function t(e) { J = e } function n(e) { return e instanceof Array || "[object Array]" === Object.prototype.toString.call(e) } function s(e) { return null != e && "[object Object]" === Object.prototype.toString.call(e) } function i(e, t) { return Object.prototype.hasOwnProperty.call(e, t) } function r(e) { var t; if (Object.getOwnPropertyNames) return 0 === Object.getOwnPropertyNames(e).length; for (t in e) if (i(e, t)) return !1; return !0 } function a(e) { return void 0 === e } function o(e) { return "number" == typeof e || "[object Number]" === Object.prototype.toString.call(e) } function u(e) { return e instanceof Date || "[object Date]" === Object.prototype.toString.call(e) } function l(e, t) { var n, s = [], i = e.length; for (n = 0; n < i; ++n)s.push(t(e[n], n)); return s } function h(e, t) { for (var n in t) i(t, n) && (e[n] = t[n]); return i(t, "toString") && (e.toString = t.toString), i(t, "valueOf") && (e.valueOf = t.valueOf), e } function d(e, t, n, s) { return t8(e, t, n, s, !0).utc() } function c(e) { return null == e._pf && (e._pf = { empty: !1, unusedTokens: [], unusedInput: [], overflow: -2, charsLeftOver: 0, nullInput: !1, invalidEra: null, invalidMonth: null, invalidFormat: !1, userInvalidated: !1, iso: !1, parsedDateParts: [], era: null, meridiem: null, rfc2822: !1, weekdayMismatch: !1 }), e._pf } function f(e) { if (null == e._isValid) { var t = c(e), n = Q.call(t.parsedDateParts, function (e) { return null != e }), s = !isNaN(e._d.getTime()) && t.overflow < 0 && !t.empty && !t.invalidEra && !t.invalidMonth && !t.invalidWeekday && !t.weekdayMismatch && !t.nullInput && !t.invalidFormat && !t.userInvalidated && (!t.meridiem || t.meridiem && n); if (e._strict && (s = s && 0 === t.charsLeftOver && 0 === t.unusedTokens.length && void 0 === t.bigHour), null != Object.isFrozen && Object.isFrozen(e)) return s; e._isValid = s } return e._isValid } function $(e) { var t = d(NaN); return null != e ? h(c(t), e) : c(t).userInvalidated = !0, t } Q = Array.prototype.some ? Array.prototype.some : function (e) { var t, n = Object(this), s = n.length >>> 0; for (t = 0; t < s; t++)if (t in n && e.call(this, n[t], t, n)) return !0; return !1 }; var m, y, g = e.momentProperties = [], _ = !1; function v(e, t) { var n, s, i, r = g.length; if (a(t._isAMomentObject) || (e._isAMomentObject = t._isAMomentObject), a(t._i) || (e._i = t._i), a(t._f) || (e._f = t._f), a(t._l) || (e._l = t._l), a(t._strict) || (e._strict = t._strict), a(t._tzm) || (e._tzm = t._tzm), a(t._isUTC) || (e._isUTC = t._isUTC), a(t._offset) || (e._offset = t._offset), a(t._pf) || (e._pf = c(t)), a(t._locale) || (e._locale = t._locale), r > 0) for (n = 0; n < r; n++)a(i = t[s = g[n]]) || (e[s] = i); return e } function w(t) { v(this, t), this._d = new Date(null != t._d ? t._d.getTime() : NaN), this.isValid() || (this._d = new Date(NaN)), !1 === _ && (_ = !0, e.updateOffset(this), _ = !1) } function p(e) { return e instanceof w || null != e && null != e._isAMomentObject } function k(t) { !1 === e.suppressDeprecationWarnings && "undefined" != typeof console && console.warn && console.warn("Deprecation warning: " + t) } function S(t, n) { var s = !0; return h(function () { if (null != e.deprecationHandler && e.deprecationHandler(null, t), s) { var r, a, o, u = [], l = arguments.length; for (a = 0; a < l; a++) { if (r = "", "object" == typeof arguments[a]) { for (o in r += "\n[" + a + "] ", arguments[0]) i(arguments[0], o) && (r += o + ": " + arguments[0][o] + ", "); r = r.slice(0, -2) } else r = arguments[a]; u.push(r) } k(t + "\nArguments: " + Array.prototype.slice.call(u).join("") + "\n" + Error().stack), s = !1 } return n.apply(this, arguments) }, n) } var Y = {}; function D(t, n) { null != e.deprecationHandler && e.deprecationHandler(t, n), Y[t] || (k(n), Y[t] = !0) } function M(e) { return "undefined" != typeof Function && e instanceof Function || "[object Function]" === Object.prototype.toString.call(e) } function O(e, t) { var n, r = h({}, e); for (n in t) i(t, n) && (s(e[n]) && s(t[n]) ? (r[n] = {}, h(r[n], e[n]), h(r[n], t[n])) : null != t[n] ? r[n] = t[n] : delete r[n]); for (n in e) i(e, n) && !i(t, n) && s(e[n]) && (r[n] = h({}, r[n])); return r } function b(e) { null != e && this.set(e) } function T(e, t, n) { var s = "" + Math.abs(e); return (e >= 0 ? n ? "+" : "" : "-") + Math.pow(10, Math.max(0, t - s.length)).toString().substr(1) + s } e.suppressDeprecationWarnings = !1, e.deprecationHandler = null, X = Object.keys ? Object.keys : function (e) { var t, n = []; for (t in e) i(e, t) && n.push(t); return n }; var x = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g, W = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, P = {}, R = {}; function C(e, t, n, s) { var i = s; "string" == typeof s && (i = function () { return this[s]() }), e && (R[e] = i), t && (R[t[0]] = function () { return T(i.apply(this, arguments), t[1], t[2]) }), n && (R[n] = function () { return this.localeData().ordinal(i.apply(this, arguments), e) }) } function N(e) { return e.match(/\[[\s\S]/) ? e.replace(/^\[|\]$/g, "") : e.replace(/\\/g, "") } function U(e, t) { return e.isValid() ? (P[t = H(t, e.localeData())] = P[t] || function e(t) { var n, s, i = t.match(x); for (n = 0, s = i.length; n < s; n++)R[i[n]] ? i[n] = R[i[n]] : i[n] = N(i[n]); return function (e) { var n, r = ""; for (n = 0; n < s; n++)r += M(i[n]) ? i[n].call(e, t) : i[n]; return r } }(t), P[t](e)) : e.localeData().invalidDate() } function H(e, t) { var n = 5; function s(e) { return t.longDateFormat(e) || e } for (W.lastIndex = 0; n >= 0 && W.test(e);)e = e.replace(W, s), W.lastIndex = 0, n -= 1; return e } var L = {}; function F(e, t) { var n = e.toLowerCase(); L[n] = L[n + "s"] = L[t] = e } function V(e) { return "string" == typeof e ? L[e] || L[e.toLowerCase()] : void 0 } function G(e) { var t, n, s = {}; for (n in e) i(e, n) && (t = V(n)) && (s[t] = e[n]); return s } var A = {}; function I(e, t) { A[e] = t } function E(e) { return e % 4 == 0 && e % 100 != 0 || e % 400 == 0 } function j(e) { return e < 0 ? Math.ceil(e) || 0 : Math.floor(e) } function Z(e) { var t = +e, n = 0; return 0 !== t && isFinite(t) && (n = j(t)), n } function z(t, n) { return function (s) { return null != s ? (B(this, t, s), e.updateOffset(this, n), this) : q(this, t) } } function q(e, t) { return e.isValid() ? e._d["get" + (e._isUTC ? "UTC" : "") + t]() : NaN } function B(e, t, n) { e.isValid() && !isNaN(n) && ("FullYear" === t && E(e.year()) && 1 === e.month() && 29 === e.date() ? (n = Z(n), e._d["set" + (e._isUTC ? "UTC" : "") + t](n, e.month(), ek(n, e.month()))) : e._d["set" + (e._isUTC ? "UTC" : "") + t](n)) } var J, Q, X, K, ee = /\d/, et = /\d\d/, en = /\d{3}/, es = /\d{4}/, ei = /[+-]?\d{6}/, er = /\d\d?/, ea = /\d\d\d\d?/, eo = /\d\d\d\d\d\d?/, eu = /\d{1,3}/, el = /\d{1,4}/, eh = /[+-]?\d{1,6}/, ed = /\d+/, ec = /[+-]?\d+/, ef = /Z|[+-]\d\d:?\d\d/gi, e$ = /Z|[+-]\d\d(?::?\d\d)?/gi, em = /[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i; function e8(e, t, n) { K[e] = M(t) ? t : function (e, s) { return e && n ? n : t } } function ey(e, t) { var n; return i(K, e) ? K[e](t._strict, t._locale) : RegExp((n = e, eg(n.replace("\\", "").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (e, t, n, s, i) { return t || n || s || i })))) } function eg(e) { return e.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") } K = {}; var e_ = {}; function ev(e, t) { var n, s, i = t; for ("string" == typeof e && (e = [e]), o(t) && (i = function (e, n) { n[t] = Z(e) }), s = e.length, n = 0; n < s; n++)e_[e[n]] = i } function ew(e, t) { ev(e, function (e, n, s, i) { s._w = s._w || {}, t(e, s._w, s, i) }) } function ep(e, t, n) { null != t && i(e_, e) && e_[e](t, n._a, n, e) } function ek(e, t) { if (isNaN(e) || isNaN(t)) return NaN; var n, s = ((n = t) % 12 + 12) % 12; return e += (t - s) / 12, 1 === s ? E(e) ? 29 : 28 : 31 - s % 7 % 2 } ej = Array.prototype.indexOf ? Array.prototype.indexOf : function (e) { var t; for (t = 0; t < this.length; ++t)if (this[t] === e) return t; return -1 }, C("M", ["MM", 2], "Mo", function () { return this.month() + 1 }), C("MMM", 0, 0, function (e) { return this.localeData().monthsShort(this, e) }), C("MMMM", 0, 0, function (e) { return this.localeData().months(this, e) }), F("month", "M"); A.month = 8, e8("M", er), e8("MM", er, et), e8("MMM", function (e, t) { return t.monthsShortRegex(e) }), e8("MMMM", function (e, t) { return t.monthsRegex(e) }), ev(["M", "MM"], function (e, t) { t[1] = Z(e) - 1 }), ev(["MMM", "MMMM"], function (e, t, n, s) { var i = n._locale.monthsParse(e, s, n._strict); null != i ? t[1] = i : c(n).invalidMonth = e }); var eS = "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), eY = "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), eD = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/, eM = em, eO = em; function eb(e, t, n) { var s, i, r, a = e.toLocaleLowerCase(); if (!this._monthsParse) for (s = 0, this._monthsParse = [], this._longMonthsParse = [], this._shortMonthsParse = []; s < 12; ++s)r = d([2e3, s]), this._shortMonthsParse[s] = this.monthsShort(r, "").toLocaleLowerCase(), this._longMonthsParse[s] = this.months(r, "").toLocaleLowerCase(); return n ? "MMM" === t ? -1 !== (i = ej.call(this._shortMonthsParse, a)) ? i : null : -1 !== (i = ej.call(this._longMonthsParse, a)) ? i : null : "MMM" === t ? -1 !== (i = ej.call(this._shortMonthsParse, a)) ? i : -1 !== (i = ej.call(this._longMonthsParse, a)) ? i : null : -1 !== (i = ej.call(this._longMonthsParse, a)) ? i : -1 !== (i = ej.call(this._shortMonthsParse, a)) ? i : null } function eT(e, t) { var n; if (!e.isValid()) return e; if ("string" == typeof t) { if (/^\d+$/.test(t)) t = Z(t); else if (!o(t = e.localeData().monthsParse(t))) return e } return n = Math.min(e.date(), ek(e.year(), t)), e._d["set" + (e._isUTC ? "UTC" : "") + "Month"](t, n), e } function e0(t) { return null != t ? (eT(this, t), e.updateOffset(this, !0), this) : q(this, "Month") } function ex() { function e(e, t) { return t.length - e.length } var t, n, s = [], i = [], r = []; for (t = 0; t < 12; t++)n = d([2e3, t]), s.push(this.monthsShort(n, "")), i.push(this.months(n, "")), r.push(this.months(n, "")), r.push(this.monthsShort(n, "")); for (s.sort(e), i.sort(e), r.sort(e), t = 0; t < 12; t++)s[t] = eg(s[t]), i[t] = eg(i[t]); for (t = 0; t < 24; t++)r[t] = eg(r[t]); this._monthsRegex = RegExp("^(" + r.join("|") + ")", "i"), this._monthsShortRegex = this._monthsRegex, this._monthsStrictRegex = RegExp("^(" + i.join("|") + ")", "i"), this._monthsShortStrictRegex = RegExp("^(" + s.join("|") + ")", "i") } C("Y", 0, 0, function () { var e = this.year(); return e <= 9999 ? T(e, 4) : "+" + e }), C(0, ["YY", 2], 0, function () { return this.year() % 100 }), C(0, ["YYYY", 4], 0, "year"), C(0, ["YYYYY", 5], 0, "year"), C(0, ["YYYYYY", 6, !0], 0, "year"), F("year", "y"); function eW(e) { return E(e) ? 366 : 365 } A.year = 1, e8("Y", ec), e8("YY", er, et), e8("YYYY", el, es), e8("YYYYY", eh, ei), e8("YYYYYY", eh, ei), ev(["YYYYY", "YYYYYY"], 0), ev("YYYY", function (t, n) { n[0] = 2 === t.length ? e.parseTwoDigitYear(t) : Z(t) }), ev("YY", function (t, n) { n[0] = e.parseTwoDigitYear(t) }), ev("Y", function (e, t) { t[0] = parseInt(e, 10) }), e.parseTwoDigitYear = function (e) { return Z(e) + (Z(e) > 68 ? 1900 : 2e3) }; var eP = z("FullYear", !0); function eR(e, t, n, s, i, r, a) { var o; return e < 100 && e >= 0 ? isFinite((o = new Date(e + 400, t, n, s, i, r, a)).getFullYear()) && o.setFullYear(e) : o = new Date(e, t, n, s, i, r, a), o } function eC(e) { var t, n; return e < 100 && e >= 0 ? (n = Array.prototype.slice.call(arguments), n[0] = e + 400, isFinite((t = new Date(Date.UTC.apply(null, n))).getUTCFullYear()) && t.setUTCFullYear(e)) : t = new Date(Date.UTC.apply(null, arguments)), t } function eN(e, t, n) { var s = 7 + t - n; return -((7 + eC(e, 0, s).getUTCDay() - t) % 7) + s - 1 } function e2(e, t, n, s, i) { var r, a, o = eN(e, s, i), u = 1 + 7 * (t - 1) + (7 + n - s) % 7 + o; return u <= 0 ? a = eW(r = e - 1) + u : u > eW(e) ? (r = e + 1, a = u - eW(e)) : (r = e, a = u), { year: r, dayOfYear: a } } function eU(e, t, n) { var s, i, r = eN(e.year(), t, n), a = Math.floor((e.dayOfYear() - r - 1) / 7) + 1; return a < 1 ? s = a + e4(i = e.year() - 1, t, n) : a > e4(e.year(), t, n) ? (s = a - e4(e.year(), t, n), i = e.year() + 1) : (i = e.year(), s = a), { week: s, year: i } } function e4(e, t, n) { var s = eN(e, t, n), i = eN(e + 1, t, n); return (eW(e) - s + i) / 7 } C("w", ["ww", 2], "wo", "week"), C("W", ["WW", 2], "Wo", "isoWeek"), F("week", "w"), F("isoWeek", "W"); A.week = 5; A.isoWeek = 5, e8("w", er), e8("ww", er, et), e8("W", er), e8("WW", er, et), ew(["w", "ww", "W", "WW"], function (e, t, n, s) { t[s.substr(0, 1)] = Z(e) }), C("d", 0, "do", "day"), C("dd", 0, 0, function (e) { return this.localeData().weekdaysMin(this, e) }), C("ddd", 0, 0, function (e) { return this.localeData().weekdaysShort(this, e) }), C("dddd", 0, 0, function (e) { return this.localeData().weekdays(this, e) }), C("e", 0, 0, "weekday"), C("E", 0, 0, "isoWeekday"), F("day", "d"), F("weekday", "e"), F("isoWeekday", "E"); A.day = 11; A.weekday = 11; function eH(e, t) { return e.slice(t, 7).concat(e.slice(0, t)) } A.isoWeekday = 11, e8("d", er), e8("e", er), e8("E", er), e8("dd", function (e, t) { return t.weekdaysMinRegex(e) }), e8("ddd", function (e, t) { return t.weekdaysShortRegex(e) }), e8("dddd", function (e, t) { return t.weekdaysRegex(e) }), ew(["dd", "ddd", "dddd"], function (e, t, n, s) { var i = n._locale.weekdaysParse(e, s, n._strict); null != i ? t.d = i : c(n).invalidWeekday = e }), ew(["d", "e", "E"], function (e, t, n, s) { t[s] = Z(e) }); var eL = [Ellucian.DaysOfWeek.Resources.Sunday, Ellucian.DaysOfWeek.Resources.Monday, Ellucian.DaysOfWeek.Resources.Tuesday, Ellucian.DaysOfWeek.Resources.Wednesday, Ellucian.DaysOfWeek.Resources.Thursday, Ellucian.DaysOfWeek.Resources.Friday, Ellucian.DaysOfWeek.Resources.Saturday,], eF = [Ellucian.DaysOfWeek.Resources.SundayAbbreviationShort, Ellucian.DaysOfWeek.Resources.MondayAbbreviationShort, Ellucian.DaysOfWeek.Resources.TuesdayAbbreviationShort, Ellucian.DaysOfWeek.Resources.WednesdayAbbreviationShort, Ellucian.DaysOfWeek.Resources.ThursdayAbbreviationShort, Ellucian.DaysOfWeek.Resources.FridayAbbreviationShort, Ellucian.DaysOfWeek.Resources.SaturdayAbbreviationShort,], eV = "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), eG = em, e1 = em, eA = em; function eI(e, t, n) { var s, i, r, a = e.toLocaleLowerCase(); if (!this._weekdaysParse) for (s = 0, this._weekdaysParse = [], this._shortWeekdaysParse = [], this._minWeekdaysParse = []; s < 7; ++s)r = d([2e3, 1]).day(s), this._minWeekdaysParse[s] = this.weekdaysMin(r, "").toLocaleLowerCase(), this._shortWeekdaysParse[s] = this.weekdaysShort(r, "").toLocaleLowerCase(), this._weekdaysParse[s] = this.weekdays(r, "").toLocaleLowerCase(); return n ? "dddd" === t ? -1 !== (i = ej.call(this._weekdaysParse, a)) ? i : null : "ddd" === t ? -1 !== (i = ej.call(this._shortWeekdaysParse, a)) ? i : null : -1 !== (i = ej.call(this._minWeekdaysParse, a)) ? i : null : "dddd" === t ? -1 !== (i = ej.call(this._weekdaysParse, a)) || -1 !== (i = ej.call(this._shortWeekdaysParse, a)) ? i : -1 !== (i = ej.call(this._minWeekdaysParse, a)) ? i : null : "ddd" === t ? -1 !== (i = ej.call(this._shortWeekdaysParse, a)) || -1 !== (i = ej.call(this._weekdaysParse, a)) ? i : -1 !== (i = ej.call(this._minWeekdaysParse, a)) ? i : null : -1 !== (i = ej.call(this._minWeekdaysParse, a)) || -1 !== (i = ej.call(this._weekdaysParse, a)) ? i : -1 !== (i = ej.call(this._shortWeekdaysParse, a)) ? i : null } function eE() { function e(e, t) { return t.length - e.length } var t, n, s, i, r, a = [], o = [], u = [], l = []; for (t = 0; t < 7; t++)n = d([2e3, 1]).day(t), s = eg(this.weekdaysMin(n, "")), i = eg(this.weekdaysShort(n, "")), r = eg(this.weekdays(n, "")), a.push(s), o.push(i), u.push(r), l.push(s), l.push(i), l.push(r); a.sort(e), o.sort(e), u.sort(e), l.sort(e), this._weekdaysRegex = RegExp("^(" + l.join("|") + ")", "i"), this._weekdaysShortRegex = this._weekdaysRegex, this._weekdaysMinRegex = this._weekdaysRegex, this._weekdaysStrictRegex = RegExp("^(" + u.join("|") + ")", "i"), this._weekdaysShortStrictRegex = RegExp("^(" + o.join("|") + ")", "i"), this._weekdaysMinStrictRegex = RegExp("^(" + a.join("|") + ")", "i") } function e3() { return this.hours() % 12 || 12 } function e5(e, t) { C(e, 0, 0, function () { return this.localeData().meridiem(this.hours(), this.minutes(), t) }) } C("H", ["HH", 2], 0, "hour"), C("h", ["hh", 2], 0, e3), C("k", ["kk", 2], 0, function e() { return this.hours() || 24 }), C("hmm", 0, 0, function () { return "" + e3.apply(this) + T(this.minutes(), 2) }), C("hmmss", 0, 0, function () { return "" + e3.apply(this) + T(this.minutes(), 2) + T(this.seconds(), 2) }), C("Hmm", 0, 0, function () { return "" + this.hours() + T(this.minutes(), 2) }), C("Hmmss", 0, 0, function () { return "" + this.hours() + T(this.minutes(), 2) + T(this.seconds(), 2) }), e5("a", !0), e5("A", !1), F("hour", "h"); function e7(e, t) { return t._meridiemParse } A.hour = 13, e8("a", e7), e8("A", e7), e8("H", er), e8("h", er), e8("k", er), e8("HH", er, et), e8("hh", er, et), e8("kk", er, et), e8("hmm", ea), e8("hmmss", eo), e8("Hmm", ea), e8("Hmmss", eo), ev(["H", "HH"], 3), ev(["k", "kk"], function (e, t, n) { var s = Z(e); t[3] = 24 === s ? 0 : s }), ev(["a", "A"], function (e, t, n) { n._isPm = n._locale.isPM(e), n._meridiem = e }), ev(["h", "hh"], function (e, t, n) { t[3] = Z(e), c(n).bigHour = !0 }), ev("hmm", function (e, t, n) { var s = e.length - 2; t[3] = Z(e.substr(0, s)), t[4] = Z(e.substr(s)), c(n).bigHour = !0 }), ev("hmmss", function (e, t, n) { var s = e.length - 4, i = e.length - 2; t[3] = Z(e.substr(0, s)), t[4] = Z(e.substr(s, 2)), t[5] = Z(e.substr(i)), c(n).bigHour = !0 }), ev("Hmm", function (e, t, n) { var s = e.length - 2; t[3] = Z(e.substr(0, s)), t[4] = Z(e.substr(s)) }), ev("Hmmss", function (e, t, n) { var s = e.length - 4, i = e.length - 2; t[3] = Z(e.substr(0, s)), t[4] = Z(e.substr(s, 2)), t[5] = Z(e.substr(i)) }); var ej, eZ, ez = z("Hours", !0), e6 = { calendar: { sameDay: "[Today at] LT", nextDay: "[Tomorrow at] LT", nextWeek: "dddd [at] LT", lastDay: "[Yesterday at] LT", lastWeek: "[Last] dddd [at] LT", sameElse: "L" }, longDateFormat: { LTS: "h:mm:ss A", LT: "h:mm A", L: "MM/DD/YYYY", LL: "MMMM D, YYYY", LLL: "MMMM D, YYYY h:mm A", LLLL: "dddd, MMMM D, YYYY h:mm A" }, invalidDate: "Invalid date", ordinal: "%d", dayOfMonthOrdinalParse: /\d{1,2}/, relativeTime: { future: "in %s", past: "%s ago", s: "a few seconds", ss: "%d seconds", m: "a minute", mm: "%d minutes", h: "an hour", hh: "%d hours", d: "a day", dd: "%d days", w: "a week", ww: "%d weeks", M: "a month", MM: "%d months", y: "a year", yy: "%d years" }, months: eS, monthsShort: eY, week: { dow: 0, doy: 6 }, weekdays: eL, weekdaysMin: eV, weekdaysShort: eF, meridiemParse: /[ap]\.?m?\.?/i }, e9 = {}, eq = {}; function eB(e, t) { var n, s = Math.min(e.length, t.length); for (n = 0; n < s; n += 1)if (e[n] !== t[n]) return n; return s } function eJ(e) { return e ? e.toLowerCase().replace("_", "-") : e } function eQ(e) { var t, n, s = null; if (void 0 === e9[e] && "undefined" != typeof module && module && module.exports && null != (n = e).match("^[^/\\\\]*$")) try { s = eZ._abbr, (t = require)("./locale/" + e), eX(s) } catch (i) { e9[e] = null } return e9[e] } function eX(e, t) { var n; return e && ((n = a(t) ? te(e) : eK(e, t)) ? eZ = n : "undefined" != typeof console && console.warn && console.warn("Locale " + e + " not found. Did you forget to load it?")), eZ._abbr } function eK(e, t) { if (null === t) return delete e9[e], null; var n, s = e6; if (t.abbr = e, null != e9[e]) D("defineLocaleOverride", "use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."), s = e9[e]._config; else if (null != t.parentLocale) { if (null != e9[t.parentLocale]) s = e9[t.parentLocale]._config; else { if (null == (n = eQ(t.parentLocale))) return eq[t.parentLocale] || (eq[t.parentLocale] = []), eq[t.parentLocale].push({ name: e, config: t }), null; s = n._config } } return e9[e] = new b(O(s, t)), eq[e] && eq[e].forEach(function (e) { eK(e.name, e.config) }), eX(e), e9[e] } function te(e) { var t; if (e && e._locale && e._locale._abbr && (e = e._locale._abbr), !e) return eZ; if (!n(e)) { if (t = eQ(e)) return t; e = [e] } return function e(t) { for (var n, s, i, r, a = 0; a < t.length;) { for (n = (r = eJ(t[a]).split("-")).length, s = (s = eJ(t[a + 1])) ? s.split("-") : null; n > 0;) { if (i = eQ(r.slice(0, n).join("-"))) return i; if (s && s.length >= n && eB(r, s) >= n - 1) break; n-- } a++ } return eZ }(e) } function tt(e) { var t, n = e._a; return n && -2 === c(e).overflow && (t = n[1] < 0 || n[1] > 11 ? 1 : n[2] < 1 || n[2] > ek(n[0], n[1]) ? 2 : n[3] < 0 || n[3] > 24 || 24 === n[3] && (0 !== n[4] || 0 !== n[5] || 0 !== n[6]) ? 3 : n[4] < 0 || n[4] > 59 ? 4 : n[5] < 0 || n[5] > 59 ? 5 : n[6] < 0 || n[6] > 999 ? 6 : -1, c(e)._overflowDayOfYear && (t < 0 || t > 2) && (t = 2), c(e)._overflowWeeks && -1 === t && (t = 7), c(e)._overflowWeekday && -1 === t && (t = 8), c(e).overflow = t), e } var tn = /^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/, ts = /^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/, ti = /Z|[+-]\d\d(?::?\d\d)?/, tr = [["YYYYYY-MM-DD", /[+-]\d{6}-\d\d-\d\d/], ["YYYY-MM-DD", /\d{4}-\d\d-\d\d/], ["GGGG-[W]WW-E", /\d{4}-W\d\d-\d/], ["GGGG-[W]WW", /\d{4}-W\d\d/, !1], ["YYYY-DDD", /\d{4}-\d{3}/], ["YYYY-MM", /\d{4}-\d\d/, !1], ["YYYYYYMMDD", /[+-]\d{10}/], ["YYYYMMDD", /\d{8}/], ["GGGG[W]WWE", /\d{4}W\d{3}/], ["GGGG[W]WW", /\d{4}W\d{2}/, !1], ["YYYYDDD", /\d{7}/], ["YYYYMM", /\d{6}/, !1], ["YYYY", /\d{4}/, !1],], ta = [["HH:mm:ss.SSSS", /\d\d:\d\d:\d\d\.\d+/], ["HH:mm:ss,SSSS", /\d\d:\d\d:\d\d,\d+/], ["HH:mm:ss", /\d\d:\d\d:\d\d/], ["HH:mm", /\d\d:\d\d/], ["HHmmss.SSSS", /\d\d\d\d\d\d\.\d+/], ["HHmmss,SSSS", /\d\d\d\d\d\d,\d+/], ["HHmmss", /\d\d\d\d\d\d/], ["HHmm", /\d\d\d\d/], ["HH", /\d\d/],], to = /^\/?Date\((-?\d+)/i, tu = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/, tl = { UT: 0, GMT: 0, EDT: -240, EST: -300, CDT: -300, CST: -360, MDT: -360, MST: -420, PDT: -420, PST: -480 }; function th(e) { var t, n, s, i, r, a, o = e._i, u = tn.exec(o) || ts.exec(o), l = tr.length, h = ta.length; if (u) { for (t = 0, c(e).iso = !0, n = l; t < n; t++)if (tr[t][1].exec(u[1])) { i = tr[t][0], s = !1 !== tr[t][2]; break } if (null == i) { e._isValid = !1; return } if (u[3]) { for (t = 0, n = h; t < n; t++)if (ta[t][1].exec(u[3])) { r = (u[2] || " ") + ta[t][0]; break } if (null == r) { e._isValid = !1; return } } if (!s && null != r) { e._isValid = !1; return } if (u[4]) { if (ti.exec(u[4])) a = "Z"; else { e._isValid = !1; return } } e._f = i + (r || "") + (a || ""), t$(e) } else e._isValid = !1 } function td(e) { var t, n, s, i, r, a, o, u, l, h, d, f = tu.exec((h = e._i).replace(/\([^()]*\)|[\n\t]/g, " ").replace(/(\s\s+)/g, " ").replace(/^\s\s*/, "").replace(/\s\s*$/, "")); if (f) { if (d = (s = f[4], i = f[3], r = f[2], a = f[5], o = f[6], u = f[7], l = [(t = s, n = parseInt(t, 10), n <= 49 ? 2e3 + n : n <= 999 ? 1900 + n : n), eY.indexOf(i), parseInt(r, 10), parseInt(a, 10), parseInt(o, 10),], u && l.push(parseInt(u, 10)), l), !function e(t, n, s) { if (t) { var i; if (eF.indexOf(t) !== new Date(n[0], n[1], n[2]).getDay()) return c(s).weekdayMismatch = !0, s._isValid = !1, !1 } return !0 }(f[1], d, e)) return; e._a = d, e._tzm = function e(t, n, s) { if (t) return tl[t]; if (n) return 0; var i = parseInt(s, 10), r = i % 100; return 60 * ((i - r) / 100) + r }(f[8], f[9], f[10]), e._d = eC.apply(null, e._a), e._d.setUTCMinutes(e._d.getUTCMinutes() - e._tzm), c(e).rfc2822 = !0 } else e._isValid = !1 } function tc(e, t, n) { return null != e ? e : null != t ? t : n } function tf(t) { var n, s, i, r, a, o, u, l, h, d, f, $, m, y, g, _, v, w = []; if (!t._d) { for (g = (f = t, $ = new Date(e.now()), f._useUTC ? [$.getUTCFullYear(), $.getUTCMonth(), $.getUTCDate(),] : [$.getFullYear(), $.getMonth(), $.getDate()]), t._w && null == t._a[2] && null == t._a[1] && (n = t, null != (s = n._w).GG || null != s.W || null != s.E ? (o = 1, u = 4, i = tc(s.GG, n._a[0], eU(ty(), 1, 4).year), r = tc(s.W, 1), ((a = tc(s.E, 1)) < 1 || a > 7) && (h = !0)) : (o = n._locale._week.dow, u = n._locale._week.doy, d = eU(ty(), o, u), i = tc(s.gg, n._a[0], d.year), r = tc(s.w, d.week), null != s.d ? ((a = s.d) < 0 || a > 6) && (h = !0) : null != s.e ? (a = s.e + o, (s.e < 0 || s.e > 6) && (h = !0)) : a = o), r < 1 || r > e4(i, o, u) ? c(n)._overflowWeeks = !0 : null != h ? c(n)._overflowWeekday = !0 : (l = e2(i, r, a, o, u), n._a[0] = l.year, n._dayOfYear = l.dayOfYear)), null != t._dayOfYear && (v = tc(t._a[0], g[0]), (t._dayOfYear > eW(v) || 0 === t._dayOfYear) && (c(t)._overflowDayOfYear = !0), y = eC(v, 0, t._dayOfYear), t._a[1] = y.getUTCMonth(), t._a[2] = y.getUTCDate()), m = 0; m < 3 && null == t._a[m]; ++m)t._a[m] = w[m] = g[m]; for (; m < 7; m++)t._a[m] = w[m] = null == t._a[m] ? 2 === m ? 1 : 0 : t._a[m]; 24 === t._a[3] && 0 === t._a[4] && 0 === t._a[5] && 0 === t._a[6] && (t._nextDay = !0, t._a[3] = 0), t._d = (t._useUTC ? eC : eR).apply(null, w), _ = t._useUTC ? t._d.getUTCDay() : t._d.getDay(), null != t._tzm && t._d.setUTCMinutes(t._d.getUTCMinutes() - t._tzm), t._nextDay && (t._a[3] = 24), t._w && void 0 !== t._w.d && t._w.d !== _ && (c(t).weekdayMismatch = !0) } } function t$(t) { if (t._f === e.ISO_8601) { th(t); return } if (t._f === e.RFC_2822) { td(t); return } t._a = [], c(t).empty = !0; var n, s, i, r, a, o, u, l, h, d, f, $ = "" + t._i, m = $.length, y = 0; for (a = 0, f = (u = H(t._f, t._locale).match(x) || []).length; a < f; a++)l = u[a], (o = ($.match(ey(l, t)) || [])[0]) && ((h = $.substr(0, $.indexOf(o))).length > 0 && c(t).unusedInput.push(h), $ = $.slice($.indexOf(o) + o.length), y += o.length), R[l] ? (o ? c(t).empty = !1 : c(t).unusedTokens.push(l), ep(l, o, t)) : t._strict && !o && c(t).unusedTokens.push(l); c(t).charsLeftOver = m - y, $.length > 0 && c(t).unusedInput.push($), t._a[3] <= 12 && !0 === c(t).bigHour && t._a[3] > 0 && (c(t).bigHour = void 0), c(t).parsedDateParts = t._a.slice(0), c(t).meridiem = t._meridiem, t._a[3] = (n = t._locale, s = t._a[3], i = t._meridiem, null == i ? s : null != n.meridiemHour ? n.meridiemHour(s, i) : (null != n.isPM && ((r = n.isPM(i)) && s < 12 && (s += 12), r || 12 !== s || (s = 0)), s)), null !== (d = c(t).era) && (t._a[0] = t._locale.erasConvertYear(d, t._a[0])), tf(t), tt(t) } function tm(t) { var i, r, d = t._i, m = t._f; return (t._locale = t._locale || te(t._l), null === d || void 0 === m && "" === d) ? $({ nullInput: !0 }) : ("string" == typeof d && (t._i = d = t._locale.preparse(d)), p(d)) ? new w(tt(d)) : (u(d) ? t._d = d : n(m) ? function e(t) { var n, s, i, r, a, o, u = !1, l = t._f.length; if (0 === l) { c(t).invalidFormat = !0, t._d = new Date(NaN); return } for (r = 0; r < l; r++)a = 0, o = !1, n = v({}, t), null != t._useUTC && (n._useUTC = t._useUTC), n._f = t._f[r], t$(n), f(n) && (o = !0), a += c(n).charsLeftOver, a += 10 * c(n).unusedTokens.length, c(n).score = a, u ? a < i && (i = a, s = n) : (null == i || a < i || o) && (i = a, s = n, o && (u = !0)); h(t, s || n) }(t) : m ? t$(t) : (i = t, r = i._i, a(r) ? i._d = new Date(e.now()) : u(r) ? i._d = new Date(r.valueOf()) : "string" == typeof r ? function t(n) { var s = to.exec(n._i); if (null !== s) { n._d = new Date(+s[1]); return } if (th(n), !1 === n._isValid) delete n._isValid, td(n), !1 === n._isValid && (delete n._isValid, n._strict ? n._isValid = !1 : e.createFromInputFallback(n)) }(i) : n(r) ? (i._a = l(r.slice(0), function (e) { return parseInt(e, 10) }), tf(i)) : s(r) ? function e(t) { if (!t._d) { var n = G(t._i), s = void 0 === n.day ? n.date : n.day; t._a = l([n.year, n.month, s, n.hour, n.minute, n.second, n.millisecond], function (e) { return e && parseInt(e, 10) }), tf(t) } }(i) : o(r) ? i._d = new Date(r) : e.createFromInputFallback(i)), f(t) || (t._d = null), t) } function t8(e, t, i, a, o) { var u, l, h = {}; return (!0 === t || !1 === t) && (a = t, t = void 0), (!0 === i || !1 === i) && (a = i, i = void 0), (s(e) && r(e) || n(e) && 0 === e.length) && (e = void 0), h._isAMomentObject = !0, h._useUTC = h._isUTC = o, h._l = i, h._i = e, h._f = t, h._strict = a, u = h, (l = new w(tt(tm(u))))._nextDay && (l.add(1, "d"), l._nextDay = void 0), l } function ty(e, t, n, s) { return t8(e, t, n, s, !1) } e.createFromInputFallback = S("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.", function (e) { e._d = new Date(e._i + (e._useUTC ? " UTC" : "")) }), e.ISO_8601 = function () { }, e.RFC_2822 = function () { }; var tg = S("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/", function () { var e = ty.apply(null, arguments); return this.isValid() && e.isValid() ? e < this ? this : e : $() }), t_ = S("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/", function () { var e = ty.apply(null, arguments); return this.isValid() && e.isValid() ? e > this ? this : e : $() }); function tv(e, t) { var s, i; if (1 === t.length && n(t[0]) && (t = t[0]), !t.length) return ty(); for (i = 1, s = t[0]; i < t.length; ++i)(!t[i].isValid() || t[i][e](s)) && (s = t[i]); return s } var tw = function () { return Date.now ? Date.now() : +new Date }, tp = ["year", "quarter", "month", "week", "day", "hour", "minute", "second", "millisecond",]; function tk(e) { var t = G(e), n = t.year || 0, s = t.quarter || 0, r = t.month || 0, a = t.week || t.isoWeek || 0, o = t.day || 0, u = t.hour || 0, l = t.minute || 0, h = t.second || 0, d = t.millisecond || 0; this._isValid = function e(t) { var n, s, r = !1, a = tp.length; for (n in t) if (i(t, n) && !(-1 !== ej.call(tp, n) && (null == t[n] || !isNaN(t[n])))) return !1; for (s = 0; s < a; ++s)if (t[tp[s]]) { if (r) return !1; parseFloat(t[tp[s]]) !== Z(t[tp[s]]) && (r = !0) } return !0 }(t), this._milliseconds = +d + 1e3 * h + 6e4 * l + 36e5 * u, this._days = +o + 7 * a, this._months = +r + 3 * s + 12 * n, this._data = {}, this._locale = te(), this._bubble() } function tS(e) { return e instanceof tk } function tY(e) { return e < 0 ? -1 * Math.round(-1 * e) : Math.round(e) } function tD(e, t) { C(e, 0, 0, function () { var e = this.utcOffset(), n = "+"; return e < 0 && (e = -e, n = "-"), n + T(~~(e / 60), 2) + t + T(~~e % 60, 2) }) } tD("Z", ":"), tD("ZZ", ""), e8("Z", e$), e8("ZZ", e$), ev(["Z", "ZZ"], function (e, t, n) { n._useUTC = !0, n._tzm = tO(e$, e) }); var tM = /([\+\-]|\d\d)/gi; function tO(e, t) { var n, s, i, r = (t || "").match(e); return null === r ? null : 0 === (i = +(60 * (s = ((n = r[r.length - 1] || []) + "").match(tM) || ["-", 0, 0])[1]) + Z(s[2])) ? 0 : "+" === s[0] ? i : -i } function tb(t, n) { var s, i; return n._isUTC ? (s = n.clone(), i = (p(t) || u(t) ? t.valueOf() : ty(t).valueOf()) - s.valueOf(), s._d.setTime(s._d.valueOf() + i), e.updateOffset(s, !1), s) : ty(t).local() } function tT(e) { return -Math.round(e._d.getTimezoneOffset()) } function t0() { return !!this.isValid() && this._isUTC && 0 === this._offset } e.updateOffset = function () { }; var tx = /^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/, tW = /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; function tP(e, t) { var n, s, r, a, u, l, h = e, d = null; return tS(e) ? h = { ms: e._milliseconds, d: e._days, M: e._months } : o(e) || !isNaN(+e) ? (h = {}, t ? h[t] = +e : h.milliseconds = +e) : (d = tx.exec(e)) ? (n = "-" === d[1] ? -1 : 1, h = { y: 0, d: Z(d[2]) * n, h: Z(d[3]) * n, m: Z(d[4]) * n, s: Z(d[5]) * n, ms: Z(tY(1e3 * d[6])) * n }) : (d = tW.exec(e)) ? (n = "-" === d[1] ? -1 : 1, h = { y: tR(d[2], n), M: tR(d[3], n), w: tR(d[4], n), d: tR(d[5], n), h: tR(d[6], n), m: tR(d[7], n), s: tR(d[8], n) }) : null == h ? h = {} : "object" == typeof h && ("from" in h || "to" in h) && (r = (a = ty(h.from), u = ty(h.to), a.isValid() && u.isValid() ? (u = tb(u, a), a.isBefore(u) ? l = tC(a, u) : ((l = tC(u, a)).milliseconds = -l.milliseconds, l.months = -l.months), l) : { milliseconds: 0, months: 0 }), (h = {}).ms = r.milliseconds, h.M = r.months), s = new tk(h), tS(e) && i(e, "_locale") && (s._locale = e._locale), tS(e) && i(e, "_isValid") && (s._isValid = e._isValid), s } function tR(e, t) { var n = e && parseFloat(e.replace(",", ".")); return (isNaN(n) ? 0 : n) * t } function tC(e, t) { var n = {}; return n.months = t.month() - e.month() + (t.year() - e.year()) * 12, e.clone().add(n.months, "M").isAfter(t) && --n.months, n.milliseconds = +t - +e.clone().add(n.months, "M"), n } function tN(e, t) { return function (n, s) { var i, r; return null === s || isNaN(+s) || (D(t, "moment()." + t + "(period, number) is deprecated. Please use moment()." + t + "(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."), r = n, n = s, s = r), i = tP(n, s), t2(this, i, e), this } } function t2(t, n, s, i) { var r = n._milliseconds, a = tY(n._days), o = tY(n._months); t.isValid() && (i = null == i || i, o && eT(t, q(t, "Month") + o * s), a && B(t, "Date", q(t, "Date") + a * s), r && t._d.setTime(t._d.valueOf() + r * s), i && e.updateOffset(t, a || o)) } tP.fn = tk.prototype, tP.invalid = function e() { return tP(NaN) }; var tU = tN(1, "add"), t4 = tN(-1, "subtract"); function tH(e) { return "string" == typeof e || e instanceof String } function tL(e, t) { if (e.date() < t.date()) return -tL(t, e); var n, s, i = (t.year() - e.year()) * 12 + (t.month() - e.month()), r = e.clone().add(i, "months"); return s = t - r < 0 ? (t - r) / (r - (n = e.clone().add(i - 1, "months"))) : (t - r) / ((n = e.clone().add(i + 1, "months")) - r), -(i + s) || 0 } function tF(e) { var t; return void 0 === e ? this._locale._abbr : (null != (t = te(e)) && (this._locale = t), this) } e.defaultFormat = "YYYY-MM-DDTHH:mm:ssZ", e.defaultFormatUtc = "YYYY-MM-DDTHH:mm:ss[Z]"; var tV = S("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.", function (e) { return void 0 === e ? this.localeData() : this.locale(e) }); function tG() { return this._locale } var t1 = 36e5, tA = 3506328 * t1; function tI(e, t) { return (e % t + t) % t } function tE(e, t, n) { return e < 100 && e >= 0 ? new Date(e + 400, t, n) - tA : new Date(e, t, n).valueOf() } function t3(e, t, n) { return e < 100 && e >= 0 ? Date.UTC(e + 400, t, n) - tA : Date.UTC(e, t, n) } function t5(e, t) { return t.erasAbbrRegex(e) } function t7() { var e, t, n = [], s = [], i = [], r = [], a = this.eras(); for (e = 0, t = a.length; e < t; ++e)s.push(eg(a[e].name)), n.push(eg(a[e].abbr)), i.push(eg(a[e].narrow)), r.push(eg(a[e].name)), r.push(eg(a[e].abbr)), r.push(eg(a[e].narrow)); this._erasRegex = RegExp("^(" + r.join("|") + ")", "i"), this._erasNameRegex = RegExp("^(" + s.join("|") + ")", "i"), this._erasAbbrRegex = RegExp("^(" + n.join("|") + ")", "i"), this._erasNarrowRegex = RegExp("^(" + i.join("|") + ")", "i") } function tj(e, t) { C(0, [e, e.length], 0, t) } C("N", 0, 0, "eraAbbr"), C("NN", 0, 0, "eraAbbr"), C("NNN", 0, 0, "eraAbbr"), C("NNNN", 0, 0, "eraName"), C("NNNNN", 0, 0, "eraNarrow"), C("y", ["y", 1], "yo", "eraYear"), C("y", ["yy", 2], 0, "eraYear"), C("y", ["yyy", 3], 0, "eraYear"), C("y", ["yyyy", 4], 0, "eraYear"), e8("N", t5), e8("NN", t5), e8("NNN", t5), e8("NNNN", function e(t, n) { return n.erasNameRegex(t) }), e8("NNNNN", function e(t, n) { return n.erasNarrowRegex(t) }), ev(["N", "NN", "NNN", "NNNN", "NNNNN"], function (e, t, n, s) { var i = n._locale.erasParse(e, s, n._strict); i ? c(n).era = i : c(n).invalidEra = e }), e8("y", ed), e8("yy", ed), e8("yyy", ed), e8("yyyy", ed), e8("yo", function e(t, n) { return n._eraYearOrdinalRegex || ed }), ev(["y", "yy", "yyy", "yyyy"], 0), ev(["yo"], function (e, t, n, s) { var i; n._locale._eraYearOrdinalRegex && (i = e.match(n._locale._eraYearOrdinalRegex)), n._locale.eraYearOrdinalParse ? t[0] = n._locale.eraYearOrdinalParse(e, i) : t[0] = parseInt(e, 10) }), C(0, ["gg", 2], 0, function () { return this.weekYear() % 100 }), C(0, ["GG", 2], 0, function () { return this.isoWeekYear() % 100 }), tj("gggg", "weekYear"), tj("ggggg", "weekYear"), tj("GGGG", "isoWeekYear"), tj("GGGGG", "isoWeekYear"), F("weekYear", "gg"), F("isoWeekYear", "GG"); A.weekYear = 1; function tZ(e, t, n, s, i) { var r; return null == e ? eU(this, s, i).year : (r = e4(e, s, i), t > r && (t = r), tz.call(this, e, t, n, s, i)) } function tz(e, t, n, s, i) { var r = e2(e, t, n, s, i), a = eC(r.year, 0, r.dayOfYear); return this.year(a.getUTCFullYear()), this.month(a.getUTCMonth()), this.date(a.getUTCDate()), this } A.isoWeekYear = 1, e8("G", ec), e8("g", ec), e8("GG", er, et), e8("gg", er, et), e8("GGGG", el, es), e8("gggg", el, es), e8("GGGGG", eh, ei), e8("ggggg", eh, ei), ew(["gggg", "ggggg", "GGGG", "GGGGG"], function (e, t, n, s) { t[s.substr(0, 2)] = Z(e) }), ew(["gg", "GG"], function (t, n, s, i) { n[i] = e.parseTwoDigitYear(t) }), C("Q", 0, "Qo", "quarter"), F("quarter", "Q"); A.quarter = 7, e8("Q", ee), ev("Q", function (e, t) { t[1] = (Z(e) - 1) * 3 }), C("D", ["DD", 2], "Do", "date"), F("date", "D"); A.date = 9, e8("D", er), e8("DD", er, et), e8("Do", function (e, t) { return e ? t._dayOfMonthOrdinalParse || t._ordinalParse : t._dayOfMonthOrdinalParseLenient }), ev(["D", "DD"], 2), ev("Do", function (e, t) { t[2] = Z(e.match(er)[0]) }); var t6 = z("Date", !0); C("DDD", ["DDDD", 3], "DDDo", "dayOfYear"), F("dayOfYear", "DDD"); A.dayOfYear = 4, e8("DDD", eu), e8("DDDD", en), ev(["DDD", "DDDD"], function (e, t, n) { n._dayOfYear = Z(e) }), C("m", ["mm", 2], 0, "minute"), F("minute", "m"); A.minute = 14, e8("m", er), e8("mm", er, et), ev(["m", "mm"], 4); var t9 = z("Minutes", !1); C("s", ["ss", 2], 0, "second"), F("second", "s"); A.second = 15, e8("s", er), e8("ss", er, et), ev(["s", "ss"], 5); var tq = z("Seconds", !1); C("S", 0, 0, function () { return ~~(this.millisecond() / 100) }), C(0, ["SS", 2], 0, function () { return ~~(this.millisecond() / 10) }), C(0, ["SSS", 3], 0, "millisecond"), C(0, ["SSSS", 4], 0, function () { return 10 * this.millisecond() }), C(0, ["SSSSS", 5], 0, function () { return 100 * this.millisecond() }), C(0, ["SSSSSS", 6], 0, function () { return 1e3 * this.millisecond() }), C(0, ["SSSSSSS", 7], 0, function () { return 1e4 * this.millisecond() }), C(0, ["SSSSSSSS", 8], 0, function () { return 1e5 * this.millisecond() }), C(0, ["SSSSSSSSS", 9], 0, function () { return 1e6 * this.millisecond() }), F("millisecond", "ms"); for (A.millisecond = 16, e8("S", eu, ee), e8("SS", eu, et), e8("SSS", eu, en), m = "SSSS"; m.length <= 9; m += "S")e8(m, ed); function tB(e, t) { t[6] = Z(("0." + e) * 1e3) } for (m = "S"; m.length <= 9; m += "S")ev(m, tB); y = z("Milliseconds", !1), C("z", 0, 0, "zoneAbbr"), C("zz", 0, 0, "zoneName"); var tJ = w.prototype; function tQ(e) { return e } tJ.add = tU, tJ.calendar = function t(a, l) { if (1 === arguments.length) { if (arguments[0]) { var h, d, c, f; if (h = arguments[0], p(h) || u(h) || tH(h) || o(h) || (d = h, c = n(d), f = !1, c && (f = 0 === d.filter(function (e) { return !o(e) && tH(d) }).length), c && f) || function e(t) { var n, a, o = s(t) && !r(t), u = !1, l = ["years", "year", "y", "months", "month", "M", "days", "day", "d", "dates", "date", "D", "hours", "hour", "h", "minutes", "minute", "m", "seconds", "second", "s", "milliseconds", "millisecond", "ms",], h = l.length; for (n = 0; n < h; n += 1)a = l[n], u = u || i(t, a); return o && u }(h) || null == h) a = arguments[0], l = void 0; else (function e(t) { var n, a, o = s(t) && !r(t), u = !1, l = ["sameDay", "nextDay", "lastDay", "nextWeek", "lastWeek", "sameElse",]; for (n = 0; n < l.length; n += 1)a = l[n], u = u || i(t, a); return o && u })(arguments[0]) && (l = arguments[0], a = void 0) } else a = void 0, l = void 0 } var $ = a || ty(), m = tb($, this).startOf("day"), y = e.calendarFormat(this, m) || "sameElse", g = l && (M(l[y]) ? l[y].call(this, $) : l[y]); return this.format(g || this.localeData().calendar(y, this, ty($))) }, tJ.clone = function e() { return new w(this) }, tJ.diff = function e(t, n, s) { var i, r, a; if (!this.isValid() || !(i = tb(t, this)).isValid()) return NaN; switch (r = (i.utcOffset() - this.utcOffset()) * 6e4, n = V(n)) { case "year": a = tL(this, i) / 12; break; case "month": a = tL(this, i); break; case "quarter": a = tL(this, i) / 3; break; case "second": a = (this - i) / 1e3; break; case "minute": a = (this - i) / 6e4; break; case "hour": a = (this - i) / 36e5; break; case "day": a = (this - i - r) / 864e5; break; case "week": a = (this - i - r) / 6048e5; break; default: a = this - i }return s ? a : j(a) }, tJ.endOf = function t(n) { var s, i; if (void 0 === (n = V(n)) || "millisecond" === n || !this.isValid()) return this; switch (i = this._isUTC ? t3 : tE, n) { case "year": s = i(this.year() + 1, 0, 1) - 1; break; case "quarter": s = i(this.year(), this.month() - this.month() % 3 + 3, 1) - 1; break; case "month": s = i(this.year(), this.month() + 1, 1) - 1; break; case "week": s = i(this.year(), this.month(), this.date() - this.weekday() + 7) - 1; break; case "isoWeek": s = i(this.year(), this.month(), this.date() - (this.isoWeekday() - 1) + 7) - 1; break; case "day": case "date": s = i(this.year(), this.month(), this.date() + 1) - 1; break; case "hour": s = this._d.valueOf(), s += t1 - tI(s + (this._isUTC ? 0 : 6e4 * this.utcOffset()), t1) - 1; break; case "minute": s = this._d.valueOf(), s += 6e4 - tI(s, 6e4) - 1; break; case "second": s = this._d.valueOf(), s += 1e3 - tI(s, 1e3) - 1 }return this._d.setTime(s), e.updateOffset(this, !0), this }, tJ.format = function t(n) { n || (n = this.isUtc() ? e.defaultFormatUtc : e.defaultFormat); var s = U(this, n); return this.localeData().postformat(s) }, tJ.from = function e(t, n) { return this.isValid() && (p(t) && t.isValid() || ty(t).isValid()) ? tP({ to: this, from: t }).locale(this.locale()).humanize(!n) : this.localeData().invalidDate() }, tJ.fromNow = function e(t) { return this.from(ty(), t) }, tJ.to = function e(t, n) { return this.isValid() && (p(t) && t.isValid() || ty(t).isValid()) ? tP({ from: this, to: t }).locale(this.locale()).humanize(!n) : this.localeData().invalidDate() }, tJ.toNow = function e(t) { return this.to(ty(), t) }, tJ.get = function e(t) { return M(this[t = V(t)]) ? this[t]() : this }, tJ.invalidAt = function e() { return c(this).overflow }, tJ.isAfter = function e(t, n) { var s = p(t) ? t : ty(t); return !!(this.isValid() && s.isValid()) && ("millisecond" === (n = V(n) || "millisecond") ? this.valueOf() > s.valueOf() : s.valueOf() < this.clone().startOf(n).valueOf()) }, tJ.isBefore = function e(t, n) { var s = p(t) ? t : ty(t); return !!(this.isValid() && s.isValid()) && ("millisecond" === (n = V(n) || "millisecond") ? this.valueOf() < s.valueOf() : this.clone().endOf(n).valueOf() < s.valueOf()) }, tJ.isBetween = function e(t, n, s, i) { var r = p(t) ? t : ty(t), a = p(n) ? n : ty(n); return !!(this.isValid() && r.isValid() && a.isValid()) && ("(" === (i = i || "()")[0] ? this.isAfter(r, s) : !this.isBefore(r, s)) && (")" === i[1] ? this.isBefore(a, s) : !this.isAfter(a, s)) }, tJ.isSame = function e(t, n) { var s, i = p(t) ? t : ty(t); return !!(this.isValid() && i.isValid()) && ("millisecond" === (n = V(n) || "millisecond") ? this.valueOf() === i.valueOf() : (s = i.valueOf(), this.clone().startOf(n).valueOf() <= s && s <= this.clone().endOf(n).valueOf())) }, tJ.isSameOrAfter = function e(t, n) { return this.isSame(t, n) || this.isAfter(t, n) }, tJ.isSameOrBefore = function e(t, n) { return this.isSame(t, n) || this.isBefore(t, n) }, tJ.isValid = function e() { return f(this) }, tJ.lang = tV, tJ.locale = tF, tJ.localeData = tG, tJ.max = t_, tJ.min = tg, tJ.parsingFlags = function e() { return h({}, c(this)) }, tJ.set = function e(t, n) { if ("object" == typeof t) { var s, r = function e(t) { var n, s = []; for (n in t) i(t, n) && s.push({ unit: n, priority: A[n] }); return s.sort(function (e, t) { return e.priority - t.priority }), s }(t = G(t)), a = r.length; for (s = 0; s < a; s++)this[r[s].unit](t[r[s].unit]) } else if (M(this[t = V(t)])) return this[t](n); return this }, tJ.startOf = function t(n) { var s, i; if (void 0 === (n = V(n)) || "millisecond" === n || !this.isValid()) return this; switch (i = this._isUTC ? t3 : tE, n) { case "year": s = i(this.year(), 0, 1); break; case "quarter": s = i(this.year(), this.month() - this.month() % 3, 1); break; case "month": s = i(this.year(), this.month(), 1); break; case "week": s = i(this.year(), this.month(), this.date() - this.weekday()); break; case "isoWeek": s = i(this.year(), this.month(), this.date() - (this.isoWeekday() - 1)); break; case "day": case "date": s = i(this.year(), this.month(), this.date()); break; case "hour": s = this._d.valueOf(), s -= tI(s + (this._isUTC ? 0 : 6e4 * this.utcOffset()), t1); break; case "minute": s = this._d.valueOf(), s -= tI(s, 6e4); break; case "second": s = this._d.valueOf(), s -= tI(s, 1e3) }return this._d.setTime(s), e.updateOffset(this, !0), this }, tJ.subtract = t4, tJ.toArray = function e() { return [this.year(), this.month(), this.date(), this.hour(), this.minute(), this.second(), this.millisecond(),] }, tJ.toObject = function e() { return { years: this.year(), months: this.month(), date: this.date(), hours: this.hours(), minutes: this.minutes(), seconds: this.seconds(), milliseconds: this.milliseconds() } }, tJ.toDate = function e() { return new Date(this.valueOf()) }, tJ.toISOString = function e(t) { if (!this.isValid()) return null; var n = !0 !== t, s = n ? this.clone().utc() : this; return 0 > s.year() || s.year() > 9999 ? U(s, n ? "YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]" : "YYYYYY-MM-DD[T]HH:mm:ss.SSSZ") : M(Date.prototype.toISOString) ? n ? this.toDate().toISOString() : new Date(this.valueOf() + 6e4 * this.utcOffset()).toISOString().replace("Z", U(s, "Z")) : U(s, n ? "YYYY-MM-DD[T]HH:mm:ss.SSS[Z]" : "YYYY-MM-DD[T]HH:mm:ss.SSSZ") }, tJ.inspect = function e() { if (!this.isValid()) return "moment.invalid(/* " + this._i + " */)"; var t, n, s, i, r = "moment", a = ""; return this.isLocal() || (r = 0 === this.utcOffset() ? "moment.utc" : "moment.parseZone", a = "Z"), t = "[" + r + '("]', n = 0 <= this.year() && 9999 >= this.year() ? "YYYY" : "YYYYYY", s = "-MM-DD[T]HH:mm:ss.SSS", i = a + '[")]', this.format(t + n + s + i) }, "undefined" != typeof Symbol && null != Symbol.for && (tJ[Symbol.for("nodejs.util.inspect.custom")] = function () { return "Moment<" + this.format() + ">" }), tJ.toJSON = function e() { return this.isValid() ? this.toISOString() : null }, tJ.toString = function e() { return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ") }, tJ.unix = function e() { return Math.floor(this.valueOf() / 1e3) }, tJ.valueOf = function e() { return this._d.valueOf() - 6e4 * (this._offset || 0) }, tJ.creationData = function e() { return { input: this._i, format: this._f, locale: this._locale, isUTC: this._isUTC, strict: this._strict } }, tJ.eraName = function e() { var t, n, s, i = this.localeData().eras(); for (t = 0, n = i.length; t < n; ++t)if (s = this.clone().startOf("day").valueOf(), i[t].since <= s && s <= i[t].until || i[t].until <= s && s <= i[t].since) return i[t].name; return "" }, tJ.eraNarrow = function e() { var t, n, s, i = this.localeData().eras(); for (t = 0, n = i.length; t < n; ++t)if (s = this.clone().startOf("day").valueOf(), i[t].since <= s && s <= i[t].until || i[t].until <= s && s <= i[t].since) return i[t].narrow; return "" }, tJ.eraAbbr = function e() { var t, n, s, i = this.localeData().eras(); for (t = 0, n = i.length; t < n; ++t)if (s = this.clone().startOf("day").valueOf(), i[t].since <= s && s <= i[t].until || i[t].until <= s && s <= i[t].since) return i[t].abbr; return "" }, tJ.eraYear = function t() { var n, s, i, r, a = this.localeData().eras(); for (n = 0, s = a.length; n < s; ++n)if (i = a[n].since <= a[n].until ? 1 : -1, r = this.clone().startOf("day").valueOf(), a[n].since <= r && r <= a[n].until || a[n].until <= r && r <= a[n].since) return (this.year() - e(a[n].since).year()) * i + a[n].offset; return this.year() }, tJ.year = eP, tJ.isLeapYear = function e() { return E(this.year()) }, tJ.weekYear = function e(t) { return tZ.call(this, t, this.week(), this.weekday(), this.localeData()._week.dow, this.localeData()._week.doy) }, tJ.isoWeekYear = function e(t) { return tZ.call(this, t, this.isoWeek(), this.isoWeekday(), 1, 4) }, tJ.quarter = tJ.quarters = function e(t) { return null == t ? Math.ceil((this.month() + 1) / 3) : this.month((t - 1) * 3 + this.month() % 3) }, tJ.month = e0, tJ.daysInMonth = function e() { return ek(this.year(), this.month()) }, tJ.week = tJ.weeks = function e(t) { var n = this.localeData().week(this); return null == t ? n : this.add((t - n) * 7, "d") }, tJ.isoWeek = tJ.isoWeeks = function e(t) { var n = eU(this, 1, 4).week; return null == t ? n : this.add((t - n) * 7, "d") }, tJ.weeksInYear = function e() { var t = this.localeData()._week; return e4(this.year(), t.dow, t.doy) }, tJ.weeksInWeekYear = function e() { var t = this.localeData()._week; return e4(this.weekYear(), t.dow, t.doy) }, tJ.isoWeeksInYear = function e() { return e4(this.year(), 1, 4) }, tJ.isoWeeksInISOWeekYear = function e() { return e4(this.isoWeekYear(), 1, 4) }, tJ.date = t6, tJ.day = tJ.days = function e(t) { if (!this.isValid()) return null != t ? this : NaN; var n, s, i = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); return null == t ? i : (t = (n = t, s = this.localeData(), "string" != typeof n ? n : isNaN(n) ? "number" == typeof (n = s.weekdaysParse(n)) ? n : null : parseInt(n, 10)), this.add(t - i, "d")) }, tJ.weekday = function e(t) { if (!this.isValid()) return null != t ? this : NaN; var n = (this.day() + 7 - this.localeData()._week.dow) % 7; return null == t ? n : this.add(t - n, "d") }, tJ.isoWeekday = function e(t) { if (!this.isValid()) return null != t ? this : NaN; if (null == t) return this.day() || 7; var n, s, i = (n = t, s = this.localeData(), "string" == typeof n ? s.weekdaysParse(n) % 7 || 7 : isNaN(n) ? null : n); return this.day(this.day() % 7 ? i : i - 7) }, tJ.dayOfYear = function e(t) { var n = Math.round((this.clone().startOf("day") - this.clone().startOf("year")) / 864e5) + 1; return null == t ? n : this.add(t - n, "d") }, tJ.hour = tJ.hours = ez, tJ.minute = tJ.minutes = t9, tJ.second = tJ.seconds = tq, tJ.millisecond = tJ.milliseconds = y, tJ.utcOffset = function t(n, s, i) { var r, a = this._offset || 0; if (!this.isValid()) return null != n ? this : NaN; if (null == n) return this._isUTC ? a : tT(this); if ("string" == typeof n) { if (null === (n = tO(e$, n))) return this } else 16 > Math.abs(n) && !i && (n *= 60); return !this._isUTC && s && (r = tT(this)), this._offset = n, this._isUTC = !0, null != r && this.add(r, "m"), a === n || (!s || this._changeInProgress ? t2(this, tP(n - a, "m"), 1, !1) : this._changeInProgress || (this._changeInProgress = !0, e.updateOffset(this, !0), this._changeInProgress = null)), this }, tJ.utc = function e(t) { return this.utcOffset(0, t) }, tJ.local = function e(t) { return this._isUTC && (this.utcOffset(0, t), this._isUTC = !1, t && this.subtract(tT(this), "m")), this }, tJ.parseZone = function e() { if (null != this._tzm) this.utcOffset(this._tzm, !1, !0); else if ("string" == typeof this._i) { var t = tO(ef, this._i); null != t ? this.utcOffset(t) : this.utcOffset(0, !0) } return this }, tJ.hasAlignedHourOffset = function e(t) { return !!this.isValid() && (t = t ? ty(t).utcOffset() : 0, (this.utcOffset() - t) % 60 == 0) }, tJ.isDST = function e() { return this.utcOffset() > this.clone().month(0).utcOffset() || this.utcOffset() > this.clone().month(5).utcOffset() }, tJ.isLocal = function e() { return !!this.isValid() && !this._isUTC }, tJ.isUtcOffset = function e() { return !!this.isValid() && this._isUTC }, tJ.isUtc = t0, tJ.isUTC = t0, tJ.zoneAbbr = function e() { return this._isUTC ? "UTC" : "" }, tJ.zoneName = function e() { return this._isUTC ? "Coordinated Universal Time" : "" }, tJ.dates = S("dates accessor is deprecated. Use date instead.", t6), tJ.months = S("months accessor is deprecated. Use month instead", e0), tJ.years = S("years accessor is deprecated. Use year instead", eP), tJ.zone = S("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/", function e(t, n) { return null != t ? ("string" != typeof t && (t = -t), this.utcOffset(t, n), this) : -this.utcOffset() }), tJ.isDSTShifted = S("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information", function e() { if (!a(this._isDSTShifted)) return this._isDSTShifted; var t, n = {}; return v(n, this), (n = tm(n))._a ? (t = n._isUTC ? d(n._a) : ty(n._a), this._isDSTShifted = this.isValid() && function e(t, n, s) { var i, r = Math.min(t.length, n.length), a = Math.abs(t.length - n.length), o = 0; for (i = 0; i < r; i++)(s && t[i] !== n[i] || !s && Z(t[i]) !== Z(n[i])) && o++; return o + a }(n._a, t.toArray()) > 0) : this._isDSTShifted = !1, this._isDSTShifted }); var tX = b.prototype; function tK(e, t, n, s) { var i = te(), r = d().set(s, t); return i[n](r, e) } function ne(e, t, n) { if (o(e) && (t = e, e = void 0), e = e || "", null != t) return tK(e, t, n, "month"); var s, i = []; for (s = 0; s < 12; s++)i[s] = tK(e, s, n, "month"); return i } function nt(e, t, n, s) { "boolean" == typeof e ? (o(t) && (n = t, t = void 0), t = t || "") : (n = t = e, e = !1, o(t) && (n = t, t = void 0), t = t || ""); var i, r = te(), a = e ? r._week.dow : 0, u = []; if (null != n) return tK(t, (n + a) % 7, s, "day"); for (i = 0; i < 7; i++)u[i] = tK(t, (i + a) % 7, s, "day"); return u } tX.calendar = function e(t, n, s) { var i = this._calendar[t] || this._calendar.sameElse; return M(i) ? i.call(n, s) : i }, tX.longDateFormat = function e(t) { var n = this._longDateFormat[t], s = this._longDateFormat[t.toUpperCase()]; return n || !s ? n : (this._longDateFormat[t] = s.match(x).map(function (e) { return "MMMM" === e || "MM" === e || "DD" === e || "dddd" === e ? e.slice(1) : e }).join(""), this._longDateFormat[t]) }, tX.invalidDate = function e() { return this._invalidDate }, tX.ordinal = function e(t) { return this._ordinal.replace("%d", t) }, tX.preparse = tQ, tX.postformat = tQ, tX.relativeTime = function e(t, n, s, i) { var r = this._relativeTime[s]; return M(r) ? r(t, n, s, i) : r.replace(/%d/i, t) }, tX.pastFuture = function e(t, n) { var s = this._relativeTime[t > 0 ? "future" : "past"]; return M(s) ? s(n) : s.replace(/%s/i, n) }, tX.set = function e(t) { var n, s; for (s in t) i(t, s) && (M(n = t[s]) ? this[s] = n : this["_" + s] = n); this._config = t, this._dayOfMonthOrdinalParseLenient = RegExp((this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + "|" + /\d{1,2}/.source) }, tX.eras = function t(n, s) { var i, r, a, o = this._eras || te("en")._eras; for (i = 0, r = o.length; i < r; ++i)switch ("string" == typeof o[i].since && (a = e(o[i].since).startOf("day"), o[i].since = a.valueOf()), typeof o[i].until) { case "undefined": o[i].until = Infinity; break; case "string": a = e(o[i].until).startOf("day").valueOf(), o[i].until = a.valueOf() }return o }, tX.erasParse = function e(t, n, s) { var i, r, a, o, u, l = this.eras(); for (i = 0, t = t.toUpperCase(), r = l.length; i < r; ++i)if (a = l[i].name.toUpperCase(), o = l[i].abbr.toUpperCase(), u = l[i].narrow.toUpperCase(), s) switch (n) { case "N": case "NN": case "NNN": if (o === t) return l[i]; break; case "NNNN": if (a === t) return l[i]; break; case "NNNNN": if (u === t) return l[i] } else if ([a, o, u].indexOf(t) >= 0) return l[i] }, tX.erasConvertYear = function t(n, s) { var i = n.since <= n.until ? 1 : -1; return void 0 === s ? e(n.since).year() : e(n.since).year() + (s - n.offset) * i }, tX.erasAbbrRegex = function e(t) { return i(this, "_erasAbbrRegex") || t7.call(this), t ? this._erasAbbrRegex : this._erasRegex }, tX.erasNameRegex = function e(t) { return i(this, "_erasNameRegex") || t7.call(this), t ? this._erasNameRegex : this._erasRegex }, tX.erasNarrowRegex = function e(t) { return i(this, "_erasNarrowRegex") || t7.call(this), t ? this._erasNarrowRegex : this._erasRegex }, tX.months = function e(t, s) { return t ? n(this._months) ? this._months[t.month()] : this._months[(this._months.isFormat || eD).test(s) ? "format" : "standalone"][t.month()] : n(this._months) ? this._months : this._months.standalone }, tX.monthsShort = function e(t, s) { return t ? n(this._monthsShort) ? this._monthsShort[t.month()] : this._monthsShort[eD.test(s) ? "format" : "standalone"][t.month()] : n(this._monthsShort) ? this._monthsShort : this._monthsShort.standalone }, tX.monthsParse = function e(t, n, s) { var i, r, a; if (this._monthsParseExact) return eb.call(this, t, n, s); for (this._monthsParse || (this._monthsParse = [], this._longMonthsParse = [], this._shortMonthsParse = []), i = 0; i < 12; i++) { if (r = d([2e3, i]), s && !this._longMonthsParse[i] && (this._longMonthsParse[i] = RegExp("^" + this.months(r, "").replace(".", "") + "$", "i"), this._shortMonthsParse[i] = RegExp("^" + this.monthsShort(r, "").replace(".", "") + "$", "i")), s || this._monthsParse[i] || (a = "^" + this.months(r, "") + "|^" + this.monthsShort(r, ""), this._monthsParse[i] = RegExp(a.replace(".", ""), "i")), s && "MMMM" === n && this._longMonthsParse[i].test(t)) return i; if (s && "MMM" === n && this._shortMonthsParse[i].test(t)) return i; if (!s && this._monthsParse[i].test(t)) return i } }, tX.monthsRegex = function e(t) { return this._monthsParseExact ? (i(this, "_monthsRegex") || ex.call(this), t) ? this._monthsStrictRegex : this._monthsRegex : (i(this, "_monthsRegex") || (this._monthsRegex = eO), this._monthsStrictRegex && t ? this._monthsStrictRegex : this._monthsRegex) }, tX.monthsShortRegex = function e(t) { return this._monthsParseExact ? (i(this, "_monthsRegex") || ex.call(this), t) ? this._monthsShortStrictRegex : this._monthsShortRegex : (i(this, "_monthsShortRegex") || (this._monthsShortRegex = eM), this._monthsShortStrictRegex && t ? this._monthsShortStrictRegex : this._monthsShortRegex) }, tX.week = function e(t) { return eU(t, this._week.dow, this._week.doy).week }, tX.firstDayOfYear = function e() { return this._week.doy }, tX.firstDayOfWeek = function e() { return this._week.dow }, tX.weekdays = function e(t, s) { var i = n(this._weekdays) ? this._weekdays : this._weekdays[t && !0 !== t && this._weekdays.isFormat.test(s) ? "format" : "standalone"]; return !0 === t ? eH(i, this._week.dow) : t ? i[t.day()] : i }, tX.weekdaysMin = function e(t) { return !0 === t ? eH(this._weekdaysMin, this._week.dow) : t ? this._weekdaysMin[t.day()] : this._weekdaysMin }, tX.weekdaysShort = function e(t) { return !0 === t ? eH(this._weekdaysShort, this._week.dow) : t ? this._weekdaysShort[t.day()] : this._weekdaysShort }, tX.weekdaysParse = function e(t, n, s) { var i, r, a; if (this._weekdaysParseExact) return eI.call(this, t, n, s); for (this._weekdaysParse || (this._weekdaysParse = [], this._minWeekdaysParse = [], this._shortWeekdaysParse = [], this._fullWeekdaysParse = []), i = 0; i < 7; i++) { if (r = d([2e3, 1]).day(i), s && !this._fullWeekdaysParse[i] && (this._fullWeekdaysParse[i] = RegExp("^" + this.weekdays(r, "").replace(".", "\\.?") + "$", "i"), this._shortWeekdaysParse[i] = RegExp("^" + this.weekdaysShort(r, "").replace(".", "\\.?") + "$", "i"), this._minWeekdaysParse[i] = RegExp("^" + this.weekdaysMin(r, "").replace(".", "\\.?") + "$", "i")), this._weekdaysParse[i] || (a = "^" + this.weekdays(r, "") + "|^" + this.weekdaysShort(r, "") + "|^" + this.weekdaysMin(r, ""), this._weekdaysParse[i] = RegExp(a.replace(".", ""), "i")), s && "dddd" === n && this._fullWeekdaysParse[i].test(t)) return i; if (s && "ddd" === n && this._shortWeekdaysParse[i].test(t)) return i; if (s && "dd" === n && this._minWeekdaysParse[i].test(t)) return i; else if (!s && this._weekdaysParse[i].test(t)) return i } }, tX.weekdaysRegex = function e(t) { return this._weekdaysParseExact ? (i(this, "_weekdaysRegex") || eE.call(this), t) ? this._weekdaysStrictRegex : this._weekdaysRegex : (i(this, "_weekdaysRegex") || (this._weekdaysRegex = eG), this._weekdaysStrictRegex && t ? this._weekdaysStrictRegex : this._weekdaysRegex) }, tX.weekdaysShortRegex = function e(t) { return this._weekdaysParseExact ? (i(this, "_weekdaysRegex") || eE.call(this), t) ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex : (i(this, "_weekdaysShortRegex") || (this._weekdaysShortRegex = e1), this._weekdaysShortStrictRegex && t ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex) }, tX.weekdaysMinRegex = function e(t) { return this._weekdaysParseExact ? (i(this, "_weekdaysRegex") || eE.call(this), t) ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex : (i(this, "_weekdaysMinRegex") || (this._weekdaysMinRegex = eA), this._weekdaysMinStrictRegex && t ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex) }, tX.isPM = function e(t) { return "p" === (t + "").toLowerCase().charAt(0) }, tX.meridiem = function e(t, n, s) { return t > 11 ? s ? "pm" : "PM" : s ? "am" : "AM" }, eX("en", { eras: [{ since: "0001-01-01", until: Infinity, offset: 1, name: "Anno Domini", narrow: "AD", abbr: "AD" }, { since: "0000-12-31", until: -1 / 0, offset: 1, name: "Before Christ", narrow: "BC", abbr: "BC" },], dayOfMonthOrdinalParse: /\d{1,2}(th|st|nd|rd)/, ordinal: function (e) { var t = e % 10, n = 1 === Z(e % 100 / 10) ? "th" : 1 === t ? "st" : 2 === t ? "nd" : 3 === t ? "rd" : "th"; return e + n } }), e.lang = S("moment.lang is deprecated. Use moment.locale instead.", eX), e.langData = S("moment.langData is deprecated. Use moment.localeData instead.", te); var nn = Math.abs; function ns(e, t, n, s) { var i = tP(t, n); return e._milliseconds += s * i._milliseconds, e._days += s * i._days, e._months += s * i._months, e._bubble() } function ni(e) { return e < 0 ? Math.floor(e) : Math.ceil(e) } function nr(e) { return 4800 * e / 146097 } function na(e) { return 146097 * e / 4800 } function no(e) { return function () { return this.as(e) } } var nu = no("ms"), nl = no("s"), nh = no("m"), nd = no("h"), nc = no("d"), nf = no("w"), n$ = no("M"), nm = no("Q"), n8 = no("y"); function ny(e) { return function () { return this.isValid() ? this._data[e] : NaN } } var ng = ny("milliseconds"), n_ = ny("seconds"), nv = ny("minutes"), nw = ny("hours"), np = ny("days"), nk = ny("months"), nS = ny("years"), nY = Math.round, nD = { ss: 44, s: 45, m: 45, h: 22, d: 26, w: null, M: 11 }; function nM(e, t, n, s, i) { return i.relativeTime(t || 1, !!n, e, s) } var nO = Math.abs; function nb(e) { return (e > 0) - (e < 0) || +e } function nT() { if (!this.isValid()) return this.localeData().invalidDate(); var e, t, n, s, i, r, a, o, u = nO(this._milliseconds) / 1e3, l = nO(this._days), h = nO(this._months), d = this.asSeconds(); return d ? (e = j(u / 60), t = j(e / 60), u %= 60, e %= 60, n = j(h / 12), h %= 12, s = u ? u.toFixed(3).replace(/\.?0+$/, "") : "", i = d < 0 ? "-" : "", r = nb(this._months) !== nb(d) ? "-" : "", a = nb(this._days) !== nb(d) ? "-" : "", o = nb(this._milliseconds) !== nb(d) ? "-" : "", i + "P" + (n ? r + n + "Y" : "") + (h ? r + h + "M" : "") + (l ? a + l + "D" : "") + (t || e || u ? "T" : "") + (t ? o + t + "H" : "") + (e ? o + e + "M" : "") + (u ? o + s + "S" : "")) : "P0D" } var n0, nx = tk.prototype; return nx.isValid = function e() { return this._isValid }, nx.abs = function e() { var t = this._data; return this._milliseconds = nn(this._milliseconds), this._days = nn(this._days), this._months = nn(this._months), t.milliseconds = nn(t.milliseconds), t.seconds = nn(t.seconds), t.minutes = nn(t.minutes), t.hours = nn(t.hours), t.months = nn(t.months), t.years = nn(t.years), this }, nx.add = function e(t, n) { return ns(this, t, n, 1) }, nx.subtract = function e(t, n) { return ns(this, t, n, -1) }, nx.as = function e(t) { if (!this.isValid()) return NaN; var n, s, i = this._milliseconds; if ("month" === (t = V(t)) || "quarter" === t || "year" === t) switch (n = this._days + i / 864e5, s = this._months + nr(n), t) { case "month": return s; case "quarter": return s / 3; case "year": return s / 12 } else switch (n = this._days + Math.round(na(this._months)), t) { case "week": return n / 7 + i / 6048e5; case "day": return n + i / 864e5; case "hour": return 24 * n + i / 36e5; case "minute": return 1440 * n + i / 6e4; case "second": return 86400 * n + i / 1e3; case "millisecond": return Math.floor(864e5 * n) + i; default: throw Error("Unknown unit " + t) } }, nx.asMilliseconds = nu, nx.asSeconds = nl, nx.asMinutes = nh, nx.asHours = nd, nx.asDays = nc, nx.asWeeks = nf, nx.asMonths = n$, nx.asQuarters = nm, nx.asYears = n8, nx.valueOf = function e() { return this.isValid() ? this._milliseconds + 864e5 * this._days + this._months % 12 * 2592e6 + 31536e6 * Z(this._months / 12) : NaN }, nx._bubble = function e() { var t, n, s, i, r, a = this._milliseconds, o = this._days, u = this._months, l = this._data; return a >= 0 && o >= 0 && u >= 0 || a <= 0 && o <= 0 && u <= 0 || (a += 864e5 * ni(na(u) + o), o = 0, u = 0), l.milliseconds = a % 1e3, t = j(a / 1e3), l.seconds = t % 60, n = j(t / 60), l.minutes = n % 60, s = j(n / 60), l.hours = s % 24, o += j(s / 24), u += r = j(nr(o)), o -= ni(na(r)), i = j(u / 12), u %= 12, l.days = o, l.months = u, l.years = i, this }, nx.clone = function e() { return tP(this) }, nx.get = function e(t) { return t = V(t), this.isValid() ? this[t + "s"]() : NaN }, nx.milliseconds = ng, nx.seconds = n_, nx.minutes = nv, nx.hours = nw, nx.days = np, nx.weeks = function e() { return j(this.days() / 7) }, nx.months = nk, nx.years = nS, nx.humanize = function e(t, n) { if (!this.isValid()) return this.localeData().invalidDate(); var s, i, r, a, o, u, l, h, d, c, f, $, m, y, g, _ = !1, v = nD; return "object" == typeof t && (n = t, t = !1), "boolean" == typeof t && (_ = t), "object" == typeof n && (v = Object.assign({}, nD, n), null != n.s && null == n.ss && (v.ss = n.s - 1)), s = this.localeData(), i = (r = this, a = !_, o = v, u = s, l = tP(r).abs(), h = nY(l.as("s")), d = nY(l.as("m")), c = nY(l.as("h")), f = nY(l.as("d")), $ = nY(l.as("M")), m = nY(l.as("w")), y = nY(l.as("y")), g = h <= o.ss && ["s", h] || h < o.s && ["ss", h] || d <= 1 && ["m"] || d < o.m && ["mm", d] || c <= 1 && ["h"] || c < o.h && ["hh", c] || f <= 1 && ["d"] || f < o.d && ["dd", f], null != o.w && (g = g || m <= 1 && ["w"] || m < o.w && ["ww", m]), (g = g || $ <= 1 && ["M"] || $ < o.M && ["MM", $] || y <= 1 && ["y"] || ["yy", y])[2] = a, g[3] = +r > 0, g[4] = u, nM.apply(null, g)), _ && (i = s.pastFuture(+this, i)), s.postformat(i) }, nx.toISOString = nT, nx.toString = nT, nx.toJSON = nT, nx.locale = tF, nx.localeData = tG, nx.toIsoString = S("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)", nT), nx.lang = tV, C("X", 0, 0, "unix"), C("x", 0, 0, "valueOf"), e8("x", ec), e8("X", /[+-]?\d+(\.\d{1,3})?/), ev("X", function (e, t, n) { n._d = new Date(1e3 * parseFloat(e)) }), ev("x", function (e, t, n) { n._d = new Date(Z(e)) }),//! moment.js
        e.version = "2.29.4", J = n0 = ty, e.fn = tJ, e.min = function e() { var t = [].slice.call(arguments, 0); return tv("isBefore", t) }, e.max = function e() { var t = [].slice.call(arguments, 0); return tv("isAfter", t) }, e.now = tw, e.utc = d, e.unix = function e(t) { return ty(1e3 * t) }, e.months = function e(t, n) { return ne(t, n, "months") }, e.isDate = u, e.locale = eX, e.invalid = $, e.duration = tP, e.isMoment = p, e.weekdays = function e(t, n, s) { return nt(t, n, s, "weekdays") }, e.parseZone = function e() { return ty.apply(null, arguments).parseZone() }, e.localeData = te, e.isDuration = tS, e.monthsShort = function e(t, n) { return ne(t, n, "monthsShort") }, e.weekdaysMin = function e(t, n, s) { return nt(t, n, s, "weekdaysMin") }, e.defineLocale = eK, e.updateLocale = function e(t, n) { if (null != n) { var s, i, r = e6; null != e9[t] && null != e9[t].parentLocale ? e9[t].set(O(e9[t]._config, n)) : (null != (i = eQ(t)) && (r = i._config), n = O(r, n), null == i && (n.abbr = t), (s = new b(n)).parentLocale = e9[t], e9[t] = s), eX(t) } else null != e9[t] && (null != e9[t].parentLocale ? (e9[t] = e9[t].parentLocale, t === eX() && eX(t)) : null != e9[t] && delete e9[t]); return e9[t] }, e.locales = function e() { return X(e9) }, e.weekdaysShort = function e(t, n, s) { return nt(t, n, s, "weekdaysShort") }, e.normalizeUnits = V, e.relativeTimeRounding = function e(t) { return void 0 === t ? nY : "function" == typeof t && (nY = t, !0) }, e.relativeTimeThreshold = function e(t, n) { return void 0 !== nD[t] && (void 0 === n ? nD[t] : (nD[t] = n, "s" === t && (nD.ss = n - 1), !0)) }, e.calendarFormat = function e(t, n) { var s = t.diff(n, "days", !0); return s < -6 ? "sameElse" : s < -1 ? "lastWeek" : s < 0 ? "lastDay" : s < 1 ? "sameDay" : s < 2 ? "nextDay" : s < 7 ? "nextWeek" : "sameElse" }, e.prototype = tJ, e.HTML5_FMT = { DATETIME_LOCAL: "YYYY-MM-DDTHH:mm", DATETIME_LOCAL_SECONDS: "YYYY-MM-DDTHH:mm:ss", DATETIME_LOCAL_MS: "YYYY-MM-DDTHH:mm:ss.SSS", DATE: "YYYY-MM-DD", TIME: "HH:mm", TIME_SECONDS: "HH:mm:ss", TIME_MS: "HH:mm:ss.SSS", WEEK: "GGGG-[W]WW", MONTH: "YYYY-MM" }, e
});
// Copyright 2018-2023 Ellucian Company L.P. and its affiliates.
// contains language configuration for moment.js for en-ca and fr-ca
// extracted from moment v2.5.1 langs.js 

// moment.js language configuration
// language : canadian english (en-ca)
// author : Jonathan Abourbih : https://github.com/jonbca

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        define(['moment'], factory); // AMD
    } else if (typeof exports === 'object') {
        module.exports = factory(require('../moment')); // Node
    } else {
        factory(window.moment); // Browser global
    }
}(function (moment) {
    return moment.lang('en-ca', {
        months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
        monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
        weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
        weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
        weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"),
        longDateFormat : {
            LT : "h:mm A",
            L : "YYYY-MM-DD",
            LL : "D MMMM, YYYY",
            LLL : "D MMMM, YYYY LT",
            LLLL : "dddd, D MMMM, YYYY LT"
        },
        calendar : {
            sameDay : '[Today at] LT',
            nextDay : '[Tomorrow at] LT',
            nextWeek : 'dddd [at] LT',
            lastDay : '[Yesterday at] LT',
            lastWeek : '[Last] dddd [at] LT',
            sameElse : 'L'
        },
        relativeTime : {
            future : "in %s",
            past : "%s ago",
            s : "a few seconds",
            m : "a minute",
            mm : "%d minutes",
            h : "an hour",
            hh : "%d hours",
            d : "a day",
            dd : "%d days",
            M : "a month",
            MM : "%d months",
            y : "a year",
            yy : "%d years"
        },
        ordinal : function (number) {
            var b = number % 10,
                output = (~~ (number % 100 / 10) === 1) ? 'th' :
                (b === 1) ? 'st' :
                (b === 2) ? 'nd' :
                (b === 3) ? 'rd' : 'th';
            return number + output;
        }
    });
}));


// moment.js language configuration
// language : canadian french (fr-ca)
// author : Jonathan Abourbih : https://github.com/jonbca

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        define(['moment'], factory); // AMD
    } else if (typeof exports === 'object') {
        module.exports = factory(require('../moment')); // Node
    } else {
        factory(window.moment); // Browser global
    }
}(function (moment) {
    return moment.lang('fr-ca', {
        months : "janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),
        monthsShort : "janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),
        weekdays : "dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),
        weekdaysShort : "dim._lun._mar._mer._jeu._ven._sam.".split("_"),
        weekdaysMin : "Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),
        longDateFormat : {
            LT : "HH:mm",
            L : "YYYY-MM-DD",
            LL : "D MMMM YYYY",
            LLL : "D MMMM YYYY LT",
            LLLL : "dddd D MMMM YYYY LT"
        },
        calendar : {
            sameDay: "[Aujourd'hui à] LT",
            nextDay: '[Demain à] LT',
            nextWeek: 'dddd [à] LT',
            lastDay: '[Hier à] LT',
            lastWeek: 'dddd [dernier à] LT',
            sameElse: 'L'
        },
        relativeTime : {
            future : "dans %s",
            past : "il y a %s",
            s : "quelques secondes",
            m : "une minute",
            mm : "%d minutes",
            h : "une heure",
            hh : "%d heures",
            d : "un jour",
            dd : "%d jours",
            M : "un mois",
            MM : "%d mois",
            y : "un an",
            yy : "%d ans"
        },
        ordinal : function (number) {
            return number + (number === 1 ? 'er' : '');
        }
    });
}));

/*! @license DOMPurify 2.3.5 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.5/LICENSE */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).DOMPurify=t()}(this,(function(){"use strict";var e=Object.hasOwnProperty,t=Object.setPrototypeOf,n=Object.isFrozen,r=Object.getPrototypeOf,o=Object.getOwnPropertyDescriptor,i=Object.freeze,a=Object.seal,l=Object.create,c="undefined"!=typeof Reflect&&Reflect,s=c.apply,u=c.construct;s||(s=function(e,t,n){return e.apply(t,n)}),i||(i=function(e){return e}),a||(a=function(e){return e}),u||(u=function(e,t){return new(Function.prototype.bind.apply(e,[null].concat(function(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}(t))))});var m,f=A(Array.prototype.forEach),d=A(Array.prototype.pop),p=A(Array.prototype.push),h=A(String.prototype.toLowerCase),g=A(String.prototype.match),y=A(String.prototype.replace),v=A(String.prototype.indexOf),b=A(String.prototype.trim),T=A(RegExp.prototype.test),N=(m=TypeError,function(){for(var e=arguments.length,t=Array(e),n=0;n<e;n++)t[n]=arguments[n];return u(m,t)});function A(e){return function(t){for(var n=arguments.length,r=Array(n>1?n-1:0),o=1;o<n;o++)r[o-1]=arguments[o];return s(e,t,r)}}function E(e,r){t&&t(e,null);for(var o=r.length;o--;){var i=r[o];if("string"==typeof i){var a=h(i);a!==i&&(n(r)||(r[o]=a),i=a)}e[i]=!0}return e}function x(t){var n=l(null),r=void 0;for(r in t)s(e,t,[r])&&(n[r]=t[r]);return n}function k(e,t){for(;null!==e;){var n=o(e,t);if(n){if(n.get)return A(n.get);if("function"==typeof n.value)return A(n.value)}e=r(e)}return function(e){return console.warn("fallback value for",e),null}}var S=i(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","section","select","shadow","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),w=i(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","filter","font","g","glyph","glyphref","hkern","image","line","lineargradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),_=i(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),O=i(["animate","color-profile","cursor","discard","fedropshadow","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor","unknown","use"]),D=i(["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmultiscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mspace","msqrt","mstyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover"]),M=i(["maction","maligngroup","malignmark","mlongdiv","mscarries","mscarry","msgroup","mstack","msline","msrow","semantics","annotation","annotation-xml","mprescripts","none"]),C=i(["#text"]),L=i(["accept","action","align","alt","autocapitalize","autocomplete","autopictureinpicture","autoplay","background","bgcolor","border","capture","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","controls","controlslist","coords","crossorigin","datetime","decoding","default","dir","disabled","disablepictureinpicture","disableremoteplayback","download","draggable","enctype","enterkeyhint","face","for","headers","height","hidden","high","href","hreflang","id","inputmode","integrity","ismap","kind","label","lang","list","loading","loop","low","max","maxlength","media","method","min","minlength","multiple","muted","name","nonce","noshade","novalidate","nowrap","open","optimum","pattern","placeholder","playsinline","poster","preload","pubdate","radiogroup","readonly","rel","required","rev","reversed","role","rows","rowspan","spellcheck","scope","selected","shape","size","sizes","span","srclang","start","src","srcset","step","style","summary","tabindex","title","translate","type","usemap","valign","value","width","xmlns","slot"]),R=i(["accent-height","accumulate","additive","alignment-baseline","ascent","attributename","attributetype","azimuth","basefrequency","baseline-shift","begin","bias","by","class","clip","clippathunits","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","cx","cy","d","dx","dy","diffuseconstant","direction","display","divisor","dur","edgemode","elevation","end","fill","fill-opacity","fill-rule","filter","filterunits","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","fx","fy","g1","g2","glyph-name","glyphref","gradientunits","gradienttransform","height","href","id","image-rendering","in","in2","k","k1","k2","k3","k4","kerning","keypoints","keysplines","keytimes","lang","lengthadjust","letter-spacing","kernelmatrix","kernelunitlength","lighting-color","local","marker-end","marker-mid","marker-start","markerheight","markerunits","markerwidth","maskcontentunits","maskunits","max","mask","media","method","mode","min","name","numoctaves","offset","operator","opacity","order","orient","orientation","origin","overflow","paint-order","path","pathlength","patterncontentunits","patterntransform","patternunits","points","preservealpha","preserveaspectratio","primitiveunits","r","rx","ry","radius","refx","refy","repeatcount","repeatdur","restart","result","rotate","scale","seed","shape-rendering","specularconstant","specularexponent","spreadmethod","startoffset","stddeviation","stitchtiles","stop-color","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke","stroke-width","style","surfacescale","systemlanguage","tabindex","targetx","targety","transform","transform-origin","text-anchor","text-decoration","text-rendering","textlength","type","u1","u2","unicode","values","viewbox","visibility","version","vert-adv-y","vert-origin-x","vert-origin-y","width","word-spacing","wrap","writing-mode","xchannelselector","ychannelselector","x","x1","x2","xmlns","y","y1","y2","z","zoomandpan"]),I=i(["accent","accentunder","align","bevelled","close","columnsalign","columnlines","columnspan","denomalign","depth","dir","display","displaystyle","encoding","fence","frame","height","href","id","largeop","length","linethickness","lspace","lquote","mathbackground","mathcolor","mathsize","mathvariant","maxsize","minsize","movablelimits","notation","numalign","open","rowalign","rowlines","rowspacing","rowspan","rspace","rquote","scriptlevel","scriptminsize","scriptsizemultiplier","selection","separator","separators","stretchy","subscriptshift","supscriptshift","symmetric","voffset","width","xmlns"]),F=i(["xlink:href","xml:id","xlink:title","xml:space","xmlns:xlink"]),H=a(/\{\{[\s\S]*|[\s\S]*\}\}/gm),U=a(/<%[\s\S]*|[\s\S]*%>/gm),z=a(/^data-[\-\w.\u00B7-\uFFFF]/),B=a(/^aria-[\-\w]+$/),j=a(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),P=a(/^(?:\w+script|data):/i),G=a(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),W="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function q(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}var Y=function(){return"undefined"==typeof window?null:window},K=function(e,t){if("object"!==(void 0===e?"undefined":W(e))||"function"!=typeof e.createPolicy)return null;var n=null,r="data-tt-policy-suffix";t.currentScript&&t.currentScript.hasAttribute(r)&&(n=t.currentScript.getAttribute(r));var o="dompurify"+(n?"#"+n:"");try{return e.createPolicy(o,{createHTML:function(e){return e}})}catch(e){return console.warn("TrustedTypes policy "+o+" could not be created."),null}};return function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:Y(),n=function(t){return e(t)};if(n.version="2.3.5",n.removed=[],!t||!t.document||9!==t.document.nodeType)return n.isSupported=!1,n;var r=t.document,o=t.document,a=t.DocumentFragment,l=t.HTMLTemplateElement,c=t.Node,s=t.Element,u=t.NodeFilter,m=t.NamedNodeMap,A=void 0===m?t.NamedNodeMap||t.MozNamedAttrMap:m,V=t.HTMLFormElement,X=t.DOMParser,$=t.trustedTypes,Z=s.prototype,J=k(Z,"cloneNode"),Q=k(Z,"nextSibling"),ee=k(Z,"childNodes"),te=k(Z,"parentNode");if("function"==typeof l){var ne=o.createElement("template");ne.content&&ne.content.ownerDocument&&(o=ne.content.ownerDocument)}var re=K($,r),oe=re?re.createHTML(""):"",ie=o,ae=ie.implementation,le=ie.createNodeIterator,ce=ie.createDocumentFragment,se=ie.getElementsByTagName,ue=r.importNode,me={};try{me=x(o).documentMode?o.documentMode:{}}catch(e){}var fe={};n.isSupported="function"==typeof te&&ae&&void 0!==ae.createHTMLDocument&&9!==me;var de=H,pe=U,he=z,ge=B,ye=P,ve=G,be=j,Te=null,Ne=E({},[].concat(q(S),q(w),q(_),q(D),q(C))),Ae=null,Ee=E({},[].concat(q(L),q(R),q(I),q(F))),xe=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),ke=null,Se=null,we=!0,_e=!0,Oe=!1,De=!1,Me=!1,Ce=!1,Le=!1,Re=!1,Ie=!1,Fe=!1,He=!0,Ue=!0,ze=!1,Be={},je=null,Pe=E({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),Ge=null,We=E({},["audio","video","img","source","image","track"]),qe=null,Ye=E({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Ke="http://www.w3.org/1998/Math/MathML",Ve="http://www.w3.org/2000/svg",Xe="http://www.w3.org/1999/xhtml",$e=Xe,Ze=!1,Je=void 0,Qe=["application/xhtml+xml","text/html"],et="text/html",tt=void 0,nt=null,rt=o.createElement("form"),ot=function(e){return e instanceof RegExp||e instanceof Function},it=function(e){nt&&nt===e||(e&&"object"===(void 0===e?"undefined":W(e))||(e={}),e=x(e),Te="ALLOWED_TAGS"in e?E({},e.ALLOWED_TAGS):Ne,Ae="ALLOWED_ATTR"in e?E({},e.ALLOWED_ATTR):Ee,qe="ADD_URI_SAFE_ATTR"in e?E(x(Ye),e.ADD_URI_SAFE_ATTR):Ye,Ge="ADD_DATA_URI_TAGS"in e?E(x(We),e.ADD_DATA_URI_TAGS):We,je="FORBID_CONTENTS"in e?E({},e.FORBID_CONTENTS):Pe,ke="FORBID_TAGS"in e?E({},e.FORBID_TAGS):{},Se="FORBID_ATTR"in e?E({},e.FORBID_ATTR):{},Be="USE_PROFILES"in e&&e.USE_PROFILES,we=!1!==e.ALLOW_ARIA_ATTR,_e=!1!==e.ALLOW_DATA_ATTR,Oe=e.ALLOW_UNKNOWN_PROTOCOLS||!1,De=e.SAFE_FOR_TEMPLATES||!1,Me=e.WHOLE_DOCUMENT||!1,Re=e.RETURN_DOM||!1,Ie=e.RETURN_DOM_FRAGMENT||!1,Fe=e.RETURN_TRUSTED_TYPE||!1,Le=e.FORCE_BODY||!1,He=!1!==e.SANITIZE_DOM,Ue=!1!==e.KEEP_CONTENT,ze=e.IN_PLACE||!1,be=e.ALLOWED_URI_REGEXP||be,$e=e.NAMESPACE||Xe,e.CUSTOM_ELEMENT_HANDLING&&ot(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(xe.tagNameCheck=e.CUSTOM_ELEMENT_HANDLING.tagNameCheck),e.CUSTOM_ELEMENT_HANDLING&&ot(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(xe.attributeNameCheck=e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),e.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(xe.allowCustomizedBuiltInElements=e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),Je=Je=-1===Qe.indexOf(e.PARSER_MEDIA_TYPE)?et:e.PARSER_MEDIA_TYPE,tt="application/xhtml+xml"===Je?function(e){return e}:h,De&&(_e=!1),Ie&&(Re=!0),Be&&(Te=E({},[].concat(q(C))),Ae=[],!0===Be.html&&(E(Te,S),E(Ae,L)),!0===Be.svg&&(E(Te,w),E(Ae,R),E(Ae,F)),!0===Be.svgFilters&&(E(Te,_),E(Ae,R),E(Ae,F)),!0===Be.mathMl&&(E(Te,D),E(Ae,I),E(Ae,F))),e.ADD_TAGS&&(Te===Ne&&(Te=x(Te)),E(Te,e.ADD_TAGS)),e.ADD_ATTR&&(Ae===Ee&&(Ae=x(Ae)),E(Ae,e.ADD_ATTR)),e.ADD_URI_SAFE_ATTR&&E(qe,e.ADD_URI_SAFE_ATTR),e.FORBID_CONTENTS&&(je===Pe&&(je=x(je)),E(je,e.FORBID_CONTENTS)),Ue&&(Te["#text"]=!0),Me&&E(Te,["html","head","body"]),Te.table&&(E(Te,["tbody"]),delete ke.tbody),i&&i(e),nt=e)},at=E({},["mi","mo","mn","ms","mtext"]),lt=E({},["foreignobject","desc","title","annotation-xml"]),ct=E({},w);E(ct,_),E(ct,O);var st=E({},D);E(st,M);var ut=function(e){var t=te(e);t&&t.tagName||(t={namespaceURI:Xe,tagName:"template"});var n=h(e.tagName),r=h(t.tagName);if(e.namespaceURI===Ve)return t.namespaceURI===Xe?"svg"===n:t.namespaceURI===Ke?"svg"===n&&("annotation-xml"===r||at[r]):Boolean(ct[n]);if(e.namespaceURI===Ke)return t.namespaceURI===Xe?"math"===n:t.namespaceURI===Ve?"math"===n&&lt[r]:Boolean(st[n]);if(e.namespaceURI===Xe){if(t.namespaceURI===Ve&&!lt[r])return!1;if(t.namespaceURI===Ke&&!at[r])return!1;var o=E({},["title","style","font","a","script"]);return!st[n]&&(o[n]||!ct[n])}return!1},mt=function(e){p(n.removed,{element:e});try{e.parentNode.removeChild(e)}catch(t){try{e.outerHTML=oe}catch(t){e.remove()}}},ft=function(e,t){try{p(n.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){p(n.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!Ae[e])if(Re||Ie)try{mt(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},dt=function(e){var t=void 0,n=void 0;if(Le)e="<remove></remove>"+e;else{var r=g(e,/^[\r\n\t ]+/);n=r&&r[0]}"application/xhtml+xml"===Je&&(e='<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>'+e+"</body></html>");var i=re?re.createHTML(e):e;if($e===Xe)try{t=(new X).parseFromString(i,Je)}catch(e){}if(!t||!t.documentElement){t=ae.createDocument($e,"template",null);try{t.documentElement.innerHTML=Ze?"":i}catch(e){}}var a=t.body||t.documentElement;return e&&n&&a.insertBefore(o.createTextNode(n),a.childNodes[0]||null),$e===Xe?se.call(t,Me?"html":"body")[0]:Me?t.documentElement:a},pt=function(e){return le.call(e.ownerDocument||e,e,u.SHOW_ELEMENT|u.SHOW_COMMENT|u.SHOW_TEXT,null,!1)},ht=function(e){return e instanceof V&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof A)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore)},gt=function(e){return"object"===(void 0===c?"undefined":W(c))?e instanceof c:e&&"object"===(void 0===e?"undefined":W(e))&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName},yt=function(e,t,r){fe[e]&&f(fe[e],(function(e){e.call(n,t,r,nt)}))},vt=function(e){var t=void 0;if(yt("beforeSanitizeElements",e,null),ht(e))return mt(e),!0;if(g(e.nodeName,/[\u0080-\uFFFF]/))return mt(e),!0;var r=tt(e.nodeName);if(yt("uponSanitizeElement",e,{tagName:r,allowedTags:Te}),!gt(e.firstElementChild)&&(!gt(e.content)||!gt(e.content.firstElementChild))&&T(/<[/\w]/g,e.innerHTML)&&T(/<[/\w]/g,e.textContent))return mt(e),!0;if("select"===r&&T(/<template/i,e.innerHTML))return mt(e),!0;if(!Te[r]||ke[r]){if(Ue&&!je[r]){var o=te(e)||e.parentNode,i=ee(e)||e.childNodes;if(i&&o)for(var a=i.length-1;a>=0;--a)o.insertBefore(J(i[a],!0),Q(e))}if(!ke[r]&&Tt(r)){if(xe.tagNameCheck instanceof RegExp&&T(xe.tagNameCheck,r))return!1;if(xe.tagNameCheck instanceof Function&&xe.tagNameCheck(r))return!1}return mt(e),!0}return e instanceof s&&!ut(e)?(mt(e),!0):"noscript"!==r&&"noembed"!==r||!T(/<\/no(script|embed)/i,e.innerHTML)?(De&&3===e.nodeType&&(t=e.textContent,t=y(t,de," "),t=y(t,pe," "),e.textContent!==t&&(p(n.removed,{element:e.cloneNode()}),e.textContent=t)),yt("afterSanitizeElements",e,null),!1):(mt(e),!0)},bt=function(e,t,n){if(He&&("id"===t||"name"===t)&&(n in o||n in rt))return!1;if(_e&&!Se[t]&&T(he,t));else if(we&&T(ge,t));else if(!Ae[t]||Se[t]){if(!(Tt(e)&&(xe.tagNameCheck instanceof RegExp&&T(xe.tagNameCheck,e)||xe.tagNameCheck instanceof Function&&xe.tagNameCheck(e))&&(xe.attributeNameCheck instanceof RegExp&&T(xe.attributeNameCheck,t)||xe.attributeNameCheck instanceof Function&&xe.attributeNameCheck(t))||"is"===t&&xe.allowCustomizedBuiltInElements&&(xe.tagNameCheck instanceof RegExp&&T(xe.tagNameCheck,n)||xe.tagNameCheck instanceof Function&&xe.tagNameCheck(n))))return!1}else if(qe[t]);else if(T(be,y(n,ve,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==v(n,"data:")||!Ge[e]){if(Oe&&!T(ye,y(n,ve,"")));else if(n)return!1}else;return!0},Tt=function(e){return e.indexOf("-")>0},Nt=function(e){var t=void 0,r=void 0,o=void 0,i=void 0;yt("beforeSanitizeAttributes",e,null);var a=e.attributes;if(a){var l={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:Ae};for(i=a.length;i--;){var c=t=a[i],s=c.name,u=c.namespaceURI;if(r=b(t.value),o=tt(s),l.attrName=o,l.attrValue=r,l.keepAttr=!0,l.forceKeepAttr=void 0,yt("uponSanitizeAttribute",e,l),r=l.attrValue,!l.forceKeepAttr&&(ft(s,e),l.keepAttr))if(T(/\/>/i,r))ft(s,e);else{De&&(r=y(r,de," "),r=y(r,pe," "));var m=tt(e.nodeName);if(bt(m,o,r))try{u?e.setAttributeNS(u,s,r):e.setAttribute(s,r),d(n.removed)}catch(e){}}}yt("afterSanitizeAttributes",e,null)}},At=function e(t){var n=void 0,r=pt(t);for(yt("beforeSanitizeShadowDOM",t,null);n=r.nextNode();)yt("uponSanitizeShadowNode",n,null),vt(n)||(n.content instanceof a&&e(n.content),Nt(n));yt("afterSanitizeShadowDOM",t,null)};return n.sanitize=function(e,o){var i=void 0,l=void 0,s=void 0,u=void 0,m=void 0;if((Ze=!e)&&(e="\x3c!--\x3e"),"string"!=typeof e&&!gt(e)){if("function"!=typeof e.toString)throw N("toString is not a function");if("string"!=typeof(e=e.toString()))throw N("dirty is not a string, aborting")}if(!n.isSupported){if("object"===W(t.toStaticHTML)||"function"==typeof t.toStaticHTML){if("string"==typeof e)return t.toStaticHTML(e);if(gt(e))return t.toStaticHTML(e.outerHTML)}return e}if(Ce||it(o),n.removed=[],"string"==typeof e&&(ze=!1),ze){if(e.nodeName){var f=tt(e.nodeName);if(!Te[f]||ke[f])throw N("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof c)1===(l=(i=dt("\x3c!----\x3e")).ownerDocument.importNode(e,!0)).nodeType&&"BODY"===l.nodeName||"HTML"===l.nodeName?i=l:i.appendChild(l);else{if(!Re&&!De&&!Me&&-1===e.indexOf("<"))return re&&Fe?re.createHTML(e):e;if(!(i=dt(e)))return Re?null:Fe?oe:""}i&&Le&&mt(i.firstChild);for(var d=pt(ze?e:i);s=d.nextNode();)3===s.nodeType&&s===u||vt(s)||(s.content instanceof a&&At(s.content),Nt(s),u=s);if(u=null,ze)return e;if(Re){if(Ie)for(m=ce.call(i.ownerDocument);i.firstChild;)m.appendChild(i.firstChild);else m=i;return Ae.shadowroot&&(m=ue.call(r,m,!0)),m}var p=Me?i.outerHTML:i.innerHTML;return De&&(p=y(p,de," "),p=y(p,pe," ")),re&&Fe?re.createHTML(p):p},n.setConfig=function(e){it(e),Ce=!0},n.clearConfig=function(){nt=null,Ce=!1},n.isValidAttribute=function(e,t,n){nt||it({});var r=tt(e),o=tt(t);return bt(r,o,n)},n.addHook=function(e,t){"function"==typeof t&&(fe[e]=fe[e]||[],p(fe[e],t))},n.removeHook=function(e){fe[e]&&d(fe[e])},n.removeHooks=function(e){fe[e]&&(fe[e]=[])},n.removeAllHooks=function(){fe={}},n}()}));

/*!
 * Knockout JavaScript library v3.5.1
 * (c) The Knockout.js team - http://knockoutjs.com/
 * License: MIT (http://www.opensource.org/licenses/mit-license.php)
 */

(function() {(function(n){var A=this||(0,eval)("this"),w=A.document,R=A.navigator,v=A.jQuery,H=A.JSON;v||"undefined"===typeof jQuery||(v=jQuery);(function(n){"function"===typeof define&&define.amd?define(["exports","require"],n):"object"===typeof exports&&"object"===typeof module?n(module.exports||exports):n(A.ko={})})(function(S,T){function K(a,c){return null===a||typeof a in W?a===c:!1}function X(b,c){var d;return function(){d||(d=a.a.setTimeout(function(){d=n;b()},c))}}function Y(b,c){var d;return function(){clearTimeout(d);
d=a.a.setTimeout(b,c)}}function Z(a,c){c&&"change"!==c?"beforeChange"===c?this.pc(a):this.gb(a,c):this.qc(a)}function aa(a,c){null!==c&&c.s&&c.s()}function ba(a,c){var d=this.qd,e=d[r];e.ra||(this.Qb&&this.mb[c]?(d.uc(c,a,this.mb[c]),this.mb[c]=null,--this.Qb):e.I[c]||d.uc(c,a,e.J?{da:a}:d.$c(a)),a.Ja&&a.gd())}var a="undefined"!==typeof S?S:{};a.b=function(b,c){for(var d=b.split("."),e=a,f=0;f<d.length-1;f++)e=e[d[f]];e[d[d.length-1]]=c};a.L=function(a,c,d){a[c]=d};a.version="3.5.1";a.b("version",
a.version);a.options={deferUpdates:!1,useOnlyNativeEvents:!1,foreachHidesDestroyed:!1};a.a=function(){function b(a,b){for(var c in a)f.call(a,c)&&b(c,a[c])}function c(a,b){if(b)for(var c in b)f.call(b,c)&&(a[c]=b[c]);return a}function d(a,b){a.__proto__=b;return a}function e(b,c,d,e){var l=b[c].match(q)||[];a.a.D(d.match(q),function(b){a.a.Na(l,b,e)});b[c]=l.join(" ")}var f=Object.prototype.hasOwnProperty,g={__proto__:[]}instanceof Array,h="function"===typeof Symbol,m={},k={};m[R&&/Firefox\/2/i.test(R.userAgent)?
"KeyboardEvent":"UIEvents"]=["keyup","keydown","keypress"];m.MouseEvents="click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave".split(" ");b(m,function(a,b){if(b.length)for(var c=0,d=b.length;c<d;c++)k[b[c]]=a});var l={propertychange:!0},p=w&&function(){for(var a=3,b=w.createElement("div"),c=b.getElementsByTagName("i");b.innerHTML="\x3c!--[if gt IE "+ ++a+"]><i></i><![endif]--\x3e",c[0];);return 4<a?a:n}(),q=/\S+/g,t;return{Jc:["authenticity_token",/^__RequestVerificationToken(_.*)?$/],
D:function(a,b,c){for(var d=0,e=a.length;d<e;d++)b.call(c,a[d],d,a)},A:"function"==typeof Array.prototype.indexOf?function(a,b){return Array.prototype.indexOf.call(a,b)}:function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},Lb:function(a,b,c){for(var d=0,e=a.length;d<e;d++)if(b.call(c,a[d],d,a))return a[d];return n},Pa:function(b,c){var d=a.a.A(b,c);0<d?b.splice(d,1):0===d&&b.shift()},wc:function(b){var c=[];b&&a.a.D(b,function(b){0>a.a.A(c,b)&&c.push(b)});return c},Mb:function(a,
b,c){var d=[];if(a)for(var e=0,l=a.length;e<l;e++)d.push(b.call(c,a[e],e));return d},jb:function(a,b,c){var d=[];if(a)for(var e=0,l=a.length;e<l;e++)b.call(c,a[e],e)&&d.push(a[e]);return d},Nb:function(a,b){if(b instanceof Array)a.push.apply(a,b);else for(var c=0,d=b.length;c<d;c++)a.push(b[c]);return a},Na:function(b,c,d){var e=a.a.A(a.a.bc(b),c);0>e?d&&b.push(c):d||b.splice(e,1)},Ba:g,extend:c,setPrototypeOf:d,Ab:g?d:c,P:b,Ga:function(a,b,c){if(!a)return a;var d={},e;for(e in a)f.call(a,e)&&(d[e]=
b.call(c,a[e],e,a));return d},Tb:function(b){for(;b.firstChild;)a.removeNode(b.firstChild)},Yb:function(b){b=a.a.la(b);for(var c=(b[0]&&b[0].ownerDocument||w).createElement("div"),d=0,e=b.length;d<e;d++)c.appendChild(a.oa(b[d]));return c},Ca:function(b,c){for(var d=0,e=b.length,l=[];d<e;d++){var k=b[d].cloneNode(!0);l.push(c?a.oa(k):k)}return l},va:function(b,c){a.a.Tb(b);if(c)for(var d=0,e=c.length;d<e;d++)b.appendChild(c[d])},Xc:function(b,c){var d=b.nodeType?[b]:b;if(0<d.length){for(var e=d[0],
l=e.parentNode,k=0,f=c.length;k<f;k++)l.insertBefore(c[k],e);k=0;for(f=d.length;k<f;k++)a.removeNode(d[k])}},Ua:function(a,b){if(a.length){for(b=8===b.nodeType&&b.parentNode||b;a.length&&a[0].parentNode!==b;)a.splice(0,1);for(;1<a.length&&a[a.length-1].parentNode!==b;)a.length--;if(1<a.length){var c=a[0],d=a[a.length-1];for(a.length=0;c!==d;)a.push(c),c=c.nextSibling;a.push(d)}}return a},Zc:function(a,b){7>p?a.setAttribute("selected",b):a.selected=b},Db:function(a){return null===a||a===n?"":a.trim?
a.trim():a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},Ud:function(a,b){a=a||"";return b.length>a.length?!1:a.substring(0,b.length)===b},vd:function(a,b){if(a===b)return!0;if(11===a.nodeType)return!1;if(b.contains)return b.contains(1!==a.nodeType?a.parentNode:a);if(b.compareDocumentPosition)return 16==(b.compareDocumentPosition(a)&16);for(;a&&a!=b;)a=a.parentNode;return!!a},Sb:function(b){return a.a.vd(b,b.ownerDocument.documentElement)},kd:function(b){return!!a.a.Lb(b,a.a.Sb)},R:function(a){return a&&
a.tagName&&a.tagName.toLowerCase()},Ac:function(b){return a.onError?function(){try{return b.apply(this,arguments)}catch(c){throw a.onError&&a.onError(c),c;}}:b},setTimeout:function(b,c){return setTimeout(a.a.Ac(b),c)},Gc:function(b){setTimeout(function(){a.onError&&a.onError(b);throw b;},0)},B:function(b,c,d){var e=a.a.Ac(d);d=l[c];if(a.options.useOnlyNativeEvents||d||!v)if(d||"function"!=typeof b.addEventListener)if("undefined"!=typeof b.attachEvent){var k=function(a){e.call(b,a)},f="on"+c;b.attachEvent(f,
k);a.a.K.za(b,function(){b.detachEvent(f,k)})}else throw Error("Browser doesn't support addEventListener or attachEvent");else b.addEventListener(c,e,!1);else t||(t="function"==typeof v(b).on?"on":"bind"),v(b)[t](c,e)},Fb:function(b,c){if(!b||!b.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var d;"input"===a.a.R(b)&&b.type&&"click"==c.toLowerCase()?(d=b.type,d="checkbox"==d||"radio"==d):d=!1;if(a.options.useOnlyNativeEvents||!v||d)if("function"==typeof w.createEvent)if("function"==
typeof b.dispatchEvent)d=w.createEvent(k[c]||"HTMLEvents"),d.initEvent(c,!0,!0,A,0,0,0,0,0,!1,!1,!1,!1,0,b),b.dispatchEvent(d);else throw Error("The supplied element doesn't support dispatchEvent");else if(d&&b.click)b.click();else if("undefined"!=typeof b.fireEvent)b.fireEvent("on"+c);else throw Error("Browser doesn't support triggering events");else v(b).trigger(c)},f:function(b){return a.O(b)?b():b},bc:function(b){return a.O(b)?b.v():b},Eb:function(b,c,d){var l;c&&("object"===typeof b.classList?
(l=b.classList[d?"add":"remove"],a.a.D(c.match(q),function(a){l.call(b.classList,a)})):"string"===typeof b.className.baseVal?e(b.className,"baseVal",c,d):e(b,"className",c,d))},Bb:function(b,c){var d=a.a.f(c);if(null===d||d===n)d="";var e=a.h.firstChild(b);!e||3!=e.nodeType||a.h.nextSibling(e)?a.h.va(b,[b.ownerDocument.createTextNode(d)]):e.data=d;a.a.Ad(b)},Yc:function(a,b){a.name=b;if(7>=p)try{var c=a.name.replace(/[&<>'"]/g,function(a){return"&#"+a.charCodeAt(0)+";"});a.mergeAttributes(w.createElement("<input name='"+
c+"'/>"),!1)}catch(d){}},Ad:function(a){9<=p&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},wd:function(a){if(p){var b=a.style.width;a.style.width=0;a.style.width=b}},Pd:function(b,c){b=a.a.f(b);c=a.a.f(c);for(var d=[],e=b;e<=c;e++)d.push(e);return d},la:function(a){for(var b=[],c=0,d=a.length;c<d;c++)b.push(a[c]);return b},Da:function(a){return h?Symbol(a):a},Zd:6===p,$d:7===p,W:p,Lc:function(b,c){for(var d=a.a.la(b.getElementsByTagName("input")).concat(a.a.la(b.getElementsByTagName("textarea"))),
e="string"==typeof c?function(a){return a.name===c}:function(a){return c.test(a.name)},l=[],k=d.length-1;0<=k;k--)e(d[k])&&l.push(d[k]);return l},Nd:function(b){return"string"==typeof b&&(b=a.a.Db(b))?H&&H.parse?H.parse(b):(new Function("return "+b))():null},hc:function(b,c,d){if(!H||!H.stringify)throw Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
return H.stringify(a.a.f(b),c,d)},Od:function(c,d,e){e=e||{};var l=e.params||{},k=e.includeFields||this.Jc,f=c;if("object"==typeof c&&"form"===a.a.R(c))for(var f=c.action,h=k.length-1;0<=h;h--)for(var g=a.a.Lc(c,k[h]),m=g.length-1;0<=m;m--)l[g[m].name]=g[m].value;d=a.a.f(d);var p=w.createElement("form");p.style.display="none";p.action=f;p.method="post";for(var q in d)c=w.createElement("input"),c.type="hidden",c.name=q,c.value=a.a.hc(a.a.f(d[q])),p.appendChild(c);b(l,function(a,b){var c=w.createElement("input");
c.type="hidden";c.name=a;c.value=b;p.appendChild(c)});w.body.appendChild(p);e.submitter?e.submitter(p):p.submit();setTimeout(function(){p.parentNode.removeChild(p)},0)}}}();a.b("utils",a.a);a.b("utils.arrayForEach",a.a.D);a.b("utils.arrayFirst",a.a.Lb);a.b("utils.arrayFilter",a.a.jb);a.b("utils.arrayGetDistinctValues",a.a.wc);a.b("utils.arrayIndexOf",a.a.A);a.b("utils.arrayMap",a.a.Mb);a.b("utils.arrayPushAll",a.a.Nb);a.b("utils.arrayRemoveItem",a.a.Pa);a.b("utils.cloneNodes",a.a.Ca);a.b("utils.createSymbolOrString",
a.a.Da);a.b("utils.extend",a.a.extend);a.b("utils.fieldsIncludedWithJsonPost",a.a.Jc);a.b("utils.getFormFields",a.a.Lc);a.b("utils.objectMap",a.a.Ga);a.b("utils.peekObservable",a.a.bc);a.b("utils.postJson",a.a.Od);a.b("utils.parseJson",a.a.Nd);a.b("utils.registerEventHandler",a.a.B);a.b("utils.stringifyJson",a.a.hc);a.b("utils.range",a.a.Pd);a.b("utils.toggleDomNodeCssClass",a.a.Eb);a.b("utils.triggerEvent",a.a.Fb);a.b("utils.unwrapObservable",a.a.f);a.b("utils.objectForEach",a.a.P);a.b("utils.addOrRemoveItem",
a.a.Na);a.b("utils.setTextContent",a.a.Bb);a.b("unwrap",a.a.f);Function.prototype.bind||(Function.prototype.bind=function(a){var c=this;if(1===arguments.length)return function(){return c.apply(a,arguments)};var d=Array.prototype.slice.call(arguments,1);return function(){var e=d.slice(0);e.push.apply(e,arguments);return c.apply(a,e)}});a.a.g=new function(){var b=0,c="__ko__"+(new Date).getTime(),d={},e,f;a.a.W?(e=function(a,e){var f=a[c];if(!f||"null"===f||!d[f]){if(!e)return n;f=a[c]="ko"+b++;d[f]=
{}}return d[f]},f=function(a){var b=a[c];return b?(delete d[b],a[c]=null,!0):!1}):(e=function(a,b){var d=a[c];!d&&b&&(d=a[c]={});return d},f=function(a){return a[c]?(delete a[c],!0):!1});return{get:function(a,b){var c=e(a,!1);return c&&c[b]},set:function(a,b,c){(a=e(a,c!==n))&&(a[b]=c)},Ub:function(a,b,c){a=e(a,!0);return a[b]||(a[b]=c)},clear:f,Z:function(){return b++ +c}}};a.b("utils.domData",a.a.g);a.b("utils.domData.clear",a.a.g.clear);a.a.K=new function(){function b(b,c){var d=a.a.g.get(b,e);
d===n&&c&&(d=[],a.a.g.set(b,e,d));return d}function c(c){var e=b(c,!1);if(e)for(var e=e.slice(0),k=0;k<e.length;k++)e[k](c);a.a.g.clear(c);a.a.K.cleanExternalData(c);g[c.nodeType]&&d(c.childNodes,!0)}function d(b,d){for(var e=[],l,f=0;f<b.length;f++)if(!d||8===b[f].nodeType)if(c(e[e.length]=l=b[f]),b[f]!==l)for(;f--&&-1==a.a.A(e,b[f]););}var e=a.a.g.Z(),f={1:!0,8:!0,9:!0},g={1:!0,9:!0};return{za:function(a,c){if("function"!=typeof c)throw Error("Callback must be a function");b(a,!0).push(c)},yb:function(c,
d){var f=b(c,!1);f&&(a.a.Pa(f,d),0==f.length&&a.a.g.set(c,e,n))},oa:function(b){a.u.G(function(){f[b.nodeType]&&(c(b),g[b.nodeType]&&d(b.getElementsByTagName("*")))});return b},removeNode:function(b){a.oa(b);b.parentNode&&b.parentNode.removeChild(b)},cleanExternalData:function(a){v&&"function"==typeof v.cleanData&&v.cleanData([a])}}};a.oa=a.a.K.oa;a.removeNode=a.a.K.removeNode;a.b("cleanNode",a.oa);a.b("removeNode",a.removeNode);a.b("utils.domNodeDisposal",a.a.K);a.b("utils.domNodeDisposal.addDisposeCallback",
a.a.K.za);a.b("utils.domNodeDisposal.removeDisposeCallback",a.a.K.yb);(function(){var b=[0,"",""],c=[1,"<table>","</table>"],d=[3,"<table><tbody><tr>","</tr></tbody></table>"],e=[1,"<select multiple='multiple'>","</select>"],f={thead:c,tbody:c,tfoot:c,tr:[2,"<table><tbody>","</tbody></table>"],td:d,th:d,option:e,optgroup:e},g=8>=a.a.W;a.a.ua=function(c,d){var e;if(v)if(v.parseHTML)e=v.parseHTML(c,d)||[];else{if((e=v.clean([c],d))&&e[0]){for(var l=e[0];l.parentNode&&11!==l.parentNode.nodeType;)l=l.parentNode;
l.parentNode&&l.parentNode.removeChild(l)}}else{(e=d)||(e=w);var l=e.parentWindow||e.defaultView||A,p=a.a.Db(c).toLowerCase(),q=e.createElement("div"),t;t=(p=p.match(/^(?:\x3c!--.*?--\x3e\s*?)*?<([a-z]+)[\s>]/))&&f[p[1]]||b;p=t[0];t="ignored<div>"+t[1]+c+t[2]+"</div>";"function"==typeof l.innerShiv?q.appendChild(l.innerShiv(t)):(g&&e.body.appendChild(q),q.innerHTML=t,g&&q.parentNode.removeChild(q));for(;p--;)q=q.lastChild;e=a.a.la(q.lastChild.childNodes)}return e};a.a.Md=function(b,c){var d=a.a.ua(b,
c);return d.length&&d[0].parentElement||a.a.Yb(d)};a.a.fc=function(b,c){a.a.Tb(b);c=a.a.f(c);if(null!==c&&c!==n)if("string"!=typeof c&&(c=c.toString()),v)v(b).html(c);else for(var d=a.a.ua(c,b.ownerDocument),e=0;e<d.length;e++)b.appendChild(d[e])}})();a.b("utils.parseHtmlFragment",a.a.ua);a.b("utils.setHtml",a.a.fc);a.aa=function(){function b(c,e){if(c)if(8==c.nodeType){var f=a.aa.Uc(c.nodeValue);null!=f&&e.push({ud:c,Kd:f})}else if(1==c.nodeType)for(var f=0,g=c.childNodes,h=g.length;f<h;f++)b(g[f],
e)}var c={};return{Xb:function(a){if("function"!=typeof a)throw Error("You can only pass a function to ko.memoization.memoize()");var b=(4294967296*(1+Math.random())|0).toString(16).substring(1)+(4294967296*(1+Math.random())|0).toString(16).substring(1);c[b]=a;return"\x3c!--[ko_memo:"+b+"]--\x3e"},bd:function(a,b){var f=c[a];if(f===n)throw Error("Couldn't find any memo with ID "+a+". Perhaps it's already been unmemoized.");try{return f.apply(null,b||[]),!0}finally{delete c[a]}},cd:function(c,e){var f=
[];b(c,f);for(var g=0,h=f.length;g<h;g++){var m=f[g].ud,k=[m];e&&a.a.Nb(k,e);a.aa.bd(f[g].Kd,k);m.nodeValue="";m.parentNode&&m.parentNode.removeChild(m)}},Uc:function(a){return(a=a.match(/^\[ko_memo\:(.*?)\]$/))?a[1]:null}}}();a.b("memoization",a.aa);a.b("memoization.memoize",a.aa.Xb);a.b("memoization.unmemoize",a.aa.bd);a.b("memoization.parseMemoText",a.aa.Uc);a.b("memoization.unmemoizeDomNodeAndDescendants",a.aa.cd);a.na=function(){function b(){if(f)for(var b=f,c=0,d;h<f;)if(d=e[h++]){if(h>b){if(5E3<=
++c){h=f;a.a.Gc(Error("'Too much recursion' after processing "+c+" task groups."));break}b=f}try{d()}catch(p){a.a.Gc(p)}}}function c(){b();h=f=e.length=0}var d,e=[],f=0,g=1,h=0;A.MutationObserver?d=function(a){var b=w.createElement("div");(new MutationObserver(a)).observe(b,{attributes:!0});return function(){b.classList.toggle("foo")}}(c):d=w&&"onreadystatechange"in w.createElement("script")?function(a){var b=w.createElement("script");b.onreadystatechange=function(){b.onreadystatechange=null;w.documentElement.removeChild(b);
b=null;a()};w.documentElement.appendChild(b)}:function(a){setTimeout(a,0)};return{scheduler:d,zb:function(b){f||a.na.scheduler(c);e[f++]=b;return g++},cancel:function(a){a=a-(g-f);a>=h&&a<f&&(e[a]=null)},resetForTesting:function(){var a=f-h;h=f=e.length=0;return a},Sd:b}}();a.b("tasks",a.na);a.b("tasks.schedule",a.na.zb);a.b("tasks.runEarly",a.na.Sd);a.Ta={throttle:function(b,c){b.throttleEvaluation=c;var d=null;return a.$({read:b,write:function(e){clearTimeout(d);d=a.a.setTimeout(function(){b(e)},
c)}})},rateLimit:function(a,c){var d,e,f;"number"==typeof c?d=c:(d=c.timeout,e=c.method);a.Hb=!1;f="function"==typeof e?e:"notifyWhenChangesStop"==e?Y:X;a.ub(function(a){return f(a,d,c)})},deferred:function(b,c){if(!0!==c)throw Error("The 'deferred' extender only accepts the value 'true', because it is not supported to turn deferral off once enabled.");b.Hb||(b.Hb=!0,b.ub(function(c){var e,f=!1;return function(){if(!f){a.na.cancel(e);e=a.na.zb(c);try{f=!0,b.notifySubscribers(n,"dirty")}finally{f=
!1}}}}))},notify:function(a,c){a.equalityComparer="always"==c?null:K}};var W={undefined:1,"boolean":1,number:1,string:1};a.b("extenders",a.Ta);a.ic=function(b,c,d){this.da=b;this.lc=c;this.mc=d;this.Ib=!1;this.fb=this.Jb=null;a.L(this,"dispose",this.s);a.L(this,"disposeWhenNodeIsRemoved",this.l)};a.ic.prototype.s=function(){this.Ib||(this.fb&&a.a.K.yb(this.Jb,this.fb),this.Ib=!0,this.mc(),this.da=this.lc=this.mc=this.Jb=this.fb=null)};a.ic.prototype.l=function(b){this.Jb=b;a.a.K.za(b,this.fb=this.s.bind(this))};
a.T=function(){a.a.Ab(this,D);D.qb(this)};var D={qb:function(a){a.U={change:[]};a.sc=1},subscribe:function(b,c,d){var e=this;d=d||"change";var f=new a.ic(e,c?b.bind(c):b,function(){a.a.Pa(e.U[d],f);e.hb&&e.hb(d)});e.Qa&&e.Qa(d);e.U[d]||(e.U[d]=[]);e.U[d].push(f);return f},notifySubscribers:function(b,c){c=c||"change";"change"===c&&this.Gb();if(this.Wa(c)){var d="change"===c&&this.ed||this.U[c].slice(0);try{a.u.xc();for(var e=0,f;f=d[e];++e)f.Ib||f.lc(b)}finally{a.u.end()}}},ob:function(){return this.sc},
Dd:function(a){return this.ob()!==a},Gb:function(){++this.sc},ub:function(b){var c=this,d=a.O(c),e,f,g,h,m;c.gb||(c.gb=c.notifySubscribers,c.notifySubscribers=Z);var k=b(function(){c.Ja=!1;d&&h===c&&(h=c.nc?c.nc():c());var a=f||m&&c.sb(g,h);m=f=e=!1;a&&c.gb(g=h)});c.qc=function(a,b){b&&c.Ja||(m=!b);c.ed=c.U.change.slice(0);c.Ja=e=!0;h=a;k()};c.pc=function(a){e||(g=a,c.gb(a,"beforeChange"))};c.rc=function(){m=!0};c.gd=function(){c.sb(g,c.v(!0))&&(f=!0)}},Wa:function(a){return this.U[a]&&this.U[a].length},
Bd:function(b){if(b)return this.U[b]&&this.U[b].length||0;var c=0;a.a.P(this.U,function(a,b){"dirty"!==a&&(c+=b.length)});return c},sb:function(a,c){return!this.equalityComparer||!this.equalityComparer(a,c)},toString:function(){return"[object Object]"},extend:function(b){var c=this;b&&a.a.P(b,function(b,e){var f=a.Ta[b];"function"==typeof f&&(c=f(c,e)||c)});return c}};a.L(D,"init",D.qb);a.L(D,"subscribe",D.subscribe);a.L(D,"extend",D.extend);a.L(D,"getSubscriptionsCount",D.Bd);a.a.Ba&&a.a.setPrototypeOf(D,
Function.prototype);a.T.fn=D;a.Qc=function(a){return null!=a&&"function"==typeof a.subscribe&&"function"==typeof a.notifySubscribers};a.b("subscribable",a.T);a.b("isSubscribable",a.Qc);a.S=a.u=function(){function b(a){d.push(e);e=a}function c(){e=d.pop()}var d=[],e,f=0;return{xc:b,end:c,cc:function(b){if(e){if(!a.Qc(b))throw Error("Only subscribable things can act as dependencies");e.od.call(e.pd,b,b.fd||(b.fd=++f))}},G:function(a,d,e){try{return b(),a.apply(d,e||[])}finally{c()}},qa:function(){if(e)return e.o.qa()},
Va:function(){if(e)return e.o.Va()},Ya:function(){if(e)return e.Ya},o:function(){if(e)return e.o}}}();a.b("computedContext",a.S);a.b("computedContext.getDependenciesCount",a.S.qa);a.b("computedContext.getDependencies",a.S.Va);a.b("computedContext.isInitial",a.S.Ya);a.b("computedContext.registerDependency",a.S.cc);a.b("ignoreDependencies",a.Yd=a.u.G);var I=a.a.Da("_latestValue");a.ta=function(b){function c(){if(0<arguments.length)return c.sb(c[I],arguments[0])&&(c.ya(),c[I]=arguments[0],c.xa()),this;
a.u.cc(c);return c[I]}c[I]=b;a.a.Ba||a.a.extend(c,a.T.fn);a.T.fn.qb(c);a.a.Ab(c,F);a.options.deferUpdates&&a.Ta.deferred(c,!0);return c};var F={equalityComparer:K,v:function(){return this[I]},xa:function(){this.notifySubscribers(this[I],"spectate");this.notifySubscribers(this[I])},ya:function(){this.notifySubscribers(this[I],"beforeChange")}};a.a.Ba&&a.a.setPrototypeOf(F,a.T.fn);var G=a.ta.Ma="__ko_proto__";F[G]=a.ta;a.O=function(b){if((b="function"==typeof b&&b[G])&&b!==F[G]&&b!==a.o.fn[G])throw Error("Invalid object that looks like an observable; possibly from another Knockout instance");
return!!b};a.Za=function(b){return"function"==typeof b&&(b[G]===F[G]||b[G]===a.o.fn[G]&&b.Nc)};a.b("observable",a.ta);a.b("isObservable",a.O);a.b("isWriteableObservable",a.Za);a.b("isWritableObservable",a.Za);a.b("observable.fn",F);a.L(F,"peek",F.v);a.L(F,"valueHasMutated",F.xa);a.L(F,"valueWillMutate",F.ya);a.Ha=function(b){b=b||[];if("object"!=typeof b||!("length"in b))throw Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");b=a.ta(b);a.a.Ab(b,
a.Ha.fn);return b.extend({trackArrayChanges:!0})};a.Ha.fn={remove:function(b){for(var c=this.v(),d=[],e="function"!=typeof b||a.O(b)?function(a){return a===b}:b,f=0;f<c.length;f++){var g=c[f];if(e(g)){0===d.length&&this.ya();if(c[f]!==g)throw Error("Array modified during remove; cannot remove item");d.push(g);c.splice(f,1);f--}}d.length&&this.xa();return d},removeAll:function(b){if(b===n){var c=this.v(),d=c.slice(0);this.ya();c.splice(0,c.length);this.xa();return d}return b?this.remove(function(c){return 0<=
a.a.A(b,c)}):[]},destroy:function(b){var c=this.v(),d="function"!=typeof b||a.O(b)?function(a){return a===b}:b;this.ya();for(var e=c.length-1;0<=e;e--){var f=c[e];d(f)&&(f._destroy=!0)}this.xa()},destroyAll:function(b){return b===n?this.destroy(function(){return!0}):b?this.destroy(function(c){return 0<=a.a.A(b,c)}):[]},indexOf:function(b){var c=this();return a.a.A(c,b)},replace:function(a,c){var d=this.indexOf(a);0<=d&&(this.ya(),this.v()[d]=c,this.xa())},sorted:function(a){var c=this().slice(0);
return a?c.sort(a):c.sort()},reversed:function(){return this().slice(0).reverse()}};a.a.Ba&&a.a.setPrototypeOf(a.Ha.fn,a.ta.fn);a.a.D("pop push reverse shift sort splice unshift".split(" "),function(b){a.Ha.fn[b]=function(){var a=this.v();this.ya();this.zc(a,b,arguments);var d=a[b].apply(a,arguments);this.xa();return d===a?this:d}});a.a.D(["slice"],function(b){a.Ha.fn[b]=function(){var a=this();return a[b].apply(a,arguments)}});a.Pc=function(b){return a.O(b)&&"function"==typeof b.remove&&"function"==
typeof b.push};a.b("observableArray",a.Ha);a.b("isObservableArray",a.Pc);a.Ta.trackArrayChanges=function(b,c){function d(){function c(){if(m){var d=[].concat(b.v()||[]),e;if(b.Wa("arrayChange")){if(!f||1<m)f=a.a.Pb(k,d,b.Ob);e=f}k=d;f=null;m=0;e&&e.length&&b.notifySubscribers(e,"arrayChange")}}e?c():(e=!0,h=b.subscribe(function(){++m},null,"spectate"),k=[].concat(b.v()||[]),f=null,g=b.subscribe(c))}b.Ob={};c&&"object"==typeof c&&a.a.extend(b.Ob,c);b.Ob.sparse=!0;if(!b.zc){var e=!1,f=null,g,h,m=0,
k,l=b.Qa,p=b.hb;b.Qa=function(a){l&&l.call(b,a);"arrayChange"===a&&d()};b.hb=function(a){p&&p.call(b,a);"arrayChange"!==a||b.Wa("arrayChange")||(g&&g.s(),h&&h.s(),h=g=null,e=!1,k=n)};b.zc=function(b,c,d){function l(a,b,c){return k[k.length]={status:a,value:b,index:c}}if(e&&!m){var k=[],p=b.length,g=d.length,h=0;switch(c){case "push":h=p;case "unshift":for(c=0;c<g;c++)l("added",d[c],h+c);break;case "pop":h=p-1;case "shift":p&&l("deleted",b[h],h);break;case "splice":c=Math.min(Math.max(0,0>d[0]?p+d[0]:
d[0]),p);for(var p=1===g?p:Math.min(c+(d[1]||0),p),g=c+g-2,h=Math.max(p,g),U=[],L=[],n=2;c<h;++c,++n)c<p&&L.push(l("deleted",b[c],c)),c<g&&U.push(l("added",d[n],c));a.a.Kc(L,U);break;default:return}f=k}}}};var r=a.a.Da("_state");a.o=a.$=function(b,c,d){function e(){if(0<arguments.length){if("function"===typeof f)f.apply(g.nb,arguments);else throw Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");return this}g.ra||
a.u.cc(e);(g.ka||g.J&&e.Xa())&&e.ha();return g.X}"object"===typeof b?d=b:(d=d||{},b&&(d.read=b));if("function"!=typeof d.read)throw Error("Pass a function that returns the value of the ko.computed");var f=d.write,g={X:n,sa:!0,ka:!0,rb:!1,jc:!1,ra:!1,wb:!1,J:!1,Wc:d.read,nb:c||d.owner,l:d.disposeWhenNodeIsRemoved||d.l||null,Sa:d.disposeWhen||d.Sa,Rb:null,I:{},V:0,Ic:null};e[r]=g;e.Nc="function"===typeof f;a.a.Ba||a.a.extend(e,a.T.fn);a.T.fn.qb(e);a.a.Ab(e,C);d.pure?(g.wb=!0,g.J=!0,a.a.extend(e,da)):
d.deferEvaluation&&a.a.extend(e,ea);a.options.deferUpdates&&a.Ta.deferred(e,!0);g.l&&(g.jc=!0,g.l.nodeType||(g.l=null));g.J||d.deferEvaluation||e.ha();g.l&&e.ja()&&a.a.K.za(g.l,g.Rb=function(){e.s()});return e};var C={equalityComparer:K,qa:function(){return this[r].V},Va:function(){var b=[];a.a.P(this[r].I,function(a,d){b[d.Ka]=d.da});return b},Vb:function(b){if(!this[r].V)return!1;var c=this.Va();return-1!==a.a.A(c,b)?!0:!!a.a.Lb(c,function(a){return a.Vb&&a.Vb(b)})},uc:function(a,c,d){if(this[r].wb&&
c===this)throw Error("A 'pure' computed must not be called recursively");this[r].I[a]=d;d.Ka=this[r].V++;d.La=c.ob()},Xa:function(){var a,c,d=this[r].I;for(a in d)if(Object.prototype.hasOwnProperty.call(d,a)&&(c=d[a],this.Ia&&c.da.Ja||c.da.Dd(c.La)))return!0},Jd:function(){this.Ia&&!this[r].rb&&this.Ia(!1)},ja:function(){var a=this[r];return a.ka||0<a.V},Rd:function(){this.Ja?this[r].ka&&(this[r].sa=!0):this.Hc()},$c:function(a){if(a.Hb){var c=a.subscribe(this.Jd,this,"dirty"),d=a.subscribe(this.Rd,
this);return{da:a,s:function(){c.s();d.s()}}}return a.subscribe(this.Hc,this)},Hc:function(){var b=this,c=b.throttleEvaluation;c&&0<=c?(clearTimeout(this[r].Ic),this[r].Ic=a.a.setTimeout(function(){b.ha(!0)},c)):b.Ia?b.Ia(!0):b.ha(!0)},ha:function(b){var c=this[r],d=c.Sa,e=!1;if(!c.rb&&!c.ra){if(c.l&&!a.a.Sb(c.l)||d&&d()){if(!c.jc){this.s();return}}else c.jc=!1;c.rb=!0;try{e=this.zd(b)}finally{c.rb=!1}return e}},zd:function(b){var c=this[r],d=!1,e=c.wb?n:!c.V,d={qd:this,mb:c.I,Qb:c.V};a.u.xc({pd:d,
od:ba,o:this,Ya:e});c.I={};c.V=0;var f=this.yd(c,d);c.V?d=this.sb(c.X,f):(this.s(),d=!0);d&&(c.J?this.Gb():this.notifySubscribers(c.X,"beforeChange"),c.X=f,this.notifySubscribers(c.X,"spectate"),!c.J&&b&&this.notifySubscribers(c.X),this.rc&&this.rc());e&&this.notifySubscribers(c.X,"awake");return d},yd:function(b,c){try{var d=b.Wc;return b.nb?d.call(b.nb):d()}finally{a.u.end(),c.Qb&&!b.J&&a.a.P(c.mb,aa),b.sa=b.ka=!1}},v:function(a){var c=this[r];(c.ka&&(a||!c.V)||c.J&&this.Xa())&&this.ha();return c.X},
ub:function(b){a.T.fn.ub.call(this,b);this.nc=function(){this[r].J||(this[r].sa?this.ha():this[r].ka=!1);return this[r].X};this.Ia=function(a){this.pc(this[r].X);this[r].ka=!0;a&&(this[r].sa=!0);this.qc(this,!a)}},s:function(){var b=this[r];!b.J&&b.I&&a.a.P(b.I,function(a,b){b.s&&b.s()});b.l&&b.Rb&&a.a.K.yb(b.l,b.Rb);b.I=n;b.V=0;b.ra=!0;b.sa=!1;b.ka=!1;b.J=!1;b.l=n;b.Sa=n;b.Wc=n;this.Nc||(b.nb=n)}},da={Qa:function(b){var c=this,d=c[r];if(!d.ra&&d.J&&"change"==b){d.J=!1;if(d.sa||c.Xa())d.I=null,d.V=
0,c.ha()&&c.Gb();else{var e=[];a.a.P(d.I,function(a,b){e[b.Ka]=a});a.a.D(e,function(a,b){var e=d.I[a],m=c.$c(e.da);m.Ka=b;m.La=e.La;d.I[a]=m});c.Xa()&&c.ha()&&c.Gb()}d.ra||c.notifySubscribers(d.X,"awake")}},hb:function(b){var c=this[r];c.ra||"change"!=b||this.Wa("change")||(a.a.P(c.I,function(a,b){b.s&&(c.I[a]={da:b.da,Ka:b.Ka,La:b.La},b.s())}),c.J=!0,this.notifySubscribers(n,"asleep"))},ob:function(){var b=this[r];b.J&&(b.sa||this.Xa())&&this.ha();return a.T.fn.ob.call(this)}},ea={Qa:function(a){"change"!=
a&&"beforeChange"!=a||this.v()}};a.a.Ba&&a.a.setPrototypeOf(C,a.T.fn);var N=a.ta.Ma;C[N]=a.o;a.Oc=function(a){return"function"==typeof a&&a[N]===C[N]};a.Fd=function(b){return a.Oc(b)&&b[r]&&b[r].wb};a.b("computed",a.o);a.b("dependentObservable",a.o);a.b("isComputed",a.Oc);a.b("isPureComputed",a.Fd);a.b("computed.fn",C);a.L(C,"peek",C.v);a.L(C,"dispose",C.s);a.L(C,"isActive",C.ja);a.L(C,"getDependenciesCount",C.qa);a.L(C,"getDependencies",C.Va);a.xb=function(b,c){if("function"===typeof b)return a.o(b,
c,{pure:!0});b=a.a.extend({},b);b.pure=!0;return a.o(b,c)};a.b("pureComputed",a.xb);(function(){function b(a,f,g){g=g||new d;a=f(a);if("object"!=typeof a||null===a||a===n||a instanceof RegExp||a instanceof Date||a instanceof String||a instanceof Number||a instanceof Boolean)return a;var h=a instanceof Array?[]:{};g.save(a,h);c(a,function(c){var d=f(a[c]);switch(typeof d){case "boolean":case "number":case "string":case "function":h[c]=d;break;case "object":case "undefined":var l=g.get(d);h[c]=l!==
n?l:b(d,f,g)}});return h}function c(a,b){if(a instanceof Array){for(var c=0;c<a.length;c++)b(c);"function"==typeof a.toJSON&&b("toJSON")}else for(c in a)b(c)}function d(){this.keys=[];this.values=[]}a.ad=function(c){if(0==arguments.length)throw Error("When calling ko.toJS, pass the object you want to convert.");return b(c,function(b){for(var c=0;a.O(b)&&10>c;c++)b=b();return b})};a.toJSON=function(b,c,d){b=a.ad(b);return a.a.hc(b,c,d)};d.prototype={constructor:d,save:function(b,c){var d=a.a.A(this.keys,
b);0<=d?this.values[d]=c:(this.keys.push(b),this.values.push(c))},get:function(b){b=a.a.A(this.keys,b);return 0<=b?this.values[b]:n}}})();a.b("toJS",a.ad);a.b("toJSON",a.toJSON);a.Wd=function(b,c,d){function e(c){var e=a.xb(b,d).extend({ma:"always"}),h=e.subscribe(function(a){a&&(h.s(),c(a))});e.notifySubscribers(e.v());return h}return"function"!==typeof Promise||c?e(c.bind(d)):new Promise(e)};a.b("when",a.Wd);(function(){a.w={M:function(b){switch(a.a.R(b)){case "option":return!0===b.__ko__hasDomDataOptionValue__?
a.a.g.get(b,a.c.options.$b):7>=a.a.W?b.getAttributeNode("value")&&b.getAttributeNode("value").specified?b.value:b.text:b.value;case "select":return 0<=b.selectedIndex?a.w.M(b.options[b.selectedIndex]):n;default:return b.value}},cb:function(b,c,d){switch(a.a.R(b)){case "option":"string"===typeof c?(a.a.g.set(b,a.c.options.$b,n),"__ko__hasDomDataOptionValue__"in b&&delete b.__ko__hasDomDataOptionValue__,b.value=c):(a.a.g.set(b,a.c.options.$b,c),b.__ko__hasDomDataOptionValue__=!0,b.value="number"===
typeof c?c:"");break;case "select":if(""===c||null===c)c=n;for(var e=-1,f=0,g=b.options.length,h;f<g;++f)if(h=a.w.M(b.options[f]),h==c||""===h&&c===n){e=f;break}if(d||0<=e||c===n&&1<b.size)b.selectedIndex=e,6===a.a.W&&a.a.setTimeout(function(){b.selectedIndex=e},0);break;default:if(null===c||c===n)c="";b.value=c}}}})();a.b("selectExtensions",a.w);a.b("selectExtensions.readValue",a.w.M);a.b("selectExtensions.writeValue",a.w.cb);a.m=function(){function b(b){b=a.a.Db(b);123===b.charCodeAt(0)&&(b=b.slice(1,
-1));b+="\n,";var c=[],d=b.match(e),p,q=[],h=0;if(1<d.length){for(var x=0,B;B=d[x];++x){var u=B.charCodeAt(0);if(44===u){if(0>=h){c.push(p&&q.length?{key:p,value:q.join("")}:{unknown:p||q.join("")});p=h=0;q=[];continue}}else if(58===u){if(!h&&!p&&1===q.length){p=q.pop();continue}}else if(47===u&&1<B.length&&(47===B.charCodeAt(1)||42===B.charCodeAt(1)))continue;else 47===u&&x&&1<B.length?(u=d[x-1].match(f))&&!g[u[0]]&&(b=b.substr(b.indexOf(B)+1),d=b.match(e),x=-1,B="/"):40===u||123===u||91===u?++h:
41===u||125===u||93===u?--h:p||q.length||34!==u&&39!==u||(B=B.slice(1,-1));q.push(B)}if(0<h)throw Error("Unbalanced parentheses, braces, or brackets");}return c}var c=["true","false","null","undefined"],d=/^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i,e=RegExp("\"(?:\\\\.|[^\"])*\"|'(?:\\\\.|[^'])*'|`(?:\\\\.|[^`])*`|/\\*(?:[^*]|\\*+[^*/])*\\*+/|//.*\n|/(?:\\\\.|[^/])+/w*|[^\\s:,/][^,\"'`{}()/:[\\]]*[^\\s,\"'`{}()/:[\\]]|[^\\s]","g"),f=/[\])"'A-Za-z0-9_$]+$/,g={"in":1,"return":1,"typeof":1},
h={};return{Ra:[],wa:h,ac:b,vb:function(e,f){function l(b,e){var f;if(!x){var k=a.getBindingHandler(b);if(k&&k.preprocess&&!(e=k.preprocess(e,b,l)))return;if(k=h[b])f=e,0<=a.a.A(c,f)?f=!1:(k=f.match(d),f=null===k?!1:k[1]?"Object("+k[1]+")"+k[2]:f),k=f;k&&q.push("'"+("string"==typeof h[b]?h[b]:b)+"':function(_z){"+f+"=_z}")}g&&(e="function(){return "+e+" }");p.push("'"+b+"':"+e)}f=f||{};var p=[],q=[],g=f.valueAccessors,x=f.bindingParams,B="string"===typeof e?b(e):e;a.a.D(B,function(a){l(a.key||a.unknown,
a.value)});q.length&&l("_ko_property_writers","{"+q.join(",")+" }");return p.join(",")},Id:function(a,b){for(var c=0;c<a.length;c++)if(a[c].key==b)return!0;return!1},eb:function(b,c,d,e,f){if(b&&a.O(b))!a.Za(b)||f&&b.v()===e||b(e);else if((b=c.get("_ko_property_writers"))&&b[d])b[d](e)}}}();a.b("expressionRewriting",a.m);a.b("expressionRewriting.bindingRewriteValidators",a.m.Ra);a.b("expressionRewriting.parseObjectLiteral",a.m.ac);a.b("expressionRewriting.preProcessBindings",a.m.vb);a.b("expressionRewriting._twoWayBindings",
a.m.wa);a.b("jsonExpressionRewriting",a.m);a.b("jsonExpressionRewriting.insertPropertyAccessorsIntoJson",a.m.vb);(function(){function b(a){return 8==a.nodeType&&g.test(f?a.text:a.nodeValue)}function c(a){return 8==a.nodeType&&h.test(f?a.text:a.nodeValue)}function d(d,e){for(var f=d,h=1,g=[];f=f.nextSibling;){if(c(f)&&(a.a.g.set(f,k,!0),h--,0===h))return g;g.push(f);b(f)&&h++}if(!e)throw Error("Cannot find closing comment tag to match: "+d.nodeValue);return null}function e(a,b){var c=d(a,b);return c?
0<c.length?c[c.length-1].nextSibling:a.nextSibling:null}var f=w&&"\x3c!--test--\x3e"===w.createComment("test").text,g=f?/^\x3c!--\s*ko(?:\s+([\s\S]+))?\s*--\x3e$/:/^\s*ko(?:\s+([\s\S]+))?\s*$/,h=f?/^\x3c!--\s*\/ko\s*--\x3e$/:/^\s*\/ko\s*$/,m={ul:!0,ol:!0},k="__ko_matchedEndComment__";a.h={ea:{},childNodes:function(a){return b(a)?d(a):a.childNodes},Ea:function(c){if(b(c)){c=a.h.childNodes(c);for(var d=0,e=c.length;d<e;d++)a.removeNode(c[d])}else a.a.Tb(c)},va:function(c,d){if(b(c)){a.h.Ea(c);for(var e=
c.nextSibling,f=0,k=d.length;f<k;f++)e.parentNode.insertBefore(d[f],e)}else a.a.va(c,d)},Vc:function(a,c){var d;b(a)?(d=a.nextSibling,a=a.parentNode):d=a.firstChild;d?c!==d&&a.insertBefore(c,d):a.appendChild(c)},Wb:function(c,d,e){e?(e=e.nextSibling,b(c)&&(c=c.parentNode),e?d!==e&&c.insertBefore(d,e):c.appendChild(d)):a.h.Vc(c,d)},firstChild:function(a){if(b(a))return!a.nextSibling||c(a.nextSibling)?null:a.nextSibling;if(a.firstChild&&c(a.firstChild))throw Error("Found invalid end comment, as the first child of "+
a);return a.firstChild},nextSibling:function(d){b(d)&&(d=e(d));if(d.nextSibling&&c(d.nextSibling)){var f=d.nextSibling;if(c(f)&&!a.a.g.get(f,k))throw Error("Found end comment without a matching opening comment, as child of "+d);return null}return d.nextSibling},Cd:b,Vd:function(a){return(a=(f?a.text:a.nodeValue).match(g))?a[1]:null},Sc:function(d){if(m[a.a.R(d)]){var f=d.firstChild;if(f){do if(1===f.nodeType){var k;k=f.firstChild;var h=null;if(k){do if(h)h.push(k);else if(b(k)){var g=e(k,!0);g?k=
g:h=[k]}else c(k)&&(h=[k]);while(k=k.nextSibling)}if(k=h)for(h=f.nextSibling,g=0;g<k.length;g++)h?d.insertBefore(k[g],h):d.appendChild(k[g])}while(f=f.nextSibling)}}}}})();a.b("virtualElements",a.h);a.b("virtualElements.allowedBindings",a.h.ea);a.b("virtualElements.emptyNode",a.h.Ea);a.b("virtualElements.insertAfter",a.h.Wb);a.b("virtualElements.prepend",a.h.Vc);a.b("virtualElements.setDomNodeChildren",a.h.va);(function(){a.ga=function(){this.nd={}};a.a.extend(a.ga.prototype,{nodeHasBindings:function(b){switch(b.nodeType){case 1:return null!=
b.getAttribute("data-bind")||a.j.getComponentNameForNode(b);case 8:return a.h.Cd(b);default:return!1}},getBindings:function(b,c){var d=this.getBindingsString(b,c),d=d?this.parseBindingsString(d,c,b):null;return a.j.tc(d,b,c,!1)},getBindingAccessors:function(b,c){var d=this.getBindingsString(b,c),d=d?this.parseBindingsString(d,c,b,{valueAccessors:!0}):null;return a.j.tc(d,b,c,!0)},getBindingsString:function(b){switch(b.nodeType){case 1:return b.getAttribute("data-bind");case 8:return a.h.Vd(b);default:return null}},
parseBindingsString:function(b,c,d,e){try{var f=this.nd,g=b+(e&&e.valueAccessors||""),h;if(!(h=f[g])){var m,k="with($context){with($data||{}){return{"+a.m.vb(b,e)+"}}}";m=new Function("$context","$element",k);h=f[g]=m}return h(c,d)}catch(l){throw l.message="Unable to parse bindings.\nBindings value: "+b+"\nMessage: "+l.message,l;}}});a.ga.instance=new a.ga})();a.b("bindingProvider",a.ga);(function(){function b(b){var c=(b=a.a.g.get(b,z))&&b.N;c&&(b.N=null,c.Tc())}function c(c,d,e){this.node=c;this.yc=
d;this.kb=[];this.H=!1;d.N||a.a.K.za(c,b);e&&e.N&&(e.N.kb.push(c),this.Kb=e)}function d(a){return function(){return a}}function e(a){return a()}function f(b){return a.a.Ga(a.u.G(b),function(a,c){return function(){return b()[c]}})}function g(b,c,e){return"function"===typeof b?f(b.bind(null,c,e)):a.a.Ga(b,d)}function h(a,b){return f(this.getBindings.bind(this,a,b))}function m(b,c){var d=a.h.firstChild(c);if(d){var e,f=a.ga.instance,l=f.preprocessNode;if(l){for(;e=d;)d=a.h.nextSibling(e),l.call(f,e);
d=a.h.firstChild(c)}for(;e=d;)d=a.h.nextSibling(e),k(b,e)}a.i.ma(c,a.i.H)}function k(b,c){var d=b,e=1===c.nodeType;e&&a.h.Sc(c);if(e||a.ga.instance.nodeHasBindings(c))d=p(c,null,b).bindingContextForDescendants;d&&!u[a.a.R(c)]&&m(d,c)}function l(b){var c=[],d={},e=[];a.a.P(b,function ca(f){if(!d[f]){var k=a.getBindingHandler(f);k&&(k.after&&(e.push(f),a.a.D(k.after,function(c){if(b[c]){if(-1!==a.a.A(e,c))throw Error("Cannot combine the following bindings, because they have a cyclic dependency: "+e.join(", "));
ca(c)}}),e.length--),c.push({key:f,Mc:k}));d[f]=!0}});return c}function p(b,c,d){var f=a.a.g.Ub(b,z,{}),k=f.hd;if(!c){if(k)throw Error("You cannot apply bindings multiple times to the same element.");f.hd=!0}k||(f.context=d);f.Zb||(f.Zb={});var g;if(c&&"function"!==typeof c)g=c;else{var p=a.ga.instance,q=p.getBindingAccessors||h,m=a.$(function(){if(g=c?c(d,b):q.call(p,b,d)){if(d[t])d[t]();if(d[B])d[B]()}return g},null,{l:b});g&&m.ja()||(m=null)}var x=d,u;if(g){var J=function(){return a.a.Ga(m?m():
g,e)},r=m?function(a){return function(){return e(m()[a])}}:function(a){return g[a]};J.get=function(a){return g[a]&&e(r(a))};J.has=function(a){return a in g};a.i.H in g&&a.i.subscribe(b,a.i.H,function(){var c=(0,g[a.i.H])();if(c){var d=a.h.childNodes(b);d.length&&c(d,a.Ec(d[0]))}});a.i.pa in g&&(x=a.i.Cb(b,d),a.i.subscribe(b,a.i.pa,function(){var c=(0,g[a.i.pa])();c&&a.h.firstChild(b)&&c(b)}));f=l(g);a.a.D(f,function(c){var d=c.Mc.init,e=c.Mc.update,f=c.key;if(8===b.nodeType&&!a.h.ea[f])throw Error("The binding '"+
f+"' cannot be used with virtual elements");try{"function"==typeof d&&a.u.G(function(){var a=d(b,r(f),J,x.$data,x);if(a&&a.controlsDescendantBindings){if(u!==n)throw Error("Multiple bindings ("+u+" and "+f+") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");u=f}}),"function"==typeof e&&a.$(function(){e(b,r(f),J,x.$data,x)},null,{l:b})}catch(k){throw k.message='Unable to process binding "'+f+": "+g[f]+'"\nMessage: '+k.message,
k;}})}f=u===n;return{shouldBindDescendants:f,bindingContextForDescendants:f&&x}}function q(b,c){return b&&b instanceof a.fa?b:new a.fa(b,n,n,c)}var t=a.a.Da("_subscribable"),x=a.a.Da("_ancestorBindingInfo"),B=a.a.Da("_dataDependency");a.c={};var u={script:!0,textarea:!0,template:!0};a.getBindingHandler=function(b){return a.c[b]};var J={};a.fa=function(b,c,d,e,f){function k(){var b=p?h():h,f=a.a.f(b);c?(a.a.extend(l,c),x in c&&(l[x]=c[x])):(l.$parents=[],l.$root=f,l.ko=a);l[t]=q;g?f=l.$data:(l.$rawData=
b,l.$data=f);d&&(l[d]=f);e&&e(l,c,f);if(c&&c[t]&&!a.S.o().Vb(c[t]))c[t]();m&&(l[B]=m);return l.$data}var l=this,g=b===J,h=g?n:b,p="function"==typeof h&&!a.O(h),q,m=f&&f.dataDependency;f&&f.exportDependencies?k():(q=a.xb(k),q.v(),q.ja()?q.equalityComparer=null:l[t]=n)};a.fa.prototype.createChildContext=function(b,c,d,e){!e&&c&&"object"==typeof c&&(e=c,c=e.as,d=e.extend);if(c&&e&&e.noChildContext){var f="function"==typeof b&&!a.O(b);return new a.fa(J,this,null,function(a){d&&d(a);a[c]=f?b():b},e)}return new a.fa(b,
this,c,function(a,b){a.$parentContext=b;a.$parent=b.$data;a.$parents=(b.$parents||[]).slice(0);a.$parents.unshift(a.$parent);d&&d(a)},e)};a.fa.prototype.extend=function(b,c){return new a.fa(J,this,null,function(c){a.a.extend(c,"function"==typeof b?b(c):b)},c)};var z=a.a.g.Z();c.prototype.Tc=function(){this.Kb&&this.Kb.N&&this.Kb.N.sd(this.node)};c.prototype.sd=function(b){a.a.Pa(this.kb,b);!this.kb.length&&this.H&&this.Cc()};c.prototype.Cc=function(){this.H=!0;this.yc.N&&!this.kb.length&&(this.yc.N=
null,a.a.K.yb(this.node,b),a.i.ma(this.node,a.i.pa),this.Tc())};a.i={H:"childrenComplete",pa:"descendantsComplete",subscribe:function(b,c,d,e,f){var k=a.a.g.Ub(b,z,{});k.Fa||(k.Fa=new a.T);f&&f.notifyImmediately&&k.Zb[c]&&a.u.G(d,e,[b]);return k.Fa.subscribe(d,e,c)},ma:function(b,c){var d=a.a.g.get(b,z);if(d&&(d.Zb[c]=!0,d.Fa&&d.Fa.notifySubscribers(b,c),c==a.i.H))if(d.N)d.N.Cc();else if(d.N===n&&d.Fa&&d.Fa.Wa(a.i.pa))throw Error("descendantsComplete event not supported for bindings on this node");
},Cb:function(b,d){var e=a.a.g.Ub(b,z,{});e.N||(e.N=new c(b,e,d[x]));return d[x]==e?d:d.extend(function(a){a[x]=e})}};a.Td=function(b){return(b=a.a.g.get(b,z))&&b.context};a.ib=function(b,c,d){1===b.nodeType&&a.h.Sc(b);return p(b,c,q(d))};a.ld=function(b,c,d){d=q(d);return a.ib(b,g(c,d,b),d)};a.Oa=function(a,b){1!==b.nodeType&&8!==b.nodeType||m(q(a),b)};a.vc=function(a,b,c){!v&&A.jQuery&&(v=A.jQuery);if(2>arguments.length){if(b=w.body,!b)throw Error("ko.applyBindings: could not find document.body; has the document been loaded?");
}else if(!b||1!==b.nodeType&&8!==b.nodeType)throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");k(q(a,c),b)};a.Dc=function(b){return!b||1!==b.nodeType&&8!==b.nodeType?n:a.Td(b)};a.Ec=function(b){return(b=a.Dc(b))?b.$data:n};a.b("bindingHandlers",a.c);a.b("bindingEvent",a.i);a.b("bindingEvent.subscribe",a.i.subscribe);a.b("bindingEvent.startPossiblyAsyncContentBinding",a.i.Cb);a.b("applyBindings",a.vc);a.b("applyBindingsToDescendants",a.Oa);
a.b("applyBindingAccessorsToNode",a.ib);a.b("applyBindingsToNode",a.ld);a.b("contextFor",a.Dc);a.b("dataFor",a.Ec)})();(function(b){function c(c,e){var k=Object.prototype.hasOwnProperty.call(f,c)?f[c]:b,l;k?k.subscribe(e):(k=f[c]=new a.T,k.subscribe(e),d(c,function(b,d){var e=!(!d||!d.synchronous);g[c]={definition:b,Gd:e};delete f[c];l||e?k.notifySubscribers(b):a.na.zb(function(){k.notifySubscribers(b)})}),l=!0)}function d(a,b){e("getConfig",[a],function(c){c?e("loadComponent",[a,c],function(a){b(a,
c)}):b(null,null)})}function e(c,d,f,l){l||(l=a.j.loaders.slice(0));var g=l.shift();if(g){var q=g[c];if(q){var t=!1;if(q.apply(g,d.concat(function(a){t?f(null):null!==a?f(a):e(c,d,f,l)}))!==b&&(t=!0,!g.suppressLoaderExceptions))throw Error("Component loaders must supply values by invoking the callback, not by returning values synchronously.");}else e(c,d,f,l)}else f(null)}var f={},g={};a.j={get:function(d,e){var f=Object.prototype.hasOwnProperty.call(g,d)?g[d]:b;f?f.Gd?a.u.G(function(){e(f.definition)}):
a.na.zb(function(){e(f.definition)}):c(d,e)},Bc:function(a){delete g[a]},oc:e};a.j.loaders=[];a.b("components",a.j);a.b("components.get",a.j.get);a.b("components.clearCachedDefinition",a.j.Bc)})();(function(){function b(b,c,d,e){function g(){0===--B&&e(h)}var h={},B=2,u=d.template;d=d.viewModel;u?f(c,u,function(c){a.j.oc("loadTemplate",[b,c],function(a){h.template=a;g()})}):g();d?f(c,d,function(c){a.j.oc("loadViewModel",[b,c],function(a){h[m]=a;g()})}):g()}function c(a,b,d){if("function"===typeof b)d(function(a){return new b(a)});
else if("function"===typeof b[m])d(b[m]);else if("instance"in b){var e=b.instance;d(function(){return e})}else"viewModel"in b?c(a,b.viewModel,d):a("Unknown viewModel value: "+b)}function d(b){switch(a.a.R(b)){case "script":return a.a.ua(b.text);case "textarea":return a.a.ua(b.value);case "template":if(e(b.content))return a.a.Ca(b.content.childNodes)}return a.a.Ca(b.childNodes)}function e(a){return A.DocumentFragment?a instanceof DocumentFragment:a&&11===a.nodeType}function f(a,b,c){"string"===typeof b.require?
T||A.require?(T||A.require)([b.require],function(a){a&&"object"===typeof a&&a.Xd&&a["default"]&&(a=a["default"]);c(a)}):a("Uses require, but no AMD loader is present"):c(b)}function g(a){return function(b){throw Error("Component '"+a+"': "+b);}}var h={};a.j.register=function(b,c){if(!c)throw Error("Invalid configuration for "+b);if(a.j.tb(b))throw Error("Component "+b+" is already registered");h[b]=c};a.j.tb=function(a){return Object.prototype.hasOwnProperty.call(h,a)};a.j.unregister=function(b){delete h[b];
a.j.Bc(b)};a.j.Fc={getConfig:function(b,c){c(a.j.tb(b)?h[b]:null)},loadComponent:function(a,c,d){var e=g(a);f(e,c,function(c){b(a,e,c,d)})},loadTemplate:function(b,c,f){b=g(b);if("string"===typeof c)f(a.a.ua(c));else if(c instanceof Array)f(c);else if(e(c))f(a.a.la(c.childNodes));else if(c.element)if(c=c.element,A.HTMLElement?c instanceof HTMLElement:c&&c.tagName&&1===c.nodeType)f(d(c));else if("string"===typeof c){var h=w.getElementById(c);h?f(d(h)):b("Cannot find element with ID "+c)}else b("Unknown element type: "+
c);else b("Unknown template value: "+c)},loadViewModel:function(a,b,d){c(g(a),b,d)}};var m="createViewModel";a.b("components.register",a.j.register);a.b("components.isRegistered",a.j.tb);a.b("components.unregister",a.j.unregister);a.b("components.defaultLoader",a.j.Fc);a.j.loaders.push(a.j.Fc);a.j.dd=h})();(function(){function b(b,e){var f=b.getAttribute("params");if(f){var f=c.parseBindingsString(f,e,b,{valueAccessors:!0,bindingParams:!0}),f=a.a.Ga(f,function(c){return a.o(c,null,{l:b})}),g=a.a.Ga(f,
function(c){var e=c.v();return c.ja()?a.o({read:function(){return a.a.f(c())},write:a.Za(e)&&function(a){c()(a)},l:b}):e});Object.prototype.hasOwnProperty.call(g,"$raw")||(g.$raw=f);return g}return{$raw:{}}}a.j.getComponentNameForNode=function(b){var c=a.a.R(b);if(a.j.tb(c)&&(-1!=c.indexOf("-")||"[object HTMLUnknownElement]"==""+b||8>=a.a.W&&b.tagName===c))return c};a.j.tc=function(c,e,f,g){if(1===e.nodeType){var h=a.j.getComponentNameForNode(e);if(h){c=c||{};if(c.component)throw Error('Cannot use the "component" binding on a custom element matching a component');
var m={name:h,params:b(e,f)};c.component=g?function(){return m}:m}}return c};var c=new a.ga;9>a.a.W&&(a.j.register=function(a){return function(b){return a.apply(this,arguments)}}(a.j.register),w.createDocumentFragment=function(b){return function(){var c=b(),f=a.j.dd,g;for(g in f);return c}}(w.createDocumentFragment))})();(function(){function b(b,c,d){c=c.template;if(!c)throw Error("Component '"+b+"' has no template");b=a.a.Ca(c);a.h.va(d,b)}function c(a,b,c){var d=a.createViewModel;return d?d.call(a,
b,c):b}var d=0;a.c.component={init:function(e,f,g,h,m){function k(){var a=l&&l.dispose;"function"===typeof a&&a.call(l);q&&q.s();p=l=q=null}var l,p,q,t=a.a.la(a.h.childNodes(e));a.h.Ea(e);a.a.K.za(e,k);a.o(function(){var g=a.a.f(f()),h,u;"string"===typeof g?h=g:(h=a.a.f(g.name),u=a.a.f(g.params));if(!h)throw Error("No component name specified");var n=a.i.Cb(e,m),z=p=++d;a.j.get(h,function(d){if(p===z){k();if(!d)throw Error("Unknown component '"+h+"'");b(h,d,e);var f=c(d,u,{element:e,templateNodes:t});
d=n.createChildContext(f,{extend:function(a){a.$component=f;a.$componentTemplateNodes=t}});f&&f.koDescendantsComplete&&(q=a.i.subscribe(e,a.i.pa,f.koDescendantsComplete,f));l=f;a.Oa(d,e)}})},null,{l:e});return{controlsDescendantBindings:!0}}};a.h.ea.component=!0})();var V={"class":"className","for":"htmlFor"};a.c.attr={update:function(b,c){var d=a.a.f(c())||{};a.a.P(d,function(c,d){d=a.a.f(d);var g=c.indexOf(":"),g="lookupNamespaceURI"in b&&0<g&&b.lookupNamespaceURI(c.substr(0,g)),h=!1===d||null===
d||d===n;h?g?b.removeAttributeNS(g,c):b.removeAttribute(c):d=d.toString();8>=a.a.W&&c in V?(c=V[c],h?b.removeAttribute(c):b[c]=d):h||(g?b.setAttributeNS(g,c,d):b.setAttribute(c,d));"name"===c&&a.a.Yc(b,h?"":d)})}};(function(){a.c.checked={after:["value","attr"],init:function(b,c,d){function e(){var e=b.checked,f=g();if(!a.S.Ya()&&(e||!m&&!a.S.qa())){var k=a.u.G(c);if(l){var q=p?k.v():k,z=t;t=f;z!==f?e&&(a.a.Na(q,f,!0),a.a.Na(q,z,!1)):a.a.Na(q,f,e);p&&a.Za(k)&&k(q)}else h&&(f===n?f=e:e||(f=n)),a.m.eb(k,
d,"checked",f,!0)}}function f(){var d=a.a.f(c()),e=g();l?(b.checked=0<=a.a.A(d,e),t=e):b.checked=h&&e===n?!!d:g()===d}var g=a.xb(function(){if(d.has("checkedValue"))return a.a.f(d.get("checkedValue"));if(q)return d.has("value")?a.a.f(d.get("value")):b.value}),h="checkbox"==b.type,m="radio"==b.type;if(h||m){var k=c(),l=h&&a.a.f(k)instanceof Array,p=!(l&&k.push&&k.splice),q=m||l,t=l?g():n;m&&!b.name&&a.c.uniqueName.init(b,function(){return!0});a.o(e,null,{l:b});a.a.B(b,"click",e);a.o(f,null,{l:b});
k=n}}};a.m.wa.checked=!0;a.c.checkedValue={update:function(b,c){b.value=a.a.f(c())}}})();a.c["class"]={update:function(b,c){var d=a.a.Db(a.a.f(c()));a.a.Eb(b,b.__ko__cssValue,!1);b.__ko__cssValue=d;a.a.Eb(b,d,!0)}};a.c.css={update:function(b,c){var d=a.a.f(c());null!==d&&"object"==typeof d?a.a.P(d,function(c,d){d=a.a.f(d);a.a.Eb(b,c,d)}):a.c["class"].update(b,c)}};a.c.enable={update:function(b,c){var d=a.a.f(c());d&&b.disabled?b.removeAttribute("disabled"):d||b.disabled||(b.disabled=!0)}};a.c.disable=
{update:function(b,c){a.c.enable.update(b,function(){return!a.a.f(c())})}};a.c.event={init:function(b,c,d,e,f){var g=c()||{};a.a.P(g,function(g){"string"==typeof g&&a.a.B(b,g,function(b){var k,l=c()[g];if(l){try{var p=a.a.la(arguments);e=f.$data;p.unshift(e);k=l.apply(e,p)}finally{!0!==k&&(b.preventDefault?b.preventDefault():b.returnValue=!1)}!1===d.get(g+"Bubble")&&(b.cancelBubble=!0,b.stopPropagation&&b.stopPropagation())}})})}};a.c.foreach={Rc:function(b){return function(){var c=b(),d=a.a.bc(c);
if(!d||"number"==typeof d.length)return{foreach:c,templateEngine:a.ba.Ma};a.a.f(c);return{foreach:d.data,as:d.as,noChildContext:d.noChildContext,includeDestroyed:d.includeDestroyed,afterAdd:d.afterAdd,beforeRemove:d.beforeRemove,afterRender:d.afterRender,beforeMove:d.beforeMove,afterMove:d.afterMove,templateEngine:a.ba.Ma}}},init:function(b,c){return a.c.template.init(b,a.c.foreach.Rc(c))},update:function(b,c,d,e,f){return a.c.template.update(b,a.c.foreach.Rc(c),d,e,f)}};a.m.Ra.foreach=!1;a.h.ea.foreach=
!0;a.c.hasfocus={init:function(b,c,d){function e(e){b.__ko_hasfocusUpdating=!0;var f=b.ownerDocument;if("activeElement"in f){var g;try{g=f.activeElement}catch(l){g=f.body}e=g===b}f=c();a.m.eb(f,d,"hasfocus",e,!0);b.__ko_hasfocusLastValue=e;b.__ko_hasfocusUpdating=!1}var f=e.bind(null,!0),g=e.bind(null,!1);a.a.B(b,"focus",f);a.a.B(b,"focusin",f);a.a.B(b,"blur",g);a.a.B(b,"focusout",g);b.__ko_hasfocusLastValue=!1},update:function(b,c){var d=!!a.a.f(c());b.__ko_hasfocusUpdating||b.__ko_hasfocusLastValue===
d||(d?b.focus():b.blur(),!d&&b.__ko_hasfocusLastValue&&b.ownerDocument.body.focus(),a.u.G(a.a.Fb,null,[b,d?"focusin":"focusout"]))}};a.m.wa.hasfocus=!0;a.c.hasFocus=a.c.hasfocus;a.m.wa.hasFocus="hasfocus";a.c.html={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.fc(b,c())}};(function(){function b(b,d,e){a.c[b]={init:function(b,c,h,m,k){var l,p,q={},t,x,n;if(d){m=h.get("as");var u=h.get("noChildContext");n=!(m&&u);q={as:m,noChildContext:u,exportDependencies:n}}x=(t=
"render"==h.get("completeOn"))||h.has(a.i.pa);a.o(function(){var h=a.a.f(c()),m=!e!==!h,u=!p,r;if(n||m!==l){x&&(k=a.i.Cb(b,k));if(m){if(!d||n)q.dataDependency=a.S.o();r=d?k.createChildContext("function"==typeof h?h:c,q):a.S.qa()?k.extend(null,q):k}u&&a.S.qa()&&(p=a.a.Ca(a.h.childNodes(b),!0));m?(u||a.h.va(b,a.a.Ca(p)),a.Oa(r,b)):(a.h.Ea(b),t||a.i.ma(b,a.i.H));l=m}},null,{l:b});return{controlsDescendantBindings:!0}}};a.m.Ra[b]=!1;a.h.ea[b]=!0}b("if");b("ifnot",!1,!0);b("with",!0)})();a.c.let={init:function(b,
c,d,e,f){c=f.extend(c);a.Oa(c,b);return{controlsDescendantBindings:!0}}};a.h.ea.let=!0;var Q={};a.c.options={init:function(b){if("select"!==a.a.R(b))throw Error("options binding applies only to SELECT elements");for(;0<b.length;)b.remove(0);return{controlsDescendantBindings:!0}},update:function(b,c,d){function e(){return a.a.jb(b.options,function(a){return a.selected})}function f(a,b,c){var d=typeof b;return"function"==d?b(a):"string"==d?a[b]:c}function g(c,d){if(x&&l)a.i.ma(b,a.i.H);else if(t.length){var e=
0<=a.a.A(t,a.w.M(d[0]));a.a.Zc(d[0],e);x&&!e&&a.u.G(a.a.Fb,null,[b,"change"])}}var h=b.multiple,m=0!=b.length&&h?b.scrollTop:null,k=a.a.f(c()),l=d.get("valueAllowUnset")&&d.has("value"),p=d.get("optionsIncludeDestroyed");c={};var q,t=[];l||(h?t=a.a.Mb(e(),a.w.M):0<=b.selectedIndex&&t.push(a.w.M(b.options[b.selectedIndex])));k&&("undefined"==typeof k.length&&(k=[k]),q=a.a.jb(k,function(b){return p||b===n||null===b||!a.a.f(b._destroy)}),d.has("optionsCaption")&&(k=a.a.f(d.get("optionsCaption")),null!==
k&&k!==n&&q.unshift(Q)));var x=!1;c.beforeRemove=function(a){b.removeChild(a)};k=g;d.has("optionsAfterRender")&&"function"==typeof d.get("optionsAfterRender")&&(k=function(b,c){g(0,c);a.u.G(d.get("optionsAfterRender"),null,[c[0],b!==Q?b:n])});a.a.ec(b,q,function(c,e,g){g.length&&(t=!l&&g[0].selected?[a.w.M(g[0])]:[],x=!0);e=b.ownerDocument.createElement("option");c===Q?(a.a.Bb(e,d.get("optionsCaption")),a.w.cb(e,n)):(g=f(c,d.get("optionsValue"),c),a.w.cb(e,a.a.f(g)),c=f(c,d.get("optionsText"),g),
a.a.Bb(e,c));return[e]},c,k);if(!l){var B;h?B=t.length&&e().length<t.length:B=t.length&&0<=b.selectedIndex?a.w.M(b.options[b.selectedIndex])!==t[0]:t.length||0<=b.selectedIndex;B&&a.u.G(a.a.Fb,null,[b,"change"])}(l||a.S.Ya())&&a.i.ma(b,a.i.H);a.a.wd(b);m&&20<Math.abs(m-b.scrollTop)&&(b.scrollTop=m)}};a.c.options.$b=a.a.g.Z();a.c.selectedOptions={init:function(b,c,d){function e(){var e=c(),f=[];a.a.D(b.getElementsByTagName("option"),function(b){b.selected&&f.push(a.w.M(b))});a.m.eb(e,d,"selectedOptions",
f)}function f(){var d=a.a.f(c()),e=b.scrollTop;d&&"number"==typeof d.length&&a.a.D(b.getElementsByTagName("option"),function(b){var c=0<=a.a.A(d,a.w.M(b));b.selected!=c&&a.a.Zc(b,c)});b.scrollTop=e}if("select"!=a.a.R(b))throw Error("selectedOptions binding applies only to SELECT elements");var g;a.i.subscribe(b,a.i.H,function(){g?e():(a.a.B(b,"change",e),g=a.o(f,null,{l:b}))},null,{notifyImmediately:!0})},update:function(){}};a.m.wa.selectedOptions=!0;a.c.style={update:function(b,c){var d=a.a.f(c()||
{});a.a.P(d,function(c,d){d=a.a.f(d);if(null===d||d===n||!1===d)d="";if(v)v(b).css(c,d);else if(/^--/.test(c))b.style.setProperty(c,d);else{c=c.replace(/-(\w)/g,function(a,b){return b.toUpperCase()});var g=b.style[c];b.style[c]=d;d===g||b.style[c]!=g||isNaN(d)||(b.style[c]=d+"px")}})}};a.c.submit={init:function(b,c,d,e,f){if("function"!=typeof c())throw Error("The value for a submit binding must be a function");a.a.B(b,"submit",function(a){var d,e=c();try{d=e.call(f.$data,b)}finally{!0!==d&&(a.preventDefault?
a.preventDefault():a.returnValue=!1)}})}};a.c.text={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.Bb(b,c())}};a.h.ea.text=!0;(function(){if(A&&A.navigator){var b=function(a){if(a)return parseFloat(a[1])},c=A.navigator.userAgent,d,e,f,g,h;(d=A.opera&&A.opera.version&&parseInt(A.opera.version()))||(h=b(c.match(/Edge\/([^ ]+)$/)))||b(c.match(/Chrome\/([^ ]+)/))||(e=b(c.match(/Version\/([^ ]+) Safari/)))||(f=b(c.match(/Firefox\/([^ ]+)/)))||(g=a.a.W||b(c.match(/MSIE ([^ ]+)/)))||
(g=b(c.match(/rv:([^ )]+)/)))}if(8<=g&&10>g)var m=a.a.g.Z(),k=a.a.g.Z(),l=function(b){var c=this.activeElement;(c=c&&a.a.g.get(c,k))&&c(b)},p=function(b,c){var d=b.ownerDocument;a.a.g.get(d,m)||(a.a.g.set(d,m,!0),a.a.B(d,"selectionchange",l));a.a.g.set(b,k,c)};a.c.textInput={init:function(b,c,k){function l(c,d){a.a.B(b,c,d)}function m(){var d=a.a.f(c());if(null===d||d===n)d="";L!==n&&d===L?a.a.setTimeout(m,4):b.value!==d&&(y=!0,b.value=d,y=!1,v=b.value)}function r(){w||(L=b.value,w=a.a.setTimeout(z,
4))}function z(){clearTimeout(w);L=w=n;var d=b.value;v!==d&&(v=d,a.m.eb(c(),k,"textInput",d))}var v=b.value,w,L,A=9==a.a.W?r:z,y=!1;g&&l("keypress",z);11>g&&l("propertychange",function(a){y||"value"!==a.propertyName||A(a)});8==g&&(l("keyup",z),l("keydown",z));p&&(p(b,A),l("dragend",r));(!g||9<=g)&&l("input",A);5>e&&"textarea"===a.a.R(b)?(l("keydown",r),l("paste",r),l("cut",r)):11>d?l("keydown",r):4>f?(l("DOMAutoComplete",z),l("dragdrop",z),l("drop",z)):h&&"number"===b.type&&l("keydown",r);l("change",
z);l("blur",z);a.o(m,null,{l:b})}};a.m.wa.textInput=!0;a.c.textinput={preprocess:function(a,b,c){c("textInput",a)}}})();a.c.uniqueName={init:function(b,c){if(c()){var d="ko_unique_"+ ++a.c.uniqueName.rd;a.a.Yc(b,d)}}};a.c.uniqueName.rd=0;a.c.using={init:function(b,c,d,e,f){var g;d.has("as")&&(g={as:d.get("as"),noChildContext:d.get("noChildContext")});c=f.createChildContext(c,g);a.Oa(c,b);return{controlsDescendantBindings:!0}}};a.h.ea.using=!0;a.c.value={init:function(b,c,d){var e=a.a.R(b),f="input"==
e;if(!f||"checkbox"!=b.type&&"radio"!=b.type){var g=[],h=d.get("valueUpdate"),m=!1,k=null;h&&("string"==typeof h?g=[h]:g=a.a.wc(h),a.a.Pa(g,"change"));var l=function(){k=null;m=!1;var e=c(),f=a.w.M(b);a.m.eb(e,d,"value",f)};!a.a.W||!f||"text"!=b.type||"off"==b.autocomplete||b.form&&"off"==b.form.autocomplete||-1!=a.a.A(g,"propertychange")||(a.a.B(b,"propertychange",function(){m=!0}),a.a.B(b,"focus",function(){m=!1}),a.a.B(b,"blur",function(){m&&l()}));a.a.D(g,function(c){var d=l;a.a.Ud(c,"after")&&
(d=function(){k=a.w.M(b);a.a.setTimeout(l,0)},c=c.substring(5));a.a.B(b,c,d)});var p;p=f&&"file"==b.type?function(){var d=a.a.f(c());null===d||d===n||""===d?b.value="":a.u.G(l)}:function(){var f=a.a.f(c()),g=a.w.M(b);if(null!==k&&f===k)a.a.setTimeout(p,0);else if(f!==g||g===n)"select"===e?(g=d.get("valueAllowUnset"),a.w.cb(b,f,g),g||f===a.w.M(b)||a.u.G(l)):a.w.cb(b,f)};if("select"===e){var q;a.i.subscribe(b,a.i.H,function(){q?d.get("valueAllowUnset")?p():l():(a.a.B(b,"change",l),q=a.o(p,null,{l:b}))},
null,{notifyImmediately:!0})}else a.a.B(b,"change",l),a.o(p,null,{l:b})}else a.ib(b,{checkedValue:c})},update:function(){}};a.m.wa.value=!0;a.c.visible={update:function(b,c){var d=a.a.f(c()),e="none"!=b.style.display;d&&!e?b.style.display="":!d&&e&&(b.style.display="none")}};a.c.hidden={update:function(b,c){a.c.visible.update(b,function(){return!a.a.f(c())})}};(function(b){a.c[b]={init:function(c,d,e,f,g){return a.c.event.init.call(this,c,function(){var a={};a[b]=d();return a},e,f,g)}}})("click");
a.ca=function(){};a.ca.prototype.renderTemplateSource=function(){throw Error("Override renderTemplateSource");};a.ca.prototype.createJavaScriptEvaluatorBlock=function(){throw Error("Override createJavaScriptEvaluatorBlock");};a.ca.prototype.makeTemplateSource=function(b,c){if("string"==typeof b){c=c||w;var d=c.getElementById(b);if(!d)throw Error("Cannot find template with ID "+b);return new a.C.F(d)}if(1==b.nodeType||8==b.nodeType)return new a.C.ia(b);throw Error("Unknown template type: "+b);};a.ca.prototype.renderTemplate=
function(a,c,d,e){a=this.makeTemplateSource(a,e);return this.renderTemplateSource(a,c,d,e)};a.ca.prototype.isTemplateRewritten=function(a,c){return!1===this.allowTemplateRewriting?!0:this.makeTemplateSource(a,c).data("isRewritten")};a.ca.prototype.rewriteTemplate=function(a,c,d){a=this.makeTemplateSource(a,d);c=c(a.text());a.text(c);a.data("isRewritten",!0)};a.b("templateEngine",a.ca);a.kc=function(){function b(b,c,d,h){b=a.m.ac(b);for(var m=a.m.Ra,k=0;k<b.length;k++){var l=b[k].key;if(Object.prototype.hasOwnProperty.call(m,
l)){var p=m[l];if("function"===typeof p){if(l=p(b[k].value))throw Error(l);}else if(!p)throw Error("This template engine does not support the '"+l+"' binding within its templates");}}d="ko.__tr_ambtns(function($context,$element){return(function(){return{ "+a.m.vb(b,{valueAccessors:!0})+" } })()},'"+d.toLowerCase()+"')";return h.createJavaScriptEvaluatorBlock(d)+c}var c=/(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'|[^>]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,
d=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{xd:function(b,c,d){c.isTemplateRewritten(b,d)||c.rewriteTemplate(b,function(b){return a.kc.Ld(b,c)},d)},Ld:function(a,f){return a.replace(c,function(a,c,d,e,l){return b(l,c,d,f)}).replace(d,function(a,c){return b(c,"\x3c!-- ko --\x3e","#comment",f)})},md:function(b,c){return a.aa.Xb(function(d,h){var m=d.nextSibling;m&&m.nodeName.toLowerCase()===c&&a.ib(m,b,h)})}}}();a.b("__tr_ambtns",a.kc.md);(function(){a.C={};a.C.F=function(b){if(this.F=b){var c=
a.a.R(b);this.ab="script"===c?1:"textarea"===c?2:"template"==c&&b.content&&11===b.content.nodeType?3:4}};a.C.F.prototype.text=function(){var b=1===this.ab?"text":2===this.ab?"value":"innerHTML";if(0==arguments.length)return this.F[b];var c=arguments[0];"innerHTML"===b?a.a.fc(this.F,c):this.F[b]=c};var b=a.a.g.Z()+"_";a.C.F.prototype.data=function(c){if(1===arguments.length)return a.a.g.get(this.F,b+c);a.a.g.set(this.F,b+c,arguments[1])};var c=a.a.g.Z();a.C.F.prototype.nodes=function(){var b=this.F;
if(0==arguments.length){var e=a.a.g.get(b,c)||{},f=e.lb||(3===this.ab?b.content:4===this.ab?b:n);if(!f||e.jd){var g=this.text();g&&g!==e.bb&&(f=a.a.Md(g,b.ownerDocument),a.a.g.set(b,c,{lb:f,bb:g,jd:!0}))}return f}e=arguments[0];this.ab!==n&&this.text("");a.a.g.set(b,c,{lb:e})};a.C.ia=function(a){this.F=a};a.C.ia.prototype=new a.C.F;a.C.ia.prototype.constructor=a.C.ia;a.C.ia.prototype.text=function(){if(0==arguments.length){var b=a.a.g.get(this.F,c)||{};b.bb===n&&b.lb&&(b.bb=b.lb.innerHTML);return b.bb}a.a.g.set(this.F,
c,{bb:arguments[0]})};a.b("templateSources",a.C);a.b("templateSources.domElement",a.C.F);a.b("templateSources.anonymousTemplate",a.C.ia)})();(function(){function b(b,c,d){var e;for(c=a.h.nextSibling(c);b&&(e=b)!==c;)b=a.h.nextSibling(e),d(e,b)}function c(c,d){if(c.length){var e=c[0],f=c[c.length-1],g=e.parentNode,h=a.ga.instance,m=h.preprocessNode;if(m){b(e,f,function(a,b){var c=a.previousSibling,d=m.call(h,a);d&&(a===e&&(e=d[0]||b),a===f&&(f=d[d.length-1]||c))});c.length=0;if(!e)return;e===f?c.push(e):
(c.push(e,f),a.a.Ua(c,g))}b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.vc(d,b)});b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.aa.cd(b,[d])});a.a.Ua(c,g)}}function d(a){return a.nodeType?a:0<a.length?a[0]:null}function e(b,e,f,h,m){m=m||{};var n=(b&&d(b)||f||{}).ownerDocument,B=m.templateEngine||g;a.kc.xd(f,B,n);f=B.renderTemplate(f,h,m,n);if("number"!=typeof f.length||0<f.length&&"number"!=typeof f[0].nodeType)throw Error("Template engine must return an array of DOM nodes");n=!1;switch(e){case "replaceChildren":a.h.va(b,
f);n=!0;break;case "replaceNode":a.a.Xc(b,f);n=!0;break;case "ignoreTargetNode":break;default:throw Error("Unknown renderMode: "+e);}n&&(c(f,h),m.afterRender&&a.u.G(m.afterRender,null,[f,h[m.as||"$data"]]),"replaceChildren"==e&&a.i.ma(b,a.i.H));return f}function f(b,c,d){return a.O(b)?b():"function"===typeof b?b(c,d):b}var g;a.gc=function(b){if(b!=n&&!(b instanceof a.ca))throw Error("templateEngine must inherit from ko.templateEngine");g=b};a.dc=function(b,c,h,m,t){h=h||{};if((h.templateEngine||g)==
n)throw Error("Set a template engine before calling renderTemplate");t=t||"replaceChildren";if(m){var x=d(m);return a.$(function(){var g=c&&c instanceof a.fa?c:new a.fa(c,null,null,null,{exportDependencies:!0}),n=f(b,g.$data,g),g=e(m,t,n,g,h);"replaceNode"==t&&(m=g,x=d(m))},null,{Sa:function(){return!x||!a.a.Sb(x)},l:x&&"replaceNode"==t?x.parentNode:x})}return a.aa.Xb(function(d){a.dc(b,c,h,d,"replaceNode")})};a.Qd=function(b,d,g,h,m){function x(b,c){a.u.G(a.a.ec,null,[h,b,u,g,r,c]);a.i.ma(h,a.i.H)}
function r(a,b){c(b,v);g.afterRender&&g.afterRender(b,a);v=null}function u(a,c){v=m.createChildContext(a,{as:z,noChildContext:g.noChildContext,extend:function(a){a.$index=c;z&&(a[z+"Index"]=c)}});var d=f(b,a,v);return e(h,"ignoreTargetNode",d,v,g)}var v,z=g.as,w=!1===g.includeDestroyed||a.options.foreachHidesDestroyed&&!g.includeDestroyed;if(w||g.beforeRemove||!a.Pc(d))return a.$(function(){var b=a.a.f(d)||[];"undefined"==typeof b.length&&(b=[b]);w&&(b=a.a.jb(b,function(b){return b===n||null===b||
!a.a.f(b._destroy)}));x(b)},null,{l:h});x(d.v());var A=d.subscribe(function(a){x(d(),a)},null,"arrayChange");A.l(h);return A};var h=a.a.g.Z(),m=a.a.g.Z();a.c.template={init:function(b,c){var d=a.a.f(c());if("string"==typeof d||"name"in d)a.h.Ea(b);else if("nodes"in d){d=d.nodes||[];if(a.O(d))throw Error('The "nodes" option must be a plain, non-observable array.');var e=d[0]&&d[0].parentNode;e&&a.a.g.get(e,m)||(e=a.a.Yb(d),a.a.g.set(e,m,!0));(new a.C.ia(b)).nodes(e)}else if(d=a.h.childNodes(b),0<d.length)e=
a.a.Yb(d),(new a.C.ia(b)).nodes(e);else throw Error("Anonymous template defined, but no template content was provided");return{controlsDescendantBindings:!0}},update:function(b,c,d,e,f){var g=c();c=a.a.f(g);d=!0;e=null;"string"==typeof c?c={}:(g="name"in c?c.name:b,"if"in c&&(d=a.a.f(c["if"])),d&&"ifnot"in c&&(d=!a.a.f(c.ifnot)),d&&!g&&(d=!1));"foreach"in c?e=a.Qd(g,d&&c.foreach||[],c,b,f):d?(d=f,"data"in c&&(d=f.createChildContext(c.data,{as:c.as,noChildContext:c.noChildContext,exportDependencies:!0})),
e=a.dc(g,d,c,b)):a.h.Ea(b);f=e;(c=a.a.g.get(b,h))&&"function"==typeof c.s&&c.s();a.a.g.set(b,h,!f||f.ja&&!f.ja()?n:f)}};a.m.Ra.template=function(b){b=a.m.ac(b);return 1==b.length&&b[0].unknown||a.m.Id(b,"name")?null:"This template engine does not support anonymous templates nested within its templates"};a.h.ea.template=!0})();a.b("setTemplateEngine",a.gc);a.b("renderTemplate",a.dc);a.a.Kc=function(a,c,d){if(a.length&&c.length){var e,f,g,h,m;for(e=f=0;(!d||e<d)&&(h=a[f]);++f){for(g=0;m=c[g];++g)if(h.value===
m.value){h.moved=m.index;m.moved=h.index;c.splice(g,1);e=g=0;break}e+=g}}};a.a.Pb=function(){function b(b,d,e,f,g){var h=Math.min,m=Math.max,k=[],l,p=b.length,q,n=d.length,r=n-p||1,v=p+n+1,u,w,z;for(l=0;l<=p;l++)for(w=u,k.push(u=[]),z=h(n,l+r),q=m(0,l-1);q<=z;q++)u[q]=q?l?b[l-1]===d[q-1]?w[q-1]:h(w[q]||v,u[q-1]||v)+1:q+1:l+1;h=[];m=[];r=[];l=p;for(q=n;l||q;)n=k[l][q]-1,q&&n===k[l][q-1]?m.push(h[h.length]={status:e,value:d[--q],index:q}):l&&n===k[l-1][q]?r.push(h[h.length]={status:f,value:b[--l],index:l}):
(--q,--l,g.sparse||h.push({status:"retained",value:d[q]}));a.a.Kc(r,m,!g.dontLimitMoves&&10*p);return h.reverse()}return function(a,d,e){e="boolean"===typeof e?{dontLimitMoves:e}:e||{};a=a||[];d=d||[];return a.length<d.length?b(a,d,"added","deleted",e):b(d,a,"deleted","added",e)}}();a.b("utils.compareArrays",a.a.Pb);(function(){function b(b,c,d,h,m){var k=[],l=a.$(function(){var l=c(d,m,a.a.Ua(k,b))||[];0<k.length&&(a.a.Xc(k,l),h&&a.u.G(h,null,[d,l,m]));k.length=0;a.a.Nb(k,l)},null,{l:b,Sa:function(){return!a.a.kd(k)}});
return{Y:k,$:l.ja()?l:n}}var c=a.a.g.Z(),d=a.a.g.Z();a.a.ec=function(e,f,g,h,m,k){function l(b){y={Aa:b,pb:a.ta(w++)};v.push(y);r||F.push(y)}function p(b){y=t[b];w!==y.pb.v()&&D.push(y);y.pb(w++);a.a.Ua(y.Y,e);v.push(y)}function q(b,c){if(b)for(var d=0,e=c.length;d<e;d++)a.a.D(c[d].Y,function(a){b(a,d,c[d].Aa)})}f=f||[];"undefined"==typeof f.length&&(f=[f]);h=h||{};var t=a.a.g.get(e,c),r=!t,v=[],u=0,w=0,z=[],A=[],C=[],D=[],F=[],y,I=0;if(r)a.a.D(f,l);else{if(!k||t&&t._countWaitingForRemove){var E=
a.a.Mb(t,function(a){return a.Aa});k=a.a.Pb(E,f,{dontLimitMoves:h.dontLimitMoves,sparse:!0})}for(var E=0,G,H,K;G=k[E];E++)switch(H=G.moved,K=G.index,G.status){case "deleted":for(;u<K;)p(u++);H===n&&(y=t[u],y.$&&(y.$.s(),y.$=n),a.a.Ua(y.Y,e).length&&(h.beforeRemove&&(v.push(y),I++,y.Aa===d?y=null:C.push(y)),y&&z.push.apply(z,y.Y)));u++;break;case "added":for(;w<K;)p(u++);H!==n?(A.push(v.length),p(H)):l(G.value)}for(;w<f.length;)p(u++);v._countWaitingForRemove=I}a.a.g.set(e,c,v);q(h.beforeMove,D);a.a.D(z,
h.beforeRemove?a.oa:a.removeNode);var M,O,P;try{P=e.ownerDocument.activeElement}catch(N){}if(A.length)for(;(E=A.shift())!=n;){y=v[E];for(M=n;E;)if((O=v[--E].Y)&&O.length){M=O[O.length-1];break}for(f=0;u=y.Y[f];M=u,f++)a.h.Wb(e,u,M)}for(E=0;y=v[E];E++){y.Y||a.a.extend(y,b(e,g,y.Aa,m,y.pb));for(f=0;u=y.Y[f];M=u,f++)a.h.Wb(e,u,M);!y.Ed&&m&&(m(y.Aa,y.Y,y.pb),y.Ed=!0,M=y.Y[y.Y.length-1])}P&&e.ownerDocument.activeElement!=P&&P.focus();q(h.beforeRemove,C);for(E=0;E<C.length;++E)C[E].Aa=d;q(h.afterMove,D);
q(h.afterAdd,F)}})();a.b("utils.setDomNodeChildrenFromArrayMapping",a.a.ec);a.ba=function(){this.allowTemplateRewriting=!1};a.ba.prototype=new a.ca;a.ba.prototype.constructor=a.ba;a.ba.prototype.renderTemplateSource=function(b,c,d,e){if(c=(9>a.a.W?0:b.nodes)?b.nodes():null)return a.a.la(c.cloneNode(!0).childNodes);b=b.text();return a.a.ua(b,e)};a.ba.Ma=new a.ba;a.gc(a.ba.Ma);a.b("nativeTemplateEngine",a.ba);(function(){a.$a=function(){var a=this.Hd=function(){if(!v||!v.tmpl)return 0;try{if(0<=v.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();
this.renderTemplateSource=function(b,e,f,g){g=g||w;f=f||{};if(2>a)throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");var h=b.data("precompiled");h||(h=b.text()||"",h=v.template(null,"{{ko_with $item.koBindingContext}}"+h+"{{/ko_with}}"),b.data("precompiled",h));b=[e.$data];e=v.extend({koBindingContext:e},f.templateOptions);e=v.tmpl(h,b,e);e.appendTo(g.createElement("div"));v.fragments={};return e};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+
a+" })()) }}"};this.addTemplate=function(a,b){w.write("<script type='text/html' id='"+a+"'>"+b+"\x3c/script>")};0<a&&(v.tmpl.tag.ko_code={open:"__.push($1 || '');"},v.tmpl.tag.ko_with={open:"with($1) {",close:"} "})};a.$a.prototype=new a.ca;a.$a.prototype.constructor=a.$a;var b=new a.$a;0<b.Hd&&a.gc(b);a.b("jqueryTmplTemplateEngine",a.$a)})()})})();})();

/// Knockout Mapping plugin v2.4.0
/// (c) 2013 Steven Sanderson, Roy Jacobs - http://knockoutjs.com/
/// License: MIT (http://www.opensource.org/licenses/mit-license.php)
(function (factory) {
	// Module systems magic dance.

	if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
		// CommonJS or Node: hard-coded dependency on "knockout"
		factory(require("knockout"), exports);
	} else if (typeof define === "function" && define["amd"]) {
		// AMD anonymous module with hard-coded dependency on "knockout"
		define(["knockout", "exports"], factory);
	} else {
		// <script> tag: use the global `ko` object, attaching a `mapping` property
		factory(ko, ko.mapping = {});
	}
}(function (ko, exports) {
	var DEBUG=true;
	var mappingProperty = "__ko_mapping__";
	var realKoDependentObservable = ko.dependentObservable;
	var mappingNesting = 0;
	var dependentObservables;
	var visitedObjects;
	var recognizedRootProperties = ["create", "update", "key", "arrayChanged"];
	var emptyReturn = {};

	var _defaultOptions = {
		include: ["_destroy"],
		ignore: [],
		copy: [],
		observe: []
	};
	var defaultOptions = _defaultOptions;

	// Author: KennyTM @ StackOverflow
	function unionArrays (x, y) {
		var obj = {};
		for (var i = x.length - 1; i >= 0; -- i) obj[x[i]] = x[i];
		for (var i = y.length - 1; i >= 0; -- i) obj[y[i]] = y[i];
		var res = [];

		for (var k in obj) {
			res.push(obj[k]);
		};

		return res;
	}

	function extendObject(destination, source) {
		var destType;

		for (var key in source) {
			if (source.hasOwnProperty(key) && source[key]) {
				destType = exports.getType(destination[key]);
				if (key && destination[key] && destType !== "array" && destType !== "string") {
					extendObject(destination[key], source[key]);
				} else {
					var bothArrays = exports.getType(destination[key]) === "array" && exports.getType(source[key]) === "array";
					if (bothArrays) {
						destination[key] = unionArrays(destination[key], source[key]);
					} else {
						destination[key] = source[key];
					}
				}
			}
		}
	}

	function merge(obj1, obj2) {
		var merged = {};
		extendObject(merged, obj1);
		extendObject(merged, obj2);

		return merged;
	}

	exports.isMapped = function (viewModel) {
		var unwrapped = ko.utils.unwrapObservable(viewModel);
		return unwrapped && unwrapped[mappingProperty];
	}

	exports.fromJS = function (jsObject /*, inputOptions, target*/ ) {
		if (arguments.length == 0) throw new Error("When calling ko.fromJS, pass the object you want to convert.");

		try {
			if (!mappingNesting++) {
				dependentObservables = [];
				visitedObjects = new objectLookup();
			}

			var options;
			var target;

			if (arguments.length == 2) {
				if (arguments[1][mappingProperty]) {
					target = arguments[1];
				} else {
					options = arguments[1];
				}
			}
			if (arguments.length == 3) {
				options = arguments[1];
				target = arguments[2];
			}

			if (target) {
				options = merge(options, target[mappingProperty]);
			}
			options = fillOptions(options);

			var result = updateViewModel(target, jsObject, options);
			if (target) {
				result = target;
			}

			// Evaluate any dependent observables that were proxied.
			// Do this after the model's observables have been created
			if (!--mappingNesting) {
				while (dependentObservables.length) {
					var DO = dependentObservables.pop();
					if (DO) DO();
				}
			}

			// Save any new mapping options in the view model, so that updateFromJS can use them later.
			result[mappingProperty] = merge(result[mappingProperty], options);

			return result;
		} catch(e) {
			mappingNesting = 0;
			throw e;
		}
	};

	exports.fromJSON = function (jsonString /*, options, target*/ ) {
		var parsed = ko.utils.parseJson(jsonString);
		arguments[0] = parsed;
		return exports.fromJS.apply(this, arguments);
	};

	exports.updateFromJS = function (viewModel) {
		throw new Error("ko.mapping.updateFromJS, use ko.mapping.fromJS instead. Please note that the order of parameters is different!");
	};

	exports.updateFromJSON = function (viewModel) {
		throw new Error("ko.mapping.updateFromJSON, use ko.mapping.fromJSON instead. Please note that the order of parameters is different!");
	};

	exports.toJS = function (rootObject, options) {
		if (!defaultOptions) exports.resetDefaultOptions();

		if (arguments.length == 0) throw new Error("When calling ko.mapping.toJS, pass the object you want to convert.");
		if (exports.getType(defaultOptions.ignore) !== "array") throw new Error("ko.mapping.defaultOptions().ignore should be an array.");
		if (exports.getType(defaultOptions.include) !== "array") throw new Error("ko.mapping.defaultOptions().include should be an array.");
		if (exports.getType(defaultOptions.copy) !== "array") throw new Error("ko.mapping.defaultOptions().copy should be an array.");

		// Merge in the options used in fromJS
		options = fillOptions(options, rootObject[mappingProperty]);

		// We just unwrap everything at every level in the object graph
		return exports.visitModel(rootObject, function (x) {
			return ko.utils.unwrapObservable(x)
		}, options);
	};

	exports.toJSON = function (rootObject, options) {
		var plainJavaScriptObject = exports.toJS(rootObject, options);
		return ko.utils.stringifyJson(plainJavaScriptObject);
	};

	exports.defaultOptions = function () {
		if (arguments.length > 0) {
			defaultOptions = arguments[0];
		} else {
			return defaultOptions;
		}
	};

	exports.resetDefaultOptions = function () {
		defaultOptions = {
			include: _defaultOptions.include.slice(0),
			ignore: _defaultOptions.ignore.slice(0),
			copy: _defaultOptions.copy.slice(0)
		};
	};

	exports.getType = function(x) {
		if ((x) && (typeof (x) === "object")) {
			if (x.constructor === Date) return "date";
			if (x.constructor === Array) return "array";
		}
		return typeof x;
	}

	function fillOptions(rawOptions, otherOptions) {
		var options = merge({}, rawOptions);

		// Move recognized root-level properties into a root namespace
		for (var i = recognizedRootProperties.length - 1; i >= 0; i--) {
			var property = recognizedRootProperties[i];
			
			// Carry on, unless this property is present
			if (!options[property]) continue;
			
			// Move the property into the root namespace
			if (!(options[""] instanceof Object)) options[""] = {};
			options[""][property] = options[property];
			delete options[property];
		}

		if (otherOptions) {
			options.ignore = mergeArrays(otherOptions.ignore, options.ignore);
			options.include = mergeArrays(otherOptions.include, options.include);
			options.copy = mergeArrays(otherOptions.copy, options.copy);
			options.observe = mergeArrays(otherOptions.observe, options.observe);
		}
		options.ignore = mergeArrays(options.ignore, defaultOptions.ignore);
		options.include = mergeArrays(options.include, defaultOptions.include);
		options.copy = mergeArrays(options.copy, defaultOptions.copy);
		options.observe = mergeArrays(options.observe, defaultOptions.observe);

		options.mappedProperties = options.mappedProperties || {};
		options.copiedProperties = options.copiedProperties || {};
		return options;
	}

	function mergeArrays(a, b) {
		if (exports.getType(a) !== "array") {
			if (exports.getType(a) === "undefined") a = [];
			else a = [a];
		}
		if (exports.getType(b) !== "array") {
			if (exports.getType(b) === "undefined") b = [];
			else b = [b];
		}

		return ko.utils.arrayGetDistinctValues(a.concat(b));
	}

	// When using a 'create' callback, we proxy the dependent observable so that it doesn't immediately evaluate on creation.
	// The reason is that the dependent observables in the user-specified callback may contain references to properties that have not been mapped yet.
	function withProxyDependentObservable(dependentObservables, callback) {
		var localDO = ko.dependentObservable;
		ko.dependentObservable = function (read, owner, options) {
			options = options || {};

			if (read && typeof read == "object") { // mirrors condition in knockout implementation of DO's
				options = read;
			}

			var realDeferEvaluation = options.deferEvaluation;

			var isRemoved = false;

			// We wrap the original dependent observable so that we can remove it from the 'dependentObservables' list we need to evaluate after mapping has
			// completed if the user already evaluated the DO themselves in the meantime.
			var wrap = function (DO) {
				// Temporarily revert ko.dependentObservable, since it is used in ko.isWriteableObservable
				var tmp = ko.dependentObservable;
				ko.dependentObservable = realKoDependentObservable;
				var isWriteable = ko.isWriteableObservable(DO);
				ko.dependentObservable = tmp;

				var wrapped = realKoDependentObservable({
					read: function () {
						if (!isRemoved) {
							ko.utils.arrayRemoveItem(dependentObservables, DO);
							isRemoved = true;
						}
						return DO.apply(DO, arguments);
					},
					write: isWriteable && function (val) {
						return DO(val);
					},
					deferEvaluation: true
				});
				if (DEBUG) wrapped._wrapper = true;
				return wrapped;
			};
			
			options.deferEvaluation = true; // will either set for just options, or both read/options.
			var realDependentObservable = new realKoDependentObservable(read, owner, options);

			if (!realDeferEvaluation) {
				realDependentObservable = wrap(realDependentObservable);
				dependentObservables.push(realDependentObservable);
			}

			return realDependentObservable;
		}
		ko.dependentObservable.fn = realKoDependentObservable.fn;
		ko.computed = ko.dependentObservable;
		var result = callback();
		ko.dependentObservable = localDO;
		ko.computed = ko.dependentObservable;
		return result;
	}

	function updateViewModel(mappedRootObject, rootObject, options, parentName, parent, parentPropertyName, mappedParent) {
		var isArray = exports.getType(ko.utils.unwrapObservable(rootObject)) === "array";

		parentPropertyName = parentPropertyName || "";

		// If this object was already mapped previously, take the options from there and merge them with our existing ones.
		if (exports.isMapped(mappedRootObject)) {
			var previousMapping = ko.utils.unwrapObservable(mappedRootObject)[mappingProperty];
			options = merge(previousMapping, options);
		}

		var callbackParams = {
			data: rootObject,
			parent: mappedParent || parent
		};

		var hasCreateCallback = function () {
			return options[parentName] && options[parentName].create instanceof Function;
		};

		var createCallback = function (data) {
			return withProxyDependentObservable(dependentObservables, function () {
				
				if (ko.utils.unwrapObservable(parent) instanceof Array) {
					return options[parentName].create({
						data: data || callbackParams.data,
						parent: callbackParams.parent,
						skip: emptyReturn
					});
				} else {
					return options[parentName].create({
						data: data || callbackParams.data,
						parent: callbackParams.parent
					});
				}				
			});
		};

		var hasUpdateCallback = function () {
			return options[parentName] && options[parentName].update instanceof Function;
		};

		var updateCallback = function (obj, data) {
			var params = {
				data: data || callbackParams.data,
				parent: callbackParams.parent,
				target: ko.utils.unwrapObservable(obj)
			};

			if (ko.isWriteableObservable(obj)) {
				params.observable = obj;
			}

			return options[parentName].update(params);
		}

		var alreadyMapped = visitedObjects.get(rootObject);
		if (alreadyMapped) {
			return alreadyMapped;
		}

		parentName = parentName || "";

		if (!isArray) {
			// For atomic types, do a direct update on the observable
			if (!canHaveProperties(rootObject)) {
				switch (exports.getType(rootObject)) {
				case "function":
					if (hasUpdateCallback()) {
						if (ko.isWriteableObservable(rootObject)) {
							rootObject(updateCallback(rootObject));
							mappedRootObject = rootObject;
						} else {
							mappedRootObject = updateCallback(rootObject);
						}
					} else {
						mappedRootObject = rootObject;
					}
					break;
				default:
					if (ko.isWriteableObservable(mappedRootObject)) {
						if (hasUpdateCallback()) {
							var valueToWrite = updateCallback(mappedRootObject);
							mappedRootObject(valueToWrite);
							return valueToWrite;
						} else {
							var valueToWrite = ko.utils.unwrapObservable(rootObject);
							mappedRootObject(valueToWrite);
							return valueToWrite;
						}
					} else {
						var hasCreateOrUpdateCallback = hasCreateCallback() || hasUpdateCallback();
						
						if (hasCreateCallback()) {
							mappedRootObject = createCallback();
						} else {
							mappedRootObject = ko.observable(ko.utils.unwrapObservable(rootObject));
						}

						if (hasUpdateCallback()) {
							mappedRootObject(updateCallback(mappedRootObject));
						}
						
						if (hasCreateOrUpdateCallback) return mappedRootObject;
					}
				}

			} else {
				mappedRootObject = ko.utils.unwrapObservable(mappedRootObject);
				if (!mappedRootObject) {
					if (hasCreateCallback()) {
						var result = createCallback();

						if (hasUpdateCallback()) {
							result = updateCallback(result);
						}

						return result;
					} else {
						if (hasUpdateCallback()) {
							return updateCallback(result);
						}

						mappedRootObject = {};
					}
				}

				if (hasUpdateCallback()) {
					mappedRootObject = updateCallback(mappedRootObject);
				}

				visitedObjects.save(rootObject, mappedRootObject);
				if (hasUpdateCallback()) return mappedRootObject;

				// For non-atomic types, visit all properties and update recursively
				visitPropertiesOrArrayEntries(rootObject, function (indexer) {
					var fullPropertyName = parentPropertyName.length ? parentPropertyName + "." + indexer : indexer;

					if (ko.utils.arrayIndexOf(options.ignore, fullPropertyName) != -1) {
						return;
					}

					if (ko.utils.arrayIndexOf(options.copy, fullPropertyName) != -1) {
						mappedRootObject[indexer] = rootObject[indexer];
						return;
					}

					if(typeof rootObject[indexer] != "object" && typeof rootObject[indexer] != "array" && options.observe.length > 0 && ko.utils.arrayIndexOf(options.observe, fullPropertyName) == -1)
					{
						mappedRootObject[indexer] = rootObject[indexer];
						options.copiedProperties[fullPropertyName] = true;
						return;
					}
					
					// In case we are adding an already mapped property, fill it with the previously mapped property value to prevent recursion.
					// If this is a property that was generated by fromJS, we should use the options specified there
					var prevMappedProperty = visitedObjects.get(rootObject[indexer]);
					var retval = updateViewModel(mappedRootObject[indexer], rootObject[indexer], options, indexer, mappedRootObject, fullPropertyName, mappedRootObject);
					var value = prevMappedProperty || retval;
					
					if(options.observe.length > 0 && ko.utils.arrayIndexOf(options.observe, fullPropertyName) == -1)
					{
						mappedRootObject[indexer] = value();
						options.copiedProperties[fullPropertyName] = true;
						return;
					}
					
					if (ko.isWriteableObservable(mappedRootObject[indexer])) {
						mappedRootObject[indexer](ko.utils.unwrapObservable(value));
					} else {
						value = mappedRootObject[indexer] === undefined ? value : ko.utils.unwrapObservable(value);
						mappedRootObject[indexer] = value;
					}

					options.mappedProperties[fullPropertyName] = true;
				});
			}
		} else { //mappedRootObject is an array
			var changes = [];

			var hasKeyCallback = false;
			var keyCallback = function (x) {
				return x;
			}
			if (options[parentName] && options[parentName].key) {
				keyCallback = options[parentName].key;
				hasKeyCallback = true;
			}

			if (!ko.isObservable(mappedRootObject)) {
				// When creating the new observable array, also add a bunch of utility functions that take the 'key' of the array items into account.
				mappedRootObject = ko.observableArray([]);

				mappedRootObject.mappedRemove = function (valueOrPredicate) {
					var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) {
							return value === keyCallback(valueOrPredicate);
						};
					return mappedRootObject.remove(function (item) {
						return predicate(keyCallback(item));
					});
				}

				mappedRootObject.mappedRemoveAll = function (arrayOfValues) {
					var arrayOfKeys = filterArrayByKey(arrayOfValues, keyCallback);
					return mappedRootObject.remove(function (item) {
						return ko.utils.arrayIndexOf(arrayOfKeys, keyCallback(item)) != -1;
					});
				}

				mappedRootObject.mappedDestroy = function (valueOrPredicate) {
					var predicate = typeof valueOrPredicate == "function" ? valueOrPredicate : function (value) {
							return value === keyCallback(valueOrPredicate);
						};
					return mappedRootObject.destroy(function (item) {
						return predicate(keyCallback(item));
					});
				}

				mappedRootObject.mappedDestroyAll = function (arrayOfValues) {
					var arrayOfKeys = filterArrayByKey(arrayOfValues, keyCallback);
					return mappedRootObject.destroy(function (item) {
						return ko.utils.arrayIndexOf(arrayOfKeys, keyCallback(item)) != -1;
					});
				}

				mappedRootObject.mappedIndexOf = function (item) {
					var keys = filterArrayByKey(mappedRootObject(), keyCallback);
					var key = keyCallback(item);
					return ko.utils.arrayIndexOf(keys, key);
				}

				mappedRootObject.mappedCreate = function (value) {
					if (mappedRootObject.mappedIndexOf(value) !== -1) {
						throw new Error("There already is an object with the key that you specified.");
					}

					var item = hasCreateCallback() ? createCallback(value) : value;
					if (hasUpdateCallback()) {
						var newValue = updateCallback(item, value);
						if (ko.isWriteableObservable(item)) {
							item(newValue);
						} else {
							item = newValue;
						}
					}
					mappedRootObject.push(item);
					return item;
				}
			}

			var currentArrayKeys = filterArrayByKey(ko.utils.unwrapObservable(mappedRootObject), keyCallback).sort();
			var newArrayKeys = filterArrayByKey(rootObject, keyCallback);
			if (hasKeyCallback) newArrayKeys.sort();
			var editScript = ko.utils.compareArrays(currentArrayKeys, newArrayKeys);

			var ignoreIndexOf = {};
			
			var i, j;

			var unwrappedRootObject = ko.utils.unwrapObservable(rootObject);
			var itemsByKey = {};
			var optimizedKeys = true;
			for (i = 0, j = unwrappedRootObject.length; i < j; i++) {
				var key = keyCallback(unwrappedRootObject[i]);
				if (key === undefined || key instanceof Object) {
					optimizedKeys = false;
					break;
				}
				itemsByKey[key] = unwrappedRootObject[i];
			}

			var newContents = [];
			var passedOver = 0;
			for (i = 0, j = editScript.length; i < j; i++) {
				var key = editScript[i];
				var mappedItem;
				var fullPropertyName = parentPropertyName + "[" + i + "]";
				switch (key.status) {
				case "added":
					var item = optimizedKeys ? itemsByKey[key.value] : getItemByKey(ko.utils.unwrapObservable(rootObject), key.value, keyCallback);
					mappedItem = updateViewModel(undefined, item, options, parentName, mappedRootObject, fullPropertyName, parent);
					if(!hasCreateCallback()) {
						mappedItem = ko.utils.unwrapObservable(mappedItem);
					}

					var index = ignorableIndexOf(ko.utils.unwrapObservable(rootObject), item, ignoreIndexOf);
					
					if (mappedItem === emptyReturn) {
						passedOver++;
					} else {
						newContents[index - passedOver] = mappedItem;
					}
						
					ignoreIndexOf[index] = true;
					break;
				case "retained":
					var item = optimizedKeys ? itemsByKey[key.value] : getItemByKey(ko.utils.unwrapObservable(rootObject), key.value, keyCallback);
					mappedItem = getItemByKey(mappedRootObject, key.value, keyCallback);
					updateViewModel(mappedItem, item, options, parentName, mappedRootObject, fullPropertyName, parent);

					var index = ignorableIndexOf(ko.utils.unwrapObservable(rootObject), item, ignoreIndexOf);
					newContents[index] = mappedItem;
					ignoreIndexOf[index] = true;
					break;
				case "deleted":
					mappedItem = getItemByKey(mappedRootObject, key.value, keyCallback);
					break;
				}

				changes.push({
					event: key.status,
					item: mappedItem
				});
			}

			mappedRootObject(newContents);

			if (options[parentName] && options[parentName].arrayChanged) {
				ko.utils.arrayForEach(changes, function (change) {
					options[parentName].arrayChanged(change.event, change.item);
				});
			}
		}

		return mappedRootObject;
	}

	function ignorableIndexOf(array, item, ignoreIndices) {
		for (var i = 0, j = array.length; i < j; i++) {
			if (ignoreIndices[i] === true) continue;
			if (array[i] === item) return i;
		}
		return null;
	}

	function mapKey(item, callback) {
		var mappedItem;
		if (callback) mappedItem = callback(item);
		if (exports.getType(mappedItem) === "undefined") mappedItem = item;

		return ko.utils.unwrapObservable(mappedItem);
	}

	function getItemByKey(array, key, callback) {
		array = ko.utils.unwrapObservable(array);
		for (var i = 0, j = array.length; i < j; i++) {
			var item = array[i];
			if (mapKey(item, callback) === key) return item;
		}

		throw new Error("When calling ko.update*, the key '" + key + "' was not found!");
	}

	function filterArrayByKey(array, callback) {
		return ko.utils.arrayMap(ko.utils.unwrapObservable(array), function (item) {
			if (callback) {
				return mapKey(item, callback);
			} else {
				return item;
			}
		});
	}

	function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
		if (exports.getType(rootObject) === "array") {
			for (var i = 0; i < rootObject.length; i++)
			visitorCallback(i);
		} else {
			for (var propertyName in rootObject)
			visitorCallback(propertyName);
		}
	};

	function canHaveProperties(object) {
		var type = exports.getType(object);
		return ((type === "object") || (type === "array")) && (object !== null);
	}

	// Based on the parentName, this creates a fully classified name of a property

	function getPropertyName(parentName, parent, indexer) {
		var propertyName = parentName || "";
		if (exports.getType(parent) === "array") {
			if (parentName) {
				propertyName += "[" + indexer + "]";
			}
		} else {
			if (parentName) {
				propertyName += ".";
			}
			propertyName += indexer;
		}
		return propertyName;
	}

	exports.visitModel = function (rootObject, callback, options) {
		options = options || {};
		options.visitedObjects = options.visitedObjects || new objectLookup();

		var mappedRootObject;
		var unwrappedRootObject = ko.utils.unwrapObservable(rootObject);

		if (!canHaveProperties(unwrappedRootObject)) {
			return callback(rootObject, options.parentName);
		} else {
			options = fillOptions(options, unwrappedRootObject[mappingProperty]);

			// Only do a callback, but ignore the results
			callback(rootObject, options.parentName);
			mappedRootObject = exports.getType(unwrappedRootObject) === "array" ? [] : {};
		}

		options.visitedObjects.save(rootObject, mappedRootObject);

		var parentName = options.parentName;
		visitPropertiesOrArrayEntries(unwrappedRootObject, function (indexer) {
			if (options.ignore && ko.utils.arrayIndexOf(options.ignore, indexer) != -1) return;

			var propertyValue = unwrappedRootObject[indexer];
			options.parentName = getPropertyName(parentName, unwrappedRootObject, indexer);

			// If we don't want to explicitly copy the unmapped property...
			if (ko.utils.arrayIndexOf(options.copy, indexer) === -1) {
				// ...find out if it's a property we want to explicitly include
				if (ko.utils.arrayIndexOf(options.include, indexer) === -1) {
					// The mapped properties object contains all the properties that were part of the original object.
					// If a property does not exist, and it is not because it is part of an array (e.g. "myProp[3]"), then it should not be unmapped.
				    if (unwrappedRootObject[mappingProperty]
				        && unwrappedRootObject[mappingProperty].mappedProperties && !unwrappedRootObject[mappingProperty].mappedProperties[indexer]
				        && unwrappedRootObject[mappingProperty].copiedProperties && !unwrappedRootObject[mappingProperty].copiedProperties[indexer]
				        && !(exports.getType(unwrappedRootObject) === "array")) {
						return;
					}
				}
			}

			var outputProperty;
			switch (exports.getType(ko.utils.unwrapObservable(propertyValue))) {
			case "object":
			case "array":
			case "undefined":
				var previouslyMappedValue = options.visitedObjects.get(propertyValue);
				mappedRootObject[indexer] = (exports.getType(previouslyMappedValue) !== "undefined") ? previouslyMappedValue : exports.visitModel(propertyValue, callback, options);
				break;
			default:
				mappedRootObject[indexer] = callback(propertyValue, options.parentName);
			}
		});

		return mappedRootObject;
	}

	function simpleObjectLookup() {
		var keys = [];
		var values = [];
		this.save = function (key, value) {
			var existingIndex = ko.utils.arrayIndexOf(keys, key);
			if (existingIndex >= 0) values[existingIndex] = value;
			else {
				keys.push(key);
				values.push(value);
			}
		};
		this.get = function (key) {
			var existingIndex = ko.utils.arrayIndexOf(keys, key);
			var value = (existingIndex >= 0) ? values[existingIndex] : undefined;
			return value;
		};
	};
	
	function objectLookup() {
		var buckets = {};
		
		var findBucket = function(key) {
			var bucketKey;
			try {
				bucketKey = key;//JSON.stringify(key);
			}
			catch (e) {
				bucketKey = "$$$";
			}

			var bucket = buckets[bucketKey];
			if (bucket === undefined) {
				bucket = new simpleObjectLookup();
				buckets[bucketKey] = bucket;
			}
			return bucket;
		};
		
		this.save = function (key, value) {
			findBucket(key).save(key, value);
		};
		this.get = function (key) {
			return findBucket(key).get(key);
		};
	};
}));

// knockout-postbox 0.5.0 | (c) 2015 Ryan Niemeyer |  http://www.opensource.org/licenses/mit-license
;(function(factory) {
    //CommonJS
    if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
        factory(require("knockout"), exports);
    //AMD
    } else if (typeof define === "function" && define.amd) {
        define(["knockout", "exports"], factory);
    //normal script tag
    } else {
        factory(ko, ko.postbox = {});
    }
}(function(ko, exports, undefined) {
    var disposeTopicSubscription, existingSubscribe,
		subscriptions = {},
		subId = 1;

	exports.subscriptions = subscriptions;

    //create a global postbox that supports subscribing/publishing
    ko.subscribable.call(exports);

    //keep a cache of the latest value and subscribers
    exports.topicCache = {};

    //allow customization of the function used to serialize values for the topic cache
    exports.serializer = ko.toJSON;

    //wrap notifySubscribers passing topic first and caching latest value
    exports.publish = function(topic, value) {
        if (topic) {
            //keep the value and a serialized version for comparison
            exports.topicCache[topic] = {
                value: value,
                serialized: exports.serializer(value)
            };
            exports.notifySubscribers(value, topic);
        }
    };

    //provide a subscribe API for the postbox that takes in the topic as first arg
    existingSubscribe = exports.subscribe;
    exports.subscribe = function(topic, action, target, initializeWithLatestValue) {
        var subscription, current, existingDispose;

        if (topic) {
            if (typeof target === "boolean") {
                initializeWithLatestValue = target;
                target = undefined;
            }

            subscription = existingSubscribe.call(exports, action, target, topic);
			subscription.subId = ++subId;
			subscriptions[ subId ] = subscription;

            if (initializeWithLatestValue) {
                current = exports.topicCache[topic];

                if (current !== undefined) {
                    action.call(target, current.value);
                }
            }

			existingDispose = subscription.dispose;
			subscription.dispose = function() {
				delete subscriptions[subscription.subId];
				existingDispose.call(subscription);
			};

            return subscription;
        }
    };

	//clean up all subscriptions and references
	exports.reset = function() {
		var subscription;

		for (var id in subscriptions) {
			if (subscriptions.hasOwnProperty(id)) {
				subscription = subscriptions[id];

				if (subscription && typeof subscription.dispose === "function") {
					subscription.dispose();
				}
			}
		}

		exports.topicCache = {};
	};

    //by default publish when the previous cached value does not equal the new value
    exports.defaultComparer = function(newValue, cacheItem) {
        return cacheItem && exports.serializer(newValue) === cacheItem.serialized;
    };

    //augment observables/computeds with the ability to automatically publish updates on a topic
    ko.subscribable.fn.publishOn = function(topic, skipInitialOrEqualityComparer, equalityComparer) {
        var skipInitialPublish, subscription, existingDispose;

        if (topic) {
            //allow passing the equalityComparer as the second argument
            if (typeof skipInitialOrEqualityComparer === "function") {
                equalityComparer = skipInitialOrEqualityComparer;
            } else {
                skipInitialPublish = skipInitialOrEqualityComparer;
            }

            equalityComparer = equalityComparer || exports.defaultComparer;

            //remove any existing subs
            disposeTopicSubscription.call(this, topic, "publishOn");

            //keep a reference to the subscription, so we can stop publishing
            subscription = this.subscribe(function(newValue) {
				if (!equalityComparer.call(this, newValue, exports.topicCache[topic])) {
					exports.publish(topic, newValue);
				}
			}, this);

			//track the subscription in case of a reset
			subscription.id = ++subId;
			subscriptions[subId] = subscription;

			//ensure that we cleanup pointers to subscription on dispose
			existingDispose = subscription.dispose;
			subscription.dispose = function() {
				delete this.postboxSubs[topic].publishOn;
				delete subscriptions[subscription.id];

				existingDispose.call(subscription);
			}.bind(this);

			this.postboxSubs[topic].publishOn = subscription;

            //do an initial publish
            if (!skipInitialPublish) {
                exports.publish(topic, this());
            }
        }

        return this;
    };

    //handle disposing a subscription used to publish or subscribe to a topic
    disposeTopicSubscription = function(topic, type) {
        var subs = this.postboxSubs = this.postboxSubs || {};
        subs[topic] = subs[topic] || {};

        if (subs[topic][type]) {
            subs[topic][type].dispose();
        }
    };

    //discontinue automatically publishing on a topic
    ko.subscribable.fn.stopPublishingOn = function(topic) {
        disposeTopicSubscription.call(this, topic, "publishOn");

        return this;
    };

    //augment observables/computeds to automatically be updated by notifications on a topic
    ko.subscribable.fn.subscribeTo = function(topic, initializeWithLatestValueOrTransform, transform) {
        var initializeWithLatestValue, current, callback, subscription, existingDispose,
            self = this;

        //allow passing the filter as the second argument
        if (typeof initializeWithLatestValueOrTransform === "function") {
            transform = initializeWithLatestValueOrTransform;
        } else {
            initializeWithLatestValue = initializeWithLatestValueOrTransform;
        }

        if (topic && ko.isWriteableObservable(this)) {
            //remove any existing subs
            disposeTopicSubscription.call(this, topic, "subscribeTo");

            //if specified, apply a filter function in the subscription
            callback = function(newValue) {
                self(transform ? transform.call(self, newValue) : newValue);
            };

			////keep a reference to the subscription, so we can unsubscribe, if necessary
			subscription = exports.subscribe(topic, callback);
			this.postboxSubs[topic].subscribeTo = subscription;

			//ensure that we cleanup pointers to subscription on dispose
			existingDispose = subscription.dispose;
			subscription.dispose = function() {
				delete this.postboxSubs[topic].subscribeTo;
				existingDispose.call(subscription);
			}.bind(this);

            if (initializeWithLatestValue) {
                current = exports.topicCache[topic];

                if (current !== undefined) {
                    callback(current.value);
                }
            }
        }

        return this;
    };

    //discontinue receiving updates on a topic
    ko.subscribable.fn.unsubscribeFrom = function(topic) {
        disposeTopicSubscription.call(this, topic, "subscribeTo");

        return this;
    };

    // both subscribe and publish on the same topic
    //   -allows the ability to sync an observable/writeable computed/observableArray between view models
    //   -subscribeTo should really not use a filter function, as it would likely cause infinite recursion
    ko.subscribable.fn.syncWith = function(topic, initializeWithLatestValue, skipInitialOrEqualityComparer, equalityComparer) {
        this.subscribeTo(topic, initializeWithLatestValue).publishOn(topic, skipInitialOrEqualityComparer, equalityComparer);

        return this;
    };

    ko.postbox = exports;
}));

// Copyright 2023 Ellucian Company L.P. and its affiliates.
/// <reference path="admin.settings.js" />
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Provides KnockOut binding definitions for custom bindings that are specific to the Self-Service.
// This includes bindings for the calendar, accordions, tabs, etc.
///////////////////////////////////////////////////////////////////////////////////////////////////////////

// Add a subscribed function that will provide access to the previous value AND the new value
ko.subscribable.fn.subscribeChanged = function (callback) {
    var previousValue;
    var previousSubscription = this.subscribe(function (_previousValue) {
        previousValue = _previousValue;
    }, undefined, 'beforeChange');
    var latestSubscription = this.subscribe(function (latestValue) {
        callback(previousValue, latestValue);
    });

    return {
        previousSubscription: previousSubscription,
        latestSubscription: latestSubscription,
        dispose: function () {
            previousSubscription.dispose();
            latestSubscription.dispose();
        }
    }
};

// custom binding for a jQuery slide effect
ko.bindingHandlers.slide = {
    update: function (element, valueAccessor, allBindings) {
        // First get the latest data that we're bound to
        var value = valueAccessor();

        // Next, whether or not the supplied model property is observable, get its current value
        var valueUnwrapped = ko.utils.unwrapObservable(value);
        // Grab some more data from another binding property
        var duration = allBindings().duration || 400; // 400ms is default duration unless otherwise specified

        // Now manipulate the DOM element
        if (valueUnwrapped === true)
            $(element).slideDown(duration, function () { $(element).css('overflow', ''); }); // Make the element visible
        else
            $(element).slideUp(duration);   // Make the element invisible
    }
};

//custom binding to initialize a jQuery UI accordion
ko.bindingHandlers.jqAccordion = {
    init: function (element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};

        //handle disposal
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            $(element).accordion("destroy");
        });

        $(element).accordion(options);
    }
};

// custom binding to initialize a jQuery tab widget
ko.bindingHandlers.jqTabs = {
    update: function (element, valueAccessor, allBindingsAccessor) {

        // If the tabs are to be destroyed, do it and skip the rest.
        if (ko.utils.unwrapObservable(allBindingsAccessor().jqTabsDestroy) === true) {
            $(element).tabs("destroy");
        } else {
            var config = ko.utils.unwrapObservable(valueAccessor());

            //make sure that elements are set from template before calling tabs API
            setTimeout(function () {
                $(element).tabs("destroy").tabs(config);

                // Tabs may be hidden by default to prevent flicker; show them after jQuery builds them out
                $(element).show();
                $(element).css('visibility', 'visible');
            }, 0);
        }
    }
};

// Custom binding to insert photos, with default fallback on error
ko.bindingHandlers.photo = {
    update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        element.onerror = function () {
            element.src = defaultPhotoUrl;
        };
        element.src = ko.unwrap(valueAccessor());
    }
};

/*
 * Adds a readonly attribute to <input /> elements. readonly is a Boolean attribute that 
 * indicates that the user cannot modify the value of the control. 
 * HTML5 ignors readonly if the value of the input type attribute is hidden, range, color, checkbox, radio, file, or a button type (such as button or submit).
 * readonly is not a valid attribute of <select> element.
 * *****
 * Example:
 * 
 * JS:
 * viewModel : {
 *  isReadOnly: ko.observable(true),
 *  isNotReadOnly: false
 * }
 * 
 * Markup and result:
 * <input type='text' data-bind='readOnlyInput: isReadOnly' readonly='readonly'/>
 * <input type='text' data-bind='readOnlyInput: isNotReadOnly' />

 */
ko.bindingHandlers.readOnlyInput = {
    update: function (element, valueAccessor) {
        var show = ko.utils.unwrapObservable(valueAccessor());
        if (show) {
            $(element).attr('readonly', 'readonly');
        } else {
            $(element).removeAttr('readonly');
        }
    }
}


// Custom binding for forcing Knockout to skip certain child elements when doing recursive bindings.
// This can be helpful if certain data will not be available until after the Ready event and you need
// to leave the markup with bindings in place (especially nested bindings).
ko.bindingHandlers.skipBindings = {
    init: function () {
        return { controlsDescendantBindings: true };
    }
};
ko.virtualElements.allowedBindings.skipBindings = true;

// Utility method to insert an array into an observableArray without having to insert one-by-one (and thus cause a chain of updates)
ko.observableArray.fn.pushAll = function (valuesToPush) {
    var underlyingArray = this();
    this.valueWillMutate();
    ko.utils.arrayPushAll(underlyingArray, valuesToPush);
    this.valueHasMutated();
    return this;
};

// Utility method to replace the values in an observableArray with all the values in the given array without having to insert one-by-one (and thus cause a chain of updates)
ko.observableArray.fn.replaceAll = function (valuesToReplace) {
    var underlyingArray = this();
    this.valueWillMutate();
    this.removeAll();
    ko.utils.arrayPushAll(underlyingArray, valuesToReplace);
    this.valueHasMutated();
    return this;
}

// Knockout binding for a date field
ko.bindingHandlers.date = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        var underlyingObservable = valueAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(underlyingObservable);
        var format = allBindingsAccessor().format || "d";

        // The jQuery date formatter uses a few different tokens than .NET, so
        // change certain formats to their jQuery equivalents
        var jqFormat = "";
        switch (format) {
            case "m":
                jqFormat = "M";
                break;
            case "s":
                jqFormat = "S";
                break;
            case "y":
                jqFormat = "Y";
                break;
            default:
                jqFormat = format;
                break;
        }

        var strDate = "";
        if (valueUnwrapped) {
            try {
                var date = valueUnwrapped;

                // If date is not a Date object, attempt to parse a Date object from its value
                if (typeof date.getMonth !== 'function') {
                    date = parseIsoDate(valueUnwrapped);
                }
                if (date) {
                    // Globalize has no "g" or "G" option, so handle those now
                    switch (jqFormat) {
                        case "g":
                            // Short date, short time
                            strDate = Globalize.format(date, "d") + " " + Globalize.format(date, "t");
                            break;
                        case "G":
                            // Short date, long time
                            strDate = Globalize.format(date, "d") + " " + Globalize.format(date, "T");
                            break;
                        default:
                            strDate = Globalize.format(date, jqFormat);
                            break;
                    }
                }
            } catch (error) { /* no-op */ }
        }
        if ($(element).is("input")) {
            $(element).val(strDate);
        } else {
            $(element).text(strDate);
        }
    }
};

// Knockout binding for a currency field
// ===================================================================================================
// Summary
// ===================================================================================================
// This binding will take a numeric value and display it in currency format. 
// ===================================================================================================
// Options
// ===================================================================================================
// - precision: (optional) Number of decimal places to display for the formatted number
//      default: 2 (use two decimal places, i.e. $1.00)
// - negativePattern: (optional) Negative currency pattern for the formatted number
//      default: 0 (use parentheses for negative numbers, i.e. ($1.00) )
//      valid values: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 
//          (see negativeCurrencyPatterns in global.js to see which pattern corresponds to each value)
// ===================================================================================================
// Mark-Up Examples (2)
// ===================================================================================================
//  <!-- This will place a number formatted as currency inside the span with 2 decimal places (default)
//      and negative pattern 0 (default) -->
// 
//      <span data-bind="currency: 5"></span>
//      <span data-bind="currency: -5"></span>
//
//      $5.00
//      ($5.00)
//
//  <!-- This will place a number formatted as currency inside the span with no decimals
//      and negative currency pattern 1 (leading minus sign) -->
// 
//      <span data-bind="currency: 5, precision: 0, negativePattern: 1"></span>
//      <span data-bind="currency: -5, precision: 0, negativePattern: 1"></span>
//      
//      $5
//      -$5
// ===================================================================================================
ko.bindingHandlers.currency = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        var value = valueAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(value);
        //try to parse the value - it may be a formatted number such as "10,000.00"
        if (valueUnwrapped && isNaN(valueUnwrapped)) valueUnwrapped = Globalize.parseFloat(valueUnwrapped);
        var precision = allBindingsAccessor().precision;
        if (isNaN(precision)) {
            precision = 2;
        }
        var negativePattern = parseInt(allBindingsAccessor().negativePattern);
        var cultureSelector = Globalize.findClosestCulture();
        if (negativePattern && !isNaN(negativePattern) && negativePattern <= negativeCurrencyPatterns.length) {
            cultureSelector.numberFormat.currency.pattern[0] = negativeCurrencyPatterns[negativePattern]
        } else {
            cultureSelector.numberFormat.currency.pattern[0] = negativeCurrencyPatterns[0];
        }

        var amount = "";
        try {
            if (valueUnwrapped !== undefined && valueUnwrapped !== null && !isNaN(valueUnwrapped)) {
                amount = parseFloat(valueUnwrapped);
                amount = Globalize.format(amount, "c" + precision, cultureSelector);
            }
        } catch (error) { /* no-op */ }
        $(element).text(amount);
    }
};

// Knockout binding for a decimal field
ko.bindingHandlers.decimal = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        var value = valueAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(value);
        var precision = allBindingsAccessor().precision;
        if (isNaN(precision)) {
            precision = 2;
        }

        var amount = "";
        try {
            if (valueUnwrapped !== undefined && valueUnwrapped !== null) {
                if (isNaN(valueUnwrapped)) {
                    amount = Globalize.parseFloat(valueUnwrapped);
                } else {
                    amount = parseFloat(valueUnwrapped);
                }
                if (!isNaN(amount)) {
                    amount = Globalize.format(amount, "n" + precision);
                }
            }
        } catch (error) { /* no-op */ }
        $(element).text(amount);
    }
};

// Knockout binding for a percentage field
ko.bindingHandlers.percentage = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        var value = valueAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(value);
        var precision = allBindingsAccessor().precision;
        if (isNaN(precision)) {
            precision = 2;
        }

        var percent = "";
        try {
            if (valueUnwrapped !== undefined && valueUnwrapped !== null) {
                if (isNaN(valueUnwrapped)) {
                    percent = Globalize.parseFloat(valueUnwrapped);
                } else {
                    percent = parseFloat(valueUnwrapped);
                }
                if (!isNaN(percent)) {
                    percent = percent > 1 ? percent / 100 : percent;
                    percent = Globalize.format(percent, "p" + precision);
                }
            }
        } catch (error) { /* no-op */ }
        $(element).text(percent.replace(" ", ""));
    }
};

// Knockout binding for a phone number
ko.bindingHandlers.phone = {
    update: function (element, valueAccessor, allBindingsAccessor) {
        var value = valueAccessor();
        var valueUnwrapped = ko.utils.unwrapObservable(value);
        var phoneNumber = "tel:";

        if ($(element).is("a")) {
            try {
                if (valueUnwrapped !== undefined && valueUnwrapped !== null) {
                    phoneNumber += valueUnwrapped.replace(/\D/g, '');
                    $(element).attr("href", phoneNumber);
                }
            } catch (error) { /* no-op */ }

            $(element).text(valueUnwrapped);
        }
    }
};

// Knockout binding for an email address
// To use do mailto: {email: emailvalue, label: labelvalue}
// The label property is optional but adds the aria-label, title and alt attributes.
ko.bindingHandlers.mailto = {
    update: function (element, valueAccessor) {
        if ($(element).is("a")) {
            var email = ko.utils.unwrapObservable(valueAccessor().email);
            $(element).attr("href", "mailto:" + email);
            if (valueAccessor().label !== null) {
                var label = ko.utils.unwrapObservable(valueAccessor().label);
                $(element).attr("aria-label", label);
                $(element).attr("title", label);
                $(element).attr("alt", label);
            }
        }
    }
};

/*
Binding adds markup to an anchor that links to a url outside of the SelfService application.
External links will open in a new tab/window and have an offscreen "External Link" identifier that will be
read by screen readers. The external link message comes from _GlobalStringConstantDefinitions and Resources.SiteResources.

Binding depends on css class 'offScreen' that positions the offscreen text absolutely outside of the viewport

Arguments (both are required):
{
    href: 'the url to link to',
    label: 'the text to display in place of the url'
}

Markup Example:
<a data-bind="externalLink: { href: Url(), label: Title() }"></a>

Result Example:
<a data-bind="externalLink: { href: Url(), label: Title() }" href="http://www.google.com" target="_blank">
    Search Google
    <span class='offScreen'>External Link</span>
</a>
*/
ko.bindingHandlers.externalLink = {
    update: function (element, valueAccessor) {
        if ($(element).is("a")) {
            var params = ko.utils.unwrapObservable(valueAccessor()),
                href = ko.utils.unwrapObservable(params.href),
                label = ko.utils.unwrapObservable(params.label);

            if (label && href) {
                $(element)
                    .attr("href", href)
                    .attr("target", "_blank")
                    .html(label + "<span class='offScreen'>" + screenReaderExternalLinkMessage + "</span>");


            }
        }
    }
}

// Knockout binding for a sliding panel
// ===================================================================================================
// Summary
// ===================================================================================================
// This binding will create a panel of content (hidden by default) that will slide into the page when 
// its controller is clicked.  The binding is applied to the controller itself, which must be a <div> 
// element, and the panel is built from the binding options. 
// ===================================================================================================
// Options
// ===================================================================================================
// - contentId: ID of the element containing the content to be shown on the panel when it slides in
// - description: Text to describe the contents of the panel (needed for accessibility)
// - parentId: (optional) ID of the parent container that the panel will cover
//      default: The main page <div> element, whose ID is "#body"
// - backLinkLabel: (optional) Text to be used for the Back link label at the top of the panel 
//      default: GlobalResources.SiteResources.BackLinkLabel value
// - backLinkText: (optional) Text to be used for the Back link text at the top of the panel
//      default: GlobalResources.SiteResources.BackLinkText value
// - stickyElement: (optional) Sets the min-height before panel is loaded for smoother transaction.
//      valid values:   true/false
//      default: false
// ===================================================================================================
// Mark-Up Examples (2)
// ===================================================================================================
//  <!-- This will create a panel whose contents are the "panel-content-id" div element, described by
//      the panelDescription() KO observable, with none of the optional binding options, its back link label will use the 
//      GlobalResources.SiteResources.BackLinkLabel value, and its back link text will use the
//      GlobalResources.SiteResources.BackLinkText value -->
// 
//  <div data-bind="panel: { contentId: 'panel-content-id', description: panelDescription() }">
//      <span>Text for button that will slide in the panel</span>
//  </div>
//  <div id="panel-content-id" class="panel-content">
//      <span>Content of the sliding panel</span>
//  </div>
//
//
//
//  <!-- This will create a panel whose contents are the "panel-content-id" div element, described by
//      the panelDescription() KO observable, with parentId, and backLinkText binding options 
//      cover the "parent-container" element, its back link 
//      label will say "Go Back," and its back link text will say "Return to Previous Page" -->
// 
//  <div data-bind="panel: { contentId: 'panel-content-id', , description: panelDescription(), 
//      parentId: 'parent-container', backLinkLabel: 'Go Back', 
//      backLinkText: 'Return to Previous Page' }">
//      <span>Text for button that will slide in the panel</span>
//  </div>
//  <div id="panel-content-id" class="panel-content">
//      <span>Content of the sliding panel</span>
//  </div>
// ===================================================================================================
ko.bindingHandlers.panel = {
    init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};

        if ($(element).is("div,button,tr")) {
            var panelContentId = options.contentId ? options.contentId : null;
            var panelDescription = options.description ? options.description : null;
            var panelId = options.contentId ? options.contentId + "-panel" : null;
            var parentId = options.parentId ? options.parentId : "main-content";
            var panelBackLabel = options.backLinkLabel ? options.backLinkLabel : panelBackLinkString;
            var panelBackText = options.backLinkText ? options.backLinkText : panelBackTextString;
            var wrappingTable = "<table class='panel-table'><tbody></tbody></table>";
            var stickyElement = options.stickyElement ? options.stickyElement : false;;

            if (panelId !== null && typeof panelId !== 'undefined' &&
                panelDescription !== null && typeof panelDescription !== 'undefined' &&
                panelContentId !== null && typeof panelContentId !== 'undefined' &&
                parentId !== null && typeof parentId !== 'undefined' &&
                panelBackLabel !== null && typeof panelBackLabel !== 'undefined' &&
                panelBackText !== null && typeof panelBackText !== 'undefined') {

                // Create shortcut for panel content container identifier
                var bodyContentId = "#" + panelContentId;

                // Remove any duplicate instances of panelContent (in cases where panelContentId is dynamic)
                var idInstances = $('[id="' + panelContentId + '"]');
                var idCounter = 0;
                if (idInstances.length > 1) {
                    do {
                        $(idInstances[idCounter]).remove();
                        idCounter++;
                    } while (idCounter < idInstances.length - 1);
                }

                // Add "button" data role to <div> elements
                if ($(element).is("div")) {
                    $(element).attr("role", "button");
                }

                // Add accessibility and navigation attributes and mark-up to panel controller
                $(element).attr("tabindex", "0");
                $(element).addClass("panel-controller");
                $(element).attr("aria-controls", panelId);

                // Ensure that panel wrapper is not placed in an invalid location if using row and that arrow is placed inside last cell if using row
                var appendPanel = "<div id='" + panelId + "' class='panel-wrapper'><div id='" + panelId + "-back-link' class='panel-back-link-container' role='button' tabindex='0'> <h2 class='esg-h2--no-margin panel-back-link'><span class='esg-icon__container'> <svg class='esg-icon esg-icon--small esg-icon--left'> <use xlink:href='#icon-arrow'></use> </svg> </span> <span class='esg-icon__label'> " + panelBackLabel + "</span></h2><p>" + panelBackText + "</p></div></div>";
                var appendPanelDescription = "<span id='" + panelId + "-description' class='hide-content'>" + panelDescription + "</span>";
                if ($(element).is("tr")) {
                    $(element).parentsUntil('table').append(appendPanel);
                    $(element).parentsUntil('table').append(appendPanelDescription);
                }
                else {
                    $(element).append(appendPanel);
                    $(element).append(appendPanelDescription);
                }

                // Add attributes to panel container
                $("#" + panelId).attr("aria-labelledby", panelId + '-description');
                $("#" + panelId).attr("aria-hidden", true);
                $("#" + panelId).attr("role", "dialog");

                // Determine if the default parent is being used
                var defaultParent = (parentId === "main-content");

                // Add "panel-content" class to panel content container so that it is suppressed initially
                $(bodyContentId).addClass("panel-content");

                // ================================
                // Click event for panel controller
                // ================================
                $(element).on("click", function () {

                    // Add the panel content container to the panel
                    $("#" + panelId).append($(bodyContentId));

                    // Check for table contents and fix excess additions
                    var checkForTable = "#" + options.contentId;

                    if ($(checkForTable).is("tr") && ($(checkForTable).parent().length > 0)) {
                        // if panel contents is a row, wrap in a table to be semantically correct
                        $(checkForTable).wrap(wrappingTable);

                        // remove any empty panel-tables (panel removed on slide out, but wrapping table remains)
                        $('.panel-table tbody').filter(function (index) {
                            return $(this).children().length < 1;
                        }).parent().remove();
                    }

                    // Show the panel contents
                    $("#" + panelId + " " + bodyContentId).show();

                    // Create shortcut for panel identifier
                    var panel = "#" + $(this).attr("aria-controls")

                    // Set the panel positioning based on the parent container
                    if (defaultParent) {
                        $(panel).css("top", '0px');
                        $(panel).css("left", "0px");
                    }
                    else {
                        var parentPosition = $("#" + parentId).position();
                        $(panel).css("top", parentPosition.top);
                        $(panel).css("left", parentPosition.left);

                        // Only set the width if we're not looking at a table row. This should be ok
                        // for everybody, but we don't want to break the panel for people already using it.
                        if (!$(element).is("tr")) {
                            $(panel).css("width", $("#" + parentId).css("width"));
                        }
                        $(panel + " div.panel-back-link-container").addClass("inner-back-link");
                    }

                    // For nested panels, use the height of the parent panel back link as an offset
                    if ($("#" + parentId).parent().hasClass("panel-wrapper")) {
                        $(panel).attr("position", "fixed");
                        $(panel).css("top", "0px");
                    }

                    // Prepend the panel to its parent
                    $(panel).prependTo("#" + parentId);

                    // Set the panel height to the greater of the parent or the panel height, plus the back link container height
                    var parentHeight = $("#" + parentId).height();
                    parentHeight -= defaultParent ? $("#page-description").height() : 0;

                    var panelHeight = $(panel).height() - $(panel + " div.panel-back-link-container").height();
                    var newHeight = 0;
                    if (parentHeight > panelHeight) {
                        newHeight = parentHeight + $(panel + " div.panel-back-link-container").height() - 1;
                        $(panel).css("height", newHeight + 'px');
                    }
                    else {
                        newHeight = panelHeight + $(panel + " div.panel-back-link-container").height() - 1;
                        $(panel).css("height", newHeight + 'px');
                    }

                    // Once the animation completes, hide all non-panel content from the parent container, 
                    // reset the parent container height to the panel's height, then remove
                    // the aria-hidden property from the panel and set focus to panel content container
                    $(panel).slideToggle("slow").promise().done(function () {
                        if (defaultParent) {
                            $("#" + parentId).children().not("#" + panelId).hide();
                        } else {
                            var topLevelPanel = $("#" + parentId).parent();
                            $("#body").children().not(topLevelPanel).hide();
                        }
                        $(panel).attr("aria-hidden", false);
                        $("#" + parentId).css("height", $(panel).height() + 1 + 'px');
                        $("#" + panelId + " " + bodyContentId).trigger("focus");
                    });
                    //Avoid scroll to top if parent is sticky element.
                    if (!stickyElement) {
                        $(window).scrollTop(0);
                    }

                    //Set min height of the parent wrapper.
                    if (stickyElement) {
                        $("#" + parentId).parent().css("min-height", $(panel).children(".panel-back-link-container").height() + $(panel).children(".panel-table").height() + 150 + 'px');
                    }

                });

                // Mimic click handler when control is executed with spacebar or return key
                $(element).on("keypress", function (e) {
                    var code = e.which;
                    if ((code === 13) || (code === 32)) {
                        $(element).trigger("click");
                    }
                });

                // ================================
                // Click event for panel back control
                // ================================
                $("#" + panelId + " div.panel-back-link-container").on("click", function () {

                    if ($(this).parent().attr("aria-hidden") === "false") {
                        $(this).parent().slideToggle("slow").promise().done(function () {
                            // Remove the panel content, and set focus to parent
                            $("#" + parentId).remove("#" + panelId);
                            $("#" + parentId).trigger("focus");
                        });

                        // Reset the parent container height to its previous height
                        if (defaultParent) {
                            $("#" + parentId).css("height", "auto");
                        }
                        else {
                            $("#" + parentId).css("height", "auto");
                        }

                        // Re-apply the aria-hidden attribute to the panel container and re-display all non-panel content inside the parent
                        $(this).parent().attr("aria-hidden", true);
                        $("#" + parentId).children().not("#" + panelId).not(".panel-wrapper").not("script").show();

                        // Collapse any accordions embedded in the panel
                        var accordions = $("#" + panelId + " div.multi-accordion-expanded");
                        if (accordions.length > 0) {
                            for (i = 0; i < accordions.length; i++) {
                                $(accordions[i]).trigger("click");
                            }
                        }
                    }
                });

                // Mimic click handler when back link is executed with spacebar or return key
                $("#" + panelId + " div.panel-back-link-container").on("keypress", function (e) {
                    var code = e.which;
                    if ((code === 13) || (code === 32)) {
                        $("#" + panelId + " div.panel-back-link-container").trigger("click");
                    }
                });
            }
        }
    }
};

// Knockout binding for an accordion that supports multiple active accordions simultaneously
// ===================================================================================================
// Summary
// ===================================================================================================
// This binding will create an accordion that can be activated alongside other accordions at the same
// time.  
// 
// Accordion can be activated/deactivated by click, spacebar, or return key.
// ===================================================================================================
// Options
// ===================================================================================================
// {
//      contentId: ID of the element containing the content to be shown when the accordion is activated,
//      
//      expanded:  (optional) Flag indicating if the accordion is expanded by default
//          valid value: true (any non-"true" value will be treated as false)
//          default: false (accordion will be collapsed on page load),
//
//      inactive: (optional) Flag indicating whether the accordion should be inactive or not. If true (inactive)
//          valid value: true (any non-"true" value will be treated as false)
//          default: false (accordion will be active on page load),
// }
// ===================================================================================================
// Mark-Up Examples (2)
// ===================================================================================================
//  <!-- This will create an accordion whose content is the "panel-content-id" <div> element;
//      this accordion will be collapsed when the page loads -->
//  <div data-bind="multiAccordion: { contentId: 'panel-content-id' }">
//  </div>
//  <div id="panel-content-id">
//      <span>Content of the sliding panel</span>
//  </div>
//
//  <!-- This will create an accordion whose content is the "panel-content-id" <div> element;
//      this accordion will be expanded when the page loads -->
// 
//  <div data-bind="multiAccordion: { contentId: 'panel-content-id', expand: true }">
//  </div>
//  <div id="panel-content-id">
//      <span>Content of the sliding panel</span>
//  </div>
// ===================================================================================================
// Implementation Notes
// ===================================================================================================
// If the content referenced by your multiAccordion binding has a dynamic ID (i.e. its ID is set by
// a data binding ("attr: { id: someValue() }"), then you will need to take 3 steps to ensure proper 
// rendering.  If you do not take these steps, then your accordions will not work because the 
// multiAccordion binding will look for an element with an ID of "contentId" that has not yet been
// rendered.  Take these steps to remedy this issue:
//
// 1. Move the content element ABOVE the multiAccordion element within your mark-up.
// 2. Assign the content element an abritrary class name - something like "multi-accordion-content"
// 3. In the "complete" function of the AJAX call that loads your view data, add jQuery code to 
//    programmatically move the content element beneath the multiAccordion element.
//
// Example - in your mark-up:
// 
//  <div data-bind="attr: { id: someContentId() }" class="multi-accordion-content">
//      <span>Content of the sliding panel</span>
//  </div>
//  <div data-bind="multiAccordion: { contentId: someContentId() }">
//  </div>
//
// Example - in the "complete" function of your AJAX call:
//
// $(".multi-accordion-content").each(function () {
//    $(this).next().after($(this));
// });
//
//
// Inactive option:
//  This can be used when you don't have any "details" for which a drop down accordion would display. One implementation
//  example is on the Financial Aid SatisfactoryAcademicProgress.cshtml page. If no explanation exists for a detail item, don't
//  attach an accordion to that item and remove any visual indication that the item is clickable.
//  To accomplish this, we turn "off" all events attached to the DOM element, change the cursor style, and 
/// make the arrow invisible (but not hidden in order to preserve padding)
// ===================================================================================================
ko.bindingHandlers.multiAccordion = {
    init: function (element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};
        //Escape special characters in the contentId
        var contentId = options.contentId ? options.contentId.replace(/(:|;|\.|\,|\!|\?|\@|\#|\$|\%|\^|\&|\*|\(|\)|\[|\]|\{|\}|\\|\/)/g, "\\$1") : null;
        var expanded = options.expanded ? (options.expanded === true ? true : false) : false;
        var inactive = options.inactive || false;
        var replaceArrow;

        if (contentId !== null && typeof contentId !== 'undefined') {
            $(element).attr("tabindex", "0");
            $(element).addClass("multi-accordion-controller");
            $(element).attr("aria-controls", contentId);
            $("#" + contentId).attr("aria-labelledby", $(element).attr("id"));

            // Initialize the expandable content
            if ($(element).is('tr')) {
                if ($(element).hasClass("multi-accordion-expanded")) {
                    $(element).find('.expand-details').attr("aria-expanded", "true");
                    replaceArrow = $(element).find('.esg-icon--down');
                    $(replaceArrow).attr("class", "esg-icon esg-icon--small esg-icon--up");
                }
                else {
                    $(element).find('.expand-details').attr("aria-expanded", "false");
                    replaceArrow = $(element).find('.esg-icon--up');
                    $(replaceArrow).attr("class", "esg-icon esg-icon--small esg-icon--down");
                }
            }
            else {
                $(element).attr("role", "button");
                if (expanded) {
                    $(element).find('.expand-details').attr("aria-expanded", "false");
                    replaceArrow = $(element).find('.esg-icon--up');
                    $(replaceArrow).attr("class", "esg-icon esg-icon--small esg-icon--down");
                    $(element).addClass("multi-accordion-expanded");
                } else {
                    $(element).addClass("multi-accordion-collapsed");
                    $(element).find('.expand-details').attr("aria-expanded", "false");
                    replaceArrow = $(element).find('.esg-icon--up');
                    $(replaceArrow).attr("class", "esg-icon esg-icon--small esg-icon--down");
                    $("#" + contentId).hide();
                }
            }

            // Take action on click/select
            $(element).on("click", function () {
                if ($(element).hasClass("multi-accordion-expanded")) {
                    if (!$(element).is("tr")) {
                        $(element).find('.expand-details').attr("aria-expanded", "false");
                        replaceArrow = $(element).find('.esg-icon--up');
                        $(replaceArrow).attr("class", "esg-icon esg-icon--small esg-icon--down");
                        $(element).removeClass("multi-accordion-expanded");
                        $(element).attr("aria-expanded", false);
                        $(element).addClass("multi-accordion-collapsed");
                    }
                }
                else {
                    if (!$(element).is("tr")) {
                        $(element).find('.expand-details').attr("aria-expanded", "true");
                        replaceArrow = $(element).find('.esg-icon--down');
                        $(replaceArrow).attr("class", "esg-icon esg-icon--small esg-icon--up");
                        $(element).removeClass("multi-accordion-collapsed");
                        $(element).attr("aria-expanded", true);
                        $(element).addClass("multi-accordion-expanded");
                        $("#" + contentId).trigger("focus");
                    }
                }


                if ($(element).is("tr")) {
                    // if expanded, then close
                    if ($(element).hasClass("multi-accordion-expanded")) {
                        $(element).find('.expand-details').attr("aria-expanded", "false");
                        replaceArrow = $(element).find('.esg-icon--up');
                        $(replaceArrow).attr("class", "esg-icon esg-icon--small esg-icon--down");
                        $(element).removeClass("multi-accordion-expanded");
                        $(element).attr("aria-expanded", false);
                        $(element).addClass("multi-accordion-collapsed");

                        // Update ARIA for the button
                        $(element).find("button.expand-details").attr("aria-expanded", false);

                        // In order to show the animation we have a DIV inside our table row. Now, a TR cannot be animated so
                        // we need to animate the DIV first, then close the TR.
                        $(element).next().find('.accordion-table-wrapper').slideToggle(function () {
                            $(element).next().toggle();
                        });
                    }
                    // if collapsed, then open
                    else if ($(element).hasClass("multi-accordion-collapsed") || !$(element).hasClass("multi-accordion-expanded")) {
                        // The '.accordion-table-wrapper' DIV needs to be displayed when the page loads so once the row is selected we
                        // need to keep things in sync by first hiding the DIV, showing the TR, then animating the DIV.
                        $(element).next().find('.accordion-table-wrapper').hide();
                        $(element).next().toggle();
                        $(element).next().find('.accordion-table-wrapper').slideToggle();

                        $(element).find('.expand-details').attr("aria-expanded", "true");
                        replaceArrow = $(element).find('.esg-icon--down');
                        $(replaceArrow).attr("class", "esg-icon esg-icon--small esg-icon--up");
                        $(element).removeClass("multi-accordion-collapsed");
                        $(element).attr("aria-expanded", true);
                        $(element).addClass("multi-accordion-expanded");

                        // Update ARIA for the button
                        $(element).find("button.expand-details").attr("aria-expanded", true);

                        $("#" + contentId).trigger("focus");
                    }
                }
                else {
                    $("#" + contentId).slideToggle("slow");
                }
            });

            // Mimic click handler when master is executed with spacebar or return key
            $(element).on("keypress", function (e) {
                var code = e.which;
                if ((code === 13) || (code === 32)) {
                    $(element).trigger("click");
                }
            });


            //See Inactive option in documentation for more info.
            //Turn off event handlers. 
            //Change the cursour style not to indicate a button.
            //Replace arrow spans with 'no-arrow'
            if (inactive) {
                $(element)
                    .off()
                    .css("cursor", "default");

                // If accordion has no details, remove role, aria, tabindex so that it appears as normal text
                $(element).removeAttr("role aria-controls tabindex");
                $('.arrow', element).replaceWith("<span class='arrow no-arrow'></span>");
            }
        }
    }
};

// Knockout binding for an accordion expand all/ collapse all button
// ===================================================================================================
// Summary
// ===================================================================================================
// This binding will create a button that allows the end user to expand or collapse all accordions
// on a page.  The expand all / collapse all functionality is tied specifically to accordions built by
// the "multiAccordion" data binding.  The text of this button will alternate depending on the state
// of the page accordions; when all accordions are expanded, users will see the value of 
// GlobalResources.SiteResources.CollapseAllLabel, otherwise users will see the value of
// GlobalResources.SiteResources.ExpandAllLabel.  
// 
// Button can be activated/deactivated by click, spacebar, or return key.
//
// Its "reset" option specifices a KO observable that, when changed, will "reset" this control. Without
// this, the control may not show the correct label when page content changes.  
// 
// Its "expandMessage" and "collapseMessage" options specify announcements for screen readers to tell 
// the user a context-appropriate result of their expanding or collapsing all accordions on a page.
// ===================================================================================================
// Options
// ===================================================================================================
// - reset: Observable that, when changed, will force the element to be reset
// - expandMessage: Message to be announced by screen readers when expanding all accordions
// - collapseMessage: Message to be announced by screen readers when collapsing all accordions
// ===================================================================================================
// Mark-Up Examples (2)
// ===================================================================================================
// <input id="expand-collapse-button" type="button" 
//  data-bind="accordionMaster: { reset: observableThatKeysReset() }" >
//
// <input id="expand-collapse-button" type="button" 
//  data-bind="accordionMaster: { reset: observableThatKeysReset(), expandMessage: 'All accordions expanded.', 
//      collapseMessage: 'All accordions collapsed.' }" >
// ===================================================================================================
ko.bindingHandlers.accordionMaster = {
    init: function (element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};
        var expandedMessage = options.expandMessage ? options.expandMessage : null;
        var collapsedMessage = options.collapseMessage ? options.collapseMessage : null;

        // Update accordion classes when the master is clicked
        $(element).on("click", function () {
            var accordionCount = $(".multi-accordion-controller").length;
            var activeAccordionCount = $(".multi-accordion-expanded").length;
            if (activeAccordionCount < accordionCount) {
                var collapsedControllers = document.getElementsByClassName(".multi-accordion-collapsed");
                if (collapsedControllers.length > 0) {
                    for (i = 0; i < collapsedControllers.length; i++) {
                        $("#" + $(collapsedControllers[i]).attr("aria-controls")).slideToggle("slow");
                        $(collapsedControllers[i]).children('.expand-details').attr("aria-expanded", "true");
                        replaceArrow = $(collapsedControllers[i]).find('.esg-icon--down');
                        $(replaceArrow).attr("class", "esg-icon esg-icon--small esg-icon--up");
                        $(collapsedControllers[i]).addClass("multi-accordion-expanded");
                        $(collapsedControllers[i]).attr("aria-expanded", true);
                        $(collapsedControllers[i]).removeClass("multi-accordion-collapsed");
                    }
                }
                if ($("#aria-announcements").length > 0) {
                    $("#aria-announcements").text(expandedMessage);
                }
            }
            if (activeAccordionCount === accordionCount) {
                var expandedControllers = document.getElementsByClassName(".multi-accordion-expanded");
                if (expandedControllers.length > 0) {
                    for (i = 0; i < expandedControllers.length; i++) {
                        $("#" + $(expandedControllers[i]).attr("aria-controls")).slideToggle("slow");
                        $(collapsedControllers[i]).children('.expand-details').attr("aria-expanded", "false");
                        replaceArrow = $(collapsedControllers[i]).find('.esg-icon--up');
                        $(replaceArrow).attr("class", "esg-icon esg-icon--small esg-icon--down");
                        $(expandedControllers[i]).removeClass("multi-accordion-expanded");
                        $(expandedControllers[i]).attr("aria-expanded", false);
                        $(expandedControllers[i]).addClass("multi-accordion-collapsed");
                    }
                }
                if ($("#aria-announcements").length > 0) {
                    $("#aria-announcements").text(collapsedMessage);
                }
            }
            $(element).trigger("focus");
        });

        // Mimic click handler for non-button elements when master is executed with spacebar or return key
        $(element).on("keypress", function (e) {
            var code = e.which;

            // Validate whether element is native button
            // (Enter/space on a native HTML button will trigger a click event)
            var isButton = $(element).is("button");
            var isInputButton = $(element).is("input[type='button']");
            var isInputSubmit = $(element).is("input[type='submit']");

            // Ensure that element is NOT a native HTML button before triggering click
            if (!isButton && !isInputButton && !isInputSubmit) {
                if ((code === 13) || (code === 32)) {
                    $(element).trigger("click");
                }
            }
        });

        // Pick up any non-master accordion actions to properly update the master text
        $("body").on("click", function () {
            var accordionCount = $(".multi-accordion-controller").length;
            var activeAccordionCount = $(".multi-accordion-expanded").length;
            if (activeAccordionCount < accordionCount) {
                $(element).val(expandAllLabel);
            } else {
                $(element).val(collapseAllLabel);
            }
            $(element).attr("aria-label", $(element).val());
        });
    },
    update: function (element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};
        var resetter = options.reset ? options.reset : null;
        var accordionCount = $(".multi-accordion-controller").length;
        var activeAccordionCount = $(".multi-accordion-expanded").length;

        if (activeAccordionCount < accordionCount) {
            $(element).val(expandAllLabel);
            $(element).attr("aria-label", $(element).val());
        } else {
            $(element).val(collapseAllLabel);
            $(element).attr("aria-label", $(element).val());
        }
    }
}

// Knockout binding for an element that will hover at the top of the page when the user scrolls
// past its initial position
// ===================================================================================================
// Summary
// ===================================================================================================
// This binding will enable an element to hover at the top of the page when the user scrolls
// down past its initial position, and then "snap back" into its original position if the user scrolls 
// up past its initial position.
// 
// Its "reset" option specifices a KO observable that, when changed, will "reset" this control. Without
// this, the control may not behave correctly when the page is loaded.
// ===================================================================================================
// Options
// ===================================================================================================
// - reset: Observable that, when changed, will force the element to be reset
// ===================================================================================================
// Mark-Up Examples (2)
// ===================================================================================================
// <div id="hovering-element" data-bind="hover: { reset: observableThatKeysReset() }" >
// ===================================================================================================
ko.bindingHandlers.hover = {
    update: function (element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};
        var resetter = options.reset ? options.reset : null;

        // Note the element's initial top position
        var floatTop = $(element).position().top;
        var floatWidth = $(element).css("width");

        var topPos = 0;
        if ($("#proxy-banner").length) {
            topPos += $("#proxy-banner").outerHeight(true) + 'px';
        }

        // Hover the box when scrolling - the box will retain its initial display properties until the user scrolls
        // past its initial top position.  At that time, the box will begin hovering as the user scrolls further 
        // down the page.  If the user scrolls back up past the initial top position, then the box will "lock" back 
        // to its initial position
        $(window).scroll(function () {
            floatWidth = $(element).css("width");

            var scroll = $(window).scrollTop();
            if (scroll >= floatTop) {
                $(element).css("width", floatWidth);
                $(element).addClass("floating");
                $(element).css("top", topPos)
            }
            if (scroll < floatTop) {
                $(element).removeClass("floating");
                $(element).css("width", "auto");
                $(element).css("top", floatTop);
            }
        });

        // Maintain proper element width during screen resizing
        $(window).on("resize", function () {
            $(element).css("width", floatWidth);
        });
    }
}

// Knockout binding for an element that is repeated on the page that will stick to the top of the page
// when the user scrolls past its position relative to the window.
// ===================================================================================================
// Summary
// ===================================================================================================
// This binding will enable an element to stick to the top of the page when the user scrolls
// down past its position relative to the window, and then "snap back" into its original position if  
// the user scrolls up past its original position.
// 
// Its "mobile" option specifices whether the user is using a mobile device and its "ignoreProxy"
// option dictates whether or not the item should stick when the user is a proxy. The "uniqueId"
// option is required and used to identify the binding and allow the use of multiple stick bindings 
// on one page. For repetitive use on a single page, the ID must be followed by a '-' and whatever 
// mechanism to index your bindings.
// ===================================================================================================
// Options
// ===================================================================================================
// - mobile: Flag that indicates if a mobile device is being used
// - ignoreProxy - Flag that determines whether or not the proxy bar is ignored and the other header 
// will stick in place of it when accessed by a proxy user. Defaulted to true. True results in the bar 
// is ignored and this binding will stick, false the proxy bar stays and this binding will not stick. 
// - uniqueId: REQUIRED flag that indicates a unique identifier and the index of the element the 
// binding is applied to seperated by a '-'. Ensure the first element starts on 0 otherwise there will
// be issues with ignore proxy.
// - constrainToParent: Flag that indicates whether or not the stick binding will act on the entire 
// page versus constraining it to inside its parent. Defaulted to false. This should be set to true
// if using multiple bindings on a page.
// ===================================================================================================
// Mark-Up Example
// ===================================================================================================
// <div id="hovering-element" data-bind="stick: { mobile: isMobile(), constrainToParent: true,
// uniqueId: 'foo-'+$index()}" >
// ===================================================================================================


ko.bindingHandlers.stick = {
    init: function (element, valueAccessor) {
        // Create the phantom bar and hide it
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};

        var id = options.uniqueId ? options.uniqueId : "stick";

        var uniqueId = "phantom-" + id;
        $("<div id='" + uniqueId + "' class='phantom-sticky-bar'></div>").insertAfter($(element));
        var phantomBar = $("#" + uniqueId);
        phantomBar.hide();
        //handle disposal (not strictly necessary in this scenario)
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            phantomBar.remove();
            $(window).off("scroll.actionbar touchmove.actionbar");
        });
        var proxyBanner = $("#proxy-banner"),
            isProxy = proxyBanner.length;
    },
    update: function (element, valueAccessor) {

        var options = ko.utils.unwrapObservable(valueAccessor()) || {};

        var phantomBar = $("#phantom-" + options.uniqueId);

        var actionBar = $(element);
        var proxyBanner = $("#proxy-banner"),
            initialPos = actionBar.position(),
            isProxy = proxyBanner.length,
            ignoreProxy = options.ignoreProxy === false ? options.ignoreProxy : true,
            isProxyBanner = false,
            constrainToParent = options.constrainToParent ? options.constrainToParent : false;
        // Special case in which allows stick to operate normally on the proxy banner
        if (isProxy && actionBar.is(proxyBanner)) {
            isProxyBanner = true;
        }
        // offset for the proxy banner from the top of the page on desktop
        else if (isProxy && actionBar.is(proxyBanner) && !options.mobile) {
            actionBar.css("margin-top", 66)
        }
        else if (isProxy && !actionBar.is(proxyBanner) && options.mobile) {
            actionBar.css("margin-top", 110)
        }
        heightAdjustment = options.mobile ? 100 : 66;

        $(window).on("resize", debounce(function () {
            if (actionBar.hasClass("stick-content")) {
                actionBar.css("width", phantomBar.outerWidth());
            }
            else {
                if (!isProxy && !actionBar.is(proxyBanner)) {
                    // remove width attribute to allow for default width style
                    actionBar.css("width", '');
                }
            }
        }));

        // for when the page is changed, but the entire page is not reloaded, only the main content
        // the proxy banner had a defect where it would not show up (and won't fire the update function) if previously hidden by another stick binding.
        // this event removes the classes to effectively reset the proxy banner.
        $(window).on('hashchange', function (e) {
            // there is an issue where the binding will still be active on subpages. 
            // addDisposeCallback does not seem to be getting called in this scenario
            // this deletes the element and tells ko to clean the DOM, removing the binding and preventing it from firing
            if (!isNullOrEmpty(options.forceUpdate)) {
                // publish on UPDATE.PROXY.BANNER with a change to the forceUpdate observable. 
                // base.view.model.js has a postbox subscribe there listening
                // the update function of the stick binding is re-fired when an observable is changed
                // which seems to resolve any issues of the proxy banner being hidden
                ko.postbox.publish("UPDATE.PROXY.BANNER", !options.forceUpdate);
            }
            if (!isProxyBanner && !isNullOrEmpty(actionBar)) {
                if (actionBar[0].id != "make-a-payment-dropdown") {
                    var actionBarToRemove = actionBar.remove();
                    ko.cleanNode(actionBarToRemove);
                    actionBar = null;
                }
            }
        });

        $(window).on("scroll.actionbar touchmove.actionbar", debounce(function () {
            if (!isProxy || ignoreProxy || isProxyBanner) {
                if (!isNullOrEmpty(actionBar)) {
                    // Get the window position for comparison against the top and bottom of the DOM element that contains the actionbar
                    // if constrained to parent. Bottom pos has an extra buffer which allows 
                    var windowpos = $(window).scrollTop(),
                        topPos = $(element).parent().position().top + heightAdjustment,
                        bottomPos = topPos + 2 * heightAdjustment + actionBar.parent().height(),
                        actionBarWidth = actionBar.outerWidth();
                    if (!constrainToParent && windowpos >= initialPos.top + heightAdjustment) {
                        phantomBar.css("height", actionBar.height());
                        actionBar.addClass("stick-content");
                        actionBar.css("width", actionBarWidth);
                        phantomBar.show();

                    } else if (constrainToParent && windowpos >= topPos && windowpos <= bottomPos && !actionBar.hasClass("stick-content")) {
                        phantomBar.css("height", actionBar.height());
                        actionBar.addClass("stick-content");
                        actionBar.css("width", actionBarWidth);
                        phantomBar.show();
                        if (isProxy && !proxyBanner.hasClass("hide-content") && !isProxyBanner) {
                            proxyBanner.addClass("hide-content");
                        }
                    } else if (((windowpos <= topPos || windowpos >= bottomPos) && actionBar.hasClass("stick-content") && constrainToParent)) {
                        if (isProxy && !isProxyBanner) {
                            var splitUniqueId = options.uniqueId.split("-"),
                                isFirstIndex = splitUniqueId[splitUniqueId.length - 1];
                            // Checks the index of the element being pinned to see if it is the first to remove hide-content on proxy banner
                            if (isFirstIndex && isFirstIndex === '0' && windowpos <= topPos) {
                                proxyBanner.removeClass("hide-content");
                            }
                        }
                        actionBar.removeClass("stick-content");
                        actionBar.css("width", phantomBar.outerWidth());
                        phantomBar.hide();
                    } else if (!constrainToParent && windowpos < initialPos.top + heightAdjustment) {
                        actionBar.removeClass("stick-content");
                        actionBar.removeClass("hide-content");
                        actionBar.css("width", "auto");
                        phantomBar.hide();
                    }
                }
            }
        }, 10))
    }
}

// Knockout binding for a tooltip
// ===================================================================================================
// Summary
// ===================================================================================================
// This binding will create a tooltip that will appear near a target element.
// 
// Tooltip can be activated/deactivated by hover/focus on desktop and focus/touch on mobile.
// ===================================================================================================
// Options
// ===================================================================================================
// {
//      message: Message to be displayed inside the tooltip
//
//      position: Position of tooltip (top, left, right, bottom) - top is default if not provided
//
//      preventParentClickOnMobile: Prevent parent click event - false is default if not provided
// }
// ===================================================================================================
// Mark-Up Examples (2)
// ===================================================================================================
//  <!-- Place binding on target element that will receive tooltip -->
//  <div data-bind="tooltip: { position: 'left', message: 'Hi I am a tooltip' }">
//  </div>
// ===================================================================================================
ko.bindingHandlers.tooltip = {
    init: function (element, valueAccessor) {

        var options = ko.utils.unwrapObservable(valueAccessor()) || {};
        var message = options.message ? options.message : null;
        var position = options.position ? options.position : 'top';
        var preventParentClickOnMobile = options.preventParentClickOnMobile ? options.preventParentClickOnMobile : false;
        var zIndex = options.zIndex ? parseInt(options.zIndex) : false;

        var tooltipViewModel = {
            messageObservable: ko.isObservable(message) ? message : ko.observable(message)
        };

        window.noOfTooltips = window.noOfTooltips + 1 || 0;

        var currentTooltip = 'tooltip-' + window.noOfTooltips;

        var target = element;

        // Don't allow svgs to be tab accessible if contained within an element with a tooltip 
        var elementSvgChildren = $(element).find('svg');
        if (elementSvgChildren.length > 0) {
            for (var i = 0; i < elementSvgChildren.length; i++) {
                $(elementSvgChildren[i]).attr("focusable", "false");
            }
        }

        // If there is no message for the tooltip, display a console warning
        if (message === null) {
            console.warn("Tooltip message is null for: " + $(target)[0].outerHTML);
        }

        // Tooltip has message, proceed
        else {
            // Create the tooltip with hidden visibility (using visibility instead of display: none for CSS animation)
            $('<div id="' + currentTooltip + '" class="esg-popover esg-popover--hidden" role="tooltip"><div class="esg-popover__arrow"></div><div class="esg-popover__content" data-bind="text: messageObservable"></div></div>').appendTo('body');
            ko.applyBindings(tooltipViewModel, document.getElementById(currentTooltip));
            var tooltip = $('#' + currentTooltip);
            if (zIndex && !isNaN(zIndex)) {
                $(tooltip).css("z-index", zIndex.toString());
            }
            // If the target element is not on a tabbable element (button, link, etc.), give it a tabIndex of 0
            if (!($(target).is("button")) && !($(target).is("a")) && !($(target).is("input")) && !($(target).hasClass("menu-item"))) {
                $(target).attr("tabIndex", "0");
            }

            // Allow the screen reader to read the tooltip (this reads only the header currently)
            $(target).attr("aria-describedby", currentTooltip);

            // Check if tooltip placement positions tooltip outside of document
            var tooltipExceedsDocument = false;

            var tooltipHeight = $(tooltip).outerHeight();
            var tooltipWidth = $(tooltip).outerWidth();
            var targetHeight = $(target).outerHeight();
            var targetMidPoint = Math.round(targetHeight / 2);
            var tooltipTextHeight = $('#' + currentTooltip + ' .esg-popover__content').height();
            var tooltipFontSize = $('#' + currentTooltip + ' .esg-popover__content').css('line-height');
            var tooltipLineHeight = parseInt(tooltipFontSize.replace('px', ''));

            // Calculate if tooltip wraps to multiple lines
            var multiLine = function () {
                if ((tooltipTextHeight / tooltipLineHeight) > 1) {
                    return true;
                }
                else {
                    return false;
                }
            }

            var top = function () {
                var spacing = 5;
                var topEnd = $(target).offset().top;
                var topPosition;

                // Return rounded numbers to avoid bouncing tooltips
                if (tooltipClass() === "esg-popover--right" || tooltipClass() === "esg-popover--left") {
                    topPosition = topEnd + targetMidPoint - tooltipHeight / 2;
                    return Math.round(topPosition);
                } else if (tooltipClass() === "esg-popover--bottom") {
                    topPosition = topEnd + targetHeight + spacing;
                    return Math.round(topPosition);
                } else {
                    topPosition = topEnd - tooltipHeight - spacing;
                    return Math.round(topPosition);
                }
            };

            var left = function () {
                var spacing = 10;
                var outerWidth = tooltipWidth + spacing;
                var targetLeftEnd = Math.round($(target).offset().left);

                // Corrected left position for tooltips going offscreen on the right side of the screen
                var tooltipCorrectedLeftPosition = Math.round(targetLeftEnd + targetMidPoint - tooltipWidth / 2);

                // Corrected left position for tooltips going offscreen on the left side of the screen
                var tooltipCorrectedRightPosition = Math.round(targetLeftEnd - targetMidPoint);

                // check if tooltip is in the sidebar (menu items have rel="menu")
                var attr = $(target).attr('rel');
                if ((typeof attr !== typeof undefined && attr !== false) && (attr.indexOf('menu') >= 0)) {
                    leftPosition = 80;
                    return leftPosition;
                } else {
                    // Return rounded numbers to avoid bouncing tooltips
                    if (position.toLowerCase() === "right") {
                        var rightPosition = Math.round(targetLeftEnd + $(target).outerWidth() + spacing);
                        if (outerWidth + rightPosition > $(document).width()) {
                            tooltipExceedsDocument = true;
                            rightPosition = tooltipCorrectedLeftPosition;
                            if (rightPosition < 0) {
                                rightPosition = tooltipCorrectedRightPosition;
                            }
                        } else {
                            tooltipExceedsDocument = false;
                        }
                        return rightPosition;
                    } else if (position.toLowerCase() === "left") {
                        var leftPosition = Math.round(targetLeftEnd - tooltipWidth);
                        if (leftPosition < 0) {
                            tooltipExceedsDocument = true;
                            leftPosition = tooltipCorrectedLeftPosition;
                        } else {
                            tooltipExceedsDocument = false;
                        }
                        return leftPosition;
                        // For top or bottom tooltip
                    } else {
                        if (outerWidth + tooltipCorrectedLeftPosition > $(document).width()) {
                            tooltipCorrectedLeftPosition = $(document).width() - outerWidth;
                        }

                        // If corrected position would place the tooltip off the screen on the left, use right positioning
                        if (tooltipCorrectedLeftPosition < 0) {
                            tooltipCorrectedLeftPosition = tooltipCorrectedRightPosition;
                        }

                        // Set tooltipExceedsDocument to false in case a previous left or right tooltip has set it to true
                        tooltipExceedsDocument = false;
                        return tooltipCorrectedLeftPosition;
                    }
                }
            };

            var tooltipClass = function () {
                if (tooltipExceedsDocument === true) {
                    return "esg-popover--top";
                } else {
                    return "esg-popover--" + position.toLowerCase();
                }
            };

            // if the target is in dialog, get the dialog's z-index as an integer and add that to tooltip +1
            if ($(target).closest('.esg-modal-dialog').length > 0) {
                var currentTooltipZindex = parseInt($(target).closest('.esg-modal-dialog').css("z-index"), 10);
                tooltip.css("z-index", currentTooltipZindex + 1);
            }

            // hide all tooltips
            var hideAllTooltips = function hideAllTooltips() {
                $('.esg-popover').removeClass('fade-in');
                $('.esg-popover').addClass('esg-popover--hidden fade-out');
            }

            // show current tooltip
            var showCurrentTooltip = function showCurrentTooltip() {
                tooltip.removeClass('esg-popover--hidden fade-out');
                tooltip.addClass('fade-in');
            }

            // hide current tooltip
            var hideCurrentTooltip = function hideCurrentTooltip() {
                tooltip.removeClass('fade-in');
                tooltip.addClass('esg-popover--hidden fade-out');
            }

            $(target).on('mouseenter focus touchstart click', function (event) {
                $(tooltip).addClass(tooltipClass);
                // reposition multiline tooltip caret for height of tooltip content + 6px (box shadow & border)
                if ((multiLine() === true) && (tooltipClass() === 'esg-popover--top')) {
                    var currentTooltipHeight = $('#' + currentTooltip + ' .esg-popover__content').innerHeight();
                    $('#' + currentTooltip + ' .esg-popover__arrow').attr('style', 'top: ' + parseInt(currentTooltipHeight + 6) + 'px;');
                }
                hideAllTooltips();
                showCurrentTooltip();

                $(tooltip).css({ 'left': left, 'top': top });

                if (event.type === 'touchstart' && preventParentClickOnMobile === true) {
                    event.stopPropagation();
                    return false;
                }
            });

            $(target).on('mouseleave blur', hideCurrentTooltip);

            // if event is bound, unbind it (prevent a document bind for each tooltip)
            $(document).off('touchstart', hideAllTooltips);

            // bind again
            $(document).on('touchstart', hideAllTooltips);

            //handle disposal (not strictly necessary in this scenario)
            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                tooltip.remove();
            });

        }
    },
    update: function (element, valueAccessor) {

    }
}

// Knockout binding for a Popover
// ===================================================================================================
// Summary
// ===================================================================================================
// This binding will create a popover that will appear near a target element.
// 
// Popover can be activated/deactivated by hover/focus on desktop and focus/touch on mobile.
// ===================================================================================================
// Options
// ===================================================================================================
// {
//      messages: (required) Array of message objects { text: 'string', cssClass: 'string' } to be displayed inside the Popover - each element will show the "text" property its own DIV and that DIV will use the cssClass property
//
//      position: (optional) Position of popover (top, left, right, bottom) - top is default if not provided
//
//      preventParentClickOnMobile: (optional) Prevent parent click event - false is default if not provided
//
//      zIndex: (optional) The zIndex to use for the popover.
// }
// ===================================================================================================
// Mark-Up Examples (2)
// ===================================================================================================
//  <!-- Place binding on target element that will receive tooltip -->
//  <div data-bind="popover: { position: 'left', messages: [ { text: 'Hi I am the first line of a popover', cssClass: 'bold'}, { text: 'Hi I am the second line of a popover'} ] }">
//  </div>
// ===================================================================================================
ko.bindingHandlers.popover = {
    init: function (element, valueAccessor) {
        var options = ko.utils.unwrapObservable(valueAccessor()) || {};
        var messages = options.messages ? options.messages : [];
        var position = options.position ? options.position : 'top';
        var preventParentClickOnMobile = options.preventParentClickOnMobile ? options.preventParentClickOnMobile : false;
        var zIndex = options.zIndex ? parseInt(options.zIndex) : false;

        var popoverViewModel = {
            messagesObservable: ko.isObservable(messages) ? messages : ko.observable(messages)
        };

        window.numberOfPopovers = window.numberOfPopovers + 1 || 0;

        var currentPopover = 'popover-' + window.numberOfPopovers;

        var target = element;

        // Don't allow svgs to be tab accessible if contained within an element with a popover 
        var elementSvgChildren = $(element).find('svg');
        if (elementSvgChildren.length > 0) {
            for (var i = 0; i < elementSvgChildren.length; i++) {
                $(elementSvgChildren[i]).attr("focusable", "false");
            }
        }

        // If there is no message for the popover, display a console warning
        if (!messages) {
            console.warn("Popover message is null or undefined for: " + $(target)[0].outerHTML);
        }
        // Popover has message(s), proceed
        else {
            // Create the popover with hidden visibility (using visibility instead of display: none for CSS animation)
            $('<div id="' + currentPopover + '" class="esg-popover esg-popover--hidden" role="tooltip"><div class="esg-popover__arrow"></div><div class="esg-popover__content" data-bind="foreach: { data: messagesObservable, as: \'messageLine\' }"><div class="esg-popover__text" data-bind="text: messageLine.text, css: messageLine.cssClass"></div></div></div>').appendTo('body');
            ko.applyBindings(popoverViewModel, document.getElementById(currentPopover));
            var popover = $('#' + currentPopover);
            if (zIndex && !isNaN(zIndex)) {
                $(popover).css("z-index", zIndex);
            }
            // If the target element is not on a tabbable element (button, link, etc.), give it a tabIndex of 0
            if (!($(target).is("button")) && !($(target).is("a")) && !($(target).is("input")) && !($(target).hasClass("menu-item"))) {
                $(target).attr("tabIndex", "0");
            }

            // Allow the screen reader to read the popover (this reads only the header currently)
            $(target).attr("aria-describedby", currentPopover);

            // Check if popover placement positions popover outside of document
            var popoverExceedsDocument = false;

            var popoverHeight = $(popover).outerHeight();
            var popoverWidth = $(popover).outerWidth();
            var targetHeight = $(target).outerHeight();
            var targetMidPoint = Math.round(targetHeight / 2);
            var popoverTextHeight = $('#' + currentPopover + ' .esg-popover__content').height();
            var popoverFontSize = $('#' + currentPopover + ' .esg-popover__content').css('line-height');
            var popoverLineHeight = parseInt(popoverFontSize.replace('px', ''));

            // Calculate if popover wraps to multiple lines
            var multiLine = function () {
                if ((popoverTextHeight / popoverLineHeight) > 1) {
                    return true;
                }
                else {
                    return false;
                }
            };

            var top = function () {
                var spacing = 5;
                var topEnd = $(target).offset().top;
                var topPosition;

                // Return rounded numbers to avoid bouncing tooltips
                if (popoverClass() === "esg-popover--right" || popoverClass() === "esg-popover--left") {
                    topPosition = topEnd + targetMidPoint - popoverHeight / 2;
                    return Math.round(topPosition);
                } else if (popoverClass() === "esg-popover--bottom") {
                    topPosition = topEnd + targetHeight + spacing;
                    return Math.round(topPosition);
                } else {
                    topPosition = topEnd - popoverHeight - spacing;
                    return Math.round(topPosition);
                }
            };

            var left = function () {
                var spacing = 10;
                var outerWidth = popoverWidth + spacing;
                var targetLeftEnd = Math.round($(target).offset().left);

                // Corrected left position for popovers going offscreen on the right side of the screen
                var popoverCorrectedLeftPosition = Math.round(targetLeftEnd + targetMidPoint - popoverWidth / 2);

                // Corrected left position for popovers going offscreen on the left side of the screen
                var popoverCorrectedRightPosition = Math.round(targetLeftEnd - targetMidPoint);

                // Check if popover is in the sidebar (menu items have rel="menu")
                var attr = $(target).attr('rel');
                if ((typeof attr !== typeof undefined && attr !== false) && (attr.indexOf('menu') >= 0)) {
                    leftPosition = 80;
                    return leftPosition;
                } else {
                    // Return rounded numbers to avoid bouncing popovers
                    if (position.toLowerCase() === "right") {
                        var rightPosition = Math.round(targetLeftEnd + $(target).outerWidth() + spacing);
                        if (outerWidth + rightPosition > $(document).width()) {
                            popoverExceedsDocument = true;
                            rightPosition = popoverCorrectedLeftPosition;
                            if (rightPosition < 0) {
                                rightPosition = popoverCorrectedRightPosition;
                            }
                        } else {
                            popoverExceedsDocument = false;
                        }
                        return rightPosition;
                    } else if (position.toLowerCase() === "left") {
                        var leftPosition = Math.round(targetLeftEnd - popoverWidth);
                        if (leftPosition < 0) {
                            popoverExceedsDocument = true;
                            leftPosition = popoverCorrectedLeftPosition;
                        } else {
                            popoverExceedsDocument = false;
                        }
                        return leftPosition;
                        // For top or bottom tooltip
                    } else {
                        if (outerWidth + popoverCorrectedLeftPosition > $(document).width()) {
                            popoverCorrectedLeftPosition = $(document).width() - outerWidth;
                        }

                        // If corrected position would place the popover off the screen on the left, use right positioning
                        if (popoverCorrectedLeftPosition < 0) {
                            popoverCorrectedLeftPosition = popoverCorrectedRightPosition;
                        }

                        // Set tooltipExceedsDocument to false in case a previous left or right popover has set it to true
                        popoverExceedsDocument = false;
                        return popoverCorrectedLeftPosition;
                    }
                }
            };

            var popoverClass = function () {
                if (popoverExceedsDocument === true) {
                    return "esg-popover--top";
                } else {
                    return "esg-popover--" + position.toLowerCase();
                }
            };

            // If the target is in dialog, get the dialog's z-index as an integer and add that to popover +1
            if ($(target).closest('.ui-dialog').length > 0) {
                var currentPopoverZindex = parseInt($(target).closest('.ui-dialog').css("z-index"), 10);
                popover.css("z-index", currentPopoverZindex + 1);
            }

            // Hide all popovers
            var hideAllPopovers = function hideAllPopovers() {
                $('.esg-popover').removeClass('fade-in');
                $('.esg-popover').addClass('esg-popover--hidden fade-out');
            };

            // Show current popover
            var showCurrentPopover = function showCurrentPopover() {
                popover.removeClass('esg-popover--hidden fade-out');
                popover.addClass('fade-in');
            };

            // Hide current popover
            var hideCurrentPopover = function hideCurrentPopover() {
                popover.removeClass('fade-in');
                popover.addClass('esg-popover--hidden fade-out');
            };

            $(target).on('mouseenter focus touchstart click', function (event) {
                $(popover).addClass(popoverClass);
                // reposition multiline popover caret for height of popover content + 6px (box shadow & border)
                if ((multiLine() === true) && (popoverClass() === 'esg-popover--top')) {
                    var currentPopoverHeight = $('#' + currentPopover + ' .esg-popover__content').innerHeight();
                    $('#' + currentPopover + ' .esg-popover__arrow').attr('style', 'top: ' + parseInt(currentPopoverHeight + 6) + 'px;');
                }
                hideAllPopovers();
                showCurrentPopover();

                $(popover).css({ 'left': left, 'top': top });

                if (event.type === 'touchstart' && preventParentClickOnMobile === true) {
                    event.stopPropagation();
                    return false;
                }
            });

            $(target).on('mouseleave blur', hideCurrentPopover);

            // If event is bound, unbind it (prevent a document bind for each popover)
            $(document).off('touchstart', hideAllPopovers);

            // Bind again
            $(document).on('touchstart', hideAllPopovers);

            // Handle disposal (not strictly necessary in this scenario)
            ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
                popover.remove();
            });
        }
    },
    update: function (element, valueAccessor) {

    }
};

ko.bindingHandlers.noParentBubbling = {
    update: function (element, valueAccessor) {
        $(element).on("click", function (event) {
            event.stopPropagation();
        });
    }
}

// Knockout binding for an element that will call the specified function when the Enter key is pressed
// ===================================================================================================
// Summary
// ===================================================================================================
// This binding will call the specified function when the Enter key is pressed.
// ===================================================================================================
// Mark-Up Example
// ===================================================================================================
// <input id="some-element" data-bind="enterKey: functionToBeTriggeredByEnterKeyPress }" >
// ===================================================================================================
ko.bindingHandlers.enterKey = {
    init: function (element, valueAccessor, allBindings, viewModel) {
        var callback = valueAccessor();
        $(element).on("keypress", function (event) {
            var keyCode = (event.which ? event.which : event.keyCode);
            if (keyCode === 13) {
                $(element).trigger("blur");
                $(element).trigger("focus");
                callback.call(viewModel);
                return false;
            }
            return true;
        });
    }
};

// Knockout binding for an element that will mimic a link when the Enter or Space key is pressed
// ===================================================================================================
// Summary
// ===================================================================================================
// This binding will call an element's click behavior when the Enter or Space key is pressed
// ===================================================================================================
// Mark-Up Example
// ===================================================================================================
// <input id="some-element" data-bind="treatAsLink: {}">
// ===================================================================================================
ko.bindingHandlers.treatAsLink = {
    init: function (element) {
        $(element).on("keypress", function (e) {
            var code = e.which;
            if ((code === 13) || (code === 32)) {
                $(element).trigger("click");
            }
        });
    }
}

//ko.bindingHandlers.defaultText = {
//    init : function() {
//        // Prevent binding on the dynamically-injected text node (as developers are unlikely to expect that, and it has security implications).
//        // It should also make things faster, as we no longer have to consider whether the text node might be bindable.
//        return { 'controlsDescendantBindings': true };
//    },
//    update: function (element, valueAccessor) {
//        var options = ko.utils.unwrapObservable(valueAccessor()) || {};
//        var defaultText = options.default || "";
//        var text = options.text || "";

//        if (text.trim()) {
//            ko.utils.setTextContent(element, text);
//        } else {
//            ko.utils.setTextContent(element, defaultText);
//        }
//    }
//}



function parseANumber(value) {
    value = value || 0;
    return (isNaN(value)) ? Globalize.parseFloat(value) : parseFloat(value);
}

// Handler for collapsible row
ko.bindingHandlers.toggleCollapsibleRow = {
    init: function (element, valueAccessor) {
        var values = ko.utils.unwrapObservable(valueAccessor()) || {};
        var isTable = values.isTable;

        // Determine whether open or false to determine which order to call
        // toggle for td and slide for wrapping div .collapsible-item
        var isOpen = values.isOpen || false;
        // to initialize visible
        if (isOpen) {
            $(element).nextUntil(".collapsible").slideToggle();
        }

        // on click toggle the line item visibility
        $(element).on("click", function () {
            // Toggle the nested rows until the next top-level row
            if (isTable === true) {
                if (isOpen === false) {
                    $(element).next().toggle();
                    $(element).next().find('.collapsible-item').slideToggle(function () {
                        $(element).next().toggle();
                        isOpen = true;
                    });
                }
                else {
                    // use callback on slideToggle to determine when animation is complete
                    $(element).next().find('.collapsible-item').slideToggle(function () {
                        $(element).next().toggle();
                        isOpen = false;
                    });
                }
            }
            else {
                $(element).nextUntil(".collapsible").slideToggle();
            }

            // Toggle the up arrow class (default to .esg-icon__down for collapsed, .esg-icon__up for expanded)
            var replaceArrow = $(element).find('.esg-icon--up');
            if (replaceArrow.length === 1) {
                $(replaceArrow).attr("class", "esg-icon esg-icon--small esg-icon--down");
            } else {
                replaceArrow = $(element).find('.esg-icon--down');
                $(replaceArrow).attr("class", "esg-icon esg-icon--small esg-icon--up");
            }
            // Toggle the aria-expanded attribute when expanded/collapsed
            if ($(element).find('.expand-details').attr("aria-expanded") === "true") {
                $(element).find('.expand-details').attr("aria-expanded", "false");
            }
            else {
                $(element).find('.expand-details').attr("aria-expanded", "true");
            }
        });
    }
};

// Masks input characters with specificity and event options
//to be used along side a textInput binding
// options
//      maskChar - default *
//      unmaskStart - default 0
//      unmaskEnd - default 0
ko.bindingHandlers.mask = {
    createMask: function (input, options) {
        var maskingChars = ko.bindingHandlers.mask.maskOptions.maskChar,
            unmaskStart = ko.bindingHandlers.mask.maskOptions.unmaskStart,
            unmaskEnd = ko.bindingHandlers.mask.maskOptions.unmaskEnd,
            maskMin = ko.bindingHandlers.mask.maskOptions.maskMin,
            maskSet1 = '',
            maskSet2 = '';

        if (!input || input.length <= maskMin) {
            return input;
        }
        for (var i = 0; i < Math.abs(unmaskStart); i++) {
            maskSet1 += maskingChars;
        }
        for (var j = Math.abs(unmaskEnd); j < input.length; j++) {
            maskSet2 += maskingChars;
        }
        return '{0}{1}{2}'.format(maskSet1, input.slice(unmaskStart, unmaskEnd), maskSet2);
    },
    maskOptions: {
        maskChar: '*',
        maskMin: 0,
        unmaskStart: 0,
        unmaskEnd: 0
    },
    setMaskOptions: function (bindingOptions) {
        if (bindingOptions.maskChar && bindingOptions.maskChar !== "")
            ko.bindingHandlers.mask.maskOptions.maskChar = bindingOptions.maskChar;

        if (bindingOptions.unmaskStart && bindingOptions.unmaskStart !== "")
            ko.bindingHandlers.mask.maskOptions.unmaskStart = bindingOptions.unmaskStart;

        if (bindingOptions.unmaskEnd && bindingOptions.unmaskEnd !== "")
            ko.bindingHandlers.mask.maskOptions.unmaskEnd = bindingOptions.unmaskEnd;

        if (bindingOptions.unmaskEnd && bindingOptions.maskMin !== "")
            ko.bindingHandlers.mask.maskOptions.maskMin = bindingOptions.maskMin;
    },
    init: function (element, valueAccessor, allBindings) {
        var e = $(element),
            observable = allBindings().textInput;

        ko.bindingHandlers.mask.setMaskOptions(
            ko.utils.unwrapObservable(valueAccessor()));

        //value should appear masked on init
        e.val(ko.bindingHandlers.mask.createMask(observable()));

        //register event handlers
        // remove the mask on hover or FOCUS!
        e.on("mouseenter focusin", function () {
            e.val(observable());
        });

        // reinstate the mask afterwards
        e.on("mouseleave", function () {
            if (!e.is(':focus'))
                e.val(ko.bindingHandlers.mask.createMask(observable()));
        });

        e.on("focusout", function () {
            e.val(ko.bindingHandlers.mask.createMask(observable()));
        });

        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            e.off();
        });

    },
    update: function (element, valueAccessor, allBindings) {
        ko.bindingHandlers.mask.setMaskOptions(
            ko.utils.unwrapObservable(valueAccessor()));
    }
};




//adding a closure function to encapsulate all the functionality for table custom bindings like - sorting, searching, paging
var tableFilters = (function () {

    //this binding is used to tell that collection that table is bound to is dynamic.
    //it means that table could be changed either with new addition of item or deletion of item or if table is re-mapped through some other action
    //addign this binding to table defines that table sort order will be maintained after it is changed dynamically
    //if this is not added and insertion/deletion or re-mapping of table bound array/contents is changed, sorting will not happen and will show the data with old sort image
    //this binding simply add a read observable to collection to create dependency
    //hence when array/collection is modified, its sortable binding update is ran, where it is verified that other bindigAccessors have dynamicCollection binding, and if it has it re-sorts it.
    //see the expample of usage in FacultySectionGradingPartila.cshtml
    ko.bindingHandlers.dynamicCollection = {
        init: function (element, valueAccessor) {
            try {
                var args = arguments;
                ko.computed({
                    read: function () {
                        ko.utils.unwrapObservable(valueAccessor());
                    },
                    owner: this,
                    disposeWhenNodeIsRemoved: element
                });
            }
            catch (e) {
                console.error(e);
            }


        }
    };




    //this function encapsulate functionality for sorting
    function sortClass() {
        var maximumNestedFieldsToSort = 3;
        var errorMessages = ["Cannot have more than " + maximumNestedFieldsToSort + " nested fields for sorting"];
        var imagePath = ko.observable();
        var bothImage = ko.computed(function () {
            return imagePath() + 'both.png';
        });
        var upImage = ko.computed(function () {
            return imagePath() + 'up.png';
        });
        var downImage = ko.computed(function () {
            return imagePath() + 'down.png';
        });

        var elementIndex = 0;
        //compare two values
        function compare(firstValue, secondValue) {
            if (typeof firstValue === 'undefined') {
                firstValue = null;
            }
            if (typeof secondValue === 'undefined') {
                secondValue = null;
            }

            //check if string
            if (typeof firstValue === 'string' && typeof secondValue === 'string') {

                //check if string has numbers
                if (isNumber(firstValue) === true && isNumber(secondValue) === true) {
                    return Number(firstValue) - Number(secondValue);
                }
                //check if string has dates
                else if (moment(firstValue).isValid() && moment(secondValue).isValid()) {
                    return moment(firstValue) - moment(secondValue);
                }
                else {
                    //this string can have any alphanumeric values
                    return firstValue.toLowerCase().localeCompare(secondValue.toLowerCase(), Globalize.findClosestCulture().name, { sensitivity: 'base' });
                }
            }
            else if (typeof firstValue === 'number' && typeof secondValue === 'number') {
                return firstValue - secondValue;

            }
            else if (typeof firstValue === 'object' && typeof secondValue === 'object') {
                if (moment(firstValue).isValid() && moment(secondValue).isValid()) {
                    return moment(firstValue) - moment(secondValue);
                }
                else {
                    //any other object, convert to json string and then compare
                    firstValue = JSON.stringify(firstValue);
                    secondValue = JSON.stringify(secondValue);
                    return firstValue.toLowerCase().localeCompare(secondValue.toLowerCase(), Globalize.findClosestCulture().name, { sensitivity: 'base' });
                }
            }
            else {
                //if one is number and other is not
                if (typeof firstValue === 'number' || typeof secondValue === 'number') {
                    if (isNullOrEmpty(firstValue) === true || isNullOrEmpty(secondValue) === true) {
                        if (isNullOrEmpty(firstValue) === true) {
                            firstValue = Number.MIN_VALUE;
                        }
                        else if (isNullOrEmpty(secondValue) === true) {
                            secondValue = Number.MIN_VALUE;
                        }
                        return Number(firstValue) - Number(secondValue);
                    }
                    //if one is string convert number to string
                    else if (typeof firstValue === "string" || typeof secondValue === "string") {
                        //if one is string convert number to string
                        if (typeof firstValue === 'number') {
                            firstValue = firstValue.toString();
                        }
                        else if (typeof secondValue === 'number') {
                            secondValue = secondValue.toString();
                        }
                        return firstValue.toLowerCase().localeCompare(secondValue.toLowerCase(), Globalize.findClosestCulture().name, { sensitivity: 'base' });
                    }
                    //if one is date convert date to number
                    else if ((firstValue instanceof Date) || (secondValue instanceof Date)) {
                        //if one is date convert to number
                        if (firstValue instanceof Date) {
                            firstValue = firstValue.valueOf();
                        }
                        if (secondValue instanceof Date) {
                            secondValue = secondValue.valueOf();
                        }
                        return Number(firstValue) - Number(secondValue);
                    }
                    //if one is object convert number to object
                    else if (typeof firstValue === 'object' || typeof secondValue === 'object') {
                        firstValue = JSON.stringify(firstValue);
                        secondValue = JSON.stringify(secondValue);
                        return firstValue.toLowerCase().localeCompare(secondValue.toLowerCase(), Globalize.findClosestCulture().name, { sensitivity: 'base' });
                    }


                }
                //if one is date and other is not
                else if ((firstValue instanceof Date) || (secondValue instanceof Date)) {
                    if (isNullOrEmpty(firstValue) === true || isNullOrEmpty(secondValue) === true) {
                        if (isNullOrEmpty(firstValue) === true) {
                            firstValue = moment(1700, 1, 1);
                        }
                        else if (isNullOrEmpty(secondValue) === true) {
                            secondValue = moment(1700, 1, 1);
                        }
                        return moment(firstValue) - moment(secondValue);
                    }
                    //if one is string convert number to string
                    else if (typeof firstValue === "string" || typeof secondValue === "string") {
                        //if one is string convert number to string
                        if (firstValue instanceof Date) {
                            firstValue = firstValue.toString();
                        }
                        else if (secondValue instanceof Date) {
                            secondValue = secondValue.toString();
                        }
                        return firstValue.toLowerCase().localeCompare(secondValue.toLowerCase(), Globalize.findClosestCulture().name, { sensitivity: 'base' });
                    }

                    //if one is object convert number to object
                    else if (typeof firstValue === 'object' || typeof secondValue === 'object') {
                        firstValue = JSON.stringify(firstValue);
                        secondValue = JSON.stringify(secondValue);
                        return firstValue.toLowerCase().localeCompare(secondValue.toLowerCase(), Globalize.findClosestCulture().name, { sensitivity: 'base' });
                    }


                }
                else if (typeof firstValue === 'string' || typeof secondValue === 'string') {
                    if (isNullOrEmpty(firstValue) === true || isNullOrEmpty(secondValue) === true) {
                        if (isNullOrEmpty(firstValue) === true) {
                            firstValue = "";
                        }
                        if (isNullOrEmpty(secondValue) === true) {
                            secondValue = "";
                        }
                        return firstValue.toLowerCase().localeCompare(secondValue.toLowerCase(), Globalize.findClosestCulture().name, { sensitivity: 'base' });
                    }
                }
                else {
                    //this will be any ohter type like boolean
                    return firstValue < secondValue ? -1 : firstValue > secondValue ? 1 : 0;
                }
            }
        };

        //sort method
        function sort(collection) {
            if (typeof collection === 'undefined' || collection === null)
                return;
            var sortField = collection.sortField();
            var sortDirection = collection.sortDirection();

            if (typeof sortField === 'undefined' || sortField === null || typeof sortDirection === 'undefined' || sortDirection === null)
                return;
            //scan sortField and split on ; to have different fields. put those in array
            var sortFields = sortField.split(';');
            //map collection to another array before sorting
            var mapped = collection();

            //sort the mapped array
            mapped.sort(function (a, b) {
                var compareResult = 0;
                for (var i = 0; i < sortFields.length && i < maximumNestedFieldsToSort; i++) {
                    var firstValue = ko.utils.unwrapObservable(a[sortFields[i]]);
                    var secondValue = ko.utils.unwrapObservable(b[sortFields[i]]);
                    if (sortDirection === 'asc') {
                        compareResult = compare(firstValue, secondValue);
                    }

                    else if (sortDirection === 'desc') {
                        compareResult = compare(secondValue, firstValue);
                    }
                    if (compareResult !== 0) {
                        break;
                    }
                }
                return compareResult;
            });

            //again push sorted array back to observable
            collection(mapped);
        };

        //custom binding for sorting at table header per field
        /* Usability:
         * this binding is required on fields/columns that needs to be sorted.
         * selective columns can have this binding. not necessary that all columns should have.
         * <th id="displayNameLfmHeader" data-bind="sortOn: {fieldName:'DisplayNameLfm', displayAs:'Names',defaultField:true, defaultDirection:'desc'}">
         * only attribute required with sortOn data-bind is fieldName.
         * displayAs is optional attribute-  needed only if you want dropdown to have this field appear with other name than the fieldName. By default dropdown would have same name as fieldName
         * defaultField is optional attribute- if provided then at page-load, table will have this field as default sortable. If none of the fields have this value, data bound to DOM will be not be sorted at all.
         * defaultDirection is optional attribute- works in conjuction with dafaultField, can have two values 'asc' or 'desc'. if not provided, defaultField will be sorted in ascending order.
         * 
         */
        ko.bindingHandlers.sortOn = {
            init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                try {
                    var unwrappedValues = ko.unwrap(valueAccessor());
                    var fieldToSortProperty = unwrappedValues.fieldName;
                    var displayAsProperty = unwrappedValues.displayAs;
                    var arrayToSort = bindingContext.contents;
                    var sameCollectionUsed = false;
                    imagePath(bindingContext.imagePath);

                    if (typeof fieldToSortProperty === 'undefined' || fieldToSortProperty === null)
                        return;
                    if (typeof arrayToSort === 'undefined' || arrayToSort === null)
                        return;
                    if (typeof displayAsProperty === 'undefined' || displayAsProperty === null) {
                        displayAsProperty = fieldToSortProperty;
                    }

                    if (fieldToSortProperty.split(';').length > maximumNestedFieldsToSort) {
                        throw new Error(errorMessages[0] + " " + fieldToSortProperty);
                    }
                    var isMobile = bindingContext.isMobile;
                    var isThisDefaultFieldToSort = unwrappedValues.defaultField;
                    var defaultDirection = unwrappedValues.defaultDirection;

                    arrayToSort.sortOptions.push({ 'sortField': fieldToSortProperty, 'displayAs': displayAsProperty, 'sortDirection': 'ascending' });
                    arrayToSort.sortOptions.push({ 'sortField': fieldToSortProperty, 'displayAs': displayAsProperty, 'sortDirection': 'descending' });
                    //add an image of being sortable
                    if (isNullOrEmpty(isMobile) || isMobile === false) {
                        $(element).addClass("sortable");
                    }


                    if (typeof isThisDefaultFieldToSort !== 'undefined' && isThisDefaultFieldToSort !== null && isThisDefaultFieldToSort === true) {
                        if (typeof defaultDirection === 'undefined' || defaultDirection === null) {
                            defaultDirection = 'asc';
                        }
                        //sort me now
                        ko.bindingHandlers.sortOn.sortMeNow(element, fieldToSortProperty, defaultDirection, arrayToSort, isMobile);
                    }
                    //add a click event to element, change the background image of current element to asc or desc and for all other siblings change it to default image
                    $(element).on("click", function () {

                        try {
                            $(element).removeClass("sortable");
                            $(element).removeClass("sortable-asc");
                            $(element).removeClass("sortable-desc");
                            if (arrayToSort.sortField() === fieldToSortProperty) {
                                if (arrayToSort.sortDirection() === 'asc') {
                                    $(element).addClass("sortable-desc");
                                    arrayToSort.sortDirection('desc');
                                }
                                else {
                                    $(element).addClass("sortable-asc");
                                    arrayToSort.sortDirection('asc');
                                }
                            }
                            else {
                                //get all the elements that are siblings of $element that have associated sorting class 
                                var siblingElement = $(element).siblings(".sortable-asc, .sortable-desc");
                                $(siblingElement).removeClass("sortable-asc");
                                $(siblingElement).removeClass("sortable-desc");
                                $(siblingElement).addClass("sortable");
                                $(element).addClass("sortable-asc");
                                arrayToSort.sortDirection('asc');
                                arrayToSort.sortField(fieldToSortProperty);
                            }
                        }
                        catch (e) {
                            console.error(e.message);
                        }
                    });
                }
                catch (e) {
                    console.error(e.message);
                }
            },


            //added functionality to sort the array by default
            sortMeNow: function (element, fieldToSortProperty, direction, collection, isMobile) {
                try {
                    if (isMobile === false) {
                        $(element).children(".sortable-column-img").remove();
                    }

                    var s = null;
                    if (direction === 'asc') {
                        if (isMobile === false) {
                            $(element).addClass("sortable-asc");
                        }
                        s = ko.utils.arrayFirstDeprecated(collection.sortOptions(), function (item) {
                            return item.sortField === fieldToSortProperty && item.sortDirection === 'ascending';
                        });
                        collection.selectedSortOption(s);
                    }
                    else {
                        if (isMobile === false) {
                            $(element).addClass("sortable-desc");
                        }
                        s = ko.utils.arrayFirstDeprecated(collection.sortOptions(), function (item) {
                            return item.sortField === fieldToSortProperty && item.sortDirection === 'descending';
                        });
                        collection.selectedSortOption(s);
                    }
                }
                catch (e) {
                    console.error(e.message);
                }
            },
        };

        //custom binddng for sorting at table
        /*
         * Usability:
         * on table element add follwoing data-bind attribute
         * data-bind="sortable:{isMobile:$parent.isMobile(),contents:StudentGrades, imagePath: '@Url.Content("~/content/images/")'}"
         * where isMobile is optional, if not provided , there won't be any sorting available in mobile view
         * contents is the observable array that needs to be sorted.
         * in desktop view, column names that are defined as sortable, will appear with sorting images
         * in mobile view, column names that are defined as sortable, will appear in drop down option twice as '<fieldName>, ascending'  and '<fieldName>, descending'
         * Currently only one field or column is sortable. there is no primary or secondary fields.
         * Please note for mobile view, you need to add mobile-view template on your cshtml. This template has dropdown mark-up.
         * @await Html.PartialAsync(@"~/Views/Shared/MobileViewSortingTemplate.cshtml")
         */
        ko.bindingHandlers.sortable = {

            init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                try {
                    var unwrappedValues = ko.unwrap(valueAccessor());
                    var isMobile = unwrappedValues.isMobile;
                    var arrayToSort = unwrappedValues.contents;
                    var imagePath = unwrappedValues.imagePath;

                    if (typeof arrayToSort.sortField === 'undefined') {

                        arrayToSort.sortField = ko.observable();

                    }
                    if (typeof arrayToSort.sortDirection === 'undefined') {
                        arrayToSort.sortDirection = ko.observable();

                    }
                    if (typeof arrayToSort.sortOptions === 'undefined') {
                        arrayToSort.sortOptions = ko.observableArray([]);
                        arrayToSort.selectedSortOption = ko.observable();
                    }



                    if (arrayToSort.sortField.getSubscriptionsCount() === 0) {
                        arrayToSort.sortField.subscribe(function () {

                            sort(arrayToSort);
                        });
                    }
                    if (arrayToSort.sortDirection.getSubscriptionsCount() === 0) {
                        arrayToSort.sortDirection.subscribe(function () {

                            sort(arrayToSort);
                        });
                    }


                    if (arrayToSort.selectedSortOption.getSubscriptionsCount() === 0) {
                        arrayToSort.selectedSortOption.subscribe(function () {
                            if (arrayToSort.selectedSortOption().sortDirection === 'ascending') {
                                arrayToSort.sortDirection("asc");
                            }
                            else {
                                arrayToSort.sortDirection("desc");
                            }
                            arrayToSort.sortField(arrayToSort.selectedSortOption().sortField);
                        });

                    }

                    //carrying over parent declared value accessors to child binding elements, this will bring contents and isMobile() values available to child elements
                    var childBindingContext = bindingContext.createChildContext(
                        bindingContext.$rawData,
                        null,
                        function (context) {
                            ko.utils.extend(context, valueAccessor());
                        });
                    ko.applyBindingsToDescendants(childBindingContext, element);

                    return { controlsDescendantBindings: true };
                }
                catch (e) {
                    console.error(e.message);
                }
            },
            update: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
                try {
                    var unwrappedValues = ko.unwrap(valueAccessor());
                    var isMobile = unwrappedValues.isMobile;
                    var arrayToSort = unwrappedValues.contents;

                    //check if mobile
                    //append  a template before current element
                    if (isMobile === true) {
                        $(element).prev("").remove("#mobile-sorting-template-id");
                        //this is to add reduntant div element before table
                        var mobileElement = "<div id='placeholder-for-sorting-template'></div>";
                        $(element).before(mobileElement);
                        //above redundant div element was required so that it could be relpaced with the template that is rendered dynamically
                        var result = ko.renderTemplate("mobile-sorting-template", valueAccessor(), {}, $(element).prev("#placeholder-for-sorting-template"), "replaceNode");
                    }
                    else {
                        $(element).prev("").remove("#mobile-sorting-template-id");
                    }


                    if (allBindingsAccessor().hasOwnProperty('dynamicCollection')) {

                        if (typeof arrayToSort !== 'undefined' && arrayToSort !== null && !isNullOrEmpty(arrayToSort.sortField()) && !isNullOrEmpty(arrayToSort.sortDirection())) {
                            sort(arrayToSort);
                        }
                    }
                }
                catch (e) {
                    console.debug(e);
                }

            }
        };

        return {
            sort: sort
        }
    }
    return {
        sorting: sortClass()
    }

}());

// Knockout binding to create a persistent alias that you can reference in child contexts
// ===================================================================================================
// Summary
// ===================================================================================================
// This binding mimics the "as" property of the foreach binding, but it can be used without foreach 
// ===================================================================================================
// Mark-Up Example
// ===================================================================================================
// <div data-bind="let: { myAlias: $data }"> 
// ===================================================================================================
ko.bindingHandlers['let'] = {
    init: function (element, valueAccessor, allBindings, vm, bindingContext) {
        // Make a modified binding context, with extra properties, and apply it to descendant elements
        var innerContext = bindingContext.extend(valueAccessor);
        ko.applyBindingsToDescendants(innerContext, element);

        return { controlsDescendantBindings: true };
    }
};
ko.virtualElements.allowedBindings['let'] = true;

// Knockout binding to execute a callback function when clicking outside of an element.
// ===================================================================================================
// Summary
// ===================================================================================================
// Allows for a function to be executed when a click is detected outside of the containing
// element. Optional arguments may be specified.
// Shorthand format is to specify just a callback function.
// If given an object, these are the options:
//   callback: required, callback function
//   arguments: optional, array of arguments to pass to the callback function
// ===================================================================================================
// Mark-Up Example
// ===================================================================================================
// (Shorthand format)
// <div data-bind="clickOutside: callbackFn"> 
// 
// (Long format)
// <div data-bind="clickOutside: { callback: callbackFn, arguments: [arg1, arg2]}"> 
// ===================================================================================================
ko.bindingHandlers['clickOutside'] = {
    init: function (element, valueAccessor, allBindings, vm, bindingContext) {
        var clickOutsideHandler = function (event) {
            if (!element.contains(event.target)) {
                var value = ko.utils.unwrapObservable(valueAccessor());
                if (typeof value === 'function') {
                    // Shorthand format, callback was passed directly
                    value();
                } else if (typeof value === 'object') {
                    if (typeof value.callback === 'function' && value.arguments) {
                        // Longhand format with arguments
                        value.callback.apply(this, value.arguments);
                    } else if (typeof value.callback === 'function') {
                        // Longhand format without arguments
                        value.callback();
                    }
                }
            }
        };

        document.addEventListener('click', clickOutsideHandler);
        ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
            document.removeEventListener('click', clickOutsideHandler);
        });
    }
};


/* Replacing knockout's default html binding with a binding that can be secured from XSS injection */
ko.bindingHandlers.html = {
    'init': function () {
        return { 'controlsDescendantBindings': true };
    },
    'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        var unsanitizedHtml = result = ko.unwrap(valueAccessor());
        var sanitizedHtml = DOMPurify.sanitize(unsanitizedHtml);
        result = sanitizedHtml;
        element.innerHTML = result;
    }
};

// A Knockout binding for textareas which automatically resize based on content
// ===================================================================================================
// Mark-Up Example 
// ===================================================================================================
//  <!-- Place binding on target textarea that will resize it based on content -->
//  <textarea data-bind="autoResize: true"></textarea>
// ===================================================================================================
function autosize(el) {
    setTimeout(function () {
        el.style.cssText = 'height:auto; padding:0';
        el.style.cssText = 'height:' + el.scrollHeight + 'px';
    }, 0);
}
ko.bindingHandlers.autoResize = {
    init: function (element) {
        autosize(element);
        $(element).on("keydown keyup input paste propertychange click submit mouseenter", function () {
            autosize(element);
        });
    },
    update: function (element) {
        autosize(element);
    }
};

/*
 * Adds a readmore link to the text after the max characters 
 * *****
 * Example:
 * 
 * Markup and result:
 * <span data-bind="readMoreLink: { maxChars: 100, moreText: 'Read more...', lessText: 'Read less'}" />
 */
ko.bindingHandlers.readMoreLink = {
    config: {
        maxChars: 100,
        moreText: 'Read more...',
        lessText: 'Read less'
    },
    toggleMoreOrLess: function (element) {

        var config = ko.bindingHandlers.readMoreLink.config;

        $(element).off("click", '.morelink');
        $(element).on({
            click: function () {
                var $this = $(this);
                if ($this.hasClass('less')) {
                    $this.removeClass('less');
                    $this.html(config.moreText);
                } else {
                    $this.addClass('less');
                    $this.html(config.lessText);
                }
                $this.prev().toggle();
                return false;
            }
        }, '.morelink');

    },
    contentUpdate: function (element) {

        var config = ko.bindingHandlers.readMoreLink.config;

        var $element = $(element);

        if ($element.hasClass("shortened")) return;

        $element.addClass("shortened");

        var content = $element.html();
        var maxLength = 0;
        if (content.length > config.maxChars) {
            //do we have any html? - this regular expression will check
            //and get all html tags within a string, including nested tags and multiline 
            var regex = /(<([^>]*)>((.|[\r\n\t])*?)<.*\/[^>]*>)/ig;
            var matches = content.match(regex);
            var index = content.search(regex);
            if (matches != null && matches != undefined && matches.length > 0) {
                var strLen = index;
                for (var i = 0; i < matches.length; i++) {
                    strLen += matches[i].length;
                }
                maxLength = strLen > config.maxChars ? strLen : config.maxChars;
                var c = content.substr(0, maxLength);
                var h = content.substr(maxLength, content.length - maxLength);
            }
            else {
                var c = content.substr(0, config.maxChars);
                var h = content.substr(config.maxChars, content.length - config.maxChars);
            }
            //Add morecontent class only if there is more text than currently displayed
            if (maxLength < content.length) {
                $element.html(c + '<span class="morecontent"><span>' + h + '</span> <a href="#" class="morelink">' + config.moreText + '</a></span>');
                $(".morecontent span").hide();
            }
        }
    },
    init: function (element, valueAccessor) {

        var options = ko.utils.unwrapObservable(valueAccessor()) || {};

        if (options) {
            $.extend(ko.bindingHandlers.readMoreLink.config, options);
        }

        ko.bindingHandlers.readMoreLink.toggleMoreOrLess(element);

        ko.bindingHandlers.readMoreLink.contentUpdate(element);
    },
    update: function (element, valueAccessor) {

    }
}

/*Copyright 2015-2016 Ellucian Company L.P. and its affiliates*/

// Extender that evaluates for correct date format uzing Globalize settings
// Properities:
//  hasFormatError - observable is true if the date is not formatted correctly.
//  errorMessage - observable has some error message value if hasFormatError is true
// Options:
//  culture: Optionally specify a culture setting. Default is the current culture
//  dateFormat: Optionally specify the Globalize date format string to use when parsing and formatting. Default is small d - "d"
//  minDate: Optionally specify an inclusive minimum date. If set, the input value cannot be before this date.
//  maxDate: Optionally specify an inclusive maximum date. If set, the input value cannot be after this date.
ko.extenders.dateCheck = function (target, options) {
    target.hasFormatError = ko.observable();
    target.errorMessage = ko.observable();

    var culture = options.culture || Globalize.culture(),
        dateFormat = options.dateFormat || "d",
        minDate = options.minDate,
        maxDate = options.maxDate;

    function success() {
        target.hasFormatError(false);
        target.errorMessage(null);
    }

    function error(message) {
        target.hasFormatError(true);
        target.errorMessage(message);
        $('#aria-announcements').text(message);
    }

    function validateDate(input) {
        var parsedDate = Globalize.parseDate(input, dateFormat, culture)
        if (input === null || input === "") {
            success();
        }
        else if (parsedDate) {
            if (minDate && minDate > parsedDate) {
                error("Please enter a date on or after " + Globalize.format(minDate, dateFormat));
                return;
            }
            if (maxDate && maxDate < parsedDate) {
                error("Please enter a date on or before " + Globalize.format(maxDate, dateFormat));
                return;
            }
            success();
        }
        else {
            error("Please enter a date in the correct format " + Globalize.getClosestCultureDateFormat());
        }
    }

    validateDate(target());
    target.subscribe(validateDate);

    return target;
}

/* 
This was copied from immediate.payments.js
numeric : {
    precision: int, precision value; default is 2
    zeroNull: bool, true - empty value equates with zero; false - empty values are replaced with zero; default is false
    maxValue: decimal, maximum value; default is no maximum
    maxMessage: string, custom error message to display when the max value is exceeded (only used if maxValue is specified)
    minValue: decimal, minimum value; default is no minimum
    minMessage: string, custom error message to display when the min value is exceeded (only used if minValue is specified)
    nanMessage: string, custom error message to display if the user-entered value is not a number
    valCondition: function, bool result of function indicates whether or not to validate the target
    observable: ko.observable, if you have an underlying observable to update with the same value of the target observable
    updateOnError: bool, true - update the target (and observable) even if there is an error; default is false
}
*/
ko.extenders.numeric = function (target, arguments) {
    // Process the arguments passed in
    //var precision = arguments.precision || 2;
    var precision = arguments.precision;
    if (isNaN(precision)) {
        precision = 2;
    }

    // Process the argument to use an empty/null value in place of zero
    var zeroNull = arguments.zeroNull || false;
    var zeroFormat = (zeroNull) ? "" : 0;

    // Get maximum value, if any, and its error message
    var maxValue = arguments.max;
    if (maxValue && isNaN(maxValue)) {
        maxValue = undefined;
    }
    if (maxValue) {
        var maxMessage = arguments.maxMessage || "You must enter a value less than {0}".format(maxValue);
    }

    // Get minimum value, if any, and its error message
    var minValue = arguments.min;
    var minMessage = "";
    if (isNaN(minValue)) {
        minValue = undefined;
    } else {
        minMessage = arguments.minMessage || "You must enter a value greater than or equal to {0}.".format(minValue);
    }

    // Get the "not a number" message and the validation condition
    var nanMessage = arguments.nanMessage || "Please enter a valid number.";
    var valCondition = arguments.valCondition;

    var underlyingObservable = arguments.observable;
    var updateOnError = arguments.updateOnError;

    //create a writeable computed observable to intercept writes to our observable
    var result = ko.computed({
        read: function () {
            return Globalize.format(target(), "n" + precision);
        },
        write: function (newValue) {
            //add some sub-observables to our observable if they don't exist
            if (!result.hasError) {
                result.hasError = ko.observable(false);
            }
            if (!result.validationMessage) {
                result.validationMessage = ko.observable("");
            }

            // Get the current value, and turn the new value into a numeric
            var current = target(),
                roundingMultiplier = Math.pow(10, precision),
                newValueAsNum = parseANumber(newValue);
            // Get the current error and message values and initialize new values
            var hasError = result.hasError(),
                newError = false;
            var valMessage = result.validationMessage(),
                newMessage = "";

            // Only validate when a new value is available (i.e. not null) and the validation condition is true, if one was specified
            if (newValue && valCondition && typeof valCondition === "function" && valCondition()) {
                // Validate the data
                if (isNaN(newValueAsNum)) {
                    // Not a number
                    newError = true;
                    newMessage = nanMessage;
                } else if (minValue != "undefined" && newValueAsNum < minValue) {
                    // Value is less than the minimum
                    newError = true;
                    newMessage = minMessage;
                } else if (maxValue && newValueAsNum > maxValue) {
                    // Value is greater than the maximum
                    newError = true;
                    newMessage = maxMessage;
                }
            }

            if (updateOnError || !newError) {

                // Make sure we have a number before determining the value to write out
                if (isNaN(newValueAsNum)) {
                    newValueAsNum = 0;
                }
                var valueToWrite = (newValueAsNum === 0) ? zeroFormat : Math.round(newValueAsNum * roundingMultiplier) / roundingMultiplier;

                // Only write the new value if it changed
                if (valueToWrite !== current) {
                    target(valueToWrite);
                    if (underlyingObservable && ko.isObservable(underlyingObservable))
                        underlyingObservable(valueToWrite);
                } else {
                    // If the rounded value is the same, but a different value was written, force a notification for the current field
                    if (newValueAsNum !== current) {
                        target.notifySubscribers(valueToWrite);
                        if (underlyingObservable && ko.isObservable(underlyingObservable))
                            underlyingObservable.notifySubscribers(valueToWrite);
                    }
                }
            }
            // If the error or message values have changed, save the new values. Because these are part of
            // a dependent (computed) observable, we have to notify the subscribers manually.
            if (newError !== hasError) {
                result.hasError(newError);
                result.hasError.notifySubscribers(newError);
            }
            if (newMessage !== valMessage) {
                result.validationMessage(newMessage);
                result.validationMessage.notifySubscribers(newMessage);
            }
        }
    });

    // Initialize the computed observable with the current value to make sure it is rounded appropriately
    result(target());
    // Return the new computed observable
    return result;
};



/*This is a simpler implementation of the numeric extender. It only validates that the input is a number 
  and optionally checks against a specified min and max value
    Properities:
        hasFormatError - observable is true if the date is not formatted correctly.
        errorMessage - observable has some error message value if hasFormatError is true
    Options:
        
        minDate: Optionally specify an inclusive minimum date. If set, the input value cannot be before this date.
        maxDate: Optionally specify an inclusive maximum date. If set, the input value cannot be after this date.
        invalidMessage: Optionally specify an errorMessage if the input is not a number. Default is "Please enter a valid value"
*/
ko.extenders.simpleNumericValidation = function (target, options) {
    target.hasError = ko.observable();
    target.errorMessage = ko.observable();

    var minValue = options.minValue,
        maxValue = options.maxValue,
        invalidMessage = options.invalidMessage || "Please enter a valid value";


    function success() {
        target.hasError(false);
        target.errorMessage(null);
    }
    function fail(message) {
        target.hasError(true);
        target.errorMessage(message);
        $('#aria-announcements').text(message);
    }

    function validateNumber(input) {
        if (!input || input === "") {
            success();
        }
        else {
            var parsedValue = Globalize.parseFloat(input);
            if (!isNaN(parsedValue)) {
                if (ko.utils.unwrapObservable(minValue) && parsedValue < ko.utils.unwrapObservable(minValue)) {
                    fail("Enter a value greater than or equal to {0}".format(ko.utils.unwrapObservable(minValue)))
                }
                if (ko.utils.unwrapObservable(maxValue) && parsedValue > ko.utils.unwrapObservable(maxValue)) {
                    fail("Enter a value less than or equal to {0}".format(ko.utils.unwrapObservable(maxValue)));
                }
                success();
            }
            else {
                fail(ko.utils.unwrapObservable(invalidMessage));
            }
        }
    }
    validateNumber(target());
    target.subscribe(validateNumber);

    return target;

};

/* The formattedPhone extender attempts to keep phone number inputs formatted consistently.
   The phone number will be stripped of characters not consisting of digits 0-9 or +-()/. or space
   If the number starts with a +, no further formatting will be applied.
   Otherwise if the number consists 7 or 10 digits, disregarding all other characters, it will be converted
   to US style formatting XXX-XXXX or XXX-XXX-XXXX
   If the number consists of some other number of digits, formatting will be preserved.

   Options:
     validateLength: If true, adds a knockout validation rule to ensure the number contains between 7 and 15
                     numerical digits, not including formatting. Also validates with 0 digits. If the number
                     is required to be entered, use a required validation to achieve this.
     validationMessage: Optional string that overrides the default validation error message
 */
ko.extenders.formattedPhone = function (target, options) {
    var applyPhoneFormatting = function (value) {
        var nonDigitsRegex = RegExp("\\D", "g"); // Matches any non digit (0-9)
        var internationalPhoneNumber = RegExp("^\\+"); // Starts with a plus (+)
        // Valid phone characters: 0-9 or the phone format characters of + - . ( ) / and space
        var invalidPhoneCharactersRegex = RegExp("[^\\d+-\\.()/ ]", "g");
        var phoneDigits = value.replace(nonDigitsRegex, '');
        var formattedPhoneNumber = "";
        if (value.match(internationalPhoneNumber)) {
            // If the number starts with a +, don't attempt to format it US style, even if it is 7 or 10 digits
            formattedPhoneNumber = value.replace(invalidPhoneCharactersRegex, '');
        } else if (phoneDigits.length === 7) {
            // Apply US style phone number formatting XXX-XXXX, keeping only digits from original
            formattedPhoneNumber = [phoneDigits.slice(0, 3), '-', phoneDigits.slice(3)].join('');
        } else if (phoneDigits.length === 10) {
            // Apply US style phone number formatting XXX-XXX-XXXX, keeping only digits from original
            formattedPhoneNumber = [phoneDigits.slice(0, 3), '-', phoneDigits.slice(3, 6), '-', phoneDigits.slice(6)].join('');
        } else {
            // The number did not contain 7 or 10 digits, preserve formatting while removing invalid characters
            formattedPhoneNumber = value.replace(invalidPhoneCharactersRegex, '');
        }
        return formattedPhoneNumber;
    }

    if (options.validateLength) {
        var validationMessage = options.validateMessage || phoneLengthErrorMessage;
        target.extend({
            validation: {
                validator: function (value) {
                    var nonDigitsRegex = RegExp("\\D", "g");
                    var phoneDigits = value.replace(nonDigitsRegex, '');
                    if (phoneDigits.length === 0 || (phoneDigits.length >= 7 && phoneDigits.length <= 15)) {
                        return true;
                    } else {
                        return false;
                    }
                },
                message: validationMessage
            }
        });
    }

    target.subscribe(function (newValue) {
        var formattedValue = applyPhoneFormatting(newValue);
        if (formattedValue !== newValue) {
            target(formattedValue);
        }
    });
};

/* The formattedSSN extender attempts to keep government ID inputs formatted consistently.
   The input will be stripped of characters not consisting of digits 0-9 or +-()/. or space
   Otherwise if the number consists of 9 digits, disregarding all other characters, it will be converted
   to US style formatting XXX-XX-XXXX
   If the number consists of some other number of digits, formatting will be preserved.

   Options:
     validateLength: If true, adds a knockout validation rule to ensure the number contains between 7 and 15
                     numerical digits, not including formatting. Also validates with 0 digits. If the number
                     is required to be entered, use a required validation to achieve this.
     validationMessage: Optional string that overrides the default validation error message
 */
ko.extenders.formattedSSN = function (target, options) {
    var applySSNFormatting = function (value) {
        if (value != null) {
            var format = true;
            if (typeof options.onlyIf === "function") {
                format = options.onlyIf();
            }
            if (format) {
                var nonDigitsRegex = RegExp("\\D", "g"); // Matches any non digit (0-9)
                // Valid  characters: 0-9 or the phone format characters of + - . ( ) / and space
                var invalidSsnCharactersRegex = RegExp("[^\\d+-\\.()/ ]", "g");
                var ssnDigits = value.replace(nonDigitsRegex, '');
                var formattedSsn = "";
                if (ssnDigits.length === 9) {
                    // Apply US style SSN number formatting XXX-XX-XXXX, keeping only digits from original
                    formattedSsn = [ssnDigits.slice(0, 3), '-', ssnDigits.slice(3, 5), '-', ssnDigits.slice(5)].join('');
                } else {
                    // The number did not contain 7 or 10 digits, preserve formatting while removing invalid characters
                    formattedSsn = value.replace(invalidSsnCharactersRegex, '');
                }
                return formattedSsn;
            }
            else {
                return value;
            }
        }
    }

    if (options.validateLength) {
        var validationMessage = options.validateMessage;
        target.extend({
            validation: {
                validator: function (value) {
                    var nonDigitsRegex = RegExp("\\D", "g");
                    var ssnDigits = value.replace(nonDigitsRegex, '');
                    if (ssnDigits.length === 0 || (ssnDigits.length >= 9 && ssnDigits.length <= 11)) {
                        return true;
                    } else {
                        return false;
                    }
                },
                message: validationMessage
            }
        });
    }

    target.subscribe(function (newValue) {
        var formattedValue = applySSNFormatting(newValue);
        if (formattedValue !== newValue) {
            target(formattedValue);
        }
    });
};

/* The formattedGovernmentId extender attempts to keep government ID inputs formatted consistently.
   The input will be stripped of characters not consisting of digits 0-9
   Otherwise if the number consists of 9 digits, disregarding all other characters, it will be converted
   to US style formatting XXX-XX-XXXX or CANADA style formatting XXX-XXX-XXX
   If the number consists of some other number of digits, formatting will be preserved.

   Options:
     validateLength: If true, adds a knockout validation rule to ensure the number contains 9
                     numerical digits, not including formatting. Also validates with 0 digits. If the number
                     is required to be entered, use a required validation to achieve this.
     validationMessage: Optional string that overrides the default validation error message.
     culture: If true, checks for current culture selected and then do the formatting. Defaults to US style (SSN).
 */
ko.extenders.formattedGovernmentId = function (target, options) {
    var applyGovernmentIdFormatting = function (value) {
        if (value != null) {
            var format = true;
            if (typeof options.onlyIf === "function") {
                format = options.onlyIf();
            }
            if (format) {
                var nonDigitsRegex = RegExp("\\D", "g"); // Matches any non digit (0-9)
                // Valid  characters: 0-9
                var invalidGovernmentIdCharactersRegex = RegExp("[^\\d]", "g");
                var governmentIdDigits = value.replace(nonDigitsRegex, '');
                var formattedGovernmentId = "";
                if (governmentIdDigits.length === 9) {
                    if (options.culture && currentCulture.Name !== 'en-US') {
                        // Apply CANADA style SIN number formatting XXX-XXX-XXX, keeping only digits from original
                        formattedGovernmentId = [governmentIdDigits.slice(0, 3), '-', governmentIdDigits.slice(3, 6), '-', governmentIdDigits.slice(6)].join('');
                    } else {
                        // Apply US style SSN number formatting XXX-XX-XXXX, keeping only digits from original
                        formattedGovernmentId = [governmentIdDigits.slice(0, 3), '-', governmentIdDigits.slice(3, 5), '-', governmentIdDigits.slice(5)].join('');
                    }
                } else {
                    // The number did not contain 7 or 10 digits, preserve formatting while removing invalid characters
                    formattedGovernmentId = value.replace(invalidGovernmentIdCharactersRegex, '');
                }
                return formattedGovernmentId;
            }
            else {
                return value;
            }
        }
    }

    if (options.validateLength) {
        var validationMessage = options.validateMessage;
        target.extend({
            validation: {
                validator: function (value) {
                    var nonDigitsRegex = RegExp("\\D", "g");
                    var governmentIdDigits = value.replace(nonDigitsRegex, '');
                    if (governmentIdDigits.length === 0 || (governmentIdDigits.length === 9)) {
                        return true;
                    } else {
                        return false;
                    }
                },
                message: validationMessage
            }
        });
    }

    target.subscribe(function (newValue) {
        var formattedValue = applyGovernmentIdFormatting(newValue);
        if (formattedValue !== newValue) {
            target(formattedValue);
        }
    });
};


/*
 * This helps to track the changes to any observable property without 
 * having validations.
 * Ex: ko.observable("Value").extend({ trackChange: true });
 */
ko.extenders.trackChange = function (target, track) {
    if (track) {
        target.isDirty = ko.observable(false);
        target.originalValue = target();
        target.subscribe(function (newValue) {
            target.isDirty(newValue != target.originalValue);
        });
        target.reset = function () {
            target.originalValue = target();
        };
    }
    return target;
};

/*
 * This helps format the input value to currency type
 * Ex: ko.observable(100).extend({ formatAsCurrency: { precision: 2 } });
 */
ko.extenders.formatAsCurrency = function (target, options) {
    var precision = options.precision;
    if (isNaN(precision)) {
        precision = 2;
    }

    var applyCurrencyFormatting = function (value) {
        var formattedValue = "";
        if (!isNullOrEmpty(value)) {
            value = parseANumber(value);
            if (!isNaN(value)) {
                formattedValue = parseFloat(value);
                formattedValue = Globalize.format(formattedValue, "c" + precision, Globalize.findClosestCulture());
            }
            else {
                return value;
            }
        }
        return formattedValue;
    }

    target.formatted = ko.computed({
        read: function () {
            return !isNaN(target()) ? applyCurrencyFormatting(target()) : target();
        },
        write: function (newValue) {
            var current = target();
            var valueToWrite = applyCurrencyFormatting(newValue);
            if (valueToWrite === null) {
                target(null);
            }
            else if (isNaN(newValue)) {
                if (!isNaN(parseANumber(newValue))) {
                    target(valueToWrite);
                }
                else {
                    target(newValue);
                }
            }
            //only write if it changed
            else if (valueToWrite !== current) {
                target(valueToWrite);
            }
            else {
                if (newValue !== current) {
                    target(null);
                    target.notifySubscribers(valueToWrite);
                    target(valueToWrite);
                }
            }
        }
    });
    return target;
};

/*
 * This helps format the input value to percentage type
 * Ex: ko.observable(100).extend({ formatAsPercentage: { precision: 2 } });
 */
ko.extenders.formatAsPercentage = function (target, options) {
    var precision = options.precision;
    if (isNaN(precision)) {
        precision = 2;
    }

    var applyPercentageFormatting = function (value) {
        var formattedValue = "";
        if (!isNullOrEmpty(value)) {
            formattedValue = parseANumber(value);

            if (isNaN(formattedValue)) {
                return value;
            }

            if (!isNaN(value) && value >= 0) {
                formattedValue = Number(value).toFixed(precision) + " %";
            }
        }
        return formattedValue;
    }
    target.formatted = ko.computed({
        read: function () {
            return !isNaN(target()) ? applyPercentageFormatting(target()) : target();
        },
        write: function (newValue) {
            var current = target();
            var valueToWrite = applyPercentageFormatting(newValue);

            var parsedValue = Globalize.parseFloat(String(valueToWrite));
            if (isNaN(parsedValue)) {
                target(valueToWrite);
            }
            else if (valueToWrite === null) {
                target(null);
            }
            //only write if it changed
            else if (valueToWrite !== current) {
                target(valueToWrite);
            }
            else {
                if (newValue !== current) {
                    target(null);
                    target.notifySubscribers(valueToWrite);
                    target(valueToWrite);
                }
            }
        }
    });
    return target;
};
// Copyright 2020-2023 Ellucian Company L.P. and its affiliates.
// Used to resolve the breaking changes introduced in upgrade to knockout 3.5.1 where the
// arrayFirst method now returns "undefined" when there are no items in the list but before it returned null.

ko.utils.arrayFirstDeprecated = function (array, predicate, predicateOwner) {
    for (var i = 0, j = array.length; i < j; i++) {
        if (predicate.call(predicateOwner, array[i], i, array))
            return array[i];
    }
    return null;
};

// Copyright 2023 Ellucian Company L.P. and its affiliates.
// Copyright 2017 Ellucian Inc.
/* ========================================================================
 * Bootstrap: tab.js v3.3.7
 * http://getbootstrap.com/javascript/#tabs
 * ========================================================================
 * Copyright 2011-2016 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * ======================================================================== 
 * Ellucian Modifications:
 * - CSS classes prepended with proprietary 'esg' prefix for alignment
 *      with Ellucian Style Guide
 * 
 * 
 * ======================================================================== 
 * */



+ function ($) {
    'use strict';

    // TAB CLASS DEFINITION
    // ====================

    var Tab = function (element) {
        this.element = $(element);
    };

    Tab.VERSION = '3.3.7';

    Tab.TRANSITION_DURATION = 150;

    Tab.prototype.show = function () {
        var $this = this.element;
        var $ul = $this.closest('ul:not(.esg-dropdown-menu)');
        var selector = $this.data('target');

        if (!selector) {
            selector = $this.attr('href');
            selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); // strip for ie7
        }

        if ($this.parent('li').hasClass('esg-is-active')) return;

        var $previous = $ul.find('.esg-is-active:last a');
        var hideEvent = $.Event('hide.bs.tab', {
            relatedTarget: $this[0]
        });
        var showEvent = $.Event('show.bs.tab', {
            relatedTarget: $previous[0]
        });

        $previous.trigger(hideEvent);
        $this.trigger(showEvent);

        if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return;

        var $target = $(selector);

        this.activate($this.closest('li'), $ul);
        this.activate($target, $target.parent(), function () {
            $previous.trigger({
                type: 'hidden.bs.tab',
                relatedTarget: $this[0]
            });
            $this.trigger({
                type: 'shown.bs.tab',
                relatedTarget: $previous[0]
            });
        });
    };

    Tab.prototype.activate = function (element, container, callback) {
        var $active = container.find('> .esg-is-active');
        var transition = callback
            && $.support.transition
            && ($active.length && $active.hasClass('esg-fade') || !!container.find('> .esg-fade').length);

        function next() {
            $active
              .removeClass('esg-is-active')
              .find('> .esg-dropdown-menu > .esg-is-active')
                .removeClass('esg-is-active')
              .end()
              .find('[data-toggle="tab"]')
                .attr('aria-expanded', false);
            element
          .addClass('esg-is-active')
          .find('[data-toggle="tab"]')
            .attr('aria-expanded', true);
            if (transition) {
                element[0].offsetWidth; // reflow for transition
                element.addClass('esg-in');
            } else {
                element.removeClass('esg-fade');
            }

            if (element.parent('.esg-dropdown-menu').length) {
                element
                  .closest('li.esg-dropdown')
                    .addClass('esg-is-active')
                  .end()
                  .find('[data-toggle="tab"]')
                    .attr('aria-expanded', true);
            }

            callback && callback();
        }

        $active.length && transition ?
          $active
            .one('bsTransitionEnd', next)
            .emulateTransitionEnd(Tab.TRANSITION_DURATION) :
          next();

        $active.removeClass('esg-in');
    };


    // TAB PLUGIN DEFINITION
    // =====================

    function Plugin(option) {
        return this.each(function () {
            var $this = $(this);
            var data = $this.data('bs.tab');
            if (!data) $this.data('bs.tab', (data = new Tab(this)));
            if (typeof option == 'string') data[option]();
        });
    }

    var old = $.fn.tab;

    $.fn.tab = Plugin;
    $.fn.tab.Constructor = Tab;


    // TAB NO CONFLICT
    // ===============

    $.fn.tab.noConflict = function () {
        $.fn.tab = old;
        return this;
    };


    // TAB DATA-API
    // ============

    var clickHandler = function (e) {
        e.preventDefault();
        Plugin.call($(this), 'show');
    };

    $(document)
        .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
        .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler);
}(jQuery);

// Copyright 2017-2023 Ellucian Company L.P. and its affiliates.

// Knockout binding for Ellucian Style Guide Tabs
// ===================================================================================================
// Summary
// ===================================================================================================
// This binding will tabify a set of content according to ESG standards
// ===================================================================================================
// Options
// ===================================================================================================
// - activeTabId: (optional) Element ID of the tab to make active
//      default: undefined; the first tab in the mark-up will be active
// ===================================================================================================
// Mark-Up Examples (2)
// NOTE: The mark-up structure below is strictly enforced:
// - An outermost <div> must receive the esgTabs data binding
//      - A child <ul> must be a container for the tabs themselves
//          - Each tab must consist of an <li> element containing an <a> element
//          - The <a> element must have an "href" property matching one of the tab content <div> elements
//      - A child <div> must be a container for the tab contents
//          - Each tab content must be a <div> element
//          - Each tab content <div> must have an "id" property matching one of the tab <li> elements
// ===================================================================================================
// <!-- Renders a tabbed interface with the first (default) tab (id = 'tab1-content') as active -->
//
//<div data-bind="esgTabs: { }>
//    <ul>
//        <li><a href="#tab1-content">Tab 1</a></li>
//        <li><a href="#tab2-content">Tab 2</a></li>
//        <li><a href="#tab3-content">Tab 3</a></li>
//    </ul>
//    <div>
//        <div id="tab1-content">
//            <!-- Tab 1 Content -->
//        </div>
//        <div id="tab2-content">
//            <!-- Tab 2 Content -->
//        </div>
//        <div id="tab3-content">
//            <!-- Tab 3 Content -->
//        </div>
//    </div>
//</div>
//
// <!-- Renders a tabbed interface with the specified tab (id = 'tab2-content') as active -->
//
//<div data-bind="esgTabs: { activeTabId: 'tab2-content' }>
//    <ul>
//        <li><a href="#tab1-content">Tab 1</a></li>
//        <li><a href="#tab2-content">Tab 2</a></li>
//        <li><a href="#tab3-content">Tab 3</a></li>
//    </ul>
//    <div>
//        <div id="tab1-content">
//            <!-- Tab 1 Content -->
//        </div>
//        <div id="tab2-content">
//            <!-- Tab 2 Content -->
//        </div>
//        <div id="tab3-content">
//            <!-- Tab 3 Content -->
//        </div>
//    </div>
//</div>
// ===================================================================================================
ko.bindingHandlers.esgTabs = {
    init: function (element, valueAccessor) {

        // Add appropriate CSS to container mark-up
        $(element).addClass("esg-tab");

        // Identify child list element to use as menu structure
        var tabMenu = $(element).children("ul").first();

        // Add CSS and mark-up to menu structure
        tabMenu.addClass("esg-tab__menu");
        tabMenu.attr("role", "tablist");
        tabMenu.attr("data-tabs", "tabs");

        // Add CSS and mark-up to tabs
        var tabs = tabMenu.children("li");
        tabs.addClass("esg-tab__menu-item");

        // Add CSS and mark-up to tab anchors
        var tabAnchors = tabs.find("a");
        tabAnchors.addClass("esg-tab__link");
        tabAnchors.attr("data-toggle", "tab");

        // Change activeTabId observable when active tab is clicked
        if (ko.isObservable(valueAccessor().activeTabId)) {
            $(tabAnchors).each(function (index) {
                $(this).on('shown.bs.tab', function () {
                    valueAccessor().activeTabId($(this).attr("href").replace("#", ""));
                    ;
                });
            });
        }

        // Identify child element to use as tab contents
        var tabContents = $(element).children("div").first();

        // Add CSS and mark-up to tab contents
        tabContents.addClass("esg-tab__content");

        // Add CSS and mark-up to tabs themselves
        var tabElements = tabContents.children("div");
        tabElements.addClass("esg-tab__panel");

        // Set the active tab using param or first tab if no param given
        var params = ko.utils.unwrapObservable(valueAccessor()) || {};
        if (params.activeTabId) {
            $('a[href="#' + ko.unwrap(params.activeTabId) + '"]').tab('show');
        } else {
            $(tabAnchors[0]).tab('show');
        }
    },
    update: function (element, valueAccessor, allBindingsAccessor) {
        // Switch to the correct tab when the activeTabId observable changes (if defined)
        var params = ko.utils.unwrapObservable(valueAccessor()) || {};
        if (params.activeTabId) {
            $('a[href="#' + ko.unwrap(params.activeTabId) + '"]').tab('show');
        }
    }
};

/*
    json2.js
    2011-10-19

    Public Domain.

    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

    See http://www.JSON.org/js.html


    This code should be minified before deployment.
    See http://javascript.crockford.com/jsmin.html

    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
    NOT CONTROL.


    This file creates a global JSON object containing two methods: stringify
    and parse.

        JSON.stringify(value, replacer, space)
            value       any JavaScript value, usually an object or array.

            replacer    an optional parameter that determines how object
                        values are stringified for objects. It can be a
                        function or an array of strings.

            space       an optional parameter that specifies the indentation
                        of nested structures. If it is omitted, the text will
                        be packed without extra whitespace. If it is a number,
                        it will specify the number of spaces to indent at each
                        level. If it is a string (such as '\t' or '&nbsp;'),
                        it contains the characters used to indent at each level.

            This method produces a JSON text from a JavaScript value.

            When an object value is found, if the object contains a toJSON
            method, its toJSON method will be called and the result will be
            stringified. A toJSON method does not serialize: it returns the
            value represented by the name/value pair that should be serialized,
            or undefined if nothing should be serialized. The toJSON method
            will be passed the key associated with the value, and this will be
            bound to the value

            For example, this would serialize Dates as ISO strings.

                Date.prototype.toJSON = function (key) {
                    function f(n) {
                        // Format integers to have at least two digits.
                        return n < 10 ? '0' + n : n;
                    }

                    return this.getUTCFullYear()   + '-' +
                         f(this.getUTCMonth() + 1) + '-' +
                         f(this.getUTCDate())      + 'T' +
                         f(this.getUTCHours())     + ':' +
                         f(this.getUTCMinutes())   + ':' +
                         f(this.getUTCSeconds())   + 'Z';
                };

            You can provide an optional replacer method. It will be passed the
            key and value of each member, with this bound to the containing
            object. The value that is returned from your method will be
            serialized. If your method returns undefined, then the member will
            be excluded from the serialization.

            If the replacer parameter is an array of strings, then it will be
            used to select the members to be serialized. It filters the results
            such that only members with keys listed in the replacer array are
            stringified.

            Values that do not have JSON representations, such as undefined or
            functions, will not be serialized. Such values in objects will be
            dropped; in arrays they will be replaced with null. You can use
            a replacer function to replace those with JSON values.
            JSON.stringify(undefined) returns undefined.

            The optional space parameter produces a stringification of the
            value that is filled with line breaks and indentation to make it
            easier to read.

            If the space parameter is a non-empty string, then that string will
            be used for indentation. If the space parameter is a number, then
            the indentation will be that many spaces.

            Example:

            text = JSON.stringify(['e', {pluribus: 'unum'}]);
            // text is '["e",{"pluribus":"unum"}]'


            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'

            text = JSON.stringify([new Date()], function (key, value) {
                return this[key] instanceof Date ?
                    'Date(' + this[key] + ')' : value;
            });
            // text is '["Date(---current time---)"]'


        JSON.parse(text, reviver)
            This method parses a JSON text to produce an object or array.
            It can throw a SyntaxError exception.

            The optional reviver parameter is a function that can filter and
            transform the results. It receives each of the keys and values,
            and its return value is used instead of the original value.
            If it returns what it received, then the structure is not modified.
            If it returns undefined then the member is deleted.

            Example:

            // Parse the text. Values that look like ISO date strings will
            // be converted to Date objects.

            myData = JSON.parse(text, function (key, value) {
                var a;
                if (typeof value === 'string') {
                    a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
                    if (a) {
                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
                            +a[5], +a[6]));
                    }
                }
                return value;
            });

            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
                var d;
                if (typeof value === 'string' &&
                        value.slice(0, 5) === 'Date(' &&
                        value.slice(-1) === ')') {
                    d = new Date(value.slice(5, -1));
                    if (d) {
                        return d;
                    }
                }
                return value;
            });


    This is a reference implementation. You are free to copy, modify, or
    redistribute.
*/

/*jslint evil: true, regexp: true */

/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    lastIndex, length, parse, prototype, push, replace, slice, stringify,
    test, toJSON, toString, valueOf
*/


// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.

var JSON;
if (!JSON) {
    JSON = {};
}

(function () {
    'use strict';

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {

        Date.prototype.toJSON = function (key) {

            return isFinite(this.valueOf())
                ? this.getUTCFullYear()     + '-' +
                    f(this.getUTCMonth() + 1) + '-' +
                    f(this.getUTCDate())      + 'T' +
                    f(this.getUTCHours())     + ':' +
                    f(this.getUTCMinutes())   + ':' +
                    f(this.getUTCSeconds())   + 'Z'
                : null;
        };

        String.prototype.toJSON      =
            Number.prototype.toJSON  =
            Boolean.prototype.toJSON = function (key) {
                return this.valueOf();
            };
    }

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;


    function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string'
                ? c
                : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }


    function str(key, holder) {

// Produce a string from holder[key].

        var i,          // The loop counter.
            k,          // The member key.
            v,          // The member value.
            length,
            mind = gap,
            partial,
            value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }

// What happens next depends on the value's type.

        switch (typeof value) {
        case 'string':
            return quote(value);

        case 'number':

// JSON numbers must be finite. Encode non-finite numbers as null.

            return isFinite(value) ? String(value) : 'null';

        case 'boolean':
        case 'null':

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.

            return String(value);

// If the type is 'object', we might be dealing with an object or an array or
// null.

        case 'object':

// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.

            if (!value) {
                return 'null';
            }

// Make an array to hold the partial results of stringifying this object value.

            gap += indent;
            partial = [];

// Is the value an array?

            if (Object.prototype.toString.apply(value) === '[object Array]') {

// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                v = partial.length === 0
                    ? '[]'
                    : gap
                    ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
                    : '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }

// If the replacer is an array, use it to select the members to be stringified.

            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    if (typeof rep[i] === 'string') {
                        k = rep[i];
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {

// Otherwise, iterate through all of the keys in the object.

                for (k in value) {
                    if (Object.prototype.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

            v = partial.length === 0
                ? '{}'
                : gap
                ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
                : '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }

// If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

            var i;
            gap = '';
            indent = '';

// If the space parameter is a number, make an indent string containing that
// many spaces.

            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }

// If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === 'string') {
                indent = space;
            }

// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                    typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }

// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.

            return str('', {'': value});
        };
    }


// If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }


// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.

            text = String(text);
            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

            if (/^[\],:{}\s]*$/
                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval('(' + text + ')');

// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                return typeof reviver === 'function'
                    ? walk({'': j}, '')
                    : j;
            }

// If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError('JSON.parse');
        };
    }
}());

// Copyright 2016-2023 Ellucian Company L.P. and its affiliates.
/*
* Provides functionality related to the AccountController to support
* login and session functionality.
*/

(function (account, $, undefined) {

    //Public properties
    account.loginLinkId = "#loginLink";
    account.ajaxEnabledAccountPopups = true;

    //Public methods

    // returns true when an invalid session was handled, false when session is OK.
    account.handleInvalidSessionResponse = function (jsonResonse, showLoginOnInvalidSession) {

        // default missing flags...
        if (showLoginOnInvalidSession == undefined) { showLoginOnInvalidSession = true; }

        try {
            if (jsonResonse.InvalidSession != null) {
                if (jsonResonse.InvalidSession === "true") {

                    if (showLoginOnInvalidSession) {
                        if (account.ajaxEnabledAccountPopups) {
                            // close all open dialogs before opening login popup
                            $(".esg-modal-dialog").addClass("hidden");
                            // show the ajax login popup dialog...
                            var id = account.loginLinkId;
                            account.loadAndShowDialog(id, logInUrl);
                        } else {
                            // redirect to the full login page...
                            var returnUrl = location.pathname + (location.search || '') + (location.hash || '');
                            location.href = jsonResonse.LoginPageUrl + "?returnUrl=" + encodeURIComponent(returnUrl);
                        }
                    }
                    return true;
                }
            }
        } catch (e) {

        }
        return false;
    };

    // returns true when an invalid session was handled, false when session is OK.
    // disableAjaxAccountPopups is to override ajax popup to not show login modal dialog instead show full login page when session tiemsout for authenticated users
    // disableAjaxAccountPopups parameter was added as workaroud to avoid antiForgeryToken issue when a controller is used by guest as well as authenticated user.
    // for guest user if invalid sesssion happens (like token expired while doing Data reads in API) then we want to reload the page (this will force SelfService to regenerate a new token for guest account)
    //This method is used in catalog and instant enrollment where the workflow can be accessed by authenticated as well as anonymous (guest) users.
    account.handleInvalidSessionResponseSkipGuest = function (jsonResponse, disableAjaxAccountPopups) {

        // default missing flags...
        if (disableAjaxAccountPopups == undefined) { disableAjaxAccountPopups = false; }


        try {
            if (jsonResponse.InvalidSession != null) {
                if (jsonResponse.InvalidSession === "true") {
                    //If the user was Guest account but session got invalid for the guest then take it back to same page where it got invalid
                    if (isGuest !== undefined && isGuest !== null && isGuest === true) {
                        location.href = location.pathname;
                        return true;
                    }
                    if (account.ajaxEnabledAccountPopups && disableAjaxAccountPopups == false) {
                        // close all open dialogs before opening login popup
                        $(".esg-modal-dialog").addClass("hidden");
                        // show the ajax login popup dialog...
                        var id = account.loginLinkId;
                        account.loadAndShowDialog(id, logInUrl);
                    } else {
                        // redirect to the full login page...
                        var returnUrl = location.pathname + (location.search || '') + (location.hash || '');
                        location.href = jsonResponse.LoginPageUrl + "?returnUrl=" + encodeURIComponent(returnUrl);
                    }

                    return true;
                }
            }
        } catch (e) {

        }
        return false;
    };


    account.loadAndShowDialog = function (id, url) {
        var separator = url.indexOf('?') >= 0 ? '&' : '?';

        // Load the dialog with the content=1 QueryString in order to get a PartialView
        $.get(url + separator + 'content=1')
            .done(function (content) {

                var $responseHtml = $('<div class="esg-modal-dialog"><div class="esg-modal-dialog__header"><h3 class="esg-modal-dialog__title"></h3></div><div class="esg-modal-dialog__body">' + content + '</div></div><div class="esg-modal-overlay"></div>');
                $responseHtml.find('.esg-modal-dialog__title').text("Sign In");

                $responseHtml
                    .hide() // Hide the dialog for now so we prevent flicker
                    .appendTo(document.body)
                    .filter('div') // Filter for the div tag only, script tags could surface
                    .show() // Show the dialog
                    .find('form') // Attach logic on forms
                    .submit(formSubmitHandler)
                    .find('esg-modal-overlay') // Find and show the overlay
                    .show()
                    .end();
            });
    };

    // Private methods

    function getValidationSummaryErrors($form) {
        // We verify if we created it beforehand
        var errorSummary = $form.find('.validation-summary-errors, .validation-summary-valid');
        if (!errorSummary.length) {
            errorSummary = $('<div class="validation-summary-errors"><span>Please correct the errors and try again.</span><ul></ul></div>')
                .prependTo($form);
        }

        return errorSummary;
    };

    function displayErrors(form, errors) {
        var errorSummary = getValidationSummaryErrors(form)
            .removeClass('validation-summary-valid')
            .addClass('validation-summary-errors');

        var items = errors.map(function (error) {
            return '<li>' + DOMPurify.sanitize(error) + '</li>';
        }).join('');

        var ul = errorSummary
            .find('ul')
            .empty()
            .append(items);
    };

    function resetForm($form) {
        // We reset the form so we make sure unobtrusive errors get cleared out.
        $form[0].reset();

        getValidationSummaryErrors($form)
            .removeClass('validation-summary-errors')
            .addClass('validation-summary-valid')
    };

    function disableInput() {
        $('input:submit').attr('disabled', 'disabled');
    };

    function enableInput() {
        $('input').removeAttr('disabled');
    };

    function formSubmitHandler(e) {
        var $form = $(this);

        if (!$form.attr('action').contains("OtpVerification")) {

            // We check if jQuery.validator exists on the form
            if (!$form.valid || $form.valid()) {

                var rvt = $form.find('input[name="__RequestVerificationToken"]').val();

                $.ajax($form.attr('action'), {
                    type: "POST",
                    data: $form.serializeArray(),
                    beforeSend: function (request) {
                        request.setRequestHeader("__RequestVerificationToken", rvt);
                    },
                    success: function (data, textStatus, jqXHR) {
                        json = data || {};

                        // In case of success, we redirect to the provided URL or the same page.
                        if (json.success) {
                            window.location.reload();
                        } else if (json.errors) {
                            displayErrors($form, json.errors);
                        }
                        enableInput();
                    },
                    error: function (jqXHR, textStatus, errorThrown) {
                        displayErrors($form, ['An unknown error happened.']);
                        enableInput();
                    },
                    complete: function () { }
                });

                // must do this after otherwise the input fields will not be picked up by the post...
                disableInput();
            }

            // Prevent the normal behavior since we opened the dialog
            e.preventDefault();
        }
    };

}(window.account = window.account || {}, jQuery));

// Copyright 2018-2023 Ellucian Company L.P. and its affiliates.
// - Modified to use resx JS variables for day-of-week names and abbreviations

/*!
 * Globalize
 *
 * http://github.com/jquery/globalize
 *
 * Copyright Software Freedom Conservancy, Inc.
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 */

var Ellucian = Ellucian || {};
Ellucian.DaysOfWeek = Ellucian.DaysOfWeek || {};
Ellucian.DaysOfWeek.Resources = Ellucian.DaysOfWeek.Resources || {};

(function( window, undefined ) {

var Globalize,
    // private variables
    regexHex,
    regexInfinity,
    regexParseFloat,
    regexTrim,
    // private JavaScript utility functions
    arrayIndexOf,
    endsWith,
    extend,
    isArray,
    isFunction,
    isObject,
    startsWith,
    trim,
    truncate,
    zeroPad,
    // private Globalization utility functions
    appendPreOrPostMatch,
    expandFormat,
    formatDate,
    formatNumber,
    getTokenRegExp,
    getEra,
    getEraYear,
    parseExact,
    parseNegativePattern;

// Global variable (Globalize) or CommonJS module (globalize)
Globalize = function( cultureSelector ) {
    return new Globalize.prototype.init( cultureSelector );
};

if ( typeof require !== "undefined" &&
    typeof exports !== "undefined" &&
    typeof module !== "undefined" ) {
    // Assume CommonJS
    module.exports = Globalize;
} else {
    // Export as global variable
    window.Globalize = Globalize;
}

Globalize.cultures = {};

Globalize.prototype = {
    constructor: Globalize,
    init: function( cultureSelector ) {
        this.cultures = Globalize.cultures;
        this.cultureSelector = cultureSelector;

        return this;
    }
};
Globalize.prototype.init.prototype = Globalize.prototype;

// 1. When defining a culture, all fields are required except the ones stated as optional.
// 2. Each culture should have a ".calendars" object with at least one calendar named "standard"
//    which serves as the default calendar in use by that culture.
// 3. Each culture should have a ".calendar" object which is the current calendar being used,
//    it may be dynamically changed at any time to one of the calendars in ".calendars".
Globalize.cultures[ "default" ] = {
    // A unique name for the culture in the form <language code>-<country/region code>
    name: "en",
    // the name of the culture in the english language
    englishName: "English",
    // the name of the culture in its own language
    nativeName: "English",
    // whether the culture uses right-to-left text
    isRTL: false,
    // "language" is used for so-called "specific" cultures.
    // For example, the culture "es-CL" means "Spanish, in Chili".
    // It represents the Spanish-speaking culture as it is in Chili,
    // which might have different formatting rules or even translations
    // than Spanish in Spain. A "neutral" culture is one that is not
    // specific to a region. For example, the culture "es" is the generic
    // Spanish culture, which may be a more generalized version of the language
    // that may or may not be what a specific culture expects.
    // For a specific culture like "es-CL", the "language" field refers to the
    // neutral, generic culture information for the language it is using.
    // This is not always a simple matter of the string before the dash.
    // For example, the "zh-Hans" culture is netural (Simplified Chinese).
    // And the "zh-SG" culture is Simplified Chinese in Singapore, whose lanugage
    // field is "zh-CHS", not "zh".
    // This field should be used to navigate from a specific culture to it's
    // more general, neutral culture. If a culture is already as general as it
    // can get, the language may refer to itself.
    language: "en",
    // numberFormat defines general number formatting rules, like the digits in
    // each grouping, the group separator, and how negative numbers are displayed.
    numberFormat: {
        // [negativePattern]
        // Note, numberFormat.pattern has no "positivePattern" unlike percent and currency,
        // but is still defined as an array for consistency with them.
        //   negativePattern: one of "(n)|-n|- n|n-|n -"
        pattern: [ "-n" ],
        // number of decimal places normally shown
        decimals: 2,
        // string that separates number groups, as in 1,000,000
        ",": ",",
        // string that separates a number from the fractional portion, as in 1.99
        ".": ".",
        // array of numbers indicating the size of each number group.
        // TODO: more detailed description and example
        groupSizes: [ 3 ],
        // symbol used for positive numbers
        "+": "+",
        // symbol used for negative numbers
        "-": "-",
        // symbol used for NaN (Not-A-Number)
        "NaN": "NaN",
        // symbol used for Negative Infinity
        negativeInfinity: "-Infinity",
        // symbol used for Positive Infinity
        positiveInfinity: "Infinity",
        percent: {
            // [negativePattern, positivePattern]
            //   negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %"
            //   positivePattern: one of "n %|n%|%n|% n"
            pattern: [ "-n %", "n %" ],
            // number of decimal places normally shown
            decimals: 2,
            // array of numbers indicating the size of each number group.
            // TODO: more detailed description and example
            groupSizes: [ 3 ],
            // string that separates number groups, as in 1,000,000
            ",": ",",
            // string that separates a number from the fractional portion, as in 1.99
            ".": ".",
            // symbol used to represent a percentage
            symbol: "%"
        },
        currency: {
            // [negativePattern, positivePattern]
            //   negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)"
            //   positivePattern: one of "$n|n$|$ n|n $"
            pattern: [ "($n)", "$n" ],
            // number of decimal places normally shown
            decimals: 2,
            // array of numbers indicating the size of each number group.
            // TODO: more detailed description and example
            groupSizes: [ 3 ],
            // string that separates number groups, as in 1,000,000
            ",": ",",
            // string that separates a number from the fractional portion, as in 1.99
            ".": ".",
            // symbol used to represent currency
            symbol: "$"
        }
    },
    // calendars defines all the possible calendars used by this culture.
    // There should be at least one defined with name "standard", and is the default
    // calendar used by the culture.
    // A calendar contains information about how dates are formatted, information about
    // the calendar's eras, a standard set of the date formats,
    // translations for day and month names, and if the calendar is not based on the Gregorian
    // calendar, conversion functions to and from the Gregorian calendar.
    calendars: {
        standard: {
            // name that identifies the type of calendar this is
            name: "Gregorian_USEnglish",
            // separator of parts of a date (e.g. "/" in 11/05/1955)
            "/": "/",
            // separator of parts of a time (e.g. ":" in 05:44 PM)
            ":": ":",
            // the first day of the week (0 = Sunday, 1 = Monday, etc)
            firstDay: 0,
            days: {
                // full day names
                names: [
                    Ellucian.DaysOfWeek.Resources.Sunday,
                    Ellucian.DaysOfWeek.Resources.Monday,
                    Ellucian.DaysOfWeek.Resources.Tuesday,
                    Ellucian.DaysOfWeek.Resources.Wednesday,
                    Ellucian.DaysOfWeek.Resources.Thursday,
                    Ellucian.DaysOfWeek.Resources.Friday,
                    Ellucian.DaysOfWeek.Resources.Saturday
                ],
                // abbreviated day names
                namesAbbr: [
                    Ellucian.DaysOfWeek.Resources.SundayAbbreviationLong,
                    Ellucian.DaysOfWeek.Resources.MondayAbbreviationLong,
                    Ellucian.DaysOfWeek.Resources.TuesdayAbbreviationLong,
                    Ellucian.DaysOfWeek.Resources.WednesdayAbbreviationLong,
                    Ellucian.DaysOfWeek.Resources.ThursdayAbbreviationLong,
                    Ellucian.DaysOfWeek.Resources.FridayAbbreviationLong,
                    Ellucian.DaysOfWeek.Resources.SaturdayAbbreviationLong
                ],
                // shortest day names
                namesShort: [
                    Ellucian.DaysOfWeek.Resources.SundayAbbreviationShort,
                    Ellucian.DaysOfWeek.Resources.MondayAbbreviationShort,
                    Ellucian.DaysOfWeek.Resources.TuesdayAbbreviationShort,
                    Ellucian.DaysOfWeek.Resources.WednesdayAbbreviationShort,
                    Ellucian.DaysOfWeek.Resources.ThursdayAbbreviationShort,
                    Ellucian.DaysOfWeek.Resources.FridayAbbreviationShort,
                    Ellucian.DaysOfWeek.Resources.SaturdayAbbreviationShort
                ]
            },
            months: {
                // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar)
                names: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "" ],
                // abbreviated month names
                namesAbbr: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" ]
            },
            // AM and PM designators in one of these forms:
            // The usual view, and the upper and lower case versions
            //   [ standard, lowercase, uppercase ]
            // The culture does not use AM or PM (likely all standard date formats use 24 hour time)
            //   null
            AM: [ "AM", "am", "AM" ],
            PM: [ "PM", "pm", "PM" ],
            eras: [
                // eras in reverse chronological order.
                // name: the name of the era in this culture (e.g. A.D., C.E.)
                // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era.
                // offset: offset in years from gregorian calendar
                {
                    "name": "A.D.",
                    "start": null,
                    "offset": 0
                }
            ],
            // when a two digit year is given, it will never be parsed as a four digit
            // year greater than this year (in the appropriate era for the culture)
            // Set it as a full year (e.g. 2029) or use an offset format starting from
            // the current year: "+19" would correspond to 2029 if the current year 2010.
            twoDigitYearMax: 2029,
            // set of predefined date and time patterns used by the culture
            // these represent the format someone in this culture would expect
            // to see given the portions of the date that are shown.
            patterns: {
                // short date pattern
                d: "M/d/yyyy",
                // long date pattern
                D: "dddd, MMMM dd, yyyy",
                // short time pattern
                t: "h:mm tt",
                // long time pattern
                T: "h:mm:ss tt",
                // long date, short time pattern
                f: "dddd, MMMM dd, yyyy h:mm tt",
                // long date, long time pattern
                F: "dddd, MMMM dd, yyyy h:mm:ss tt",
                // month/day pattern
                M: "MMMM dd",
                // month/year pattern
                Y: "yyyy MMMM",
                // S is a sortable format that does not vary by culture
                S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss"
            }
            // optional fields for each calendar:
            /*
            monthsGenitive:
                Same as months but used when the day preceeds the month.
                Omit if the culture has no genitive distinction in month names.
                For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx
            convert:
                Allows for the support of non-gregorian based calendars. This convert object is used to
                to convert a date to and from a gregorian calendar date to handle parsing and formatting.
                The two functions:
                    fromGregorian( date )
                        Given the date as a parameter, return an array with parts [ year, month, day ]
                        corresponding to the non-gregorian based year, month, and day for the calendar.
                    toGregorian( year, month, day )
                        Given the non-gregorian year, month, and day, return a new Date() object
                        set to the corresponding date in the gregorian calendar.
            */
        }
    },
    // For localized strings
    messages: {}
};

Globalize.cultures[ "default" ].calendar = Globalize.cultures[ "default" ].calendars.standard;

Globalize.cultures.en = Globalize.cultures[ "default" ];

Globalize.cultureSelector = "en";

//
// private variables
//

regexHex = /^0x[a-f0-9]+$/i;
regexInfinity = /^[+\-]?infinity$/i;
regexParseFloat = /^[+\-]?\d*\.?\d*(e[+\-]?\d+)?$/;
regexTrim = /^\s+|\s+$/g;

//
// private JavaScript utility functions
//

arrayIndexOf = function( array, item ) {
    if ( array.indexOf ) {
        return array.indexOf( item );
    }
    for ( var i = 0, length = array.length; i < length; i++ ) {
        if ( array[i] === item ) {
            return i;
        }
    }
    return -1;
};

endsWith = function( value, pattern ) {
    return value.substr( value.length - pattern.length ) === pattern;
};

extend = function() {
    var options, name, src, copy, copyIsArray, clone,
        target = arguments[0] || {},
        i = 1,
        length = arguments.length,
        deep = false;

    // Handle a deep copy situation
    if ( typeof target === "boolean" ) {
        deep = target;
        target = arguments[1] || {};
        // skip the boolean and the target
        i = 2;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if ( typeof target !== "object" && !isFunction(target) ) {
        target = {};
    }

    for ( ; i < length; i++ ) {
        // Only deal with non-null/undefined values
        if ( (options = arguments[ i ]) != null ) {
            // Extend the base object
            for ( name in options ) {
                src = target[ name ];
                copy = options[ name ];

                // Prevent never-ending loop
                if ( target === copy ) {
                    continue;
                }

                // Recurse if we're merging plain objects or arrays
                if ( deep && copy && ( isObject(copy) || (copyIsArray = isArray(copy)) ) ) {
                    if ( copyIsArray ) {
                        copyIsArray = false;
                        clone = src && isArray(src) ? src : [];

                    } else {
                        clone = src && isObject(src) ? src : {};
                    }

                    // Never move original objects, clone them
                    target[ name ] = extend( deep, clone, copy );

                // Don't bring in undefined values
                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }

    // Return the modified object
    return target;
};

isArray = Array.isArray || function( obj ) {
    return Object.prototype.toString.call( obj ) === "[object Array]";
};

isFunction = function( obj ) {
    return Object.prototype.toString.call( obj ) === "[object Function]";
};

isObject = function( obj ) {
    return Object.prototype.toString.call( obj ) === "[object Object]";
};

startsWith = function( value, pattern ) {
    return value.indexOf( pattern ) === 0;
};

trim = function( value ) {
    return ( value + "" ).replace( regexTrim, "" );
};

truncate = function( value ) {
    if ( isNaN( value ) ) {
        return NaN;
    }
    return Math[ value < 0 ? "ceil" : "floor" ]( value );
};

zeroPad = function( str, count, left ) {
    var l;
    for ( l = str.length; l < count; l += 1 ) {
        str = ( left ? ("0" + str) : (str + "0") );
    }
    return str;
};

//
// private Globalization utility functions
//

appendPreOrPostMatch = function( preMatch, strings ) {
    // appends pre- and post- token match strings while removing escaped characters.
    // Returns a single quote count which is used to determine if the token occurs
    // in a string literal.
    var quoteCount = 0,
        escaped = false;
    for ( var i = 0, il = preMatch.length; i < il; i++ ) {
        var c = preMatch.charAt( i );
        switch ( c ) {
            case "\'":
                if ( escaped ) {
                    strings.push( "\'" );
                }
                else {
                    quoteCount++;
                }
                escaped = false;
                break;
            case "\\":
                if ( escaped ) {
                    strings.push( "\\" );
                }
                escaped = !escaped;
                break;
            default:
                strings.push( c );
                escaped = false;
                break;
        }
    }
    return quoteCount;
};

expandFormat = function( cal, format ) {
    // expands unspecified or single character date formats into the full pattern.
    format = format || "F";
    var pattern,
        patterns = cal.patterns,
        len = format.length;
    if ( len === 1 ) {
        pattern = patterns[ format ];
        if ( !pattern ) {
            throw "Invalid date format string \'" + format + "\'.";
        }
        format = pattern;
    }
    else if ( len === 2 && format.charAt(0) === "%" ) {
        // %X escape format -- intended as a custom format string that is only one character, not a built-in format.
        format = format.charAt( 1 );
    }
    return format;
};

formatDate = function( value, format, culture ) {
    var cal = culture.calendar,
        convert = cal.convert,
        ret;

    if ( !format || !format.length || format === "i" ) {
        if ( culture && culture.name.length ) {
            if ( convert ) {
                // non-gregorian calendar, so we cannot use built-in toLocaleString()
                ret = formatDate( value, cal.patterns.F, culture );
            }
            else {
                var eraDate = new Date( value.getTime() ),
                    era = getEra( value, cal.eras );
                eraDate.setFullYear( getEraYear(value, cal, era) );
                ret = eraDate.toLocaleString();
            }
        }
        else {
            ret = value.toString();
        }
        return ret;
    }

    var eras = cal.eras,
        sortable = format === "s";
    format = expandFormat( cal, format );

    // Start with an empty string
    ret = [];
    var hour,
        zeros = [ "0", "00", "000" ],
        foundDay,
        checkedDay,
        dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g,
        quoteCount = 0,
        tokenRegExp = getTokenRegExp(),
        converted;

    function padZeros( num, c ) {
        var r, s = num + "";
        if ( c > 1 && s.length < c ) {
            r = ( zeros[c - 2] + s);
            return r.substr( r.length - c, c );
        }
        else {
            r = s;
        }
        return r;
    }

    function hasDay() {
        if ( foundDay || checkedDay ) {
            return foundDay;
        }
        foundDay = dayPartRegExp.test( format );
        checkedDay = true;
        return foundDay;
    }

    function getPart( date, part ) {
        if ( converted ) {
            return converted[ part ];
        }
        switch ( part ) {
            case 0:
                return date.getFullYear();
            case 1:
                return date.getMonth();
            case 2:
                return date.getDate();
            default:
                throw "Invalid part value " + part;
        }
    }

    if ( !sortable && convert ) {
        converted = convert.fromGregorian( value );
    }

    for ( ; ; ) {
        // Save the current index
        var index = tokenRegExp.lastIndex,
            // Look for the next pattern
            ar = tokenRegExp.exec( format );

        // Append the text before the pattern (or the end of the string if not found)
        var preMatch = format.slice( index, ar ? ar.index : format.length );
        quoteCount += appendPreOrPostMatch( preMatch, ret );

        if ( !ar ) {
            break;
        }

        // do not replace any matches that occur inside a string literal.
        if ( quoteCount % 2 ) {
            ret.push( ar[0] );
            continue;
        }

        var current = ar[ 0 ],
            clength = current.length;

        switch ( current ) {
            case "ddd":
                //Day of the week, as a three-letter abbreviation
            case "dddd":
                // Day of the week, using the full name
                var names = ( clength === 3 ) ? cal.days.namesAbbr : cal.days.names;
                ret.push( names[value.getDay()] );
                break;
            case "d":
                // Day of month, without leading zero for single-digit days
            case "dd":
                // Day of month, with leading zero for single-digit days
                foundDay = true;
                ret.push(
                    padZeros( getPart(value, 2), clength )
                );
                break;
            case "MMM":
                // Month, as a three-letter abbreviation
            case "MMMM":
                // Month, using the full name
                var part = getPart( value, 1 );
                ret.push(
                    ( cal.monthsGenitive && hasDay() ) ?
                    ( cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ] ) :
                    ( cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] )
                );
                break;
            case "M":
                // Month, as digits, with no leading zero for single-digit months
            case "MM":
                // Month, as digits, with leading zero for single-digit months
                ret.push(
                    padZeros( getPart(value, 1) + 1, clength )
                );
                break;
            case "y":
                // Year, as two digits, but with no leading zero for years less than 10
            case "yy":
                // Year, as two digits, with leading zero for years less than 10
            case "yyyy":
                // Year represented by four full digits
                part = converted ? converted[ 0 ] : getEraYear( value, cal, getEra(value, eras), sortable );
                if ( clength < 4 ) {
                    part = part % 100;
                }
                ret.push(
                    padZeros( part, clength )
                );
                break;
            case "h":
                // Hours with no leading zero for single-digit hours, using 12-hour clock
            case "hh":
                // Hours with leading zero for single-digit hours, using 12-hour clock
                hour = value.getHours() % 12;
                if ( hour === 0 ) hour = 12;
                ret.push(
                    padZeros( hour, clength )
                );
                break;
            case "H":
                // Hours with no leading zero for single-digit hours, using 24-hour clock
            case "HH":
                // Hours with leading zero for single-digit hours, using 24-hour clock
                ret.push(
                    padZeros( value.getHours(), clength )
                );
                break;
            case "m":
                // Minutes with no leading zero for single-digit minutes
            case "mm":
                // Minutes with leading zero for single-digit minutes
                ret.push(
                    padZeros( value.getMinutes(), clength )
                );
                break;
            case "s":
                // Seconds with no leading zero for single-digit seconds
            case "ss":
                // Seconds with leading zero for single-digit seconds
                ret.push(
                    padZeros( value.getSeconds(), clength )
                );
                break;
            case "t":
                // One character am/pm indicator ("a" or "p")
            case "tt":
                // Multicharacter am/pm indicator
                part = value.getHours() < 12 ? ( cal.AM ? cal.AM[0] : " " ) : ( cal.PM ? cal.PM[0] : " " );
                ret.push( clength === 1 ? part.charAt(0) : part );
                break;
            case "f":
                // Deciseconds
            case "ff":
                // Centiseconds
            case "fff":
                // Milliseconds
                ret.push(
                    padZeros( value.getMilliseconds(), 3 ).substr( 0, clength )
                );
                break;
            case "z":
                // Time zone offset, no leading zero
            case "zz":
                // Time zone offset with leading zero
                hour = value.getTimezoneOffset() / 60;
                ret.push(
                    ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), clength )
                );
                break;
            case "zzz":
                // Time zone offset with leading zero
                hour = value.getTimezoneOffset() / 60;
                ret.push(
                    ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), 2 ) +
                    // Hard coded ":" separator, rather than using cal.TimeSeparator
                    // Repeated here for consistency, plus ":" was already assumed in date parsing.
                    ":" + padZeros( Math.abs(value.getTimezoneOffset() % 60), 2 )
                );
                break;
            case "g":
            case "gg":
                if ( cal.eras ) {
                    ret.push(
                        cal.eras[ getEra(value, eras) ].name
                    );
                }
                break;
        case "/":
            ret.push( cal["/"] );
            break;
        default:
            throw "Invalid date format pattern \'" + current + "\'.";
        }
    }
    return ret.join( "" );
};

// formatNumber
(function() {
    var expandNumber;

    expandNumber = function( number, precision, formatInfo ) {
        var groupSizes = formatInfo.groupSizes,
            curSize = groupSizes[ 0 ],
            curGroupIndex = 1,
            factor = Math.pow( 10, precision ),
            rounded = Math.round( number * factor ) / factor;

        if ( !isFinite(rounded) ) {
            rounded = number;
        }
        number = rounded;

        var numberString = number+"",
            right = "",
            split = numberString.split( /e/i ),
            exponent = split.length > 1 ? parseInt( split[1], 10 ) : 0;
        numberString = split[ 0 ];
        split = numberString.split( "." );
        numberString = split[ 0 ];
        right = split.length > 1 ? split[ 1 ] : "";

        if ( exponent > 0 ) {
            right = zeroPad( right, exponent, false );
            numberString += right.slice( 0, exponent );
            right = right.substr( exponent );
        }
        else if ( exponent < 0 ) {
            exponent = -exponent;
            numberString = zeroPad( numberString, exponent + 1, true );
            right = numberString.slice( -exponent, numberString.length ) + right;
            numberString = numberString.slice( 0, -exponent );
        }

        if ( precision > 0 ) {
            right = formatInfo[ "." ] +
                ( (right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision) );
        }
        else {
            right = "";
        }

        var stringIndex = numberString.length - 1,
            sep = formatInfo[ "," ],
            ret = "";

        while ( stringIndex >= 0 ) {
            if ( curSize === 0 || curSize > stringIndex ) {
                return numberString.slice( 0, stringIndex + 1 ) + ( ret.length ? (sep + ret + right) : right );
            }
            ret = numberString.slice( stringIndex - curSize + 1, stringIndex + 1 ) + ( ret.length ? (sep + ret) : "" );

            stringIndex -= curSize;

            if ( curGroupIndex < groupSizes.length ) {
                curSize = groupSizes[ curGroupIndex ];
                curGroupIndex++;
            }
        }

        return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right;
    };

    formatNumber = function( value, format, culture ) {
        if ( !isFinite(value) ) {
            if ( value === Infinity ) {
                return culture.numberFormat.positiveInfinity;
            }
            if ( value === -Infinity ) {
                return culture.numberFormat.negativeInfinity;
            }
            return culture.numberFormat.NaN;
        }
        if ( !format || format === "i" ) {
            return culture.name.length ? value.toLocaleString() : value.toString();
        }
        format = format || "D";

        var nf = culture.numberFormat,
            number = Math.abs( value ),
            precision = -1,
            pattern;
        if ( format.length > 1 ) precision = parseInt( format.slice(1), 10 );

        var current = format.charAt( 0 ).toUpperCase(),
            formatInfo;

        switch ( current ) {
            case "D":
                pattern = "n";
                number = truncate( number );
                if ( precision !== -1 ) {
                    number = zeroPad( "" + number, precision, true );
                }
                if ( value < 0 ) number = "-" + number;
                break;
            case "N":
                formatInfo = nf;
                /* falls through */
            case "C":
                formatInfo = formatInfo || nf.currency;
                /* falls through */
            case "P":
                formatInfo = formatInfo || nf.percent;
                pattern = value < 0 ? formatInfo.pattern[ 0 ] : ( formatInfo.pattern[1] || "n" );
                if ( precision === -1 ) precision = formatInfo.decimals;
                number = expandNumber( number * (current === "P" ? 100 : 1), precision, formatInfo );
                break;
            default:
                throw "Bad number format specifier: " + current;
        }

        var patternParts = /n|\$|-|%/g,
            ret = "";
        for ( ; ; ) {
            var index = patternParts.lastIndex,
                ar = patternParts.exec( pattern );

            ret += pattern.slice( index, ar ? ar.index : pattern.length );

            if ( !ar ) {
                break;
            }

            switch ( ar[0] ) {
                case "n":
                    ret += number;
                    break;
                case "$":
                    ret += nf.currency.symbol;
                    break;
                case "-":
                    // don't make 0 negative
                    if ( /[1-9]/.test(number) ) {
                        ret += nf[ "-" ];
                    }
                    break;
                case "%":
                    ret += nf.percent.symbol;
                    break;
            }
        }

        return ret;
    };

}());

getTokenRegExp = function() {
    // regular expression for matching date and time tokens in format strings.
    return (/\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g);
};

getEra = function( date, eras ) {
    if ( !eras ) return 0;
    var start, ticks = date.getTime();
    for ( var i = 0, l = eras.length; i < l; i++ ) {
        start = eras[ i ].start;
        if ( start === null || ticks >= start ) {
            return i;
        }
    }
    return 0;
};

getEraYear = function( date, cal, era, sortable ) {
    var year = date.getFullYear();
    if ( !sortable && cal.eras ) {
        // convert normal gregorian year to era-shifted gregorian
        // year by subtracting the era offset
        year -= cal.eras[ era ].offset;
    }
    return year;
};

// parseExact
(function() {
    var expandYear,
        getDayIndex,
        getMonthIndex,
        getParseRegExp,
        outOfRange,
        toUpper,
        toUpperArray;

    expandYear = function( cal, year ) {
        // expands 2-digit year into 4 digits.
        if ( year < 100 ) {
            var now = new Date(),
                era = getEra( now ),
                curr = getEraYear( now, cal, era ),
                twoDigitYearMax = cal.twoDigitYearMax;
            twoDigitYearMax = typeof twoDigitYearMax === "string" ? new Date().getFullYear() % 100 + parseInt( twoDigitYearMax, 10 ) : twoDigitYearMax;
            year += curr - ( curr % 100 );
            if ( year > twoDigitYearMax ) {
                year -= 100;
            }
        }
        return year;
    };

    getDayIndex = function	( cal, value, abbr ) {
        var ret,
            days = cal.days,
            upperDays = cal._upperDays;
        if ( !upperDays ) {
            cal._upperDays = upperDays = [
                toUpperArray( days.names ),
                toUpperArray( days.namesAbbr ),
                toUpperArray( days.namesShort )
            ];
        }
        value = toUpper( value );
        if ( abbr ) {
            ret = arrayIndexOf( upperDays[1], value );
            if ( ret === -1 ) {
                ret = arrayIndexOf( upperDays[2], value );
            }
        }
        else {
            ret = arrayIndexOf( upperDays[0], value );
        }
        return ret;
    };

    getMonthIndex = function( cal, value, abbr ) {
        var months = cal.months,
            monthsGen = cal.monthsGenitive || cal.months,
            upperMonths = cal._upperMonths,
            upperMonthsGen = cal._upperMonthsGen;
        if ( !upperMonths ) {
            cal._upperMonths = upperMonths = [
                toUpperArray( months.names ),
                toUpperArray( months.namesAbbr )
            ];
            cal._upperMonthsGen = upperMonthsGen = [
                toUpperArray( monthsGen.names ),
                toUpperArray( monthsGen.namesAbbr )
            ];
        }
        value = toUpper( value );
        var i = arrayIndexOf( abbr ? upperMonths[1] : upperMonths[0], value );
        if ( i < 0 ) {
            i = arrayIndexOf( abbr ? upperMonthsGen[1] : upperMonthsGen[0], value );
        }
        return i;
    };

    getParseRegExp = function( cal, format ) {
        // converts a format string into a regular expression with groups that
        // can be used to extract date fields from a date string.
        // check for a cached parse regex.
        var re = cal._parseRegExp;
        if ( !re ) {
            cal._parseRegExp = re = {};
        }
        else {
            var reFormat = re[ format ];
            if ( reFormat ) {
                return reFormat;
            }
        }

        // expand single digit formats, then escape regular expression characters.
        var expFormat = expandFormat( cal, format ).replace( /([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1" ),
            regexp = [ "^" ],
            groups = [],
            index = 0,
            quoteCount = 0,
            tokenRegExp = getTokenRegExp(),
            match;

        // iterate through each date token found.
        while ( (match = tokenRegExp.exec(expFormat)) !== null ) {
            var preMatch = expFormat.slice( index, match.index );
            index = tokenRegExp.lastIndex;

            // don't replace any matches that occur inside a string literal.
            quoteCount += appendPreOrPostMatch( preMatch, regexp );
            if ( quoteCount % 2 ) {
                regexp.push( match[0] );
                continue;
            }

            // add a regex group for the token.
            var m = match[ 0 ],
                len = m.length,
                add;
            switch ( m ) {
                case "dddd": case "ddd":
                case "MMMM": case "MMM":
                case "gg": case "g":
                    add = "(\\D+)";
                    break;
                case "tt": case "t":
                    add = "(\\D*)";
                    break;
                case "yyyy":
                case "fff":
                case "ff":
                case "f":
                    add = "(\\d{" + len + "})";
                    break;
                case "dd": case "d":
                case "MM": case "M":
                case "yy": case "y":
                case "HH": case "H":
                case "hh": case "h":
                case "mm": case "m":
                case "ss": case "s":
                    add = "(\\d\\d?)";
                    break;
                case "zzz":
                    add = "([+-]?\\d\\d?:\\d{2})";
                    break;
                case "zz": case "z":
                    add = "([+-]?\\d\\d?)";
                    break;
                case "/":
                    add = "(\\/)";
                    break;
                default:
                    throw "Invalid date format pattern \'" + m + "\'.";
            }
            if ( add ) {
                regexp.push( add );
            }
            groups.push( match[0] );
        }
        appendPreOrPostMatch( expFormat.slice(index), regexp );
        regexp.push( "$" );

        // allow whitespace to differ when matching formats.
        var regexpStr = regexp.join( "" ).replace( /\s+/g, "\\s+" ),
            parseRegExp = { "regExp": regexpStr, "groups": groups };

        // cache the regex for this format.
        return re[ format ] = parseRegExp;
    };

    outOfRange = function( value, low, high ) {
        return value < low || value > high;
    };

    toUpper = function( value ) {
        // "he-IL" has non-breaking space in weekday names.
        return value.split( "\u00A0" ).join( " " ).toUpperCase();
    };

    toUpperArray = function( arr ) {
        var results = [];
        for ( var i = 0, l = arr.length; i < l; i++ ) {
            results[ i ] = toUpper( arr[i] );
        }
        return results;
    };

    parseExact = function( value, format, culture ) {
        // try to parse the date string by matching against the format string
        // while using the specified culture for date field names.
        value = trim( value );
        var cal = culture.calendar,
            // convert date formats into regular expressions with groupings.
            // use the regexp to determine the input format and extract the date fields.
            parseInfo = getParseRegExp( cal, format ),
            match = new RegExp( parseInfo.regExp ).exec( value );
        if ( match === null ) {
            return null;
        }
        // found a date format that matches the input.
        var groups = parseInfo.groups,
            era = null, year = null, month = null, date = null, weekDay = null,
            hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null,
            pmHour = false;
        // iterate the format groups to extract and set the date fields.
        for ( var j = 0, jl = groups.length; j < jl; j++ ) {
            var matchGroup = match[ j + 1 ];
            if ( matchGroup ) {
                var current = groups[ j ],
                    clength = current.length,
                    matchInt = parseInt( matchGroup, 10 );
                switch ( current ) {
                    case "dd": case "d":
                        // Day of month.
                        date = matchInt;
                        // check that date is generally in valid range, also checking overflow below.
                        if ( outOfRange(date, 1, 31) ) return null;
                        break;
                    case "MMM": case "MMMM":
                        month = getMonthIndex( cal, matchGroup, clength === 3 );
                        if ( outOfRange(month, 0, 11) ) return null;
                        break;
                    case "M": case "MM":
                        // Month.
                        month = matchInt - 1;
                        if ( outOfRange(month, 0, 11) ) return null;
                        break;
                    case "y": case "yy":
                    case "yyyy":
                        year = clength < 4 ? expandYear( cal, matchInt ) : matchInt;
                        if ( outOfRange(year, 0, 9999) ) return null;
                        break;
                    case "h": case "hh":
                        // Hours (12-hour clock).
                        hour = matchInt;
                        if ( hour === 12 ) hour = 0;
                        if ( outOfRange(hour, 0, 11) ) return null;
                        break;
                    case "H": case "HH":
                        // Hours (24-hour clock).
                        hour = matchInt;
                        if ( outOfRange(hour, 0, 23) ) return null;
                        break;
                    case "m": case "mm":
                        // Minutes.
                        min = matchInt;
                        if ( outOfRange(min, 0, 59) ) return null;
                        break;
                    case "s": case "ss":
                        // Seconds.
                        sec = matchInt;
                        if ( outOfRange(sec, 0, 59) ) return null;
                        break;
                    case "tt": case "t":
                        // AM/PM designator.
                        // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of
                        // the AM tokens. If not, fail the parse for this format.
                        pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] );
                        if (
                            !pmHour && (
                                !cal.AM || ( matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2] )
                            )
                        ) return null;
                        break;
                    case "f":
                        // Deciseconds.
                    case "ff":
                        // Centiseconds.
                    case "fff":
                        // Milliseconds.
                        msec = matchInt * Math.pow( 10, 3 - clength );
                        if ( outOfRange(msec, 0, 999) ) return null;
                        break;
                    case "ddd":
                        // Day of week.
                    case "dddd":
                        // Day of week.
                        weekDay = getDayIndex( cal, matchGroup, clength === 3 );
                        if ( outOfRange(weekDay, 0, 6) ) return null;
                        break;
                    case "zzz":
                        // Time zone offset in +/- hours:min.
                        var offsets = matchGroup.split( /:/ );
                        if ( offsets.length !== 2 ) return null;
                        hourOffset = parseInt( offsets[0], 10 );
                        if ( outOfRange(hourOffset, -12, 13) ) return null;
                        var minOffset = parseInt( offsets[1], 10 );
                        if ( outOfRange(minOffset, 0, 59) ) return null;
                        tzMinOffset = ( hourOffset * 60 ) + ( startsWith(matchGroup, "-") ? -minOffset : minOffset );
                        break;
                    case "z": case "zz":
                        // Time zone offset in +/- hours.
                        hourOffset = matchInt;
                        if ( outOfRange(hourOffset, -12, 13) ) return null;
                        tzMinOffset = hourOffset * 60;
                        break;
                    case "g": case "gg":
                        var eraName = matchGroup;
                        if ( !eraName || !cal.eras ) return null;
                        eraName = trim( eraName.toLowerCase() );
                        for ( var i = 0, l = cal.eras.length; i < l; i++ ) {
                            if ( eraName === cal.eras[i].name.toLowerCase() ) {
                                era = i;
                                break;
                            }
                        }
                        // could not find an era with that name
                        if ( era === null ) return null;
                        break;
                }
            }
        }
        var result = new Date(), defaultYear, convert = cal.convert;
        defaultYear = convert ? convert.fromGregorian( result )[ 0 ] : result.getFullYear();
        if ( year === null ) {
            year = defaultYear;
        }
        else if ( cal.eras ) {
            // year must be shifted to normal gregorian year
            // but not if year was not specified, its already normal gregorian
            // per the main if clause above.
            year += cal.eras[( era || 0 )].offset;
        }
        // set default day and month to 1 and January, so if unspecified, these are the defaults
        // instead of the current day/month.
        if ( month === null ) {
            month = 0;
        }
        if ( date === null ) {
            date = 1;
        }
        // now have year, month, and date, but in the culture's calendar.
        // convert to gregorian if necessary
        if ( convert ) {
            result = convert.toGregorian( year, month, date );
            // conversion failed, must be an invalid match
            if ( result === null ) return null;
        }
        else {
            // have to set year, month and date together to avoid overflow based on current date.
            result.setFullYear( year, month, date );
            // check to see if date overflowed for specified month (only checked 1-31 above).
            if ( result.getDate() !== date ) return null;
            // invalid day of week.
            if ( weekDay !== null && result.getDay() !== weekDay ) {
                return null;
            }
        }
        // if pm designator token was found make sure the hours fit the 24-hour clock.
        if ( pmHour && hour < 12 ) {
            hour += 12;
        }
        result.setHours( hour, min, sec, msec );
        if ( tzMinOffset !== null ) {
            // adjust timezone to utc before applying local offset.
            var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() );
            // Safari limits hours and minutes to the range of -127 to 127.  We need to use setHours
            // to ensure both these fields will not exceed this range.	adjustedMin will range
            // somewhere between -1440 and 1500, so we only need to split this into hours.
            result.setHours( result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60 );
        }
        return result;
    };
}());

parseNegativePattern = function( value, nf, negativePattern ) {
    var neg = nf[ "-" ],
        pos = nf[ "+" ],
        ret;
    switch ( negativePattern ) {
        case "n -":
            neg = " " + neg;
            pos = " " + pos;
            /* falls through */
        case "n-":
            if ( endsWith(value, neg) ) {
                ret = [ "-", value.substr(0, value.length - neg.length) ];
            }
            else if ( endsWith(value, pos) ) {
                ret = [ "+", value.substr(0, value.length - pos.length) ];
            }
            break;
        case "- n":
            neg += " ";
            pos += " ";
            /* falls through */
        case "-n":
            if ( startsWith(value, neg) ) {
                ret = [ "-", value.substr(neg.length) ];
            }
            else if ( startsWith(value, pos) ) {
                ret = [ "+", value.substr(pos.length) ];
            }
            break;
        case "(n)":
            if ( startsWith(value, "(") && endsWith(value, ")") ) {
                ret = [ "-", value.substr(1, value.length - 2) ];
            }
            break;
    }
    return ret || [ "", value ];
};

//
// public instance functions
//

Globalize.prototype.findClosestCulture = function( cultureSelector ) {
    return Globalize.findClosestCulture.call( this, cultureSelector );
};

Globalize.prototype.format = function( value, format, cultureSelector ) {
    return Globalize.format.call( this, value, format, cultureSelector );
};

Globalize.prototype.localize = function( key, cultureSelector ) {
    return Globalize.localize.call( this, key, cultureSelector );
};

Globalize.prototype.parseInt = function( value, radix, cultureSelector ) {
    return Globalize.parseInt.call( this, value, radix, cultureSelector );
};

Globalize.prototype.parseFloat = function( value, radix, cultureSelector ) {
    return Globalize.parseFloat.call( this, value, radix, cultureSelector );
};

Globalize.prototype.culture = function( cultureSelector ) {
    return Globalize.culture.call( this, cultureSelector );
};

//
// public singleton functions
//

Globalize.addCultureInfo = function( cultureName, baseCultureName, info ) {

    var base = {},
        isNew = false;

    if ( typeof cultureName !== "string" ) {
        // cultureName argument is optional string. If not specified, assume info is first
        // and only argument. Specified info deep-extends current culture.
        info = cultureName;
        cultureName = this.culture().name;
        base = this.cultures[ cultureName ];
    } else if ( typeof baseCultureName !== "string" ) {
        // baseCultureName argument is optional string. If not specified, assume info is second
        // argument. Specified info deep-extends specified culture.
        // If specified culture does not exist, create by deep-extending default
        info = baseCultureName;
        isNew = ( this.cultures[ cultureName ] == null );
        base = this.cultures[ cultureName ] || this.cultures[ "default" ];
    } else {
        // cultureName and baseCultureName specified. Assume a new culture is being created
        // by deep-extending an specified base culture
        isNew = true;
        base = this.cultures[ baseCultureName ];
    }

    this.cultures[ cultureName ] = extend(true, {},
        base,
        info
    );
    // Make the standard calendar the current culture if it's a new culture
    if ( isNew ) {
        this.cultures[ cultureName ].calendar = this.cultures[ cultureName ].calendars.standard;
    }
};

Globalize.findClosestCulture = function( name ) {
    var match;
    if ( !name ) {
        return this.findClosestCulture( this.cultureSelector ) || this.cultures[ "default" ];
    }
    if ( typeof name === "string" ) {
        name = name.split( "," );
    }
    if ( isArray(name) ) {
        var lang,
            cultures = this.cultures,
            list = name,
            i, l = list.length,
            prioritized = [];
        for ( i = 0; i < l; i++ ) {
            name = trim( list[i] );
            var pri, parts = name.split( ";" );
            lang = trim( parts[0] );
            if ( parts.length === 1 ) {
                pri = 1;
            }
            else {
                name = trim( parts[1] );
                if ( name.indexOf("q=") === 0 ) {
                    name = name.substr( 2 );
                    pri = parseFloat( name );
                    pri = isNaN( pri ) ? 0 : pri;
                }
                else {
                    pri = 1;
                }
            }
            prioritized.push({ lang: lang, pri: pri });
        }
        prioritized.sort(function( a, b ) {
            if ( a.pri < b.pri ) {
                return 1;
            } else if ( a.pri > b.pri ) {
                return -1;
            }
            return 0;
        });
        // exact match
        for ( i = 0; i < l; i++ ) {
            lang = prioritized[ i ].lang;
            match = cultures[ lang ];
            if ( match ) {
                return match;
            }
        }

        // neutral language match
        for ( i = 0; i < l; i++ ) {
            lang = prioritized[ i ].lang;
            do {
                var index = lang.lastIndexOf( "-" );
                if ( index === -1 ) {
                    break;
                }
                // strip off the last part. e.g. en-US => en
                lang = lang.substr( 0, index );
                match = cultures[ lang ];
                if ( match ) {
                    return match;
                }
            }
            while ( 1 );
        }

        // last resort: match first culture using that language
        for ( i = 0; i < l; i++ ) {
            lang = prioritized[ i ].lang;
            for ( var cultureKey in cultures ) {
                var culture = cultures[ cultureKey ];
                if ( culture.language === lang ) {
                    return culture;
                }
            }
        }
    }
    else if ( typeof name === "object" ) {
        return name;
    }
    return match || null;
};

Globalize.format = function( value, format, cultureSelector ) {
    var culture = this.findClosestCulture( cultureSelector );
    if ( value instanceof Date ) {
        value = formatDate( value, format, culture );
    }
    else if ( typeof value === "number" ) {
        value = formatNumber( value, format, culture );
    }
    return value;
};

Globalize.localize = function( key, cultureSelector ) {
    return this.findClosestCulture( cultureSelector ).messages[ key ] ||
        this.cultures[ "default" ].messages[ key ];
};

Globalize.parseDate = function( value, formats, culture ) {
    culture = this.findClosestCulture( culture );

    var date, prop, patterns;
    if ( formats ) {
        if ( typeof formats === "string" ) {
            formats = [ formats ];
        }
        if ( formats.length ) {
            for ( var i = 0, l = formats.length; i < l; i++ ) {
                var format = formats[ i ];
                if ( format ) {
                    date = parseExact( value, format, culture );
                    if ( date ) {
                        break;
                    }
                }
            }
        }
    } else {
        patterns = culture.calendar.patterns;
        for ( prop in patterns ) {
            date = parseExact( value, patterns[prop], culture );
            if ( date ) {
                break;
            }
        }
    }

    return date || null;
};

Globalize.parseInt = function( value, radix, cultureSelector ) {
    return truncate( Globalize.parseFloat(value, radix, cultureSelector) );
};

Globalize.parseFloat = function( value, radix, cultureSelector ) {
    // radix argument is optional
    if ( typeof radix !== "number" ) {
        cultureSelector = radix;
        radix = 10;
    }

    var culture = this.findClosestCulture( cultureSelector );
    var ret = NaN,
        nf = culture.numberFormat;

    if ( value.indexOf(culture.numberFormat.currency.symbol) > -1 ) {
        // remove currency symbol
        value = value.replace( culture.numberFormat.currency.symbol, "" );
        // replace decimal seperator
        value = value.replace( culture.numberFormat.currency["."], culture.numberFormat["."] );
    }

    //Remove percentage character from number string before parsing
    if ( value.indexOf(culture.numberFormat.percent.symbol) > -1){
        value = value.replace( culture.numberFormat.percent.symbol, "" );
    }

    // remove spaces: leading, trailing and between - and number. Used for negative currency pt-BR
    value = value.replace( / /g, "" );

    // allow infinity or hexidecimal
    if ( regexInfinity.test(value) ) {
        ret = parseFloat( value );
    }
    else if ( !radix && regexHex.test(value) ) {
        ret = parseInt( value, 16 );
    }
    else {

        // determine sign and number
        var signInfo = parseNegativePattern( value, nf, nf.pattern[0] ),
            sign = signInfo[ 0 ],
            num = signInfo[ 1 ];

        // #44 - try parsing as "(n)"
        if ( sign === "" && nf.pattern[0] !== "(n)" ) {
            signInfo = parseNegativePattern( value, nf, "(n)" );
            sign = signInfo[ 0 ];
            num = signInfo[ 1 ];
        }

        // try parsing as "-n"
        if ( sign === "" && nf.pattern[0] !== "-n" ) {
            signInfo = parseNegativePattern( value, nf, "-n" );
            sign = signInfo[ 0 ];
            num = signInfo[ 1 ];
        }

        // try parsing as "n-" (Added by jtm on 09/24/2013)
        if (sign === "" && nf.pattern[0] !== "n-") {
            signInfo = parseNegativePattern(value, nf, "n-");
            sign = signInfo[0];
            num = signInfo[1];
        }

        sign = sign || "+";

        // determine exponent and number
        var exponent,
            intAndFraction,
            exponentPos = num.indexOf( "e" );
        if ( exponentPos < 0 ) exponentPos = num.indexOf( "E" );
        if ( exponentPos < 0 ) {
            intAndFraction = num;
            exponent = null;
        }
        else {
            intAndFraction = num.substr( 0, exponentPos );
            exponent = num.substr( exponentPos + 1 );
        }
        // determine decimal position
        var integer,
            fraction,
            decSep = nf[ "." ],
            decimalPos = intAndFraction.indexOf( decSep );
        if ( decimalPos < 0 ) {
            integer = intAndFraction;
            fraction = null;
        }
        else {
            integer = intAndFraction.substr( 0, decimalPos );
            fraction = intAndFraction.substr( decimalPos + decSep.length );
        }
        // handle groups (e.g. 1,000,000)
        var groupSep = nf[ "," ];
        integer = integer.split( groupSep ).join( "" );
        var altGroupSep = groupSep.replace( /\u00A0/g, " " );
        if ( groupSep !== altGroupSep ) {
            integer = integer.split( altGroupSep ).join( "" );
        }
        // build a natively parsable number string
        var p = sign + integer;
        if ( fraction !== null ) {
            p += "." + fraction;
        }
        if ( exponent !== null ) {
            // exponent itself may have a number patternd
            var expSignInfo = parseNegativePattern( exponent, nf, "-n" );
            p += "e" + ( expSignInfo[0] || "+" ) + expSignInfo[ 1 ];
        }
        if ( regexParseFloat.test(p) ) {
            ret = parseFloat( p );
        }
    }
    return ret;
};

Globalize.culture = function( cultureSelector ) {
    // setter
    if ( typeof cultureSelector !== "undefined" ) {
        this.cultureSelector = cultureSelector;
    }
    // getter
    return this.findClosestCulture( cultureSelector ) || this.cultures[ "default" ];
};

}( this ));

// Copyright 2012-2021 Ellucian Company L.P. and its affiliates.
/////////////////////////////////////////////////////////////////////////////////////////////
//  This file contains utility functionality that needs to be made available across
//  two or more views in the entire Self Service application tree. Functions placed into this
//  file should be as succint and context-less as possible. These functions will be available
//  in every view in the Self Service solution, including all Areas, and loaded in the global
//  scripts bundle. Do NOT place anything into this file that is specific to areas such
//  Planning or Finance.
/////////////////////////////////////////////////////////////////////////////////////////////

// Add an antiforgerytoken request header to all ajax requests. 
// This code is run when the document is loaded and it assumes all pages/forms 
// contain the hidden antiforgerytoken field named "__RequestVerificationToken". 
// Note: headers specified by an individual $.ajax() call will be appended
// to the list of headers of that ajax request.
$(document).ready(function () {
    var antiForgeryToken = $('input[name="__RequestVerificationToken"]').val();
    $.ajaxSetup({
        headers: {
            __RequestVerificationToken: antiForgeryToken,
            __IsGuestUser: typeof isGuest == "undefined" ? null : isGuest
        },
        converters: {
            "text json": function (msg) {
                return $.parseJSON(msg, true);
            }
        }
    });
});

// New jQuery selector to do case-insensitive contains()
(function ($) {
    $.expr.pseudos.containsany = $.expr.createPseudo(function (arg) {
        if (typeof arg === "undefined") arg = "";
        return function (e) {
            return $(e).text().toLowerCase().indexOf(arg.toLowerCase()) >= 0;
        };
    });
})(jQuery);

// A content-type property for AJAX calls which send JSON data.
var jsonContentType = "application/json, charset=utf-8";

// Setting cookies to help with unsupported browser check
var browserCookieName = "Ellucian.Web.Student__SupportedBrowserCheck";

// Checks whether a variable is null or empty. Any of the following conditions mean that it is:
//   value = ''
//   value = null
//   value = 'null' (this is helpful for checking values that come from serialized JSON)
//   value = undefined
function isNullOrEmpty(value) {
    return (typeof value === "undefined" || value === null || value === "" || value === "null" || value === undefined);
}

// Converts the given number of minutes to a formatted time string. This begins at 12:00AM,
// so that passing in a value of 90 will yield a string of "1:30 AM".
function minutesToTime(minutes) {
    var time = "";
    var hours = Math.floor(minutes / 60);
    var minutesRemainder = minutes % (60 * (hours ? hours : 1));
    var AMPM = "AM";

    if (minutes >= 720 && minutes != 1440) AMPM = "PM";
    if (hours > 12) {
        hours = hours - 12;
    }
    if (hours == 0) hours = "12";
    if (minutesRemainder < 10) minutesRemainder = "0" + minutesRemainder;

    return hours + ":" + minutesRemainder + " " + AMPM;
}


// Date utility to easily add a given number of hours to a Date object
Date.prototype.addHours = function (hours) {
    this.setTime(this.getTime() + (hours * 3600 * 1000));
    return this;
}

// Static Date utility to return a new Date object (time at midnight) of today;
Date.Today = function today() {
    var todayDate = new Date();
    return new Date(todayDate.getFullYear(), todayDate.getMonth(), todayDate.getDate());
}

// Static Date utility to return a new Date object (time at midnight) of today + the integer offset number of days
// offset can be positive or negative, and it properly rolls the month and year forward or back depending on the date
Date.TodayDateOffset = function todayOffset(offset) {
    var todayDate = new Date();
    return new Date(todayDate.getFullYear(), todayDate.getMonth(), todayDate.getDate() + offset);
}

// Static Date utility to return a new Date object (time at midnight) of the date passed in + the integer offset number
// of days. Offset can be positive or negative  and it properly rolls the month and year forward or back depending on the date
Date.DateOffset = function dateOffset(date, offset) {
    return new Date(date.getFullYear(), date.getMonth(), date.getDate() + offset);
}

//Adds a method to the date prototype to add an offset number of days to this Date object.
Date.prototype.addDays = function (offset) {
    this.setDate(this.getDate() + offset);
    return this;
}

Date.prototype.toUTCDateExact = function () {
    var utcMoment = moment.utc(this);
    return new Date(utcMoment.year(), utcMoment.month(), utcMoment.date(), utcMoment.hour(), utcMoment.minute(), utcMoment.second());
}

//Static Date utility to compare two Dates. This function use the specific Time value of the date
//to compare.
//Returns:
//  1 if date1 is greater than date2.
//  0 if date1 is equal to date2
//  -1 if date1 is less than date2
Date.Compare = function compareDates(date1, date2) {
    var time1 = (date1) ? date1.getTime() : null,
        time2 = (date2) ? date2.getTime() : null;

    if (time1 > time2) return 1;
    else if (time1 === time2) return 0;
    else return -1;
}

// Determines whether a given string represents a number. So, both 12 and "12"
// are numbers, but "12a" is not.
function isNumber(string) {
    return !isNaN(parseFloat(string)) && isFinite(string);
}

// Replicate .NET string functionality in JavaScript
String.prototype.format = function () {
    var text = this;

    // If argument is an array, substitute the array members
    if (Array.isArray(arguments[0])) {
        for (var i = 0; i < arguments[0].length; i++) {
            var match = "{" + i + "}";
            text = text.replace(match, arguments[0][i]);
        }
        return text;
    } else {
        // First argument is not an array; substitute arguments
        for (var i = 0; i < arguments.length; i++) {
            var match = "{" + i + "}";
            text = text.replace(match, arguments[i]);
        }
        return text;
    }
};
if (!String.prototype.startsWith) {
    Object.defineProperty(String.prototype, 'startsWith', {
        value: function (search, rawPos) {
            var pos = rawPos > 0 ? rawPos | 0 : 0;
            return this.substring(pos, pos + search.length) === search;
        }
    });
}

String.prototype.endsWith = function (suffix) {
    return this.indexOf(suffix, this.length - suffix.length) !== -1;
};

String.prototype.contains = function (str) {
    return this.indexOf(str) !== -1;
};

if (!String.prototype.includes) {
    String.prototype.includes = function (search, start) {
        'use strict';

        if (search instanceof RegExp) {
            throw TypeError('first argument must not be a RegExp');
        }
        if (start === undefined) { start = 0; }
        return this.indexOf(search, start) !== -1;
    };
}

// Provide easy access to query string parameters within script on any given page
function getUrlVars() {
    var vars = [], hash;
    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
    for (var i = 0; i < hashes.length; i++) {
        hash = hashes[i].split('=');
        if (hash[0] && (hash[0] === '__proto__' || hash[0] === 'constructor' || hash[0] === 'prototype')) {
            continue;
        }
        vars.push(hash[0]);
        vars[hash[0]] = hash[1];
    }
    return vars;
}

// Provide easy access to query string parameters for URLs within existing JS variables
function getVariableUrlVars(url) {
    if (!url) {
        return null;
    }
    var vars = [], hash;
    var hashes = url.slice(url.indexOf('?') + 1).split('&');
    for (var i = 0; i < hashes.length; i++) {
        hash = hashes[i].split('=');
        if (hash[0] && (hash[0] === '__proto__' || hash[0] === 'constructor' || hash[0] === 'prototype')) {
            continue;
        }
        vars.push(hash[0]);
        vars[hash[0]] = hash[1];
    }
    return vars;
}

/**************    Array functions    ***************/
// Polyfill for Array.prototype.some
if (!Array.prototype.some) {
    Array.prototype.some = function (fun, thisArg) {
        'use strict';

        if (this == null) {
            throw new TypeError('Array.prototype.some called on null or undefined');
        }

        if (typeof fun !== 'function') {
            throw new TypeError();
        }

        var t = Object(this);
        var len = t.length >>> 0;

        for (var i = 0; i < len; i++) {
            if (i in t && fun.call(thisArg, t[i], i, t)) {
                return true;
            }
        }

        return false;
    };
}

// function to determine if an array contains duplicates
function hasDuplicates(element, index, array) {
    return array.indexOf(element) !== array.lastIndexOf(element);
}

// Array function to determine if an array contains duplicates
Array.prototype.hasDuplicates = function () {
    return this.some(hasDuplicates);

}

Array.prototype.remove = function (from, to) {
    var rest = this.slice((to || from) + 1 || this.length);
    this.length = from < 0 ? this.length + from : from;
    return this.push.apply(this, rest);
};

Array.prototype.mergeAll = function () {
    var results = [];
    this.forEach(function (subArray) {
        results.push.apply(results, subArray);
    })
    return results;
};

Array.prototype.flatMap = function (projectionFunctionThatReturnsArray) {
    return this.
        map(function (item) {
            return projectionFunctionThatReturnsArray(item);
        }).
        mergeAll();
};

Array.prototype.unique = function (functionThatReturnsKeyOfArrayItem) {
    var results = {};
    this.forEach(function (item) {
        var key = functionThatReturnsKeyOfArrayItem(item);
        results[key] = item;
    });
    return Object.keys(results).map(function (key) {
        return results[key];
    });
};

Array.zip = function (left, right, combinerFunction) {
    var counter,
        results = [];

    for (counter = 0; counter < Math.min(left.length, right.length); counter++) {
        results.push(combinerFunction(left[counter], right[counter]));
    }
    return results;
};

Array.prototype.contains = function (obj) {
    var i = this.length;
    while (i--) {
        if (this[i] == obj) {
            return true;
        }
    }
    return false;
};

if (!Array.prototype.includes) {
    Object.defineProperty(Array.prototype, 'includes', {
        value: function (searchElement, fromIndex) {

            if (this == null) {
                throw new TypeError('"this" is null or not defined');
            }

            // 1. Let O be ? ToObject(this value).
            var o = Object(this);

            // 2. Let len be ? ToLength(? Get(O, "length")).
            var len = o.length >>> 0;

            // 3. If len is 0, return false.
            if (len === 0) {
                return false;
            }

            // 4. Let n be ? ToInteger(fromIndex).
            //    (If fromIndex is undefined, this step produces the value 0.)
            var n = fromIndex | 0;

            // 5. If n ≥ 0, then
            //  a. Let k be n.
            // 6. Else n < 0,
            //  a. Let k be len + n.
            //  b. If k < 0, let k be 0.
            var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);

            function sameValueZero(x, y) {
                return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
            }

            // 7. Repeat, while k < len
            while (k < len) {
                // a. Let elementK be the result of ? Get(O, ! ToString(k)).
                // b. If SameValueZero(searchElement, elementK) is true, return true.
                if (sameValueZero(o[k], searchElement)) {
                    return true;
                }
                // c. Increase k by 1.
                k++;
            }

            // 8. Return false
            return false;
        }
    });
}


// jQuery extension to convert incoming dates into true JS Date objects
//  - Code source: http://erraticdev.blogspot.com/2010/12/converting-dates-in-json-strings-using.html
//  - Minor changes were made to the code in the blog based on comments on the blog entry
/*!
 * jQuery.parseJSON() extension (supports ISO & Asp.net date conversion)
 *
 * Version 1.0 (13 Jan 2011)
 *
 * Copyright (c) 2011 Robert Koritnik
 * Licensed under the terms of the MIT license
 * http://www.opensource.org/licenses/mit-license.php
 */
(function ($) {

    // JSON RegExp
    var rvalidchars = /^[\],:{}\s]*$/;
    var rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
    var rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
    var rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g;
    var dateISO = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:[.,]\d+)?Z?/i;
    var dateNet = /\/Date\((-?\d+)(?:[-\+]\d+)?\)\//i;

    // replacer RegExp
    var replaceISO = /"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:[.,](\d+))?Z?"/gi;
    var replaceNet = /"\\\/Date\((-?\d+)(?:[-\+]\d+)?\)\\\/"/gi;

    // determine JSON native support
    var nativeJSON = (window.JSON && window.JSON.parse) ? true : false;
    var extendedJSON = nativeJSON && window.JSON.parse('{"x":9}', function (k, v) { return "Y"; }) === "Y";

    var jsonDateConverter = function (key, value) {
        if (typeof (value) === "string") {
            if (dateISO.test(value)) {
                return new Date(value);
            }
            if (dateNet.test(value)) {
                return new Date(parseInt(value.replace(dateNet, "$1")));
            }
        }
        return value;
    };

    $.extend({
        parseJSON: function (data, convertDates) {
            /// <summary>Takes a well-formed JSON string and returns the resulting JavaScript object.</summary>
            /// <param name="data" type="String">The JSON string to parse.</param>
            /// <param name="convertDates" optional="true" type="Boolean">Set to true when you want ISO/Asp.net dates to be auto-converted to dates.</param>

            if (typeof data !== "string" || !data) {
                return null;
            }

            // Make sure leading/trailing whitespace is removed (IE can't handle it)
            data = data.trim();

            // Make sure the incoming data is actual JSON
            // Logic borrowed from http://json.org/json2.js
            if (rvalidchars.test(data
                .replace(rvalidescape, "@")
                .replace(rvalidtokens, "]")
                .replace(rvalidbraces, ""))) {
                // Try to use the native JSON parser
                if (extendedJSON || (nativeJSON && convertDates !== true)) {
                    return window.JSON.parse(data, convertDates === true ? jsonDateConverter : undefined);
                }
                else {
                    data = convertDates === true ?
                        data.replace(replaceISO, "new Date(parseInt('$1',10),parseInt('$2',10)-1,parseInt('$3',10),parseInt('$4',10),parseInt('$5',10),parseInt('$6',10),(function(s){return parseInt(s,10)||0;})('$7'))")
                            .replace(replaceNet, "new Date($1)") :
                        data;
                    return (new Function("return " + data))();
                }
            } else {
                $.error("Invalid JSON: " + data);
            }
        }
    });
})(jQuery);

// JavaScript function to parse an ISO date, date-time, or date-time-offset string
// and return a JavaScript Date object.
function parseIsoDate(input) {
    var iso = /^([+-]?\d{4})(?:(?:-?(\d{2})-?(\d{2}))|-?W(\d{2})(?:-?(\d))?|-?(\d{3})|-(\d{2}))(?:[T ](\d{2})(?::?(\d{2}))?(?::?(\d{2}))?(?:[,.](\d+))?)?(?:(Z)?(?:([+-]\d{2}))?(?::?(\d{2}))?)?$/;

    var parts = String(input).match(iso);
    if (parts === null) {
        parts = input.toISOString().match(iso);;
    }

    if (parts === null) {
        return undefined;
    }

    var year = Number(parts[1]);
    var months, days, weeks;
    if (typeof parts[2] !== "undefined") {
        months = Number(parts[2]) - 1;
        days = Number(parts[3]);
    } else if (typeof parts[4] !== "undefined") {
        /* Convert weeks to days, months 0 */
        weeks = Number(parts[4]) - 1;
        days = Number(parts[5]);
        if (typeof days === "undefined") {
            days = 0;
        }
        days += weeks * 7;
        months = 0;
    } else if (typeof parts[6] !== "undefined") {
        /* it's an ordinal date... */
        days = Number(parts[6]);
        months = 0;
    } else {
        // Month only - assume first of the month
        months = Number(parts[7]) - 1;
        days = 1;
    }

    var hours = 0, minutes = 0, seconds = 0, milliseconds = 0;
    if (typeof parts[8] !== "undefined") {
        hours = Number(parts[8]);
        if (typeof parts[9] !== "undefined") {
            minutes = Number(parts[9]);

            if (typeof parts[10] !== "undefined") {
                seconds = Number(parts[10]);

                if (typeof parts[11] !== "undefined") {
                    var fractional = Number(parts[11]);
                    milliseconds = fractional / 100;
                }
            } else if (typeof parts[11] !== "undefined") {
                var len = parts[11].length;
                var power = Math.pow(10, len);
                var fractional = Number(parts[11]) / power;
                //var fractional = Number(parts[11]) / Math.pow(10, parts[11].length);
                seconds = fractional * 60;
            }
        } else if (typeof parts[11] !== "undefined") {
            var len = parts[11].length;
            var power = Math.pow(10, len);
            var fractional = Number(parts[11]) / power;
            //var fractional = Number(parts[11]) / Math.pow(10, parts[11].length);
            minutes = fractional * 60;
        }

        var timezone;
        var localzone = new Date().getTimezoneOffset();
        if (typeof parts[12] !== "undefined" && typeof parts[13] === "undefined") {
            // Zulu specified with no additional offset
            timezone = 0;
        } else if (typeof parts[12] === "undefined" && typeof parts[13] === "undefined") {
            // Zulu not specified and no additional offset - use local offset
            timezone = localzone * -1;
        } else {
            var offsetHours = Number(parts[13]);
            var offsetMinutes = Number(parts[14] || 0);
            timezone = ((offsetHours * 60) + offsetMinutes);
        }

        minutes = Number(minutes) - (timezone + localzone);
    }
    else {
        var hours = 0;
        var minutes = 0;
        var seconds = 0;
        var fractional = 0;
        var milliseconds = 0;
    }

    return new Date(year, months, days, hours, minutes, seconds, milliseconds);
}

var negativeNumberPatterns = ["(n)", "-n", "- n", "n-", "n -"];
var negativePercentPatterns = ["-n %", "-n%", "-%n", "%-n", "%n-", "n-%", "n%-", "-% n", "n %-", "% n-", "% -n", "n- %"];
var positivePercentPatterns = ["n %", "n%", "%n", "% n"];
var negativeCurrencyPatterns = ["($n)", "-$n", "$-n", "$n-", "(n$)", "-n$", "n-$", "n$-", "-n $", "-$ n", "n $-", "$ n-", "$ -n", "n- $", "($ n)", "(n $)"];
var positiveCurrencyPatterns = ["$n", "n$", "$ n", "n $"];

// Set culture definitions for Globalize to use
function setGlobalizeDefaults(currentCulture) {
    if (!currentCulture || typeof currentCulture !== "object") {
        return;
    }
    //Globalize.culture(currentCulture.Name);
    var cultureInfo = {};

    // A unique name for the culture in the form <language code>-<country/region code>
    if (currentCulture.Name) {
        cultureInfo["name"] = currentCulture.Name;
    }
    // the name of the culture in the english language
    if (currentCulture.EnglishName) {
        cultureInfo["englishName"] = currentCulture.EnglishName;
    }
    // the name of the culture in its own language
    if (currentCulture.NativeName) {
        cultureInfo["nativeName"] = currentCulture.NativeName;
    }
    // whether the culture uses right-to-left text
    cultureInfo["isRTL"] = currentCulture.TextInfo.IsRightToLeft || false;
    // "language" is used for so-called "specific" cultures.
    if (currentCulture.TwoLetterISOLanguageName) {
        cultureInfo["language"] = currentCulture.TwoLetterISOLanguageName;
    }

    var numberFormatInfo = currentCulture.NumberFormat;
    if (numberFormatInfo && typeof numberFormatInfo === "object") {
        // numberFormat defines general number formatting rules, like the digits in
        // each grouping, the group separator, and how negative numbers are displayed.
        var numberInfo = {};
        // [negativePattern]
        // Note, numberFormat.pattern has no "positivePattern" unlike percent and currency,
        // but is still defined as an array for consistency with them.
        //   negativePattern: one of "(n)|-n|- n|n-|n -"
        var negativeNumberPattern = numberFormatInfo.NumberNegativePattern || 0;
        if (negativeNumberPattern > negativeNumberPatterns.length) {
            negativeNumberPattern = 0;
        }
        numberInfo["pattern"] = negativeNumberPatterns[negativeNumberPattern];
        // number of decimal places normally shown
        numberInfo["decimals"] = numberFormatInfo.NumberDecimalDigits || 2;
        // string that separates number groups, as in 1,000,000
        if (numberFormatInfo.NumberGroupSeparator) {
            numberInfo[","] = numberFormatInfo.NumberGroupSeparator;
        }
        // string that separates a number from the fractional portion, as in 1.99
        if (numberFormatInfo.NumberDecimalSeparator) {
            numberInfo["."] = numberFormatInfo.NumberDecimalSeparator;
        }
        // array of numbers indicating the size of each number group.
        if (numberFormatInfo.NumberGroupSizes) {
            numberInfo["groupSizes"] = numberFormatInfo.NumberGroupSizes;
        }
        // symbol used for positive numbers
        if (numberFormatInfo.PositiveSign) {
            numberInfo["+"] = numberFormatInfo.PositiveSign;
        }
        // symbol used for negative numbers
        if (numberFormatInfo.NegativeSign) {
            numberInfo["-"] = numberFormatInfo.NegativeSign;
        }
        // symbol used for NaN (Not-A-Number)
        if (numberFormatInfo.NaNSymbol) {
            numberInfo["NaN"] = numberFormatInfo.NaNSymbol;
        }
        // symbol used for Negative Infinity
        if (numberFormatInfo.NegativeInfinitySymbol) {
            numberInfo["negativeInfinity"] = numberFormatInfo.NegativeInfinitySymbol;
        }
        // symbol used for Positive Infinity
        if (numberFormatInfo.PositiveInfinitySymbol) {
            numberInfo["positiveInfinity"] = numberFormatInfo.PositiveInfinitySymbol;
        }

        var percentInfo = {};
        // [negativePattern, positivePattern]
        //   negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %"
        //   positivePattern: one of "n %|n%|%n|% n"
        var negativePercentPattern = numberFormatInfo.PercentNegativePattern || 0;
        if (negativePercentPattern > negativePercentPatterns.length) {
            negativePercentPattern = 0;
        }
        var positivePercentPattern = numberFormatInfo.PercentPositivePattern || 0;
        if (positivePercentPattern > positivePercentPatterns.length) {
            positivePercentPattern = 0;
        }
        percentInfo["pattern"] = [negativePercentPatterns[negativePercentPattern], positivePercentPatterns[positivePercentPattern]];
        // number of decimal places normally shown
        percentInfo["decimals"] = numberFormatInfo.PercentDecimalDigits || 2;
        // array of numbers indicating the size of each number group.
        percentInfo["groupSizes"] = numberFormatInfo.PercentGroupSizes;
        // string that separates number groups, as in 1,000,000
        percentInfo[","] = numberFormatInfo.PercentGroupSeparator;
        // string that separates a number from the fractional portion, as in 1.99
        percentInfo["."] = numberFormatInfo.PercentDecimalSeparator;
        // symbol used to represent a percentage
        percentInfo["symbol"] = numberFormatInfo.PercentSymbol;
        if (percentInfo && percentInfo.length) {
            numberInfo["percent"] = percentInfo;
        }

        var currencyInfo = {};
        // [negativePattern, positivePattern]
        //   negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)"
        //   positivePattern: one of "$n|n$|$ n|n $"
        var negativeCurrencyPattern = numberFormatInfo.CurrencyNegativePattern || 0;
        if (negativeCurrencyPattern > negativeCurrencyPatterns.length) {
            negativeCurrencyPattern = 0;
        }
        var positiveCurrencyPattern = numberFormatInfo.CurrencyPositivePattern || 0;
        if (positiveCurrencyPattern > positiveCurrencyPatterns.length) {
            positiveCurrencyPattern = 0;
        }
        currencyInfo["pattern"] = [negativeCurrencyPatterns[negativeCurrencyPattern], positiveCurrencyPatterns[positiveCurrencyPattern]];
        // number of decimal places normally shown
        currencyInfo["decimals"] = numberFormatInfo.CurrencyDecimalDigits || 2;
        // array of numbers indicating the size of each number group.
        currencyInfo["groupSizes"] = numberFormatInfo.CurrencyGroupSizes;
        // string that separates number groups, as in 1,000,000
        currencyInfo[","] = numberFormatInfo.CurrencyGroupSeparator;
        // string that separates a number from the fractional portion, as in 1.99
        currencyInfo["."] = numberFormatInfo.CurrencyDecimalSeparator;
        // symbol used to represent currency
        currencyInfo["symbol"] = numberFormatInfo.CurrencySymbol;
        if (currencyInfo && currencyInfo.length) {
            numberInfo["currency"] = currencyInfo;
        }

        // Update the culture with the number formatting info
        if (numberInfo && numberInfo.length) {
            cultureInfo["numberFormat"] = numberInfo;
        }
    }

    var calendarInfo = currentCulture.DateTimeFormat;
    if (calendarInfo && typeof calendarInfo === "object") {
        var standardCalendar = {};
        standardCalendar["name"] = calendarInfo.NativeCalendarName;
        standardCalendar["/"] = calendarInfo.DateSeparator;
        standardCalendar[":"] = calendarInfo.TimeSeparator;
        standardCalendar["firstDay"] = calendarInfo.FirstDayOfWeek;
        standardCalendar["days"] = {
            names: calendarInfo.DayNames,
            namesAbbr: calendarInfo.AbbreviatedDayNames,
            namesShort: calendarInfo.ShortestDayNames
        };
        standardCalendar["months"] = {
            names: calendarInfo.MonthNames,
            namesAbbr: calendarInfo.AbbreviatedMonthNames
        };
        standardCalendar["AM"] = [calendarInfo.AMDesignator];
        standardCalendar["PM"] = [calendarInfo.PMDesignator];
        standardCalendar["eras"] = [
            // eras in reverse chronological order.
            // name: the name of the era in this culture (e.g. A.D., C.E.)
            // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era.
            // offset: offset in years from gregorian calendar
            {
                "name": "A.D.",
                "start": null,
                "offset": 0
            }
        ];
        // No .NET equivalent to this, so don't do anything with it
        // standardCalendar["twoDigitYearMax"] = "";
        // A set of predefined date and time patterns used by the culture
        // these represent the format someone in this culture would expect
        // to see given the portions of the date that are shown.
        standardCalendar["patterns"] = {
            // short date pattern
            d: calendarInfo.ShortDatePattern,
            // long date pattern
            D: calendarInfo.LongDatePattern,
            // short time pattern
            t: calendarInfo.ShortTimePattern,
            // long time pattern
            T: calendarInfo.LongTimePattern,
            // long date, short time pattern
            f: calendarInfo.LongDatePattern + " " + calendarInfo.ShortTimePattern,
            // long date, long time pattern
            F: calendarInfo.FullDateTimePattern,
            // month/day pattern
            M: calendarInfo.MonthDayPattern,
            // month/year pattern
            Y: calendarInfo.YearMonthPattern,
            // S is a sortable format that does not vary by culture
            S: calendarInfo.SortableDateTimePattern
        };
        cultureInfo["calendars"] = {};
        cultureInfo.calendars["standard"] = standardCalendar;
    }

    // Add the culture settings and make then active
    Globalize.addCultureInfo(currentCulture.Name, cultureInfo);
    Globalize.culture(currentCulture.Name);
}

//Static function that gets the specified Date format of the closest current culture
//  Arguments:
//      dateFormat: optionally, specify the date format to find. The default is small d.
Globalize.getClosestCultureDateFormat = function (dateFormat) {
    var format = dateFormat || "d";
    return Globalize.findClosestCulture().calendars['standard'].patterns[format];
}

/*
Object to compute whether any property of the model has changed. This object
subscribes to every property on the model, so consider the usage of this carefully on
large, complex models. Also carefully consider whether you want dirty notification for
ALL properties of your model (including computeds) or just specific properties of your model - in which case
don't use this object =)
    Constructor:        
        Pass in your viewModel and whether or not the model is initially dirty,
        Example:
            new ko.dirtyFlag(viewModel, false)

    Methods:
        isDirty() - ko.computed that compares the initial state to the current state.
        reset() - function to reset the initial state of the model to the current state of the model
*/
ko.dirtyFlag = function (model, isInitiallyDirty) {
    var result = function () { },
        _initialState = ko.observable(ko.toJSON(model)),
        _isInitiallyDirty = ko.observable(isInitiallyDirty);

    result.isDirty = ko.computed(function () {
        return _isInitiallyDirty() || _initialState() !== ko.toJSON(model);
    });

    result.reset = function () {
        _initialState(ko.toJSON(model));
        _isInitiallyDirty(false);
    };

    return result;
};

// Function to remove mark-up from submitted data
function sanitizeFormData(data) {
    if (data) {
        if (data.constructor === Array) {
            for (i = 0; i < data.length; i++) {
                if (data[i].constructor === String) {
                    data[i] = data[i].replace(/(<([^>]+)>)/ig, "");
                }
            }
        }
        if (data.constructor === String) {
            data = data.replace(/(<([^>]+)>)/ig, "");
        }
        return data;
    }
}

// Function to shift focus to supplied element
function setFocus(jqSelector) {
    if (jqSelector) {
        $(jqSelector).first().trigger("focus");
    }
}

// Function to push an ARIA announcement
function makeAnnouncement(announcement) {
    if (announcement) {
        $("#aria-announcements").text(announcement);
    }
}

// Function to show or hide a DOM element based on presence or lack of multiple items
function showOrHideElement(items, selector) {
    if (items.length > 1) {
        // Make sure the link is visible
        selector.show("fast");
    } else {
        // Hide the link
        selector.hide("fast");
    }
};

// Generic AJAX error handler that throws an error notification into notification center
// and triggers a callback function
// Arguments:
// - jqXHR: inherited from $.ajax's error event
// - textStatus: inherited from $.ajax's error event
// - errorThrown: inherited from $.ajax's error event
// - errorMessageOverride: application's preferred notification message; if not specified,
//      the message returned from the controller will be used
// - callback: optional function to execute application-specific logic independent of 
//      the generic error handling
function handleAjaxError(jqXHR, textStatus, errorThrown, errorMessageOverride, callback) {
    if (jqXHR.status != 0) {
        $('#notificationHost').notificationCenter('addNotification', { message: errorMessageOverride ? errorMessageOverride : jqXHR.statusText, type: "error" });
    }
    if (callback != null && typeof callback == 'function') {
        callback();
    }
}

// Function from David Walsh: http://davidwalsh.name/css-animation-callback
// Posible that multiple transitions triggering in Chrome was fixed post 2014
var transitionEvent = function whichTransitionEvent() {
    var t,
        el = document.createElement("fakeelement");

    var transitions = {
        "transition": "transitionend",
        "OTransition": "oTransitionEnd",
        "MozTransition": "transitionend",
        "WebkitTransition": "webkitTransitionEnd"
    }

    for (t in transitions) {
        if (el.style[t] !== undefined) {
            return transitions[t];
        }
    }
}

// Function from: http://stackoverflow.com/questions/1129216/sort-array-of-objects-by-string-property-value-in-javascript/4760279#4760279
// Allows sorting of array of objects by string property value
function dynamicSort(property) {
    var sortOrder = 1;
    if (property[0] === "-") {
        sortOrder = -1;
        property = property.substr(1);
    }
    return function (a, b) {
        // modified from the original, a and b are observables and need to be called to access their properties
        var result = (a[property]() < b[property]()) ? -1 : (a[property]() > b[property]()) ? 1 : 0;
        return result * sortOrder;
    }
}

//
// Function to sanitize a number in string format. Uses NumberDecimalSeparator to determine which group separators to filter.
//
function sanitizeNumber(currencyString) {
    if (Globalize.NumberDecimalSeparator === ",") {
        decMark = /[^0-9,]+/g;
    }
    else {
        decMark = /[^0-9.]+/g;
    }

    if (currencyString) {
        return currencyString.replace(decMark, "").trim();
    }
    else {
        return 0;
    }
}

//Escape any special characters
function formatString(inputString) {
    var outputString = (inputString !== null && inputString !== undefined) ? inputString.replace(/(:|;|\.|\,|\!|\?|\@|\#|\$|\%|\^|\&|\*|\(|\)|\[|\]|\{|\}|\\|\/)/g, "\\$1") : null;
    return outputString;
}

// Function to escape HTML
function escapeHtml(inputString) {
    var div = document.createElement('div');
    div.appendChild(document.createTextNode(inputString));
    return div.innerHTML;
}

// Function to decode HTML
function decodeHtml(inputString) {
    var span = document.createElement("SPAN");
    span.innerHTML = inputString;
    return span.innerText;
}

// Function to sanitise text
function sanitize(event) {
    var tmp = new DOMParser().parseFromString(event.value, 'text/html');
    tmp = tmp.body.textContent || "";
    tmp = tmp.replace(/[^\w\s.!&?\\-]/gi, "");
    event.value = tmp;
}

// Function to generate a unique guid (can be used when unique Ids are needed)
function guidGenerator() {
    var S4 = function () {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    };
    return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}

//// Function to scroll to view
//function scrollTo(sectionIdToScroll) {
//    if (!isNullOrEmpty(sectionIdToScroll)) {
//        var offset = $(sectionIdToScroll).offset();
//        $('html, body').animate({ scrollTop: offset.top || 0 });
//    }
//};

DOMPurify.setConfig(
    {
        //Prevents HTML5 Data Attributes
        ALLOW_DATA_ATTR: false,
        FORBID_TAGS: ['form', 'button', 'select', 'option', 'optgroup', 'textarea', 'legend', 'datalist', 'output'],
        // Data inside of tags that violates the rules will be deleted
        KEEP_CONTENT: false
    });

(function ($) {
    /// Makes a table responsive.  This function determines if the supplied elements are <table> elements, 
    /// then iterates over each one, grabbing the values of the <th> elements in the <thead> section and 
    /// using them as labels for the corresponding <td> elements in the <tbody> section of the table.  
    ///
    /// For the <tfoot> section, add "data-role" properties to each <td> element to give each one a 
    /// column heading on mobile devices. 
    ///
    /// A CSS class is assigned to each table after DOM inspection to provide the appropriate styling 
    /// and breakpoints for the table.  A "suppress-on-mobile" class can be added to any <td> element
    /// to hide that element on mobile devices.
    ///
    /// For proper rendering on smaller screens, cells with no text will be appended with an <span> 
    /// containing "&nbsp". This plug-in and its associated CSS use CSS3 generated content that will 
    //  not display nicely when <td> elements are empty.
    ///
    /// Valid Table Mark-Up:
    ///
    ///  <table id="tableId">
    ///     <caption>Table Caption</caption>
    ///     <thead>
    ///          <tr>
    ///              <th>Column Heading 1</th>
    ///              <th>Column Heading 2</th>    
    ///              <th>Column Heading 3</th>
    ///          </tr>
    ///      </thead>
    ///      <tbody>
    ///          <tr>
    ///              <td>Row 1 Cell 1</td>
    ///              <td>Row 1 Cell 2</td>
    ///              <td>Row 1 Cell 3</td>
    ///          </tr>
    ///      </tbody>
    ///      <tfoot>
    ///          <tr>
    ///              <td data-role="Footer Heading 1">Row 1 Cell 1</td>
    ///              <td data-role="Footer Heading 2">Row 1 Cell 2</td>
    ///              <td data-role="Footer Heading 3">Row 1 Cell 3</td>
    ///          </tr>
    ///      </tfoot>
    ///  </table>

    $.fn.makeTableResponsive = function () {

        var responsiveTableClass = "responsiveTable";

        // Confirm that the jQuery object is a table
        if (this.is("table")) {

            // Determine if at least one table element has been supplied
            if (this.length > 0) {

                // Iterate over each table in the supplied collection
                for (t = 0; t < this.length; t++) {

                    // Determine the number of columns and non-header rows in this table
                    var numberOfColumns = $(this[t]).find('tr')[0].cells.length;
                    var numberOfRows = $(this[t]).find('tbody tr').length;

                    // Initialize the header label array
                    var headerLabels = [];

                    // Ensure that this table has at least one non-header row and one column
                    if (numberOfColumns > 0 && numberOfRows > 0) {

                        // Add table header labels (i.e. <th> element values) to the array
                        for (columnNumber = 1; columnNumber <= numberOfColumns; columnNumber++) {
                            var th = $(this[t]).find('thead tr th:nth-child(' + columnNumber + ')');

                            // If a table header has the "blank" class then use a blank space 
                            // Note: this is done for accessibility purposes; <th> elements should never be blank
                            if ($(th).find('.offScreen').length != 0) {
                                headerLabels.push('');
                            } else {
                                headerLabels.push($(th).html());
                            }
                        }

                        // Assign header labels as data-role attributes on appropriate <td> elements
                        for (rowNumber = 1; rowNumber <= numberOfRows; rowNumber++) {
                            for (columnNumber = 0; columnNumber < numberOfColumns; columnNumber++) {
                                // <td> elements are 1-indexed but our column counter is 0-indexed, so our <td> counter must be incremented by 1
                                var tdIndex = columnNumber + 1;
                                var tdElement = $(this[t]).find('tbody tr:nth-child(' + rowNumber + ') td:nth-child(' + tdIndex + ')');
                                if (!tdElement.attr('data-role')) {
                                    tdElement.attr('data-role', headerLabels[columnNumber]);

                                    // Force a refresh of the <td> element so that column header shows in all browsers
                                    tdElement.addClass("force-refresh");
                                    tdElement.removeClass("force-refresh");
                                }

                                // If a <td> element is empty, add a space to give it content so that formatting is preserved when the table is flipped
                                var element = tdElement.html();
                                if (typeof element === "string" && element.trim() == '') {
                                    tdElement.append("<span>&nbsp</span>");
                                }

                                // wrap the contents of the td for height fix if it hasn't already been wrapped
                                if ((tdElement).find('div.layout-table-cell').length == 0) {
                                    tdElement.wrapInner("<div class='layout-table-cell'></div>");
                                }
                            }
                        }

                        for (columnNumber = 0; columnNumber < numberOfColumns; columnNumber++) {
                            // <td> elements are 1-indexed but our column counter is 0-indexed, so our <td> counter must be incremented by 1
                            var tdIndex = columnNumber + 1;
                            var tdElement = $(this[t]).find('tfoot td:nth-child(' + tdIndex + ')');

                            // If a <td> element is empty, add a space to give it content so that formatting is preserved when the table is flipped
                            var colelement = tdElement.html();
                            if (typeof colelement === "string" && colelement.trim() == '') {
                                tdElement.append("<span>&nbsp</span>");
                            }
                            // wrap the contents of the td for height fix if it hasn't already been wrapped
                            if ((tdElement).find('div.layout-table-cell').length == 0) {
                                tdElement.wrapInner("<div class='layout-table-cell'></div>");
                            }
                        }
                    }
                }
            }
            // Add the responsive table CSS class to the table for styling and mobile breakpoints
            $(this).addClass(responsiveTableClass);
        }
    }
}(jQuery));


(function ($) {

    var notifications = [];

    var methods = {

        init: function (options) {

            // setup
            return this.each(function () {
                $(this).find('.esg-notification-center__dropdown').on('click', methods.toggle);
                updateVisualState(false);
            });

        },

        destroy: function () {
            return this.each(function () {
                $(this).off('click');
            });
        },

        toggle: function () {
            if ($("ul.esg-notification-center").is(":visible")) {
                if ($('.esg-notification-center__prompt').length == 0) {
                    $.notificationCenter('hide');
                }
            } else {
                $.notificationCenter('show');
            }
        },

        show: function () {
            if ($('ul[id="notification-messages-list"] > li').length > 0) {
                $("#notificationMenu").attr("aria-expanded", true);
                $(".esg-notification-center__dropdown").addClass("esg-is-open");
                $("ul.esg-notification-center").trigger("focus");
                updateVisualState(true);
            }
        },

        hide: function () {
            $("#notificationMenu").attr("aria-expanded", false);
            $(".esg-notification-center__dropdown").removeClass("esg-is-open");
            updateVisualState(false);
        },

        addNotification: function (input) {
            if (input != null) {

                if (!Array.isArray(input)) {
                    input = [input];
                }

                $.each(input, function (i, notification) {
                    notification = createNotificationObject(notification);
                    var notifIndex = notifications.push(notification) - 1;
                    notification.index = notifIndex;
                    if (notification.message.length > 0) {

                        // check for this message in the stack...
                        var okToAdd = true;
                        $('ul[id="notification-messages-list"] > li > div > span').each(function () {
                            if ($(this).text().toLowerCase() == notification.message.toLowerCase()) { okToAdd = false; }
                        });

                        if (okToAdd) {
                            var fullMessage = "";
                            // If there is no Notification Center UI (most likely because you are not logged in) then just show an alert
                            // This is needed for when initially configuring the system via the Admin page
                            if ($("#notificationHost").length === 0) {
                                //some of the expected messages may contain quotes and slashes that display poorly unless massaged
                                var content = notification.message;
                                try {
                                    content = $.parseJSON(notification.message);
                                } catch (e) {
                                    //this may fail if you try it in older browsers, not much we can do about it
                                }
                                fullMessage = content;
                                switch (notification.type.toLowerCase()) {
                                    case "error":
                                        fullMessage = "Error!\n\n" + content;
                                        break;
                                    case "warning":
                                        fullMessage = "Warning!\n\n" + content;
                                        break;
                                }
                                if (fullMessage.length > 0) {
                                    alert(fullMessage);
                                }
                                return true;
                            }

                            var notificationClass = "esg-alert--info";
                            var iconMarkup = '<span class="esg-alert__icon-wrapper"><span class="esg-alert__icon esg-icon__container" aria-hidden="True"><svg class="esg-icon esg-icon--info-dark"><use xlink:href="#icon-info"></use></svg></span></span>';
                            var sortOrder = 3;

                            switch (notification.type.toLowerCase()) {
                                case "success":
                                    notificationClass = "esg-alert--success";
                                    iconMarkup = '<span class="esg-alert__icon-wrapper"><span class="esg-alert__icon esg-icon__container" aria-hidden="True"><svg class="esg-icon esg-icon--success-dark"><use xlink:href="#icon-check"></use></svg></span></span>';
                                    sortOrder = 1;
                                    break;
                                case "error":
                                    notificationClass = "esg-alert--error";
                                    iconMarkup = '<span class="esg-alert__icon-wrapper"><span class="esg-alert__icon esg-icon__container" aria-hidden="True"><svg class="esg-icon esg-icon--error-dark"><use xlink:href="#icon-error"></use></svg></span></span>';
                                    sortOrder = 2;
                                    break;
                                case "warning":
                                    notificationClass = "esg-alert--warning";
                                    iconMarkup = '<span class="esg-alert__icon-wrapper"><span class="esg-alert__icon esg-icon__container" aria-hidden="True"><svg class="esg-icon esg-icon--warning-dark"><use xlink:href="#icon-warning"></use></svg></span></span>';
                                    sortOrder = 3;
                                    break;
                                case "information":
                                    notificationClass = "esg-alert--info";
                                    iconMarkup = '<span class="esg-alert__icon-wrapper"><span class="esg-alert__icon esg-icon__container" aria-hidden="True"><svg class="esg-icon esg-icon--info-dark"><use xlink:href="#icon-info"></use></svg></span></span>';
                                    sortOrder = 3;
                                    break;
                            }

                                    var listItem = $("<li></li>")
                                            .addClass("esg-notification-center__item")
                                            .attr({ 'data-sort-Order': sortOrder })
                                            .attr({ 'data-index': notifIndex });

                            // Mark the notification list element as "permanent" and/or "flash", according to the properties.
                            if (notification.permanent === true) {
                                listItem = listItem.attr("data-type", "permanent");
                            }
                            if (notification.flash === true) {
                                listItem = listItem.attr("data-type", "flash");
                            }
                            // Build the notification message from the provided arguments...
                            var fullMessage = "";
                            // Title (will be bold weight font)
                            if (notification.title && notification.title.length > 0) {
                                fullMessage += "<div class='esg-alert__message--title'>" + notification.title + "</div>";
                            }
                            // Message (no additional styling)
                            if (notification.message && notification.message.length > 0) {
                                fullMessage += "<div class='esg-alert__message-text'>" + notification.message + "</div>";
                            }
                            // Link
                            if (notification.link && notification.link.length > 0) {
                                fullMessage += "<a class='esg-alert__message--link' href='" + notification.link + "' target='_blank'>";
                                if (notification.linkText && notification.linkText.length > 0) {
                                    fullMessage += notification.linkText;
                                } else {
                                    fullMessage += notification.link;
                                }
                                fullMessage += "</a>";
                            }

                            // Insert the message content
                            var messageDiv = $("<div></div>")
                                .addClass("esg-alert")
                                .addClass(notificationClass)
                                .append(iconMarkup)
                                .append($("<div class='esg-alert__message'></div>").append(fullMessage));

                            // Append the message to the list element
                            listItem.append(messageDiv);

                            // Append the listItem to the parent ul element
                            $('ul[id="notification-messages-list"]').append(listItem);

                                        if (notification.prompts && notification.prompts.length > 0) {
                                            var promptsDiv = $("<div></div>")
                                                .addClass("esg-notification-center__prompt")
                                                .append('<div class="esg-button-group" role="group" aria-label="button group"></div>');

                                            $.each(notification.prompts, function (i, prompt) {
                                                var b = $("<button class='eds-button eds-button--secondary'></button>").html(prompt.label).on("click", prompt.action);
                                                promptsDiv.children(".esg-button-group").append(b).append(" ");
                                            });

                                            listItem.append(promptsDiv);
                                        }

                                        // Flash notifications should self-remove after a set duration (default: 10 seconds)...
                                        if (notification.flash == true) {
                                            setTimeout(function () {
                                                $.notificationCenter('removeNotification', notification);
                                                // Determine if there are any remaining "flash" type notifications.
                                                var flashNotificationCount = $('ul[id="notification-messages-list"] > li:not([data-type="flash"])').length;
                                                if (flashNotificationCount === 0) {
                                                    // If the last "flash" notification was just removed, hide the notification flyout.
                                                    $.notificationCenter('hide');
                                                }
                                            }, 10000);
                                        } 
                                        // Non-"flash" type notifications...
                                        else {
                                            // "Permanent" notifications should not include a removal link (X to the right of the message).
                                            // Notifications with prompts are permanent (because the user needs to select something)
                                            if (notification.permanent === false && !(notification.prompts && notification.prompts.length > 0)) {
                                                messageDiv.children(".esg-alert__message").append(
                                                    "<button class='esg-icon__container esg-notification-center__close' aria-label=" +
                                                    Ellucian.SharedComponents.ButtonLabels.buttonTextClose +
                                                    "><svg class='esg-icon--small esg-icon--neutral'><use xlink:href='#icon-close'></use></svg></button>"
                                                );
                                                messageDiv.children(".esg-alert__message div").addClass("esg-alert__message-text");

                                                messageDiv.find(".esg-notification-center__close").on("click", function () {
                                                    $.notificationCenter('removeNotification', notification);
                                                });
                                            }
                                        }

                                        $.notificationCenter('update');

                                        // Show notifications that have "showByDefault" set to true (default: true).
                                        if (notification.showByDefault === true) {
                                            $.notificationCenter('show');   // Visual state is updated by "show" function.
                                            var offset = $('.esg-notification-center__dropdown').offset();
                                            $('html, body').animate({ scrollTop: offset.top || 0 });
                                        } else {
                                            // force the visual state to update without showing notifications (needed for the styling).
                                            updateVisualState(false);
                                        }
     
                        }
                    }
                });
            }

            return this.each(function () {
            });
        },

        removeNotification: function (notification) {
            if (notification.index != null) {
                $('ul.esg-notification-center > li').each(function () {
                    if ($(this).attr('data-index') == notification.index) {
                        $(this).remove();
                    }
                });
            } else if (notification.message.length > 0) {
                $('ul.esg-notification-center > li').each(function () {
                    var s = $(this).find('div.esg-alert__message > div:not([class])');

                    if (s.length > 0) {
                        if (s.text().toLowerCase() == notification.message.toLowerCase()) {
                            $(this).remove();
                        }
                    }
                });
            }
            $.notificationCenter('update');

            return this.each(function () {
            });
        },

        update: function () {
            if (updateCount() == 0) {
                $.notificationCenter('hide');
            }
        },

        reset: function () {
            // Remove all non-"permanent" notifications from the DOM
            $('ul[id="notification-messages-list"] > li:not([data-type = "permanent"])').each(function () {
                $(this).remove();
            });
            $.notificationCenter('update');
            return this.each(function () {
            });
        }
    };

    function updateCount() {
        var count = $('ul[id="notification-messages-list"] > li').length || 0;
        var label = count == 1 ? ' notification' : ' notifications';
        $("#notificationMenu").html(count).attr("aria-label", count + label);
        return count;
    }

    function updateVisualState(open) {
        var count = updateCount();

        if (count <= 0) {
            $("#notificationHost").css("display", "none");
            $(".esg-header-bar__menu-item").addClass("esg-header-bar__menu-item-no-notifications");
        } else {
            $("#notificationHost").css("display", "inline-block");
            $(".esg-header-bar__menu-item").removeClass("esg-header-bar__menu-item-no-notifications");
        }
    }

    // Given a "raw" notification (may be missing some attributes, or use incorrect casing on attribute names),
    // create a usable notification object (defaults assigned, etc).
    function createNotificationObject(rawNotification) {
        var notification = {
            message: "",
            type: "information",
            flash: false,
            prompts: [],
            index: null,
            showByDefault: true,    // Show the notification as soon as it is added?
            permanent: false,       // Prevent the notification from being removed by "removeNotification", "reset", etc?
            link: "",               // href value for the hyperlink
            linkText: "",           // Text value for the hyperlink
            title: ""               // Title for the notification 
        };

        if (rawNotification != null) {
            // message prop
            if (rawNotification.message != undefined) {
                notification.message = DOMPurify.sanitize(rawNotification.message);
            } else if (rawNotification.Message != undefined) {
                notification.message = DOMPurify.sanitize(rawNotification.Message);
            } else {
                $.error("jQuery.notificationCenter: Missing property 'message'");
            }

            // type prop
            if (rawNotification.type != undefined) {
                notification.type = rawNotification.type;
            } else if (rawNotification.Type != undefined) {
                notification.type = rawNotification.Type;
            }

            // flash prop
            if (rawNotification.flash != undefined) {
                notification.flash = rawNotification.flash;
            } else if (rawNotification.Flash != undefined) {
                notification.flash = rawNotification.Flash;
            }

            // prompts
            if (rawNotification.prompts != undefined) {
                notification.prompts = rawNotification.prompts;
            } else if (rawNotification.Prompts != undefined) {
                notification.prompts = rawNotification.Prompts;
            }

            // showByDefault property
            if (rawNotification.showByDefault != undefined) {
                notification.showByDefault = rawNotification.showByDefault;
            } else if (rawNotification.ShowByDefault != undefined) {
                notification.showByDefault = rawNotification.ShowByDefault;
            }

            // permanent property
            if (rawNotification.permanent != undefined) {
                notification.permanent = rawNotification.permanent;
            } else if (rawNotification.Permanent != undefined) {
                notification.permanent = rawNotification.Permanent;
            }

            // link property
            if (rawNotification.link != undefined) {
                notification.link = DOMPurify.sanitize(rawNotification.link);
            } else if (rawNotification.Link != undefined) {
                notification.link = DOMPurify.sanitize(rawNotification.Link);
            }

            // link text property
            if (rawNotification.linkText != undefined) {
                notification.linkText = DOMPurify.sanitize(rawNotification.linkText);
            } else if (rawNotification.LinkText != undefined) {
                notification.linkText = DOMPurify.sanitize(rawNotification.LinkText);
            }

            // title property
            if (rawNotification.title != undefined) {
                notification.title = DOMPurify.sanitize(rawNotification.title);
            } else if (rawNotification.Title != undefined) {
                notification.title = DOMPurify.sanitize(rawNotification.Title);
            }
        }

        return notification;
    }

    $.fn.notificationCenter = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.notificationCenter');
        }
    };

    $.notificationCenter = function (method) {
        if (methods[method]) {
            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || !method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' + method + ' does not exist on jQuery.notificationCenter');
        }
    };

})(jQuery);


function Notification(message, type, flash) {
    this.type = "success";
    this.message = "";
    this.flash = false;
    this.prompts = [];

    if (type != undefined) { this.type = type; }
    if (message != undefined) { this.message = message; }
    if (flash != undefined) { this.flash = flash; }
}

function Notification(message, type, flash, title, link, linkText, permanent, showByDefault) {
    this.type = "success";
    this.message = "";
    this.flash = false;
    this.prompts = [];
    this.title = "";
    this.link = "";
    this.linkText = "";
    this.permanent = false;
    this.showByDefault = true;

    if (type != undefined) { this.type = type; }
    if (message != undefined) { this.message = message; }
    if (flash != undefined) { this.flash = flash; }
    if (title != undefined) { this.title = title; }
    if (link != undefined) { this.link = link; }
    if (linkText != undefined) { this.linkText = linkText; }
    if (permanent != undefined) { this.permanent = permanent; }
    if (showByDefault != undefined) { this.showByDefault = showByDefault; }
}

Notification.prototype.addPromptAction = function (label, action) {
    if (label != 'undefined') {
        this.prompts.push(new NotificationPrompt(label, action || function () { }));
    }
}

function NotificationPrompt(label, action) {
    this.label = label;
    this.action = action;
}


$(document).ready(function () {
    if ($("#notificationHost").length > 0) {
        $("#notificationHost").notificationCenter();
    }
});

; (function ($) {
    // This will create an accordion for the menu navigation structure
    // It should be invoked on the top-level <ul> element of the navigation
    // Example: $("ul#nav-root").menuAccordion();
    //
    // Creating submenus:
    //      <li> elements with a class of 'menu-wrapper' are assumed to contain a child menu, and should contain
    //      a <ul> element with a class of 'child-menu'. The first element with a class of 'menu-item' will be
    //      wrapped with a button to control collapse state
    // Expected sample navigation list:
    // <ul�id="nav-root"> <!-- This is the element that the plugin should be invoked on -->
    //����<li�class="menu-wrapper">
    //��������<span�class="menu-item"><a�href="#">Top level menu 1</a></span>
    //��������<ul�class="child-menu">
    //������������<li�class="menu-wrapper">
    //����������������<span�class="menu-item"><a�href="#">Submenu 1-1</a></span>
    //����������������<ul�class="child-menu">
    //��������������������<li><span�class="menu-item"><a�href="#">Item 1-1-1</a></span></li>
    //��������������������<li><span�class="menu-item"><a�href="#">Item 1-1-2</a></span></li>
    //����������������</ul>
    //������������</li>
    //������������<li><span�class="menu-item">Item 1-1</span></li>
    //��������</ul>
    //����</li>
    //����<li�class="menu-wrapper">
    //��������<span�class="menu-item">Top level menu 2</span>
    //��������<ul�class="child-menu">
    //������������<li><span�class="menu-item">Item 2-1</span></li>
    //������������<li><span�class="menu-item">Item 2-2</span></li>
    //������������<li><span�class="menu-item">Item 2-3</span></li>
    //������������<li�class="menu-wrapper">
    //����������������<span�class="menu-item"><a�href="#">Submenu 2-1</a></span>
    //����������������<ul�class="child-menu">
    //��������������������<li><span�class="menu-item"><a�href="#">Item 2-1-1</a></span></li>
    //��������������������<li><span�class="menu-item"><a�href="#">Item 2-1-2</a></span></li>
    //����������������</ul>
    //������������</li>
    //��������</ul>
    //����</li>
    //����<li><span�class="menu-item">Top level menu 3</span></li>
    //</ul>

    $.fn.menuAccordion = function (options) {
        var self = this;
        // Use resource string if available
        this.defaults = {
            menuLevelClasses: ['primary-menu', 'secondary-menu', 'tertiary-menu'],
            headingClass: 'menu-header',
            menuWrapperClass: 'menu-wrapper',
            menuItemClass: 'menu-item',
            includeCollapseStateIcon: true,
            collapseStateElement: "<span class='esg-icon__container float-right' aria-hidden='true'><svg class='esg-icon esg-icon--down esg-icon--white'><use xmlns:xlink='http://www.w3.org/1999/xlink' xlink:href='#icon-arrow'></use></svg></span>",
            collapseStateClosedClass: 'esg-icon--down',
            collapseStateOpenClass: 'esg-icon--up',
            buttonIdPrefix: 'nav-group'
        };

        var opts = $.extend({}, this.defaults, options);

        // Builds accordion functionality into the navigation menu.
        // When not called recursively from itself, it is expected that menuElement be the top level
        // menu <ul>, level be 0, and the buttonIdPrefix should be a valid string for use in element ids
        // Accordion functionality is provided via buttons that are wrapped around menu headers.
        var initializeMenu = function (menuElement, level, buttonIdPrefix) {
            return new Promise(function (resolve, reject) {
                if (opts.menuLevelClasses.length > level) {
                    $(menuElement).addClass(opts.menuLevelClasses[level]);
                }
                $(menuElement).children("li." + opts.menuWrapperClass).each(function (index, element) {
                    var headerLink = $(element).children("." + opts.menuItemClass).first();
                    var childMenu = $(element).children("ul.child-menu").first();
                    if (headerLink.length && childMenu.length) {
                        // This list item has a nested list -- generate accordion
                        var buttonId = buttonIdPrefix + "-" + index;
                        var ariaControls = childMenu.attr('id');
                        var openButton = $("<button type='button' class='menu-item-button' id='" + buttonId + "' data-menu-level='" + level + "' aria-expanded='false' aria-controls='" + ariaControls + "'></button>");
                        // For accessibility, each button needs to be wrapped in div with heading role and aria-level with non-zero index
                        var accordionHeader = $("<div role='heading' aria-level='" + (level + 1) + "' class='" + opts.headingClass + "'></div>");

                        // Start collapsed
                        var collapseElement = $(opts.collapseStateElement);
                        childMenu.slideUp();

                        // When a button is clicked, show the related menu and hide others on the same 'level'
                        // If clicked again, hide the related menu
                        // State is maintained via aria-expanded attribute
                        openButton.on('click', function (e) {
                            if ($(this).attr('aria-expanded') === 'false') {
                                collapseElement.children(".esg-icon").removeAttr("class", "esg-icon  " + opts.collapseStateClosedClass).attr("class", "esg-icon  " + opts.collapseStateOpenClass);
                                $(this).attr('aria-expanded', true);
                                $(self).find('button[data-menu-level=' + level + ']').each(function (ev) {
                                    if ($(this).prop('id') !== openButton.prop('id') && $(this).attr('aria-expanded') === 'true') {
                                        $(this).prop('aria-expanded', false).trigger('click');
                                    }
                                });

                                // Wait for sidebar to be open (finish CSS transition animation) before sliding down
                                // Used when directly accessing a top-level menu item when sidebar is closed (no active-nav class on html element)
                                // Do not use on breadcrumbs (buttonIdPrefix is nav-group, not bc-group)
                                if (!$('html').hasClass('active-nav') && buttonIdPrefix == 'nav-group') {
                                    //  Wait for transition to complete before slideDown
                                    $(".sidebar-nav-wrapper").one('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function (event) {
                                        childMenu.slideDown();
                                    });
                                } else {
                                    // Sidebar is already open or nav is breadcrumb, slideDown normally
                                    childMenu.slideDown();
                                }
                            } else {
                                collapseElement.children(".esg-icon").removeAttr("class", "esg-icon  " + opts.collapseStateOpenClass).attr("class", "esg-icon  " + opts.collapseStateClosedClass);
                                $(this).attr('aria-expanded', false);
                                childMenu.slideUp();
                            }
                        });

                        // Trigger mouse event when button has focus to toggle tooltip
                        // Parent button receives focus but tooltip is attached to child span and is not triggered normally
                        openButton.on('focusin', function (e) {
                            $(this).children("span[rel~='menu']").first().trigger("mouseenter");
                        });
                        openButton.on('focusout', function (e) {
                            $(this).children("span[rel~='menu']").first().trigger("mouseleave");
                        });

                        // Wrap menu-item (span) in accordion heading, then wrap menu-item in button
                        headerLink.wrap(accordionHeader).wrap(openButton);

                        // Append caret to menu-item (span)
                        headerLink.append(collapseElement);

                        // Add aria-labelledby to child menu (ul)
                        childMenu.attr('aria-labelledby', openButton.attr('id'));

                        // Recurse into child menus
                        initializeMenu(childMenu, level + 1, buttonId);
                    }
                });
                resolve();
            });
        };

        this.addClassToElementAndParentMenus = function (element, classToAdd) {
            $(element).addClass(classToAdd);
            $(element).parentsUntil(self, '.' + opts.menuWrapperClass).each(function (e) {
                $(this).addClass(classToAdd);
            });
        }

        this.menuLoad = initializeMenu(this, 0, opts.buttonIdPrefix);

        return this;
    };
}(jQuery));

/* Copyright 2012-2024 Ellucian Company L.P. and its affiliates. 
 LOAD LAST! */

// base site namespace and functions
(function (site, $, undefined) {

    // **************
    // public members
    // **************

    // Init site - call into other init funcitons as well.
    site.initialize = function () {
        site.toolbar.initialize();
    };

    // Logout function to clean up resources before session ends
    site.logout = function () {
        if (Ellucian && Ellucian.Experience && Ellucian.Experience.headerkitLogout) {
            Ellucian.Experience.headerkitLogout();
        }
        Ellucian.Storage.session.clear();
    };

}(window.site = window.site || {}, jQuery));


// toolbar namespace and functions
(function (toolbar, $, undefined) {
    // **************
    // public members
    // **************

    // message shown when no help is found for the help URL.
    toolbar.helpNotAvailableMessage = "";

    // Init the toolbar
    toolbar.initialize = function () {
        // Only enable toggling of the user options if there are items to display
        // Proxy users acting on behalf of another user currently will have no items in the options menu
        if ($("#userOptions").has("li").length > 0) {
            $("#userName").on("click", function () {
                if ($("#userName").hasClass('esg-is-open')) {
                    hideUserOptions();
                }
                else {
                    showUserOptions();
                }
            });
        }
        if ($("#logOff").length > 0) {
            $("#logOff").on("click", function () {

                // Clear storage to ensure nobody is accessing data from another user on a shared browser
                // Also done during log-in, but do here as well just to be double-sure
                site.logout();

                window.location.href = logOffUrl
            });
        }

        $("#logIn").on("click", function () {
            window.location.href = logInUrl
        });


        $("#help").on("click", function () {
            if ($("#help").hasClass('esg-is-open')) {
                hideHelp();
            }
            else {
                showHelp();
            }
        });
        // notifications takes care of itself, but we need to know when the control
        // open's itself and simulate a click to ensure the other tabs are closed
        $("#siteToolbar").on("notifications_open", function (e) {
            closeOpenTabs("javascript:void(0)");
        });

        $(document).on("mouseup", function (e) {
            closeOpenTabs(e);
        });
    };

    // Function to drive menu behavior based on underlying data
    toolbar.setMenuBehavior = function (data) {
        $("#logOff").off("click");
        $("#logOff").on("click", function () {
            if ($("#logOff").hasClass('esg-is-open')) {
                hideLogOffOptions();
            }
            else {
                showLogOffOptions();
            }
        });
        showOrHideElement(data.ProxySubjects, $("#changeProxyUser-link"));
        showOrHideElement(data.ProxySubjects, $("#changeProxyUser-opsLink"));
    }

    // Fetches the help content for the help tab.
    toolbar.addHelpContent = function (helpUrl) {
        var formattedHelpMessage = "<h2>" + toolbar.helpNotAvailableMessage + "</h2>";

        // Fails if helpUrl is null, empty string, or undefined
        if (helpUrl) {
            $("#help").one("click", function () {
                var jqxhr = $.get(helpUrl, function (data) {

                    if (data.length > 0) {
                        $("#help-body").html(data);
                    }
                    else {
                        $("#help-body").html(formattedHelpMessage);
                    }

                    // jQuery UI Accordian Replacement

                    var helpPanels = $('.help-content > div').hide();
                    //add the dropdown carat and give ids to each elem
                    var h3_id = 0;
                    $('.help-content h3').each(function () {
                        $(this).prepend(
                            "<span id = 'inner-span-" + h3_id + "' class='esg-icon__container' aria-hidden='True'>" +
                            "<svg id= 'inner-svg-" + h3_id + "' class='esg-icon esg-icon--xsmall'>" +
                            "<use xlink:href='#icon-arrow'></use>" +
                            "</svg>" +
                            "</span>"
                        );
                        $(this).attr('id', 'outer-h3-' + h3_id);
                        h3_id++;
                    });

                    $('.help-content > h3').on("click", function (e) {
                        $target = $(this).next();
                        $h3_outer_id = ($(this).attr('id'));
                        // grab the id from the h3 elem
                        $svg_inner_id = $h3_outer_id.substring($h3_outer_id.lastIndexOf('-') + 1, $h3_outer_id.length)
                        $svgCarat = $('#inner-svg-' + $svg_inner_id);
                        // resets all of the open carats.
                        $h3_ids_remove = 0;
                        $('.help-content h3').each(function () {
                            $('#inner-svg-' + $h3_ids_remove).attr('class', 'esg-icon esg-icon--xsmall');
                            $h3_ids_remove++;
                        });
                        // opens a div if its not active
                        if (!$target.hasClass('active')) {
                            helpPanels.removeClass('active').slideUp();
                            $target.addClass('active').slideDown();
                            $svgCarat.attr('class', 'esg-icon esg-icon--xsmall esg-icon--down');
                        }
                        // closes div if it is active and clicked again
                        else {
                            helpPanels.removeClass('active').slideUp();
                        }
                        return false;
                    });

                    // Hide the help regions initially
                    $(".more-help").hide();

                    $('<a href="#" class="more-help-link">Click Here For More Information</a>').insertBefore(".more-help");

                    $(".more-help-link").one("click", function () {
                        $(this).hide();
                        $(this).next("div").show();
                        $(this).next("div").trigger("focus");
                    });
                }).fail(function () { $("#help-body").html(formattedHelpMessage); });
            });
        } else {
            $("#help-body").html(formattedHelpMessage);
        }
    }

    // ***************
    // private members
    // ***************

    // Used to manage the tabs in the site toolbar (user options, help, notifications). Note, notifications is not, in this list
    // because it is a custom jQuery UI plugin and takes care of itself.
    //
    // clickTarget = jQuery selector that represents the element a user clicks on (used to determine if the tab was clicked and simulate clicks from the script)
    // childTargets = jQuery selectors that represent child elements of the tab (e.g. if the mouse click is on these, it is ignored)
    // visible = flag to track tab visibility (always init to false)
    tabInformation =
        // user options menu
        [{
            clickTarget: "#userName",
            childTargets: ["#userName", "#userOptions"],
            visible: false
        },
        // Sign out menu
        {
            clickTarget: "#logOff",
            childTargets: ["#logOff", "#logOffOptions"],
            visible: false
        },
        // help menu
        {
            clickTarget: "#help",
            childTargets: ["#help", "#help-region"],
            visible: false
        }];

    // helper functions to show/hide the user options menu
    function showUserOptions() {
        $("#userName").addClass("esg-is-open");
        $("#userName").attr("aria-expanded", true);
        tabInformation[0].visible = true;
        return false;
    }
    function hideUserOptions() {
        tabInformation[0].visible = false;
        $("#userName").removeClass("esg-is-open");
        $("#userName").attr("aria-expanded", false);
        return false;
    }

    // helper functions to show/hide the log off menu
    function showLogOffOptions() {
        $("#logOff").addClass("esg-is-open");
        //close other site toolbar menus if they're open
        $("#logOff").attr("aria-expanded", true);
        tabInformation[1].visible = true;
        return false;
    }
    function hideLogOffOptions() {
        tabInformation[1].visible = false;
        $("#logOff").removeClass("esg-is-open");
        $("#logOff").attr("aria-expanded", false);
        return false;
    }

    // helper functions to show/hide the help menu
    function showHelp() {
        $("#help").addClass("esg-is-open");
        //close other site toolbar menus if they're open
        $("#help").attr("aria-expanded", true);
        tabInformation[2].visible = true;
        return false;
    }
    function hideHelp() {
        tabInformation[2].visible = false;
        $("#help").removeClass("esg-is-open");
        $("#help").attr("aria-expanded", false);
        return false;
    }

    function checkNavItemVisibility() {
        let proxySubjects = JSON.parse(sessionStorage.getItem('Ellucian.proxySubjects'));
        if (proxySubjects) {
            // Determine visibility
            if (proxySubjects.length > 1) {
                toolbar.setMenuBehavior({ ProxySubjects: proxySubjects });
            }
        } else {
            // Fetch the data if not available
            $.ajax({
                url: getProxySubjectsActionUrl,
                type: 'GET',
                contentType: jsonContentType,
                dataType: 'json',
                success: function (data) {
                    if (!account.handleInvalidSessionResponse(data)) {
                        if (data.ProxySubjects) {
                            Ellucian.Storage.session.setItem('Ellucian.proxySubjects', data.ProxySubjects);
                            if (data.ProxySubjects.length > 1) {
                                toolbar.setMenuBehavior(data);
                            }
                        }
                    }
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    console.error("Error fetching proxy subjects...");
                }
            });
        }
    }

    $(document).ready(function ($) {

        let blurred, focused;

        // If the condition from proxy.selection.js is not met attempt to get proxy information to show change proxy option if toolbar menus are available
        if ($("#proxy-banner").length && $("#home").length && ($("#userOptions").length > 0 || $("#logOff").length > 0)) {
            checkNavItemVisibility();
        }

        // Listen for the custom event to update visibility logic
        $(document).on("proxySubjectsReady", function () {
            checkNavItemVisibility();
        });

        $('.esg-dropdown__link, .esg-dropdown__toggle, .helpOption, .esg-dropdown-notifications').on("blur", function () {
            blurred = this;
        });

        $('.esg-dropdown__link, .esg-dropdown__toggle, .helpOption, .esg-dropdown-notifications, #toggle-nav-button').on("focus", function () {
            focused = this;
            let switchVar;
            setTimeout(function () {

                // if the newly focused item does not belong to same menu, find out which menu was open and close it
                if ($(focused).is('.esg-dropdown__toggle, .esg-dropdown-notifications, #toggle-nav-button')) {
                    // this needs to switch on previously focused element
                    if ($(blurred).is('.esg-dropdown__link')) {
                        switchVar = $(blurred).parents().siblings('.esg-dropdown__toggle').attr('id');
                    }

                    else if ($(blurred).is('.esg-dropdown__toggle, .esg-dropdown-notifications')) {
                        switchVar = $(blurred).attr('id');
                    }

                    else if ($(focused).is('#toggle-nav-button')) {
                        switchVar = $(focused).attr('id');
                    }

                    switch (switchVar) {
                        case 'userName':
                            hideUserOptions();
                            break;
                        case 'logOff':
                            hideLogOffOptions();
                            break;
                        case 'help':
                            hideHelp();
                            break;
                        case 'notificationMenu':
                        case 'toggle-nav-button':
                            $("#notificationHost").notificationCenter('hide');
                            break;
                    }
                }

            }, 0);
        });
    });

    // Tab management function that closes open tabs when a mouse click occurs.
    //
    // If a click is on another tab, then this will close any other open tabs so only one is open.
    // If a click is outside a tab (e.g. somewhere on the page) then this will close any open tabs.
    // This function has to handle the notifications center as a special case, as it can only be closed
    // by a tab click (either another tab or itself), so the only time this will close the notifications
    // tab is when the click was on another tab.
    function closeOpenTabs(clickEvent) {
        // try to close all open tabs in the tabInformation list
        $.each(tabInformation, function (i, tab) {
            var close = true;
            // if the click is within the tab itself, do not close (indicated by the 
            $.each(tab.childTargets, function (j, target) {
                if ($(target).is(clickEvent.target) || $(target).has(clickEvent.target).length > 0) {
                    close = false;
                }
            });

            if (close && tab.visible) {
                $(tab.clickTarget).trigger("click");
            }
        });

        var isTabClick = false;
        // check to see if this is a 'tab' click
        $.each(tabInformation, function (i, tab) {
            if ($(tab.clickTarget).is(clickEvent.target) || $(tab.clickTarget).has(clickEvent.target).length > 0) {
                isTabClick = true;
            }
        });

        // close notifications if the click was on another tab
        if (isTabClick) {
            $("#notificationHost").notificationCenter('hide');
        }
    }

}(window.site.toolbar = window.site.toolbar || {}, jQuery));


(function (window, $) {

    openUrl = function (url) {
        if (url) {
            window.location.href = url;
        } else {
            return;
        }
    }

    // Size of maximum width of single column media query
    var smallScreen = 769;

    // checks window size on page load and adjusts page appropriately (copied/pasted from resize function)
    var adjustSingleColumnClasses = function () {

        if ($(window).width() < smallScreen) {
            $("body").addClass("small-screen");
        } else {
            $("body").removeClass("small-screen");
        }
    }

    $(window).on("resize", function () {
        // add/remove classes everytime the window resize event fires
        adjustSingleColumnClasses();
        if ($(window).width() >= smallScreen) {
            $('#new-filter-button').removeClass("esg-is-open");
            $('#new-filter-button').attr("aria-expanded", false);
            $('#new-filter-dropdown').hide()
        }
    });

    $(document).ready(function ($) {

        // New drawer + accordion navigation
        navMenu = $('ul#nav-root').menuAccordion();

        var openNavigation = function () {
            $('.sidebar-nav-wrapper').removeClass('sidebar-nav-wrapper--hidden');

            setTimeout(function () {
                $('html').addClass('active-nav');

            }, 100);

            if (navigationMenuCloseText) {
                $('#toggle-nav-button').attr('aria-label', navigationMenuCloseText);
            }
        }

        var closeNavigation = function () {
            $('html').removeClass('active-nav');

            setTimeout(function () {
                $('.sidebar-nav-wrapper').addClass('sidebar-nav-wrapper--hidden');

            }, 500);

            if (navigationMenuOpenText) {
                $('#toggle-nav-button').attr('aria-label', navigationMenuOpenText);
            }

            $('ul#nav-root button[data-menu-level][aria-expanded=true]').each(function (e) {
                // Toggle.supress-expand on button to prevent additional openNavigation trigger
                // while collapsing open menu options with hamburger menu
                $(this).addClass('supress-expand');
                $(this).trigger('click');
                $(this).removeClass('supress-expand');
            })
        }


        var mainContent = "#main-content";
        var skipLink = "#skip-link";

        // Sets focus to #main-content 
        var mainContentFocus = function () {

            $(mainContent).trigger("focus");
        }

        // Sets focus only when the skip-link is clicked
        skipLink.onclick = mainContentFocus;

        $(document).on("keydown", function (e) {
            if (e.keyCode === 27) {
                closeNavigation();
            }
        });

        $(".nav-overlay").on('click', function (event) {
            closeNavigation();
        });

        $("#nav-root > li:not(.supress-expand)").on('click', function (event) {
            // Check again to ensure that click target does not have .suppress-expand 
            // (on button this time)
            if (!$(event.target).hasClass('supress-expand')) {
                openNavigation();
                event.stopPropagation();
            }
        });

        $("#toggle-nav-button").on('click', function (event) {
            if ($('html').hasClass('active-nav')) {
                closeNavigation();
            } else {
                openNavigation();
                event.stopPropagation();
            }
        });

        adjustSingleColumnClasses();
    });

    window.activateFilter = function () {
        if (document.getElementById("new-filter-dropdown")) {
            $('#new-filter-dropdown').toggle();
        } else {
            if ($("#new-filter-button").hasClass('esg-is-open')) {
                $('#new-filter-button').removeClass("esg-is-open");
                $("#new-filter-button").attr("aria-expanded", false);
            } else {
                $('#new-filter-button').addClass("esg-is-open");
                $("#new-filter-button").attr("aria-expanded", true);
            }
        }
    };
})(window, jQuery);

//debounce function used for the window scroll event to improve performance
function debounce(callback, timeout) {
    var timeoutInstance;
    return function () {
        //investigate clearTimeout
        clearTimeout(timeoutInstance);
        timeoutInstance = ko.utils.setTimeout(callback, timeout);
    };
}

/****************************************************/
//
// Library that wraps sessionStorage for easier use
//
// Uses the same interface as sessionStorage, but
// handles conversion from JavaScript objects to JSON
/****************************************************/
var Ellucian = Ellucian || {};
Ellucian.Storage = Ellucian.Storage || {};

Ellucian.Storage = function (global) {

    // Simple function to return the type of an object (more robust/accurate than "typeof")
    var toType = function (obj) {
        return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
    };

    var getItem = function (webStorage, key) {
        var item = webStorage.getItem(key);

        try {
            item = JSON.parse(item);
        } catch (e) {
            // parse is expected to fail on some types, ignore this and return anyway.
        }

        return item;
    };

    var setItem = function (webStorage, key, value) {
        // Get the type of value
        var type = toType(value);

        // If value is an object or array, stringify it for webStorage,
        // as sessionStorage only accepts strings 
        if (/object|array/.test(type)) {
            value = JSON.stringify(value);
        }
        try {
            webStorage.setItem(key, value);
        } catch (error) {
            // If we don't handle errors, the action could grace in a less than graceful way
            // We handle the errors as a no-op, but we could add an Analytics event or log
            if (error instanceof DOMException && error.code === 22) {
                // Ooops, ran out of webStorage space
            } else {
                // Some other error
            }
        }
    };

    var removeItem = function (webStorage, key) {
        webStorage.removeItem(key);
    };

    var clear = function (webStorage) {
        webStorage.clear();
    };

    return {
        session: {
            getItem: function (key) {
                return getItem(global.sessionStorage, key);
            },
            setItem: function (key, value) {
                return setItem(global.sessionStorage, key, value);
            },
            removeItem: function (key) {
                return removeItem(global.sessionStorage, key);
            },
            clear: function () {
                return clear(global.sessionStorage);
            }
        },
        local: {
            getItem: function (key) {
                return getItem(global.localStorage, key);
            },
            setItem: function (key, value) {
                return setItem(global.localStorage, key, value);
            },
            removeItem: function (key) {
                return removeItem(global.localStorage, key);
            },
            clear: function () {
                return clear(global.localStorage);
            }
        }
    };
}(this);

// Copyright 2023 Ellucian Company L.P. and its affiliates.
/*!
 * clipboard.js v1.7.1
 * https://zenorocha.github.io/clipboard.js
 *
 * Licensed MIT © Zeno Rocha
 */
(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.Clipboard = 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){
var DOCUMENT_NODE_TYPE = 9;

/**
 * A polyfill for Element.matches()
 */
if (typeof Element !== 'undefined' && !Element.prototype.matches) {
    var proto = Element.prototype;

    proto.matches = proto.matchesSelector ||
                    proto.mozMatchesSelector ||
                    proto.msMatchesSelector ||
                    proto.oMatchesSelector ||
                    proto.webkitMatchesSelector;
}

/**
 * Finds the closest parent that matches a selector.
 *
 * @param {Element} element
 * @param {String} selector
 * @return {Function}
 */
function closest (element, selector) {
    while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {
        if (typeof element.matches === 'function' &&
            element.matches(selector)) {
          return element;
        }
        element = element.parentNode;
    }
}

module.exports = closest;

},{}],2:[function(require,module,exports){
var closest = require('./closest');

/**
 * Delegates event to a selector.
 *
 * @param {Element} element
 * @param {String} selector
 * @param {String} type
 * @param {Function} callback
 * @param {Boolean} useCapture
 * @return {Object}
 */
function delegate(element, selector, type, callback, useCapture) {
    var listenerFn = listener.apply(this, arguments);

    element.addEventListener(type, listenerFn, useCapture);

    return {
        destroy: function() {
            element.removeEventListener(type, listenerFn, useCapture);
        }
    }
}

/**
 * Finds closest match and invokes callback.
 *
 * @param {Element} element
 * @param {String} selector
 * @param {String} type
 * @param {Function} callback
 * @return {Function}
 */
function listener(element, selector, type, callback) {
    return function(e) {
        e.delegateTarget = closest(e.target, selector);

        if (e.delegateTarget) {
            callback.call(element, e);
        }
    }
}

module.exports = delegate;

},{"./closest":1}],3:[function(require,module,exports){
/**
 * Check if argument is a HTML element.
 *
 * @param {Object} value
 * @return {Boolean}
 */
exports.node = function(value) {
    return value !== undefined
        && value instanceof HTMLElement
        && value.nodeType === 1;
};

/**
 * Check if argument is a list of HTML elements.
 *
 * @param {Object} value
 * @return {Boolean}
 */
exports.nodeList = function(value) {
    var type = Object.prototype.toString.call(value);

    return value !== undefined
        && (type === '[object NodeList]' || type === '[object HTMLCollection]')
        && ('length' in value)
        && (value.length === 0 || exports.node(value[0]));
};

/**
 * Check if argument is a string.
 *
 * @param {Object} value
 * @return {Boolean}
 */
exports.string = function(value) {
    return typeof value === 'string'
        || value instanceof String;
};

/**
 * Check if argument is a function.
 *
 * @param {Object} value
 * @return {Boolean}
 */
exports.fn = function(value) {
    var type = Object.prototype.toString.call(value);

    return type === '[object Function]';
};

},{}],4:[function(require,module,exports){
var is = require('./is');
var delegate = require('delegate');

/**
 * Validates all params and calls the right
 * listener function based on its target type.
 *
 * @param {String|HTMLElement|HTMLCollection|NodeList} target
 * @param {String} type
 * @param {Function} callback
 * @return {Object}
 */
function listen(target, type, callback) {
    if (!target && !type && !callback) {
        throw new Error('Missing required arguments');
    }

    if (!is.string(type)) {
        throw new TypeError('Second argument must be a String');
    }

    if (!is.fn(callback)) {
        throw new TypeError('Third argument must be a Function');
    }

    if (is.node(target)) {
        return listenNode(target, type, callback);
    }
    else if (is.nodeList(target)) {
        return listenNodeList(target, type, callback);
    }
    else if (is.string(target)) {
        return listenSelector(target, type, callback);
    }
    else {
        throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');
    }
}

/**
 * Adds an event listener to a HTML element
 * and returns a remove listener function.
 *
 * @param {HTMLElement} node
 * @param {String} type
 * @param {Function} callback
 * @return {Object}
 */
function listenNode(node, type, callback) {
    node.addEventListener(type, callback);

    return {
        destroy: function() {
            node.removeEventListener(type, callback);
        }
    }
}

/**
 * Add an event listener to a list of HTML elements
 * and returns a remove listener function.
 *
 * @param {NodeList|HTMLCollection} nodeList
 * @param {String} type
 * @param {Function} callback
 * @return {Object}
 */
function listenNodeList(nodeList, type, callback) {
    Array.prototype.forEach.call(nodeList, function(node) {
        node.addEventListener(type, callback);
    });

    return {
        destroy: function() {
            Array.prototype.forEach.call(nodeList, function(node) {
                node.removeEventListener(type, callback);
            });
        }
    }
}

/**
 * Add an event listener to a selector
 * and returns a remove listener function.
 *
 * @param {String} selector
 * @param {String} type
 * @param {Function} callback
 * @return {Object}
 */
function listenSelector(selector, type, callback) {
    return delegate(document.body, selector, type, callback);
}

module.exports = listen;

},{"./is":3,"delegate":2}],5:[function(require,module,exports){
function select(element) {
    var selectedText;

    if (element.nodeName === 'SELECT') {
        element.focus();

        selectedText = element.value;
    }
    else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
        var isReadOnly = element.hasAttribute('readonly');

        if (!isReadOnly) {
            element.setAttribute('readonly', '');
        }

        element.select();
        element.setSelectionRange(0, element.value.length);

        if (!isReadOnly) {
            element.removeAttribute('readonly');
        }

        selectedText = element.value;
    }
    else {
        if (element.hasAttribute('contenteditable')) {
            element.focus();
        }

        var selection = window.getSelection();
        var range = document.createRange();

        range.selectNodeContents(element);
        selection.removeAllRanges();
        selection.addRange(range);

        selectedText = selection.toString();
    }

    return selectedText;
}

module.exports = select;

},{}],6:[function(require,module,exports){
function E () {
  // Keep this empty so it's easier to inherit from
  // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
}

E.prototype = {
  on: function (name, callback, ctx) {
    var e = this.e || (this.e = {});

    (e[name] || (e[name] = [])).push({
      fn: callback,
      ctx: ctx
    });

    return this;
  },

  once: function (name, callback, ctx) {
    var self = this;
    function listener () {
      self.off(name, listener);
      callback.apply(ctx, arguments);
    };

    listener._ = callback
    return this.on(name, listener, ctx);
  },

  emit: function (name) {
    var data = [].slice.call(arguments, 1);
    var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
    var i = 0;
    var len = evtArr.length;

    for (i; i < len; i++) {
      evtArr[i].fn.apply(evtArr[i].ctx, data);
    }

    return this;
  },

  off: function (name, callback) {
    var e = this.e || (this.e = {});
    var evts = e[name];
    var liveEvents = [];

    if (evts && callback) {
      for (var i = 0, len = evts.length; i < len; i++) {
        if (evts[i].fn !== callback && evts[i].fn._ !== callback)
          liveEvents.push(evts[i]);
      }
    }

    // Remove event from queue to prevent memory leak
    // Suggested by https://github.com/lazd
    // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910

    (liveEvents.length)
      ? e[name] = liveEvents
      : delete e[name];

    return this;
  }
};

module.exports = E;

},{}],7:[function(require,module,exports){
(function (global, factory) {
    if (typeof define === "function" && define.amd) {
        define(['module', 'select'], factory);
    } else if (typeof exports !== "undefined") {
        factory(module, require('select'));
    } else {
        var mod = {
            exports: {}
        };
        factory(mod, global.select);
        global.clipboardAction = mod.exports;
    }
})(this, function (module, _select) {
    'use strict';

    var _select2 = _interopRequireDefault(_select);

    function _interopRequireDefault(obj) {
        return obj && obj.__esModule ? obj : {
            default: obj
        };
    }

    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
        return typeof obj;
    } : function (obj) {
        return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
    };

    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    }

    var _createClass = function () {
        function defineProperties(target, props) {
            for (var i = 0; i < props.length; i++) {
                var descriptor = props[i];
                descriptor.enumerable = descriptor.enumerable || false;
                descriptor.configurable = true;
                if ("value" in descriptor) descriptor.writable = true;
                Object.defineProperty(target, descriptor.key, descriptor);
            }
        }

        return function (Constructor, protoProps, staticProps) {
            if (protoProps) defineProperties(Constructor.prototype, protoProps);
            if (staticProps) defineProperties(Constructor, staticProps);
            return Constructor;
        };
    }();

    var ClipboardAction = function () {
        /**
         * @param {Object} options
         */
        function ClipboardAction(options) {
            _classCallCheck(this, ClipboardAction);

            this.resolveOptions(options);
            this.initSelection();
        }

        /**
         * Defines base properties passed from constructor.
         * @param {Object} options
         */


        _createClass(ClipboardAction, [{
            key: 'resolveOptions',
            value: function resolveOptions() {
                var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

                this.action = options.action;
                this.container = options.container;
                this.emitter = options.emitter;
                this.target = options.target;
                this.text = options.text;
                this.trigger = options.trigger;

                this.selectedText = '';
            }
        }, {
            key: 'initSelection',
            value: function initSelection() {
                if (this.text) {
                    this.selectFake();
                } else if (this.target) {
                    this.selectTarget();
                }
            }
        }, {
            key: 'selectFake',
            value: function selectFake() {
                var _this = this;

                var isRTL = document.documentElement.getAttribute('dir') == 'rtl';

                this.removeFake();

                this.fakeHandlerCallback = function () {
                    return _this.removeFake();
                };
                this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;

                this.fakeElem = document.createElement('textarea');
                // Prevent zooming on iOS
                this.fakeElem.style.fontSize = '12pt';
                // Reset box model
                this.fakeElem.style.border = '0';
                this.fakeElem.style.padding = '0';
                this.fakeElem.style.margin = '0';
                // Move element out of screen horizontally
                this.fakeElem.style.position = 'absolute';
                this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';
                // Move element to the same position vertically
                var yPosition = window.pageYOffset || document.documentElement.scrollTop;
                this.fakeElem.style.top = yPosition + 'px';

                this.fakeElem.setAttribute('readonly', '');
                this.fakeElem.value = this.text;

                this.container.appendChild(this.fakeElem);

                this.selectedText = (0, _select2.default)(this.fakeElem);
                this.copyText();
            }
        }, {
            key: 'removeFake',
            value: function removeFake() {
                if (this.fakeHandler) {
                    this.container.removeEventListener('click', this.fakeHandlerCallback);
                    this.fakeHandler = null;
                    this.fakeHandlerCallback = null;
                }

                if (this.fakeElem) {
                    this.container.removeChild(this.fakeElem);
                    this.fakeElem = null;
                }
            }
        }, {
            key: 'selectTarget',
            value: function selectTarget() {
                this.selectedText = (0, _select2.default)(this.target);
                this.copyText();
            }
        }, {
            key: 'copyText',
            value: function copyText() {
                var succeeded = void 0;

                try {
                    succeeded = document.execCommand(this.action);
                } catch (err) {
                    succeeded = false;
                }

                this.handleResult(succeeded);
            }
        }, {
            key: 'handleResult',
            value: function handleResult(succeeded) {
                this.emitter.emit(succeeded ? 'success' : 'error', {
                    action: this.action,
                    text: this.selectedText,
                    trigger: this.trigger,
                    clearSelection: this.clearSelection.bind(this)
                });
            }
        }, {
            key: 'clearSelection',
            value: function clearSelection() {
                if (this.trigger) {
                    this.trigger.focus();
                }

                window.getSelection().removeAllRanges();
            }
        }, {
            key: 'destroy',
            value: function destroy() {
                this.removeFake();
            }
        }, {
            key: 'action',
            set: function set() {
                var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy';

                this._action = action;

                if (this._action !== 'copy' && this._action !== 'cut') {
                    throw new Error('Invalid "action" value, use either "copy" or "cut"');
                }
            },
            get: function get() {
                return this._action;
            }
        }, {
            key: 'target',
            set: function set(target) {
                if (target !== undefined) {
                    if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) {
                        if (this.action === 'copy' && target.hasAttribute('disabled')) {
                            throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
                        }

                        if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {
                            throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');
                        }

                        this._target = target;
                    } else {
                        throw new Error('Invalid "target" value, use a valid Element');
                    }
                }
            },
            get: function get() {
                return this._target;
            }
        }]);

        return ClipboardAction;
    }();

    module.exports = ClipboardAction;
});

},{"select":5}],8:[function(require,module,exports){
(function (global, factory) {
    if (typeof define === "function" && define.amd) {
        define(['module', './clipboard-action', 'tiny-emitter', 'good-listener'], factory);
    } else if (typeof exports !== "undefined") {
        factory(module, require('./clipboard-action'), require('tiny-emitter'), require('good-listener'));
    } else {
        var mod = {
            exports: {}
        };
        factory(mod, global.clipboardAction, global.tinyEmitter, global.goodListener);
        global.clipboard = mod.exports;
    }
})(this, function (module, _clipboardAction, _tinyEmitter, _goodListener) {
    'use strict';

    var _clipboardAction2 = _interopRequireDefault(_clipboardAction);

    var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter);

    var _goodListener2 = _interopRequireDefault(_goodListener);

    function _interopRequireDefault(obj) {
        return obj && obj.__esModule ? obj : {
            default: obj
        };
    }

    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
        return typeof obj;
    } : function (obj) {
        return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
    };

    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    }

    var _createClass = function () {
        function defineProperties(target, props) {
            for (var i = 0; i < props.length; i++) {
                var descriptor = props[i];
                descriptor.enumerable = descriptor.enumerable || false;
                descriptor.configurable = true;
                if ("value" in descriptor) descriptor.writable = true;
                Object.defineProperty(target, descriptor.key, descriptor);
            }
        }

        return function (Constructor, protoProps, staticProps) {
            if (protoProps) defineProperties(Constructor.prototype, protoProps);
            if (staticProps) defineProperties(Constructor, staticProps);
            return Constructor;
        };
    }();

    function _possibleConstructorReturn(self, call) {
        if (!self) {
            throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
        }

        return call && (typeof call === "object" || typeof call === "function") ? call : self;
    }

    function _inherits(subClass, superClass) {
        if (typeof superClass !== "function" && superClass !== null) {
            throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
        }

        subClass.prototype = Object.create(superClass && superClass.prototype, {
            constructor: {
                value: subClass,
                enumerable: false,
                writable: true,
                configurable: true
            }
        });
        if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
    }

    var Clipboard = function (_Emitter) {
        _inherits(Clipboard, _Emitter);

        /**
         * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
         * @param {Object} options
         */
        function Clipboard(trigger, options) {
            _classCallCheck(this, Clipboard);

            var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this));

            _this.resolveOptions(options);
            _this.listenClick(trigger);
            return _this;
        }

        /**
         * Defines if attributes would be resolved using internal setter functions
         * or custom functions that were passed in the constructor.
         * @param {Object} options
         */


        _createClass(Clipboard, [{
            key: 'resolveOptions',
            value: function resolveOptions() {
                var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

                this.action = typeof options.action === 'function' ? options.action : this.defaultAction;
                this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;
                this.text = typeof options.text === 'function' ? options.text : this.defaultText;
                this.container = _typeof(options.container) === 'object' ? options.container : document.body;
            }
        }, {
            key: 'listenClick',
            value: function listenClick(trigger) {
                var _this2 = this;

                this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) {
                    return _this2.onClick(e);
                });
            }
        }, {
            key: 'onClick',
            value: function onClick(e) {
                var trigger = e.delegateTarget || e.currentTarget;

                if (this.clipboardAction) {
                    this.clipboardAction = null;
                }

                this.clipboardAction = new _clipboardAction2.default({
                    action: this.action(trigger),
                    target: this.target(trigger),
                    text: this.text(trigger),
                    container: this.container,
                    trigger: trigger,
                    emitter: this
                });
            }
        }, {
            key: 'defaultAction',
            value: function defaultAction(trigger) {
                return getAttributeValue('action', trigger);
            }
        }, {
            key: 'defaultTarget',
            value: function defaultTarget(trigger) {
                var selector = getAttributeValue('target', trigger);

                if (selector) {
                    return document.querySelector(selector);
                }
            }
        }, {
            key: 'defaultText',
            value: function defaultText(trigger) {
                return getAttributeValue('text', trigger);
            }
        }, {
            key: 'destroy',
            value: function destroy() {
                this.listener.destroy();

                if (this.clipboardAction) {
                    this.clipboardAction.destroy();
                    this.clipboardAction = null;
                }
            }
        }], [{
            key: 'isSupported',
            value: function isSupported() {
                var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];

                var actions = typeof action === 'string' ? [action] : action;
                var support = !!document.queryCommandSupported;

                actions.forEach(function (action) {
                    support = support && !!document.queryCommandSupported(action);
                });

                return support;
            }
        }]);

        return Clipboard;
    }(_tinyEmitter2.default);

    /**
     * Helper function to retrieve attribute value.
     * @param {String} suffix
     * @param {Element} element
     */
    function getAttributeValue(suffix, element) {
        var attribute = 'data-clipboard-' + suffix;

        if (!element.hasAttribute(attribute)) {
            return;
        }

        return element.getAttribute(attribute);
    }

    module.exports = Clipboard;
});

},{"./clipboard-action":7,"good-listener":4,"tiny-emitter":6}]},{},[8])(8)
});

// Copyright 2023 Ellucian Company L.P. and its affiliates.
// Copyright 2018-2021 Ellucian Company L.P. and its affiliates.
var Ellucian = Ellucian || {};
Ellucian.SelfService = Ellucian.SelfService || {};

Ellucian.SelfService.configurationRepository = (function () {

    var storage = Ellucian.Storage.session;
    return {
        get: function () {

            var ssConfigKey = 'selfServiceConfiguration';
            var cachedData = storage.getItem(ssConfigKey);
            if (cachedData !== null) {
                return Promise.resolve(cachedData);
            } else {
                return new Promise(function (resolve, reject) {
                    $.ajax({
                        url: Ellucian.SelfServiceConfiguration.GetSelfServiceConfigurationAsync,
                        type: 'GET',
                        contentType: jsonContentType
                    })
                        .done(function (data) {
                            if (!account.handleInvalidSessionResponse(data)) {
                                storage.setItem(ssConfigKey, data);
                                resolve(data);
                            }
                            reject(Ellucian.SelfServiceConfiguration.Resources.DataRetrievalErrorMessage);
                        })
                        .fail(function () {
                            reject(Ellucian.SelfServiceConfiguration.Resources.DataRetrievalErrorMessage);
                        })
                        .always(function () { });
                });
            }
        }
    }
})();

// Copyright 2018-2025 Ellucian Company L.P. and its affiliates.

// Register the ESG spinner component
ko.components.register('spinner', {
    require: 'Spinner/_Spinner'
});

// Register the ESG modal spinner component
ko.components.register('modal-spinner', {
    require: 'ModalSpinner/_ModalSpinner'
});

// Register the ESG message component
ko.components.register('message', {
    require: 'Message/_Message'
});

// Register the profile photo component (used by proxy, etc)
ko.components.register('profile-photo', {
    require: 'ProfilePhoto/_ProfilePhoto'
});

// Register the icon component
ko.components.register('icon', {
    require: 'Icon/_Icon'
});

// Register the modal dialog component
ko.components.register('modal-dialog', {
    require: 'ModalDialog/_ModalDialog'
});

// Register the attachment dialog component
ko.components.register('attachment-dialog', {
    require: 'AttachmentDialog/_AttachmentDialog'
});

// Register the badge component
ko.components.register('badge', {
    require: 'Badge/_Badge'
});

// Register the step progress component
ko.components.register('step-progress', {
    require: 'StepProgress/_StepProgress'
});

// Register the status label component
ko.components.register('status-label', {
    require: 'StatusLabel/_StatusLabel'
});

// Register the person collapsible group component
ko.components.register('collapsible-group', {
    require: 'CollapsibleGroup/_CollapsibleGroup'
});

// Register the autocomplete component
ko.components.register("input-autocomplete", {
    require: 'InputAutocomplete/_InputAutocomplete'
});

// Register the autocomplete component
ko.components.register("input-feedback", {
    require: 'InputFeedback/_InputFeedback'
});

// Register the autocomplete component
ko.components.register("required-select", {
    require: 'RequiredSelect/_RequiredSelect'
});

// Register the dropdown component
ko.components.register("dropdown", {
    require: 'Dropdown/_Dropdown'
});

// Register the complex table component
ko.components.register("complex-table", {
    require: 'ComplexTable/_ComplexTable'
});

// Register the email all component
ko.components.register('email-all', {
    require: 'EmailAll/_EmailAll'
});

// Register the Coach Mark component
ko.components.register('coach-mark', {
    require: 'CoachMark/_CoachMark'
});

// Register the Yes No Toggle component
ko.components.register('yes-no-toggle', {
    require: 'YesNoToggle/_YesNoToggle'
});

// Register the Floating Action Button component
ko.components.register('floating-action-button', {
    require: 'FloatingActionButton/_FloatingActionButton'
});
// Register the date picker component
ko.components.register('date-picker', {
    require: 'DatePicker/_DatePicker'
});

// Register the date only picker component
ko.components.register('date-only-picker', {
    require: 'DateOnlyPicker/_DateOnlyPicker'
});

// Register the Coach Mark component
ko.components.register('progress-bar', {
    require: 'ProgressBar/_ProgressBar'
});

// Register the GL Lookup component
ko.components.register('gl-lookup', {
    require: 'GlLookup/_GlLookup'
});

// Register the Approvers component
ko.components.register('approvers', {
    require: 'Approvers/_Approvers'
});

// Register the CalendarPillbox component
ko.components.register('calendar-pillbox', {
    require: 'CalendarPillbox/_CalendarPillbox'
});

// Register the GovernemntId component
ko.components.register('government-id', {
    require: 'GovernmentId/_GovernmentId'
});

// Copyright 2019-2023 Ellucian Company L.P. and its affiliates.
// draggableColumns - To make a table equipped with draggable control on headers to change the positions or using column controls like hide/show the columns
//
// Parameters:
//      - memoryKey                       - (optional) - string   - (recommended)memory key used to save or retrieve the current column order of the table in the session storage
//      - hiddenColumnsMemoryKey          - (optional) - string   - (recommended)memory key used to save or retrieve the hidden columns of the table in the session storage
//      - destinationRightClass           - (optional) - string   - custom class for destination column if on right 
//      - destinationLeftClass            - (optional) - string   - custom class for destination column if on left
//      - dragInProgressClass             - (optional) - string   - custom class for column for which the drag in progress
//      - sourceColumnBeingDraggedClass   - (optional) - string   - custom class for source column being dragged
//      - headerInMotionClass             - (optional) - string   - custom class for header column which is in motion
//      - isAddColumnsModalDialogVisible  - (optional) - string   - flag to show modal dialog for adding the hidden columns
//      - columnControlOptions            - (optional) - object   - column control options
//      - pageChanged                     - (optional) - boolean  - (used with pager component) in/out observable which sets based on page content changes by pager
//      - defaultColumnOrder              - (optional) - string   - (used with pager component) default column order used for reorganizing the page during pager content refresh
//      - dragIconSpanAddOnClass          - (optional) - string   - additional class for drag icon span element  
//      - saveDragColumnPreferences       - (optional) - boolean   - yes/no to invoke save Preferences only when called from FinQ as of now 
// Usage:
//      var defaultTableColumnOrder =["header-id-1", "header-id-2", "header-id-3"];
//      $("#table-id").draggableColumns({ "memoryKey": "table-column-order", "hiddenColumnsMemoryKey": "table-hidden-columns", "defaultColumnOrder": defaultTableColumnOrder});
// Usage with pager:
// Additionally than initializing as above, When a table uses pager component, use 'pageChanged' param from pager component to get the changes from pager 
//and re-invoke draggableColumns from a computed function as follows
/*    
    self.pageContentHasChanged = ko.computed(function () {
        var result = false;
        if (self.PageChanged()) {
        // this is the order in which the actual order of columns the page is rendered
        var defaultTableColumnOrder =["header-id-1", "header-id-2", "header-id-3"];
        $("#table-id").draggableColumns({"memoryKey": "table-column-order", "hiddenColumnsMemoryKey": "table-hidden-columns", "hasPageChanged": PageChanged, "defaultColumnOrder": defaultTableColumnOrder});
        self.PageChanged(false);
        result = true;
        }
        return result;
    });
*/

(function ($) {
    var self = this;

    // jQuery object on which the "draggableColumns" function is invoked.
    self.draggableColumnContext = null;

    // Session storage key
    self.memoryKey = "column-order";

    // Session storage key for hidden columns
    self.hiddenColumnsMemoryKey = "hidden-columns";

    // Destination CSS classes
    self.destinationClass = "draggable-column__destination";
    self.destinationRightClass = "draggable-column__destination--right";
    self.destinationLeftClass = "draggable-column__destination--left";
    self.dragInProgressClass = "draggable-columns--drag-in-progress";
    self.sourceColumnBeingDraggedClass = "draggable-columns__source-column--being-dragged";
    self.headerInMotionClass = "draggable-header__in-motion";
    // CSS selectors
    self.getDestinationClassSelector = function () {
        return "." + self.destinationClass;
    };
    self.getHeaderInMotionClassSelector = function () {
        return "." + self.headerInMotionClass;
    };

    // observable flag for tracking paging changes from pager component
    self.hasPageChanged = ko.observable(false);

    // observable flag for save preference to invoke
    self.saveDragColumnPreferences = ko.observable(false);

    // Array received as the default column order to reset the original column order
    self.defaultColumnOrder = [];

    // Array received as the default column order used to store the current page column order
    self.defaultPageColumnOrder = [];

    // Array tracked based on intermediate column order of data while swapping the data
    self.intermediateDataColumnOrder = [];

    //Any add on class applied to the draggable icon span element
    self.dragIconSpanAddOnClass = "";

    //white space to seperate any add on class
    self.whiteSpace = " ";

    // Variables to keep track of the drag state.
    self.dragStartPosition = 0;
    self.dragEndPosition = 0;

    self.touchEnterElement;
    self.touchMoveTimeout;
    self.isTouchThrottled = false;
    self.postThrottleEvent;

    // An array containing the order of the table columns (value = ID attribute).
    self.columnPositions = [];

    self.columnText = {};

    // Function to save the column order to session storage.
    // The index is the position (zero indexed) and the value is the ID of the DOM element.
    self.saveColumnOrder = function () {
        var jqueryTableElement = $(self.draggableColumnContext);
        jqueryTableElement.find("th").each(function (index) {
            if (this.id != "") {
                self.columnPositions[index] = this.id;
            }
        });

        self.evaluateAlignment();

        Ellucian.Storage.session.setItem(self.memoryKey, self.columnPositions);
        if (self.saveDragColumnPreferences()) {
            self.setDragabbleOrderPreference();
        }

    };

    // Function to retrieve column order from session storage.
    self.loadColumnOrder = function () {
        columnPositionsFromMemory = Ellucian.Storage.session.getItem(self.memoryKey);

        if (columnPositionsFromMemory) {
            self.columnPositions = columnPositionsFromMemory;
            var activeElement;
            var startPosition;
            var destinationPosition

            // Handle the shift of columns
            for (var i = 0; i < self.columnPositions.length; i++) {
                activeElement = $("#" + self.columnPositions[i]).length > 0;
                if (activeElement) {
                    startPosition = $("#" + self.columnPositions[i]).closest('th').prevAll().length + 1;
                    destinationPosition = i + 1;
                    self.shiftColumn(startPosition, destinationPosition);
                }

            }
        }
    };

    // computed function to run based on pagination param changes
    self.refreshDataOnPaging = ko.computed(function () {
        if (self.hasPageChanged()) {
            self.reOrganizeThePage();
            var jqueryTableElement = $(self.draggableColumnContext);
            jqueryTableElement.css("table-layout", "auto");
        }
    });

    // Function to re-invoke column preferences because of paging param changes
    self.reOrganizeThePage = function () {
        self.loadColumnOrderForPaging();
        self.loadHiddenColumns();
    };

    // Function to retrieve column order from session storage(for paging).
    self.loadColumnOrderForPaging = function () {
        columnPositionsFromMemory = Ellucian.Storage.session.getItem(self.memoryKey);
        if (columnPositionsFromMemory) {
            self.columnPositions = columnPositionsFromMemory;
            var activeElement;
            var startPosition;
            var destinationPosition;

            // keep track of intermediate data column position            
            self.intermediateDataColumnOrder = self.defaultColumnOrder;

            // Find out the start and end position from the default column position and the current column positions
            for (var j = 0; j < self.columnPositions.length; j++) {
                activeElement = $("#" + self.columnPositions[j]).length > 0;
                if (activeElement) {
                    for (var i = 0; i < self.intermediateDataColumnOrder.length; i++) {
                        if (self.columnPositions[j] == self.intermediateDataColumnOrder[i]) {
                            startPosition = i + 1;
                            destinationPosition = j + 1;
                            self.shiftColumnExceptHeadersAndFooters(startPosition, destinationPosition);
                        }
                    }
                }
            }
        }
    };

    // shift only tds for pagination support
    self.shiftColumnExceptHeadersAndFooters = function (start, end) {
        var jqueryTableElement = $(self.draggableColumnContext);
        if (start < end) {
            for (var i = start; i < end; i++) {
                var columnToShift = jqueryTableElement.find("tr > td:nth-child(" + i + ")").add(jqueryTableElement.find("tr > th:nth-child(" + i + ")"));
                var secondColumn = jqueryTableElement.find("tr > td:nth-child(" + (i + 1) + ")").add(jqueryTableElement.find("tr > th:nth-child(" + (i + 1) + ")"));
                // Perform the column swap, if the column has td elements                
                if (columnToShift && columnToShift.length > 0) {
                    // swap only the non-table head elements
                    for (var j = 1; j < columnToShift.length; j++) {
                        self.swapDomElement(columnToShift[j], secondColumn[j]);
                    }
                }

                var columnHeaderIdToShift, columnNextHeaderIdToShift, oldDataIndex, newDataIndex;
                // get the current data column id to be shifted
                columnHeaderIdToShift = self.intermediateDataColumnOrder[i - 1];
                // get the next data column id
                columnNextHeaderIdToShift = self.intermediateDataColumnOrder[i];
                // Once swapping is done, immediately store the new positions of the data, by swapping the positions of two column ids
                oldDataIndex = self.intermediateDataColumnOrder.findIndex(function (colId) { return colId == columnHeaderIdToShift });
                newDataIndex = self.intermediateDataColumnOrder.findIndex(function (colId) { return colId == columnNextHeaderIdToShift });
                //interchange the data
                if (oldDataIndex != -1) {
                    self.intermediateDataColumnOrder[oldDataIndex] = columnNextHeaderIdToShift;
                }
                if (newDataIndex != -1) {
                    self.intermediateDataColumnOrder[newDataIndex] = columnHeaderIdToShift;
                }
            }
        }
        else {
            for (var i = start; i > end; i--) {
                var columnToShift = jqueryTableElement.find("tr > td:nth-child(" + i + ")").add(jqueryTableElement.find("tr > th:nth-child(" + i + ")"));
                var secondColumn = jqueryTableElement.find("tr > td:nth-child(" + (i - 1) + ")").add(jqueryTableElement.find("tr > th:nth-child(" + (i - 1) + ")"));
                // Perform the column swap, if the column has td elements
                if (columnToShift && columnToShift.length > 0) {
                    // swap only the non-table head elements- so 0th index is skipped, as pager component doesn't affect column headers
                    for (var j = 1; j < columnToShift.length; j++) {
                        self.swapDomElement(columnToShift[j], secondColumn[j]);
                    }
                }
                var columnHeaderIdToShift, columnNextHeaderIdToShift, oldDataIndex, newDataIndex;
                // get the current data column id to be shifted
                columnHeaderIdToShift = self.intermediateDataColumnOrder[i - 1];
                // get the next data column id
                columnNextHeaderIdToShift = self.intermediateDataColumnOrder[i - 2];
                // Once swapping is done, immediately store the new positions of the data, by swapping the positions of two column ids
                oldDataIndex = self.intermediateDataColumnOrder.findIndex(function (colId) { return colId == columnHeaderIdToShift });
                newDataIndex = self.intermediateDataColumnOrder.findIndex(function (colId) { return colId == columnNextHeaderIdToShift });
                //exchange the data
                if (oldDataIndex != -1) {
                    self.intermediateDataColumnOrder[oldDataIndex] = columnNextHeaderIdToShift;
                }
                if (newDataIndex != -1) {
                    self.intermediateDataColumnOrder[newDataIndex] = columnHeaderIdToShift;
                }
            }
        }
    };

    // TODO: implement server storage?

    // Move column by shifting it left or right (each time through the loop is one column shift of movement).
    // The "start" and "end" arguments are indexed at 1.
    self.shiftColumn = function (start, end) {
        var jqueryTableElement = $(self.draggableColumnContext);
        if (start < end) {
            for (var i = start; i < end; i++) {
                var columnToShift = jqueryTableElement.find("tr > td:nth-child(" + i + ")").add(jqueryTableElement.find("tr > th:nth-child(" + i + ")"));
                var secondColumn = jqueryTableElement.find("tr > td:nth-child(" + (i + 1) + ")").add(jqueryTableElement.find("tr > th:nth-child(" + (i + 1) + ")"));
                for (var j = 0; j < columnToShift.length; j++) {
                    self.swapDomElement(columnToShift[j], secondColumn[j]);
                }
            }
        }
        else {
            for (var i = start; i > end; i--) {
                var columnToShift = jqueryTableElement.find("tr > td:nth-child(" + i + ")").add(jqueryTableElement.find("tr > th:nth-child(" + i + ")"));
                var secondColumn = jqueryTableElement.find("tr > td:nth-child(" + (i - 1) + ")").add(jqueryTableElement.find("tr > th:nth-child(" + (i - 1) + ")"));
                // For each row in the columns, swap their positions
                for (var j = 0; j < columnToShift.length; j++) {
                    self.swapDomElement(columnToShift[j], secondColumn[j]);
                }
            }
        }
    };

    // Swap two sibling DOM elements on the page.
    self.swapDomElement = function (firstElement, secondElement) {
        if (firstElement && secondElement) {
            // Find the parent element of the first element (this will be the TR containing both elements).
            var firstElementParent = firstElement.parentNode;

            // Determine the element that the second element will be inserted in front of.
            var firstElementSibling = firstElement.nextSibling === secondElement ? firstElement : firstElement.nextSibling;

            // Move the first element in front of the second element. This may result in no change. (example: A-B-C-D, where firstElement = B and secondElement = C)
            firstElementParent.insertBefore(firstElement, secondElement);

            // Move the second element in front of the calculated element from above. This may result in no change. (example: A-B-C-D, where firstElement = C and secondElement = B)
            firstElementParent.insertBefore(secondElement, firstElementSibling);
        }
    };

    // When the mouse button is pressed, determine the column that will be moved.
    self.handleMouseDown = function (event) {
        // Validate the context and determine the start position.
        if (event && event.target) {
            var id = event.target.id;
            self.dragStartPosition = $("#" + id).closest("th").prevAll().length + 1;

            // Style the column that will be dragged.
            $('#' + id).closest("th").addClass(self.sourceColumnBeingDraggedClass);

            // Track the state on the table.
            $(self.draggableColumnContext).addClass(self.dragInProgressClass);

            // Determine the coordinates of the cursor and place the "in-motion" header on the the page at that location.
            var initialPageX = event.pageX;
            var initialPageY = event.pageY;
            self.addMovingColumnMarkup($("#" + id).closest("th"), initialPageX, initialPageY);
        }
    };

    // Touch Event - emulate the needed information in order to reuse the Mouse Event handler.
    self.handleTouchStart = function (event) {
        if (event && event.originalEvent && event.originalEvent.changedTouches) {
            var targetElement;
            var tempEvent;
            for (var i = 0; i < event.originalEvent.changedTouches.length; i++) {
                self.handleMouseDown(event.originalEvent.changedTouches[i]);
            }
        }
    };

    // Add markup and listeners for the in-motion header based on the argument (which contains the clicked "th" element).
    self.addMovingColumnMarkup = function (headerElement, x, y) {
        // Create the in-motion header using the text from the header element argument.
        var inMotionDiv = $("<div class='" + self.headerInMotionClass + "'></div>").text(self.columnText[headerElement.attr("id")]);

        // Copy the styles from the header element (IE doesn't support the shorthand versions of "border" and "padding").
        inMotionDiv.css("background-color", headerElement.css("background-color"));
        inMotionDiv.css("outline", headerElement.css("outline"));
        inMotionDiv.css("outline-offset", headerElement.css("outline-offset"));
        inMotionDiv.css("border-left-style", headerElement.css("border-left-style"));
        inMotionDiv.css("border-left-width", headerElement.css("border-left-width"));
        inMotionDiv.css("border-left-color", headerElement.css("border-left-color"));
        inMotionDiv.css("border-right-style", headerElement.css("border-right-style"));
        inMotionDiv.css("border-right-width", headerElement.css("border-right-width"));
        inMotionDiv.css("border-right-color", headerElement.css("border-right-color"));
        inMotionDiv.css("border-top-style", headerElement.css("border-top-style"));
        inMotionDiv.css("border-top-width", headerElement.css("border-top-width"));
        inMotionDiv.css("border-top-color", headerElement.css("border-top-color"));
        inMotionDiv.css("border-bottom-style", headerElement.css("border-bottom-style"));
        inMotionDiv.css("border-bottom-width", headerElement.css("border-bottom-width"));
        inMotionDiv.css("border-bottom-color", headerElement.css("border-bottom-color"));
        inMotionDiv.css("padding-left", headerElement.css("padding-left"));
        inMotionDiv.css("padding-right", headerElement.css("padding-right"));
        inMotionDiv.css("padding-top", headerElement.css("padding-top"));
        inMotionDiv.css("padding-bottom", headerElement.css("padding-bottom"));

        // Add the in-motion div to the DOM, set the initial position, and attach a listener.
        $("body").append(inMotionDiv);
        self.updateInMotionHeaderCoordinates(x, y);
        $(document).on("mousemove", self.movingColumnMouseMoveEvent);
        $(document).on("touchmove", self.movingColumnTouchMoveEvent);
    };

    // Function - Remove the in-motion header markup and event listener.
    self.removeMovingColumnMarkup = function () {
        $(document).off("mousemove", self.movingColumnMouseMoveEvent);
        $(document).off("touchmove", self.movingColumnTouchMoveEvent);
        $(self.getHeaderInMotionClassSelector()).remove();
    };

    // Function - Adjust the position of the active, in-motion header markup based on args.
    self.updateInMotionHeaderCoordinates = function (x, y) {
        $(self.getHeaderInMotionClassSelector()).each(function () {
            var width = $(this).outerWidth() || 0;
            $(this).css("left", (x - (width / 2)) + "px");
            $(this).css("top", (10 + y) + "px");
        });
    }

    // Mouse Event - Reposition the active, in-motion header based on the cursor's position on the page.
    self.movingColumnMouseMoveEvent = function (event) {
        self.updateInMotionHeaderCoordinates(event.pageX, event.pageY);
    };

    // Helper function that calls the repositioning function for the in-motion header element, and emulates the mouse enter and mouse leave events.
    function updateCoordinatesAndEmulatedMouseStates(event) {
        var targetElement;
        if (event && event.originalEvent && event.originalEvent.changedTouches) {
            for (var i = 0; i < event.originalEvent.changedTouches.length; i++) {
                self.updateInMotionHeaderCoordinates(event.originalEvent.changedTouches[i].pageX, event.originalEvent.changedTouches[i].pageY);
                targetElement = $(document.elementFromPoint(event.originalEvent.changedTouches[i].clientX, event.originalEvent.changedTouches[i].clientY));
                if (targetElement !== self.touchEnterElement) {
                    if (self.touchEnterElement && self.touchEnterElement.mouseleave) {
                        self.touchEnterElement.mouseleave();
                    }
                    self.touchEnterElement = targetElement;
                    self.touchEnterElement.mouseenter();
                }
            }
        }
    }

    // Touch Event - Reposition the active, in-motion header based on the touch event position on the page.
    self.movingColumnTouchMoveEvent = function (event) {
        // Always track the latest event to ensure that the final one will run after throttling is complete.
        self.postThrottleEvent = event;
        // If the touch event is not currently being throttled...
        if (!self.isTouchThrottled) {
            // Flip the throttling flag.
            self.isTouchThrottled = true;
            // Update the coordinates.
            updateCoordinatesAndEmulatedMouseStates(event);
            // Set the touchMoveTimout to limit the rate of coordinate updates.
            self.touchMoveTimeout = setTimeout(function unthrottle() {
                // Clear the timeout flag.
                self.isTouchThrottled = false;
                // Update the coordinates with the final event.
                updateCoordinatesAndEmulatedMouseStates(self.postThrottleEvent);
            }, 100);
        }
    };

    // Mouse Event - When the mouse button is released, move the column that was being dragged.
    self.handleMouseUp = function (event) {
        // Validate the context and determine the destination.
        if (event && event.target) {
            var id = event.target.id;
            self.dragEndPosition = $("#" + id).closest("th").prevAll().length + 1;

            // Move the column if the drag was valid.
            if (self.dragStartPosition > 0 && self.dragEndPosition > 0) {
                self.shiftColumn(self.dragStartPosition, self.dragEndPosition);
                self.saveColumnOrder();
            }

        }
        // Remove the "in motion" header.
        self.removeMovingColumnMarkup();

        // Clear out the CSS classes and restore the default start and end variables.
        var jqueryTableElement = $(self.draggableColumnContext);
        jqueryTableElement.removeClass(self.dragInProgressClass);
        jqueryTableElement.find("th").each(function () {
            $(this).removeClass(self.sourceColumnBeingDraggedClass);
            $(this).find(self.getDestinationClassSelector()).each(function removeDestinationIndicators() {
                $(this).remove();
            });
        });

        self.dragStartPosition = self.dragEndPosition = 0;
    };

    // Touch Event - emulate the needed information in order to reuse the Mouse Event handler.
    self.handleTouchEnd = function (event) {
        if (event && event.originalEvent && event.originalEvent.changedTouches) {
            var targetElement;
            var tempEvent;
            for (var i = 0; i < event.originalEvent.changedTouches.length; i++) {
                // The Touch Event target element id is always the original DOM element that the Touch Event was started on; using elementFromPoint to determine the "destination" DOM element.
                targetElement = document.elementFromPoint(event.originalEvent.changedTouches[i].clientX, event.originalEvent.changedTouches[i].clientY);
                if (targetElement.classList.contains("draggable-header")) {
                    // Construct an emulated Mouse Event with the neccessary properties.
                    tempEvent = {
                        target: {
                            id: targetElement.id
                        }
                    };

                    self.handleMouseUp(tempEvent);
                }
                else {
                    self.handleMouseUp(null);
                }
            }
        }
    };

    // Mouse Event - handle moving into a potential destination TH element.
    self.handleMouseEnter = function (event) {
        if (event && event.currentTarget && self.dragStartPosition > 0) {
            // Determine the potential end position given the element that the cursor entered.
            var potentialEndPosition = $(event.currentTarget).closest("th").prevAll().length + 1;

            if (potentialEndPosition) {
                var id = event.currentTarget.id;
                var el = $("#" + id).closest("th");

                // Determine the height of the element so the indicator can be appropriately sized. This can't be done in the CSS file due to the dynamic nature of header heights.
                var height = $(el).outerHeight();
                if (potentialEndPosition > self.dragStartPosition) {
                    // If moving to the right, add a div with the corresponding destination CSS classes.
                    $(el).append($("<div id='" + id + "-position-indicator' style='height: " + height + "px;' class='" + self.destinationClass + " " + self.destinationRightClass + "'></div>"));
                }
                else if (potentialEndPosition < self.dragStartPosition) {
                    // If moving to the left, add a div with the corresponding destination CSS classes.
                    $(el).append($("<div id='" + id + "-position-indicator' style='height: " + height + "px' class='" + self.destinationClass + " " + self.destinationLeftClass + "'></div>"));
                }
            }
        }
    };

    // Mouse Event - handle moving out of a potential destination TH element.
    self.handleMouseLeave = function (event) {
        if (event && event.currentTarget) {
            var id = event.currentTarget.id;
            var el = $("#" + id).closest("th");
            // Remove destination indicator DIV (typically there will only be one).
            $(el).find(self.getDestinationClassSelector()).each(function removeDestinationIndicators() {
                $(this).remove();
            });
        }
    };

    // Mouse Event - dropping outside of a valid drop location should cancel the move.
    self.handleInvalidMouseUp = function () {
        self.dragStartPosition = self.dragEndPosition = 0;
        self.handleMouseUp(null);
    };

    // Mouse Event - dropping outside of a valid drop location should cancel the move.
    self.handleInvalidTouchEnd = function () {
        self.dragStartPosition = self.dragEndPosition = 0;
        self.handleTouchEnd(null);
    };

    // Column control -----------------------------------

    // Observable Array - The list of columns that are not shown.
    self.hiddenColumns = ko.observableArray([]);

    self.saveHiddenColumns = function (newValue) {
        Ellucian.Storage.session.setItem(self.hiddenColumnsMemoryKey, newValue);
        if (self.saveDragColumnPreferences()) {
            self.setDragabbleHiddenPreference();
        }
    };

    self.loadHiddenColumns = function () {
        var hiddenColumnsFromMemory = Ellucian.Storage.session.getItem(self.hiddenColumnsMemoryKey);
        if (hiddenColumnsFromMemory) {
            var jqueryTableElement = $(self.draggableColumnContext);
            self.hiddenColumns(hiddenColumnsFromMemory);
            for (var i = 0; i < hiddenColumnsFromMemory.length; i++) {
                //$("#" + hiddenColumnsFromMemory[i].id).addClass("not-shown");
                var columnIndexToHide = $("#" + hiddenColumnsFromMemory[i].id).closest("th").prevAll().length + 1;
                var columnToHide = jqueryTableElement.find("tr > td:nth-child(" + columnIndexToHide + ")").add(jqueryTableElement.find("tr > th:nth-child(" + columnIndexToHide + ")"));
                $(columnToHide).addClass("not-shown");
                $(columnToHide).removeClass("shown");
            }
        }
    };

    self.hiddenColumns.subscribe(self.saveHiddenColumns);

    self.sortedHiddenColumns = ko.computed(function () {
        var hiddenColumns = self.hiddenColumns();
        var results = [];
        var columnId;
        for (var i = 0; i < self.columnPositions.length; i++) {
            columnId = self.columnPositions[i];
            var matchingHiddenColumn = ko.utils.arrayFirstDeprecated(hiddenColumns, function (hiddenColumn) {
                return hiddenColumn.id === columnId;
            });
            if (matchingHiddenColumn) {
                results.push(matchingHiddenColumn);
            }
        }
        return results;
    });

    // The list of checked checkbox-type input fields (from the modal dialog).
    self.selectedHiddenColumns = ko.observableArray([]);

    // Observable - controls the visibility of the modal dialog.
    self.isAddColumnsDialogVisible = ko.observable(false);

    self.isAllColumnsMessageVisible = ko.computed(function () {
        return self.sortedHiddenColumns().length <= 0;
    });

    // Observable - controls the desired destination of a column.
    self.moveColumnToDestination = ko.observable("");

    // String - Hide column button text.
    self.hideColumnText = Ellucian.DraggableColumns.columnControlHideColumn;

    // String - Add columns button text.
    self.addColumnsText = Ellucian.DraggableColumns.columnControlAddColumns;

    // String - Move column right button text.
    self.moveColumnRightText = Ellucian.DraggableColumns.columnControlMoveColumnRight;

    // String - Move column left button text.
    self.moveColumnLeftText = Ellucian.DraggableColumns.columnControlMoveColumnLeft;

    // String - Restore Columns to Default text.
    self.restoreDefaultColumnText = Ellucian.DraggableColumns.columnControlRestoreDefault;

    // String - Modal dialog header text.
    self.addColumnsDialogTitle = Ellucian.DraggableColumns.columnControlAddColumnsDialogTitle;

    // String - Modal dialog toggle button aria-label text.
    self.toggleContextMenuAriaLabel = Ellucian.DraggableColumns.columnControlToggleContextMenuAria;

    self.allColumnsVisible = Ellucian.DraggableColumns.columnControlAllColumnsVisible;

    // Function - hides all column control context menus except for the one specified as the argument.
    //            The context menu specified (using 0-based index) as the argument toggles its visibility.
    self.hideAllContextMenusToggleIndex = function (index) {
        $(".draggable-header__context-menu").each(function hideEachNonClickedMenu(i) {
            if (i !== index) {
                $("#column-options-menu-" + i).removeClass("esg-is-active");
            }
            else {
                $("#column-options-menu-" + i).toggleClass("esg-is-active");
            }
        });

        // find the table headers, and remove the 'active-column-header' class on every
        // one except the table header that has been clicked on. 
        var jqueryTableElement = $(self.draggableColumnContext);
        jqueryTableElement.find("tr > th").each(function (i) {
            if (i !== index) {
                jqueryTableElement.find(".draggable-header-" + i).removeClass("active-column-header");
            }
            else {
                jqueryTableElement.find(".draggable-header-" + i).toggleClass("active-column-header");
            }
        }
        )
    };

    // Function - click event handler for each TH to toggle the context menu.
    self.toggleContextMenu = function (event) {
        if (event) {
            self.hideAllContextMenusToggleIndex(event.data);
        }
        self.moveColumnToDestination("");

        return false;
    };

    // find all of the table headers with a class of 'shown' and for each one
    // find the child element with the draggable-header__menu-wrapper class, and then
    // find the child element with the draggable-header__context-menu class, and make the 
    // first one left aligned and rest right aligned.
    self.evaluateAlignment = function () {
        var jqueryTableElement = $(self.draggableColumnContext);

        jqueryTableElement.find("tr > th.shown").each(function (index) {
            var menuWrapper = $(this).find(".draggable-header__menu-wrapper");
            if (menuWrapper) {
                var contextMenu = menuWrapper.find(".draggable-header__context-menu");
                if (contextMenu) {
                    if (index === 0) {
                        menuWrapper.removeClass("draggable-header__menu-wrapper--right-aligned");
                        menuWrapper.addClass("draggable-header__menu-wrapper--left-aligned");

                        contextMenu.removeClass("draggable-header__context-menu--right-aligned");
                        contextMenu.addClass("draggable-header__context-menu--left-aligned");
                    }
                    else {
                        menuWrapper.removeClass("draggable-header__menu-wrapper--left-aligned");
                        menuWrapper.addClass("draggable-header__menu-wrapper--right-aligned");

                        contextMenu.removeClass("draggable-header__context-menu--left-aligned");
                        contextMenu.addClass("draggable-header__context-menu--right-aligned");
                    }
                }
            }
        });
    };

    // find all table header element with a class of 'shown'. If there is more than one, show
    // all of the elements with the class 'show-column-control-option'. If there is only one, 
    // hide all of the elements with the class 'show-column-control-option'.
    self.updateVisibleColumnControlOptions = function () {
        var jqueryTableElement = $(self.draggableColumnContext);
        var count = 0;
        count = jqueryTableElement.find("tr > th.shown").length;
        if (count > 1) {
            // .show;
            $(".show-column-control-option").show();
        } else {
            // Hide the un-necessary column control options
            $(".show-column-control-option").hide();
            // .hide;
        }
        return count;
    };

    // Function - click event handler for hiding column.
    self.hideColumn = function (event) {
        if (event) {
            var jqueryTableElement = $(self.draggableColumnContext);
            var headerIndex = $("#" + event.data).closest("th").prevAll().length + 1;

            var otherColumnCounter = $("#" + event.data).closest("th").prevAll(":not(.not-shown)").length + $("#" + event.data).closest("th").nextAll(":not(.not-shown)").length;

            if (otherColumnCounter > 0) {
                // Add the CSS class that hides each cell in this column.
                var columnToHide = jqueryTableElement.find("tr > td:nth-child(" + headerIndex + ")").add(jqueryTableElement.find("tr > th:nth-child(" + headerIndex + ")"));
                $(columnToHide).addClass("not-shown");
                $(columnToHide).removeClass("shown");
                // Track the column's visibility.
                var closestTh = $("#" + event.data).closest('th');
                var closestThId = $(closestTh).attr("id");
                self.hiddenColumns.push({ id: closestThId, text: self.columnText[closestThId] });
            }

            self.evaluateAlignment();

            self.updateVisibleColumnControlOptions();

            // Close the context menu.
            self.toggleContextMenu(event);
        }

        return false;
    };

    // Function - click event handler to show the modal dialog.
    self.showAddColumnsDialog = function () {
        self.isAddColumnsDialogVisible(true);
    };

    // Function - hides the modal dialog for adding budget worksheet columns.
    self.hideAddColumnsDialog = function () {
        // Clear any checkbox choices.
        self.selectedHiddenColumns.removeAll();
        // Hide the modal dialog.
        self.isAddColumnsDialogVisible(false);
    };

    // Function - click event handler that resets the checkboxes and closes the modal dialog.
    self.cancelAddColumns = function () {
        // Hide the "Add columns modal dialog".
        self.hideAddColumnsDialog();

        return false;
    };

    // Function - shows the column that has a TH as the parent of the selector specified as the argument.
    self.showColumn = function (columnHeaderSelector) {
        var jqueryTableElement = $(self.draggableColumnContext);
        // CSS uses 1-based index.
        var headerIndex = $(columnHeaderSelector).closest("th").prevAll().length + 1;
        // Remove the CSS class that hides each of the cells in this column.
        var columnToShow = jqueryTableElement.find("tr > td:nth-child(" + headerIndex + ")").add(jqueryTableElement.find("tr > th:nth-child(" + headerIndex + ")"));
        $(columnToShow).each(function () {
            $(this).removeClass("not-shown");
            $(this).addClass("shown");
        });

        return false;
    };

    // Function - click event handler that shows the selected columns and closes the modal dialog.
    self.updateAddColumns = function () {
        for (var i = 0; i < self.selectedHiddenColumns().length; i++) {
            // Show the selected columns in the table.
            self.showColumn("#" + self.selectedHiddenColumns()[i]);
        }
        // Remove the column from the list of hidden columns.
        self.hiddenColumns.remove(function (column) {
            return column && selectedHiddenColumns().indexOf(column.id) > -1;
        });

        self.evaluateAlignment();

        self.updateVisibleColumnControlOptions();

        // Hide the "Add columns modal dialog".
        self.hideAddColumnsDialog();

        return false;
    };

    // Function - move the specified column to the right.
    self.moveColumnRight = function (selector) {
        // Determine start and end positions.
        var start = $(selector).closest("th").prevAll().length + 1;     // Starting element provided by selector; get 1-based index.
        var nextVisibleColumns = $(selector).closest("th").nextAll(":not(.not-shown)");
        if (nextVisibleColumns.length > 0) {
            var destinationElement = nextVisibleColumns[0];      // Determine destination element based on displayed columns.
            var end = $(destinationElement).closest("th").prevAll().length + 1;   // Ending element index determination.

            // Shift the column to the right.
            self.shiftColumn(start, end);
            self.saveColumnOrder();
        }
    };

    // Function - click event handler that moves the column to the right.
    self.moveThisColumnToTheRight = function (event) {
        if (event) {
            self.moveColumnRight("#" + event.data);
        }
        // Close the context menu.
        self.toggleContextMenu(event);

        return false;
    };

    // Function - move the specified column to the left.
    self.moveColumnLeft = function (selector) {
        // Determine start and end positions.
        var start = $(selector).closest("th").prevAll().length + 1;       // Starting element provided by selector; get 1-based index.
        var previousVisibleColumns = $(selector).closest("th").prevAll(":not(.not-shown)");
        if (previousVisibleColumns.length > 0) {
            var destinationElement = previousVisibleColumns[0];      // Determine destination element based on displayed columns.
            var end = $(destinationElement).closest("th").prevAll().length + 1;   // Ending element index determination.

            // Shift the column to the left.
            self.shiftColumn(start, end);
            self.saveColumnOrder();
        }
    };

    // Function - click event handler that moves the column to the left.
    self.moveThisColumnToTheLeft = function (event) {
        if (event) {
            self.moveColumnLeft("#" + event.data);
        }
        // Close the context menu.
        self.toggleContextMenu(event);

        return false;
    };


    self.restoreDefaultColumnOrder = function () {
        self.restoreDefaultColumnOrderAll();
        self.restoreHiddenColumns();
        Ellucian.Storage.session.setItem(self.memoryKey, self.defaultPageColumnOrder);
        Ellucian.Storage.session.setItem(self.hiddenColumnsMemoryKey, null);
        if (self.saveDragColumnPreferences()) {
            setDragabbleOrderPreference();
            setDragabbleHiddenPreference();
        }
    };

    self.restoreHiddenColumns = function () {
        for (var i = 0; i < self.hiddenColumns().length; i++) {
            // Show the selected columns in the table.
            self.showColumn("#" + self.hiddenColumns()[i].id);
        }
        // Remove the column from the list of hidden columns.
        self.hiddenColumns([]);

        self.evaluateAlignment();
        self.updateVisibleColumnControlOptions();
    };

    // Function to retrieve column order from session storage(for paging).
    self.restoreDefaultColumnOrderAll = function () {
        if (self.defaultPageColumnOrder) {
            self.columnPositions = self.defaultPageColumnOrder.slice();
            var activeElement;
            var startPosition;
            var destinationPosition;

            // Handle the shift of columns
            for (var i = 0; i < self.columnPositions.length; i++) {
                activeElement = $("#" + self.columnPositions[i]).length > 0;
                if (activeElement) {
                    startPosition = $("#" + self.columnPositions[i]).closest('th').prevAll().length + 1;
                    destinationPosition = i + 1;
                    self.shiftColumn(startPosition, destinationPosition);
                }

            }
        }
    };

    // used Called on click of save preference Button, currently its enabled always we have to put condition on when this button should get enabled and disabled
    self.setDragabbleOrderPreference = function () {
        // take memory(session) value and call setDraggableViewPreference to set Preferences for column position
        var dragabbleColumnPreferenceFromMemory = memory.getItem(self.memoryKey);
        self.setDraggableColumnOrderPreference(dragabbleColumnPreferenceFromMemory);

    }

    // used Called on click of save preference Button, currently its enabled always we have to put condition on when this button should get enabled and disabled
    self.setDragabbleHiddenPreference = function () {

        // take memory(session) value and call setDraggableViewPreference to set Preferences for column position  
        var hiddenColumnsFromMemory = memory.getItem(self.hiddenColumnsMemoryKey);
        self.setDraggableHiddenColumnPreference(hiddenColumnsFromMemory);

    }


    //set preferences for column order Preference
    self.setDraggableColumnOrderPreference = function (columnPosition) {
        var setPreferenceFailedNotification = {
            message: Ellucian.ColleagueFinance.setPreferenceFailedMessage,
            type: "error"
        };

        $.ajax({
            url: Ellucian.ColleagueFinance.setSetDraggablePreferenceUrl,
            type: "POST",
            data: JSON.stringify(columnPosition),
            contentType: jsonContentType,
            dataType: "json",
            beforeSend: function () {
                // Remove the error notification.
                $('#notificationHost').notificationCenter('removeNotification', setPreferenceFailedNotification);
            },
            success: function () { },
            error: function (jqXHR, textStatus, errorThrown) {
                $('#notificationHost').notificationCenter('addNotification', setPreferenceFailedNotification);
            },
            complete: function () { }
        });
    }

    //set preferences for hidden column 
    self.setDraggableHiddenColumnPreference = function (hiddenColumn) {
        var setPreferenceFailedNotification = {
            message: Ellucian.ColleagueFinance.setPreferenceFailedMessage,
            type: "error"
        };

        $.ajax({
            url: Ellucian.ColleagueFinance.setDraggableHiddenColumnPreferenceUrl,
            type: "POST",
            data: JSON.stringify(hiddenColumn),
            contentType: jsonContentType,
            dataType: "json",
            beforeSend: function () {
                // Remove the error notification.
                $('#notificationHost').notificationCenter('removeNotification', setPreferenceFailedNotification);
            },
            success: function () { },
            error: function (jqXHR, textStatus, errorThrown) {
                $('#notificationHost').notificationCenter('addNotification', setPreferenceFailedNotification);
            },
            complete: function () { }
        });
    }


    // Array of Objects - holds the button view-model data (for the modal dialog).
    self.addColumnsDialogButtons = [
        {
            title: Ellucian.DraggableColumns.columnControlAddColumnsDialogCancel,
            id: "add-columns-cancel-button",
            callback: self.cancelAddColumns,
            isPrimary: false
        },
        {
            title: Ellucian.DraggableColumns.columnControlAddColumnsDialogDone,
            id: "add-columns-done-button",
            callback: self.updateAddColumns,
            isPrimary: true
        }
    ];

    // End column control --------------------------------------

    self.useColumnControl = true;

    // The jQuery function to enable draggable columns on a table.
    $.fn.draggableColumns = function (params) {
        // Use any provided parameters.
        if (params) {
            if (params.memoryKey) {
                self.memoryKey = params.memoryKey;
            }
            if (params.hiddenColumnsMemoryKey) {
                self.hiddenColumnsMemoryKey = params.hiddenColumnsMemoryKey;
            }
            if (params.destinationClass) {
                self.destinationClass = params.destinationClass;
            }
            if (params.destinationRightClass) {
                self.destinationRightClass = params.destinationRightClass;
            }
            if (params.destinationLeftClass) {
                self.destinationLeftClass = params.destinationLeftClass;
            }
            if (params.dragInProgressClass) {
                self.dragInProgressClass = params.dragInProgressClass;
            }
            if (params.sourceColumnBeingDraggedClass) {
                self.sourceColumnBeingDraggedClass = params.sourceColumnBeingDraggedClass;
            }
            if (params.headerInMotionClass) {
                self.headerInMotionClass = params.headerInMotionClass;
            }
            if (params.isAddColumnsModalDialogVisible && ko.isObservable(params.isAddColumnsModalDialogVisible)) {
                self.isAddColumnsDialogVisible = params.isAddColumnsModalDialogVisible;
            }
            if (params.columnControlOptions) {
                // If column control options are provided, override the default values.
                var options = ko.unwrap(params.columnControlOptions);
                self.toggleContextMenuAriaLabel = ko.unwrap(options.toggleContextMenuAriaLabel || self.toggleContextMenuAriaLabel);
                self.hideColumnText = ko.unwrap(options.hideColumnText || self.hideColumnText);
                self.addColumnsText = ko.unwrap(options.addColumnsText || self.addColumnsText);
                self.moveColumnRightText = ko.unwrap(options.moveColumnRightText || self.moveColumnRightText);
                self.moveColumnLeftText = ko.unwrap(options.moveColumnLeftText || self.moveColumnLeftText);
                self.addColumnsDialogTitle = ko.unwrap(options.addColumnsDialogTitle || self.addColumnsDialogTitle);
                self.allColumnsVisible = ko.unwrap(options.allColumnsVisible || self.allColumnsVisible);
                self.addColumnsDialogButtons[0].title = ko.unwrap(options.addDialogCancelButtonText || self.addColumnsDialogButtons[0].title);
                self.addColumnsDialogButtons[1].title = ko.unwrap(options.addDialogDoneButtonText || self.addColumnsDialogButtons[1].title);
                self.restoreDefaultColumnText = ko.unwrap(options.restoreDefaultColumnText || self.restoreDefaultColumnText);
            }
            // when the pager is involved, detect the pager changes inorder to refresh the order of data
            if (params.defaultColumnOrder) {
                //set the default column order for reset by copying values
                self.defaultColumnOrder = params.defaultColumnOrder.slice();
                self.defaultPageColumnOrder = params.defaultColumnOrder.slice();
            }
            // if saveDragColumnPreferences is yes call save preference 
            if (params.saveDragColumnPreferences) {
                self.saveDragColumnPreferences(ko.unwrap(params.saveDragColumnPreferences) || false);
                self.saveDragColumnPreferences.notifySubscribers();
            }
            else {
                self.saveDragColumnPreferences(false);
            }

            if (params.hasPageChanged) {
                self.hasPageChanged(ko.unwrap(params.hasPageChanged) || false);
                if (self.hasPageChanged())
                    self.hasPageChanged.notifySubscribers();
            }
            else {
                self.hasPageChanged(false);
            }
            if (params.dragIconSpanAddOnClass) {
                self.dragIconSpanAddOnClass = self.whiteSpace + params.dragIconSpanAddOnClass;
            }
        }

        // Determine the jQuery table element that is being modified.
        self.draggableColumnContext = this;
        var jqueryTableElement = $(self.draggableColumnContext);

        // Retrieve column order from browser memory.
        if (!self.hasPageChanged()) {
            self.loadColumnOrder();
            self.loadHiddenColumns();

            // Ensure that the column order is initialized.
            if (self.columnPositions.length === 0) {
                jqueryTableElement.find("th").each(function (index) {
                    if (this.id != "") {
                        self.columnPositions[index] = this.id;
                    }
                });
            }


            // Remove pre-existing column text values.
            self.columnText = {};

            // Remove pre-existing modal dialog markup.
            $("#add-columns-dialog").remove();

            if (self.useColumnControl) {
                // Create dynamic modal dialog markup and add it to the DOM.
                $("body").append(
                    $("<modal-dialog id='add-columns-dialog' params='isVisible: isAddColumnsDialogVisible, title: addColumnsDialogTitle, buttons: addColumnsDialogButtons, defaultCloseOff: true, content: \"\" '><message params='isVisible: isAllColumnsMessageVisible, message: allColumnsVisible, type: \"info\"'></message><div data-bind='foreach: { data: sortedHiddenColumns, as: \"hiddenColumn\" }'><div class='esg-checkbox'><input type='checkbox' data-bind='value: hiddenColumn.id, checked: selectedHiddenColumns, attr: { \"id\": \"add-column-checkbox-\" + $index() }' /><label data-bind='text: hiddenColumn.text, attr: { for: \"add-column-checkbox-\" + $index(), \"id\": \"add-column-label-\" + $index() }'></label></div></div></modal-dialog>")
                );
                // Apply knockout bindings using the current context.
                ko.applyBindings(self, document.getElementById("add-columns-dialog"));
            }

            // For each TH element...Give the draggable handle to th under thead column
            jqueryTableElement.find('thead > tr > th').each(function (i) {

                var thisId = $(this).attr("id");
                // Store the text of the TH element.
                self.columnText[thisId] = ($(this).text());

                // Add "draggable-header" CSS class to the TH element.
                $(this).addClass("draggable-header");
                $(this).addClass("draggable-header-" + i);

                if ($(this).attr("class").indexOf('not-shown') < 0) {
                    $(this).addClass("shown");
                }

                // Remove pre-existing event handlers.
                $(this).off("mousedown mouseup mouseenter mouseleave touchstart touchend");
                $(this).off("click", "", self.toggleContextMenu);
                $(this).off("click", "#hide-column-button-li-" + i, self.hideColumn);
                $(this).off("click", "#add-columns-button-li-" + i, self.showAddColumnsDialog);
                $(this).off("click", "#move-column-right-button-li-" + i, self.moveThisColumnToTheRight);
                $(this).off("click", "#move-column-left-button-li-" + i, self.moveThisColumnToTheLeft);
                $(this).off("click", "#restore-columns-defaults-li-" + i, self.restoreDefaultColumnOrder);

                // Add bind events to the TH element (for drag/drop functionality).
                $(this).on("mousedown", self.handleMouseDown);
                $(this).on("mouseup", self.handleMouseUp);
                $(this).on("touchstart", self.handleTouchStart);
                $(this).on("touchend", self.handleTouchEnd);
                $(this).on("mouseenter", self.handleMouseEnter);
                $(this).on("mouseleave", self.handleMouseLeave);


                // Remove dynamic markup (eg: re-applied draggableColumns)...
                // Remove any pre-existing drag handle icons.
                $(this).find("[id^='drag-handle-span-']").remove();
                // Remove any pre-existing context menu buttons.
                $(this).find("button").remove();
                // Remove any pre-existing context menu content.
                $(this).find(".draggable-header__menu-wrapper").remove();

                // Add dynamic markup...
                // Include the drag-dots icon.
                $(this).prepend($("<span id='drag-handle-span-" + i + "' class='draggable-header esg-icon__container float-left'><svg id='drag-handle-svg-" + i + "' class='draggable-header esg-icon esg-icon--neutral esg-icon--xsmall" + self.dragIconSpanAddOnClass + "'><use id='drag-handle-use-" + i + "' class='draggable-header' xlink:href='#icon-drag-dots'></use></svg></span>"));

                if (self.useColumnControl) {
                    // Include button for keyboard navigation.
                    $(this).append($("<button id='draggable-columns-context-menu-" + i + "' class='offScreen' type='button' onfocus='draggableColumnFocusEvent(\"" + thisId + "\")' onblur='draggableColumnBlurEvent(\"" + thisId + "\")'></button>").text(self.toggleContextMenuAriaLabel.format($(this).text())));
                    // Include context menu markup.
                    $(this)
                        .append(
                            $("<div class='draggable-header__menu-wrapper'>")
                                .append(
                                    $("<div id='column-options-menu-" + i + "' class='draggable-header__context-menu'>")
                                        .append(
                                            $("<ul class='esg-list-group'>")
                                                .append(
                                                    $("<li id='hide-column-button-li-" + i + "' class='esg-list-group__item show-column-control-option'>")
                                                        .append(
                                                            $("<button id='hide-column-button-" + i + "' type='button'>").text(self.hideColumnText)
                                                        )
                                                )
                                                .append(
                                                    $("<li id='add-columns-button-li-" + i + "' class='esg-list-group__item'>")
                                                        .append(
                                                            $("<button id='add-columns-button-" + i + "' type='button'>").text(self.addColumnsText)
                                                        )
                                                )
                                                .append(
                                                    $("<li id='move-column-right-button-li-" + i + "' class='esg-list-group__item show-column-control-option'>")
                                                        .append(
                                                            $("<button id='move-column-right-button-" + i + "' type='button'>").text(self.moveColumnRightText)
                                                        )
                                                )
                                                .append(
                                                    $("<li id='move-column-left-button-li-" + i + "' class='esg-list-group__item show-column-control-option'>")
                                                        .append(
                                                            $("<button id='move-column-left-button-" + i + "' type='button'>").text(self.moveColumnLeftText)
                                                        )
                                                )
                                                .append(
                                                    $("<li id='restore-columns-defaults-li-" + i + "' class='esg-list-group__item show-column-control-option'>")
                                                        .append(
                                                            $("<button id='restore-columns-defaults-button-" + i + "' type='button'>").text(self.restoreDefaultColumnText)
                                                        )
                                                )
                                        )
                                )
                        );

                    // Context menu click event bindings...
                    // Context menu toggle button (event attached to TH/parent element).
                    $(this).on("click", "", i, self.toggleContextMenu);
                    // Hide column button (event attached to LI/parent element).
                    $(this).on("click", "#hide-column-button-li-" + i, thisId, self.hideColumn);
                    // Add columns button (event attached to LI/parent element).
                    $(this).on("click", "#add-columns-button-li-" + i, thisId, self.showAddColumnsDialog);
                    // Move column right button (event attached to LI/parent element).
                    $(this).on("click", "#move-column-right-button-li-" + i, thisId, self.moveThisColumnToTheRight);
                    // Move column left button (event attached to LI/parent element).
                    $(this).on("click", "#move-column-left-button-li-" + i, thisId, self.moveThisColumnToTheLeft);
                    // Move column left button (event attached to LI/parent element).
                    $(this).on("click", "#restore-columns-defaults-li-" + i, thisId, self.restoreDefaultColumnOrder);


                    var menuWrapper = $(this).find(".draggable-header__menu-wrapper");
                    if (menuWrapper) {
                        var contextMenu = menuWrapper.find(".draggable-header__context-menu");
                        if (contextMenu) {
                            if (i === 0) {
                                menuWrapper.removeClass("draggable-header__menu-wrapper--right-aligned");
                                menuWrapper.addClass("draggable-header__menu-wrapper--left-aligned");

                                contextMenu.removeClass("draggable-header__context-menu--right-aligned");
                                contextMenu.addClass("draggable-header__context-menu--left-aligned");
                            }
                            else {
                                menuWrapper.removeClass("draggable-header__menu-wrapper--left-aligned");
                                menuWrapper.addClass("draggable-header__menu-wrapper--right-aligned");

                                contextMenu.removeClass("draggable-header__context-menu--left-aligned");
                                contextMenu.addClass("draggable-header__context-menu--right-aligned");
                            }
                        }
                    }
                }

                self.updateVisibleColumnControlOptions();

            });

            // Remove pre-existing event handlers.
            $(document).off("mouseup", self.handleInvalidMouseUp);
            $(document).off("touchend", self.handleInvalidTouchEnd);
            $(document).off("click", self.hideAllContextMenusToggleIndex);

            // Handle event where user releases the mouse button outside of the valid drop locations.
            $(document).on("mouseup", self.handleInvalidMouseUp);
            $(document).on("touchend", self.handleInvalidTouchEnd);

            if (self.useColumnControl) {
                // Hide context menus event.
                $(document).on("click", self.hideAllContextMenusToggleIndex);
            }
        }
    };
})(jQuery);

function draggableColumnFocusEvent(elementId) {
    $("#" + elementId).addClass("active");
}

function draggableColumnBlurEvent(elementId) {
    $("#" + elementId).removeClass("active");
}
// Copyright 2023 Ellucian Company L.P. and its affiliates.
//  Provides a globally-scoped incremental string value
//  example: use in the Id of repeated DOM elements to assure uniqueness

window.Ellucian = window.Ellucian || {};

window.Ellucian.indexCounter = window.Ellucian.indexCounter || {
    value: 0,
    next: function () {
        window.Ellucian.indexCounter.value++;
        return window.Ellucian.indexCounter.value.toString();
    }
}

