From 96b1512e24d88a37369482e14c9ca2ad407372f0 Mon Sep 17 00:00:00 2001 From: iziram Date: Mon, 17 Apr 2023 15:44:55 +0200 Subject: [PATCH] Assiduites : Front End --- app/__init__.py | 4 + app/scodoc/html_sidebar.py | 2 +- app/scodoc/sco_abs.py | 41 + app/scodoc/sco_formsemestre_status.py | 8 +- app/scodoc/sco_photos.py | 4 +- app/static/css/assiduites.css | 475 +++ app/static/icons/absent.svg | 11 + app/static/icons/present.svg | 13 + app/static/icons/retard.svg | 12 + app/static/js/assiduites.js | 1792 +++++++++ app/static/libjs/moment-timezone.js | 1597 ++++++++ app/static/libjs/moment.new.min.js | 3309 +++++++++++++++++ app/templates/assiduites/alert.j2 | 156 + app/templates/assiduites/minitimeline.j2 | 105 + .../assiduites/moduleimpl_dynamic_selector.j2 | 113 + .../assiduites/moduleimpl_selector.j2 | 14 + app/templates/assiduites/prompt.j2 | 211 ++ .../assiduites/signal_assiduites_etud.j2 | 99 + .../assiduites/signal_assiduites_group.j2 | 76 + app/templates/assiduites/timeline.j2 | 212 ++ app/templates/assiduites/toast.j2 | 88 + app/templates/sidebar.j2 | 2 +- app/views/__init__.py | 2 + app/views/assiduites.py | 377 ++ 24 files changed, 8715 insertions(+), 8 deletions(-) mode change 100644 => 100755 app/__init__.py mode change 100644 => 100755 app/scodoc/html_sidebar.py mode change 100644 => 100755 app/scodoc/sco_abs.py mode change 100644 => 100755 app/scodoc/sco_formsemestre_status.py mode change 100644 => 100755 app/scodoc/sco_photos.py create mode 100644 app/static/css/assiduites.css create mode 100755 app/static/icons/absent.svg create mode 100755 app/static/icons/present.svg create mode 100755 app/static/icons/retard.svg create mode 100644 app/static/js/assiduites.js create mode 100644 app/static/libjs/moment-timezone.js create mode 100644 app/static/libjs/moment.new.min.js create mode 100644 app/templates/assiduites/alert.j2 create mode 100644 app/templates/assiduites/minitimeline.j2 create mode 100644 app/templates/assiduites/moduleimpl_dynamic_selector.j2 create mode 100644 app/templates/assiduites/moduleimpl_selector.j2 create mode 100644 app/templates/assiduites/prompt.j2 create mode 100644 app/templates/assiduites/signal_assiduites_etud.j2 create mode 100644 app/templates/assiduites/signal_assiduites_group.j2 create mode 100644 app/templates/assiduites/timeline.j2 create mode 100644 app/templates/assiduites/toast.j2 mode change 100644 => 100755 app/templates/sidebar.j2 create mode 100644 app/views/assiduites.py diff --git a/app/__init__.py b/app/__init__.py old mode 100644 new mode 100755 index c2b78959..86646eb4 --- a/app/__init__.py +++ b/app/__init__.py @@ -322,6 +322,7 @@ def create_app(config_class=DevConfig): from app.views import notes_bp from app.views import users_bp from app.views import absences_bp + from app.views import assiduites_bp from app.api import api_bp from app.api import api_web_bp @@ -340,6 +341,9 @@ def create_app(config_class=DevConfig): app.register_blueprint( absences_bp, url_prefix="/ScoDoc//Scolarite/Absences" ) + app.register_blueprint( + assiduites_bp, url_prefix="/ScoDoc//Scolarite/Assiduites" + ) app.register_blueprint(api_bp, url_prefix="/ScoDoc/api") app.register_blueprint(api_web_bp, url_prefix="/ScoDoc//api") diff --git a/app/scodoc/html_sidebar.py b/app/scodoc/html_sidebar.py old mode 100644 new mode 100755 index 33132a05..5403b4d9 --- a/app/scodoc/html_sidebar.py +++ b/app/scodoc/html_sidebar.py @@ -126,7 +126,7 @@ def sidebar(etudid: int = None): if current_user.has_permission(Permission.ScoAbsChange): H.append( f""" -
  • Ajouter
  • +
  • Ajouter
  • Justifier
  • Supprimer
  • """ diff --git a/app/scodoc/sco_abs.py b/app/scodoc/sco_abs.py old mode 100644 new mode 100755 index 1e56ca87..e1672492 --- a/app/scodoc/sco_abs.py +++ b/app/scodoc/sco_abs.py @@ -42,6 +42,8 @@ from app.scodoc import sco_cache from app.scodoc import sco_etud from app.scodoc import sco_formsemestre_inscriptions from app.scodoc import sco_preferences +from app.models import Assiduite, Justificatif +import app.scodoc.sco_assiduites as scass import app.scodoc.sco_utils as scu # --- Misc tools.... ------------------ @@ -1052,6 +1054,45 @@ def get_abs_count_in_interval(etudid, date_debut_iso, date_fin_iso): return r +def get_assiduites_count_in_interval(etudid, date_debut_iso, date_fin_iso): + """Les comptes d'absences de cet étudiant entre ces deux dates, incluses: + tuple (nb abs, nb abs justifiées) + Utilise un cache. + """ + key = str(etudid) + "_" + date_debut_iso + "_" + date_fin_iso + "_assiduites" + r = sco_cache.AbsSemEtudCache.get(key) + if not r: + + date_debut: datetime.datetime = scu.is_iso_formated(date_debut_iso, True) + date_fin: datetime.datetime = scu.is_iso_formated(date_fin_iso, True) + + assiduites: Assiduite = Assiduite.query.filter_by(etudid=etudid) + justificatifs: Justificatif = Justificatif.query.filter_by(etudid=etudid) + + assiduites = scass.filter_by_date(assiduites, Assiduite, date_debut, date_fin) + justificatifs = scass.filter_by_date( + justificatifs, Justificatif, date_debut, date_fin + ) + + calculator: scass.CountCalculator = scass.CountCalculator() + calculator.compute_assiduites(assiduites) + nb_abs: dict = calculator.to_dict()["demi"] + + abs_just: list[Assiduite] = scass.get_all_justified( + etudid, date_debut, date_fin + ) + + calculator.reset() + calculator.compute_assiduites(abs_just) + nb_abs_just: dict = calculator.to_dict()["demi"] + + r = (nb_abs, nb_abs_just) + ans = sco_cache.AbsSemEtudCache.set(key, r) + if not ans: + log("warning: get_assiduites_count failed to cache") + return r + + def invalidate_abs_count(etudid, sem): """Invalidate (clear) cached counts""" date_debut = sem["date_debut_iso"] diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py old mode 100644 new mode 100755 index f971171c..e7f47b0b --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -829,9 +829,9 @@ def _make_listes_sem(formsemestre: FormSemestre, with_absences=True):
    - @@ -848,8 +848,8 @@ def _make_listes_sem(formsemestre: FormSemestre, with_absences=True): saisie par semaine + url_for("assiduites.signal_assiduites_group", scodoc_dept=g.scodoc_dept) + }?group_ids=%(group_id)s&jour={datetime.date.today().isoformat()}&formsemestre_id={formsemestre.id}">saisie par semaine
    """ else: diff --git a/app/scodoc/sco_photos.py b/app/scodoc/sco_photos.py old mode 100644 new mode 100755 index b1e73dcb..44acb448 --- a/app/scodoc/sco_photos.py +++ b/app/scodoc/sco_photos.py @@ -148,11 +148,11 @@ def get_photo_image(etudid=None, size="small"): filename = photo_pathname(etud.photo_filename, size=size) if not filename: filename = UNKNOWN_IMAGE_PATH - r = _http_jpeg_file(filename) + r = build_image_response(filename) return r -def _http_jpeg_file(filename): +def build_image_response(filename): """returns an image as a Flask response""" st = os.stat(filename) last_modified = st.st_mtime # float timestamp diff --git a/app/static/css/assiduites.css b/app/static/css/assiduites.css new file mode 100644 index 00000000..eb6086a7 --- /dev/null +++ b/app/static/css/assiduites.css @@ -0,0 +1,475 @@ +* { + box-sizing: border-box; +} + +.selectors>* { + margin: 10px 0; +} + +.selectors:disabled { + opacity: 0.5; +} + +.no-display { + display: none !important; +} + +/* === Gestion de la timeline === */ + +#tl_date { + visibility: hidden; + width: 0px; + height: 0px; + position: absolute; + left: 15%; +} + + +.infos { + position: relative; + width: fit-content; +} + +#datestr { + cursor: pointer; + background-color: white; + border: 1px #444 solid; + border-radius: 5px; + padding: 5px; +} + +#tl_slider { + width: 90%; + cursor: grab; + + /* visibility: hidden; */ +} + +#datestr, +#time { + width: fit-content; +} + +.ui-slider-handle.tl_handle { + background: none; + width: 25px; + height: 25px; + visibility: visible; + background-position: top; + background-size: cover; + border: none; + top: -180%; + cursor: grab; + +} + +#l_handle { + background-image: url(../icons/l_handle.svg); +} + +#r_handle { + background-image: url(../icons/r_handle.svg); +} + +.ui-slider-range.ui-widget-header.ui-corner-all { + background-color: #F9C768; + background-image: none; + opacity: 0.50; + visibility: visible; +} + + +/* === Gestion des etuds row === */ + +.etud_holder { + margin-top: 5vh; +} + +.etud_row { + display: grid; + grid-template-columns: 10% 20% 30% 30%; + gap: 3.33%; + + background-color: white; + border-radius: 15px; + width: 80%; + height: 50px; + + margin: 0.5% 0; + + box-shadow: -1px 12px 5px -8px rgba(68, 68, 68, 0.61); + -webkit-box-shadow: -1px 12px 5px -8px rgba(68, 68, 68, 0.61); + -moz-box-shadow: -1px 12px 5px -8px rgba(68, 68, 68, 0.61); +} + +.etud_row * { + display: flex; + justify-content: center; + align-items: center; + + height: 50px; + +} + +/* --- Index --- */ +.etud_row .index_field { + grid-column: 1; +} + +/* --- Nom étud --- */ +.etud_row .name_field { + grid-column: 2; + height: 100%; +} + +.etud_row .name_field .name_set { + flex-direction: column; + align-items: flex-start; + margin: 0 5%; +} + +.etud_row .name_field .name_set * { + padding: 0; + margin: 0; +} + +.etud_row .name_field .name_set h4 { + font-size: small; + font-weight: 600; +} + +.etud_row .name_field .name_set h5 { + font-size: x-small; +} + +/* --- Barre assiduités --- */ +.etud_row .assiduites_bar { + display: flex; + flex-wrap: wrap; + grid-column: 3; + position: relative; +} + + + +.etud_row .assiduites_bar .filler { + height: 5px; + width: 90%; + + background-color: white; + border: 1px solid #444; +} + +.etud_row .assiduites_bar #prevDateAssi { + height: 7px; + width: 7px; + + background-color: white; + border: 1px solid #444; + margin: 0px 8px; +} + +.etud_row .assiduites_bar #prevDateAssi.single { + height: 9px; + width: 9px; +} + +.etud_row.conflit { + background-color: #ff000061; +} + +.etud_row .assiduites_bar .absent { + background-color: #ec5c49 !important; +} + +.etud_row .assiduites_bar .present { + background-color: #37f05f !important; +} + +.etud_row .assiduites_bar .retard { + background-color: #ecb52a !important; +} + + +/* --- Boutons assiduités --- */ +.etud_row .btns_field { + grid-column: 4; +} + +.btns_field:disabled { + opacity: 0.7; +} + +.etud_row .btns_field * { + margin: 0 5%; + cursor: pointer; + width: 35px; + height: 35px; +} + +.rbtn { + -webkit-appearance: none; + appearance: none; + +} + +.rbtn::before { + content: ""; + display: inline-block; + width: 35px; + height: 35px; + background-position: center; + background-size: cover; +} + +.rbtn.present::before { + background-image: url(../icons/present.svg); +} + +.rbtn.absent::before { + background-image: url(../icons/absent.svg); +} + +.rbtn.retard::before { + background-image: url(../icons/retard.svg); +} + +.rbtn:checked:before { + outline: 3px solid #7059FF; + border-radius: 5px; +} + +.rbtn:focus { + outline: none !important; +} + +/*<== Modal conflit ==>*/ +.modal { + display: none; + position: fixed; + z-index: 500; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0, 0, 0, 0.4); +} + +.modal-content { + background-color: #fefefe; + margin: 5% auto; + padding: 20px; + border: 1px solid #888; + width: 80%; + height: 30%; + position: relative; + border-radius: 10px; + +} + + +.close { + color: #111; + position: absolute; + right: 5px; + top: 0px; + font-size: 28px; + font-weight: bold; + cursor: pointer; +} + +/* Ajout de styles pour la frise chronologique */ +.modal-timeline { + display: flex; + flex-direction: column; + align-items: stretch; + margin-bottom: 20px; +} + +.time-labels, +.assiduites-container { + display: flex; + justify-content: space-between; + position: relative; +} + +.time-label { + font-size: 14px; + margin-bottom: 4px; +} + +.assiduite { + position: absolute; + top: 20px; + cursor: pointer; + border-radius: 4px; + z-index: 10; + height: 100px; + padding: 4px; +} + + +.assiduite-info { + display: flex; + flex-direction: column; + height: 100%; + justify-content: space-between; +} + +.assiduite-id, +.assiduite-period, +.assiduite-state, +.assiduite-user_id { + font-size: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: center; +} + +.assiduites-container { + min-height: 20px; + height: calc(50% - 60px); + /* Augmentation de la hauteur du conteneur des assiduités */ + position: relative; + margin-bottom: 10px; +} + + +.action-buttons { + position: absolute; + text-align: center; + display: flex; + justify-content: space-evenly; + align-items: center; + height: 60px; + width: 100%; + bottom: 5%; +} + + +/* Ajout de la classe CSS pour la bordure en pointillés */ +.assiduite.selected { + border: 2px dashed black; +} + +.assiduite-special { + height: 120px; + position: absolute; + z-index: 5; + border: 2px solid #000; + background-color: rgba(36, 36, 36, 0.25); + background-image: repeating-linear-gradient(135deg, transparent, transparent 5px, rgba(81, 81, 81, 0.61) 5px, rgba(81, 81, 81, 0.61) 10px); + border-radius: 5px; +} + + +/*<== Info sur l'assiduité sélectionnée ==>*/ +.modal-assiduite-content { + background-color: #fefefe; + margin: 5% auto; + padding: 20px; + border: 1px solid #888; + width: max-content; + height: 30%; + position: relative; + border-radius: 10px; + display: none; +} + + +.modal-assiduite-content.show { + display: block; +} + +.modal-assiduite-content .infos { + display: flex; + flex-direction: column; + justify-content: space-evenly; + align-items: flex-start; +} + + +/*<=== Mass Action ==>*/ + +.mass-selection { + display: flex; + justify-content: flex-start; + align-items: center; + width: 100%; + margin: 2% 0; +} + +.mass-selection span { + margin: 0 1%; +} + +.mass-selection .rbtn { + background-color: transparent; + cursor: pointer; +} + +/*<== Loader ==> */ + +.loader-container { + display: none; + /* Cacher le loader par défaut */ + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + /* Fond semi-transparent pour bloquer les clics */ + z-index: 9999; + /* Placer le loader au-dessus de tout le contenu */ +} + +.loader { + border: 6px solid #f3f3f3; + border-radius: 50%; + border-top: 6px solid #3498db; + width: 60px; + height: 60px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + animation: spin 2s linear infinite; +} + +@keyframes spin { + 0% { + transform: translate(-50%, -50%) rotate(0deg); + } + + 100% { + transform: translate(-50%, -50%) rotate(360deg); + } +} + +.fieldsplit { + display: flex; + justify-content: flex-start; + align-items: center; + flex-direction: column; +} + +.fieldsplit legend { + margin: 0; +} + + + + +#page-assiduite-content { + display: flex; + flex-wrap: wrap; + gap: 5%; + flex-direction: column; +} + +#page-assiduite-content>* { + margin: 1.5% 0; +} \ No newline at end of file diff --git a/app/static/icons/absent.svg b/app/static/icons/absent.svg new file mode 100755 index 00000000..697635cd --- /dev/null +++ b/app/static/icons/absent.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/app/static/icons/present.svg b/app/static/icons/present.svg new file mode 100755 index 00000000..e1628c83 --- /dev/null +++ b/app/static/icons/present.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/app/static/icons/retard.svg b/app/static/icons/retard.svg new file mode 100755 index 00000000..b8a7f3d2 --- /dev/null +++ b/app/static/icons/retard.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js new file mode 100644 index 00000000..796015bd --- /dev/null +++ b/app/static/js/assiduites.js @@ -0,0 +1,1792 @@ +// <=== CONSTANTS and GLOBALS ===> + +const TIMEZONE = "Europe/Paris"; +let url; + +function getUrl() { + if (!url) { + url = SCO_URL.substring(0, SCO_URL.lastIndexOf("/")); + } + return url; +} + +//Les valeurs par défaut de la timeline (8h -> 18h) +let currentValues = [8.0, 10.0]; + +//Objet stockant les étudiants et les assiduités +let etuds = {}; +let assiduites = {}; + +// Variable qui définit si le processus d'action de masse est lancé +let currentMassAction = false; + +/** + * Variable de gestion des conflits + */ +let modal; +let closeBtn; +let timeline; +let deleteBtn; +let splitBtn; +let editBtn; +let selectedAssiduite; + +/** + * Ajout d'une fonction `capitalize` sur tous les strings + * alice.capitalize() -> Alice + */ +Object.defineProperty(String.prototype, "capitalize", { + value: function () { + return this.charAt(0).toUpperCase() + this.slice(1).toLowerCase(); + }, + enumerable: false, +}); +// <<== Outils ==>> + +/** + * Ajout des évents sur les boutons d'assiduité + * @param {Document | HTMLFieldSetElement} parent par défaut le document, un field sinon + */ +function setupCheckBox(parent = document) { + const checkboxes = Array.from(parent.querySelectorAll(".rbtn")); + checkboxes.forEach((box) => { + box.addEventListener("click", (event) => { + if (!uniqueCheckBox(box)) { + event.preventDefault(); + } + if (!box.parentElement.classList.contains("mass")) { + assiduiteAction(box); + } + }); + }); +} + +/** + * Validation préalable puis désactivation des chammps : + * - Groupe + * - Module impl + * - Date + */ +function validateSelectors() { + const action = () => { + const group_ids = getGroupIds(); + + etuds = {}; + group_ids.forEach((group_id) => { + sync_get( + getUrl() + `/api/group/${group_id}/etudiants`, + (data, status) => { + if (status === "success") { + data.forEach((etud) => { + if (!(etud.id in etuds)) { + etuds[etud.id] = etud; + } + }); + } + } + ); + }); + + getAssiduitesFromEtuds(true); + + document.querySelector(".selectors").disabled = true; + generateMassAssiduites(); + generateAllEtudRow(); + }; + + if (!verifyDateInSemester()) { + const HTML = ` +

    Attention, la date sélectionnée n'est pas comprise dans le semestre.

    +

    Cette page permet l'affichage et la modification des assiduités uniquement pour le semestre sélectionné.

    +

    Vous n'aurez donc pas accès aux assiduités.

    +

    Appuyer sur "Valider" uniquement si vous souhaitez poursuivre sans modifier la date.

    + `; + + const content = document.createElement("div"); + content.innerHTML = HTML; + + openPromptModal("Vérification de la date", content, action); + return; + } + + action(); +} + +/** + * Limite le nombre de checkbox marquée + * Vérifie aussi si le cliqué est fait sur des assiduités conflictuelles + * @param {HTMLInputElement} box la checkbox utilisée + * @returns {boolean} Faux si il y a un conflit d'assiduité, Vrai sinon + */ +function uniqueCheckBox(box) { + const type = box.parentElement.getAttribute("type") === "conflit"; + if (!type) { + const checkboxs = Array.from(box.parentElement.children); + + checkboxs.forEach((chbox) => { + if (chbox.checked && chbox.value !== box.value) { + chbox.checked = false; + } + }); + return true; + } + + return false; +} + +/** + * Fait une requête GET de façon synchrone + * @param {String} path adresse distante + * @param {CallableFunction} success fonction à effectuer en cas de succès + * @param {CallableFunction} errors fonction à effectuer en cas d'échec + */ +function sync_get(path, success, errors) { + $.ajax({ + async: false, + type: "GET", + url: path, + success: success, + error: errors, + }); +} +/** + * Fait une requête POST de façon synchrone + * @param {String} path adresse distante + * @param {object} data données à envoyer (objet js) + * @param {CallableFunction} success fonction à effectuer en cas de succès + * @param {CallableFunction} errors fonction à effectuer en cas d'échec + */ +function sync_post(path, data, success, errors) { + $.ajax({ + async: false, + type: "POST", + url: path, + data: JSON.stringify(data), + success: success, + error: errors, + }); +} +// <<== Gestion des actions de masse ==>> +const massActionQueue = new Map(); + +/** + * Cette fonction remet à zero la gestion des actions de masse + */ +function resetMassActionQueue() { + massActionQueue.set("supprimer", []); + massActionQueue.set("editer", []); + massActionQueue.set("creer", []); +} + +/** + * Fonction pour alimenter la queue des actions de masse + * @param {String} type Le type de queue ("creer", "supprimer", "editer") + * @param {*} obj L'objet qui sera utilisé par les API + */ +function addToMassActionQueue(type, obj) { + massActionQueue.get(type)?.push(obj); +} + +/** + * Fonction pour exécuter les actions de masse + */ +function executeMassActionQueue() { + if (!currentMassAction) return; + + //Récupération des queues + const toCreate = massActionQueue.get("creer"); + const toEdit = massActionQueue.get("editer"); + const toDelete = massActionQueue.get("supprimer"); + + //Fonction qui créé les assidutiés de la queue "creer" + const create = () => { + /** + * Création du template de l'assiduité + * + * { + * date_debut: #debut_timeline, + * date_fin: #fin_timeline, + * moduleimpl_id ?: <> + * } + */ + const tlTimes = getTimeLineTimes(); + const assiduite = { + date_debut: tlTimes.deb.format(), + date_fin: tlTimes.fin.format(), + }; + const moduleimpl = getModuleImplId(); + + if (moduleimpl !== null) { + assiduite["moduleimpl_id"] = moduleimpl; + } + + const createQueue = []; //liste des assiduités qui seront créées. + + /** + * Pour chaque état de la queue 'creer' on génère une + * assiduitée précise depuis le template + */ + toCreate.forEach((obj) => { + const curAssiduite = structuredClone(assiduite); + curAssiduite.etudid = obj.etudid; + curAssiduite.etat = obj.etat; + + createQueue.push(curAssiduite); + }); + + /** + * On envoie les données à l'API + */ + const path = getUrl() + `/api/assiduites/create`; + sync_post( + path, + createQueue, + (data, status) => { + //success + }, + (data, status) => { + //error + console.error(data, status); + } + ); + }; + + //Fonction qui modifie les assiduités de la queue 'edition' + const edit = () => { + //On ajoute le moduleimpl (s'il existe) aux assiduités à modifier + const editQueue = toEdit.map((assiduite) => { + const moduleimpl = getModuleImplId(); + if (moduleimpl !== null) { + assiduite["moduleimpl_id"] = moduleimpl; + } + return assiduite; + }); + + const path = getUrl() + `/api/assiduites/edit`; + sync_post( + path, + editQueue, + (data, status) => { + //success + }, + (data, status) => { + //error + console.error(data, status); + } + ); + }; + + //Fonction qui supprime les assiduités de la queue 'supprimer' + const supprimer = () => { + const path = getUrl() + `/api/assiduite/delete`; + sync_post( + path, + toDelete, + (data, status) => { + //success + }, + (data, status) => { + //error + console.error(data, status); + } + ); + }; + + //On exécute les fonctions de queue + create(); + edit(); + supprimer(); + //On récupère les assiduités puis on regénère les lignes d'étudiants + getAssiduitesFromEtuds(true); + generateAllEtudRow(); +} +/** + * Processus de peuplement des queues + * puis d'exécution + */ +function massAction() { + //On récupère tous les boutons d'assiduités + const fields = Array.from(document.querySelectorAll(".btns_field.single")); + //On récupère l'état de l'action de masse + const action = getAssiduiteValue(document.querySelector(".btns_field.mass")); + //On remet à 0 les queues + resetMassActionQueue(); + + //on met à vrai la variable pour la suite + currentMassAction = true; + + //On affiche le "loader" le temps du processus + showLoader(); + + //On timeout 0 pour le mettre à la fin de l'event queue de JS + setTimeout(() => { + const conflicts = []; + /** + * Pour chaque étudiant : + * On vérifie s'il y a un conflit -> on place l'étudiant dans l'array conflicts + * Sinon -> on fait comme si l'utilisateur cliquait sur le bouton d'assiduité + */ + fields.forEach((field) => { + if (field.getAttribute("type") != "conflit") { + field.querySelector(`.rbtn.${action}`).click(); + } else { + const etudid = field.getAttribute("etudid"); + conflicts.push(etuds[parseInt(etudid)]); + } + }); + + //on exécute les queues puis on cache le loader + executeMassActionQueue(); + hideLoader(); + + //Fin du processus, on remet à false + currentMassAction = false; + + //On remet à zero les boutons d'assiduité de masse + const boxes = Array.from( + document.querySelector(".btns_field.mass").querySelectorAll(".rbtn") + ); + boxes.forEach((box) => { + box.checked = false; + }); + + //Si il y a des conflits d'assiduité, on affiche la liste dans une alert + if (conflicts.length > 0) { + const div = document.createElement("div"); + const sub = document.createElement("p"); + sub.textContent = + "L'assiduité des étudiants suivant n'a pas pu être modifiée"; + div.appendChild(sub); + const ul = document.createElement("ul"); + conflicts.forEach((etu) => { + const li = document.createElement("li"); + li.textContent = `${etu.nom} ${etu.prenom.capitalize()}`; + ul.appendChild(li); + }); + div.appendChild(ul); + openAlertModal("Conflits d'assiduités", div, ""); + } + }, 0); +} + +/** + * On génère les boutons d'assiduités de masse + * puis on ajoute les événements associés + */ +function generateMassAssiduites() { + const content = document.getElementById("content"); + + const mass = document.createElement("div"); + mass.className = "mass-selection"; + mass.innerHTML = ` + Mettre tout le monde : +
    + + + +
    `; + + content.insertBefore(mass, content.querySelector(".etud_holder")); + + const mass_btn = Array.from(mass.querySelectorAll(".rbtn")); + mass_btn.forEach((btn) => { + btn.addEventListener("click", () => { + massAction(); + }); + }); + + if (!verifyDateInSemester()) { + content.querySelector(".btns_field.mass").setAttribute("disabled", "true"); + } +} + +/** + * Affichage du loader + */ +function showLoader() { + document.getElementById("loaderContainer").style.display = "block"; +} +/** + * Dissimulation du loader + */ +function hideLoader() { + document.getElementById("loaderContainer").style.display = "none"; +} + +// <<== Gestion du temps ==>> + +/** + * Transforme un temps numérique en string + * 8.75 -> 08h45 + * @param {number} time Le temps (float) + * @returns {string} le temps (string) + */ +function toTime(time) { + let heure = Math.floor(time); + let minutes = (time - heure) * 60; + if (minutes < 1) { + minutes = "00"; + } + if (heure < 10) { + heure = `0${heure}`; + } + return `${heure}h${minutes}`; +} +/** + * Transforme une date iso en une date lisible: + * new Date('2023-03-03') -> "vendredi 3 mars 2023" + * @param {Date} date + * @param {object} styles + * @returns + */ +function formatDate(date, styles = { dateStyle: "full" }) { + return new Intl.DateTimeFormat("fr-FR", styles).format(date); +} + +/** + * Met à jour la date visible sur la page en la formatant + */ +function updateDate() { + const dateInput = document.querySelector("#tl_date"); + + const date = dateInput.valueAsDate; + + $("#datestr").text(formatDate(date).capitalize()); +} + +function verifyDateInSemester() { + const date = new moment.tz( + document.querySelector("#tl_date").value, + TIMEZONE + ); + + const periodSemester = getFormSemestreDates(); + + return date.isBetween(periodSemester.deb, periodSemester.fin); +} + +/** + * Ajoute la possibilité d'ouvrir le calendrier + * lorsqu'on clique sur la date + */ +function setupDate(onchange = null) { + const datestr = document.querySelector("#datestr"); + const input = document.querySelector("#tl_date"); + + datestr.addEventListener("click", () => { + if (!input.disabled) { + input.showPicker(); + } + }); + + if (onchange != null) { + input.addEventListener("change", onchange); + } +} + +/** + * GetAssiduitesOnDateChange + * (Utilisé uniquement avec étudiant unique) + */ + +function getAssiduitesOnDateChange() { + if (!isSingleEtud()) return; + actualizeEtud(etudid); +} +/** + * Transforme une date iso en date intelligible + * @param {String} str date iso + * @param {String} separator le séparateur de la date intelligible (01/01/2000 {separtor} 10:00) + * @returns {String} la date intelligible + */ +function formatDateModal(str, separator = "·") { + return new moment.tz(str, TIMEZONE).format(`DD/MM/Y ${separator} HH:mm`); +} + +/** + * Fonction qui vérifie si une période est dans un interval + * Objet période / interval + * { + * deb: moment.tz(), + * fin: moment.tz(), + * } + * @param {object} period + * @param {object} interval + * @returns {boolean} Vrai si la période est dans l'interval + */ +function hasTimeConflict(period, interval) { + return period.deb.isBefore(interval.fin) && period.fin.isAfter(interval.deb); +} + +/** + * On récupère la période de la timeline + * @returns {deb : moment.tz(), fin: moment.tz()} + */ +function getTimeLineTimes() { + //getPeriodValues() -> retourne la position de la timeline [a,b] avec a et b des number + let values = getPeriodValues(); + //On récupère la date + const dateiso = document.querySelector("#tl_date").value; + + //On génère des objets temps (moment.tz) + values = values.map((el) => { + el = toTime(el).replace("h", ":"); + el = `${dateiso}T${el}`; + return moment.tz(el, TIMEZONE); + }); + + return { deb: values[0], fin: values[1] }; +} + +/** + * Vérification de l'égalité entre un conflit et la période de la timeline + * @param {object} conflict + * @returns {boolean} Renvoie Vrai si la période de la timeline est égal au conflit + */ +function isConflictSameAsTimeLine(conflict) { + const tlTimes = getTimeLineTimes(); + const clTimes = { + deb: moment.tz(conflict.date_debut, TIMEZONE), + fin: moment.tz(conflict.date_fin, TIMEZONE), + }; + return tlTimes.deb.isSame(clTimes.deb) && tlTimes.fin.isSame(clTimes.fin); +} + +/** + * Retourne un objet Date de la date sélectionnée + * @returns {Date} la date sélectionnée + */ +function getDate() { + const date = document.querySelector("#tl_date").valueAsDate; + date.setHours(0, 0, 0, 0); + return date; +} + +/** + * Retourne un objet date représentant le jour suivant + * @returns {Date} le jour suivant + */ +function getNextDate() { + const date = getDate(); + const next = new Date(date.valueOf()); + next.setDate(date.getDate() + 1); + next.setHours(0, 0, 0, 0); + return next; +} +/** + * Retourne un objet date représentant le jour précédent + * @returns {Date} le jour précédent + */ +function getPrevDate() { + const date = getDate(); + const next = new Date(date.valueOf()); + next.setDate(date.getDate() - 1); + next.setHours(0, 0, 0, 0); + return next; +} + +/** + * Transformation d'un objet Date en chaîne ISO + * @param {Date} date + * @returns {string} la date iso avec le timezone + */ +function toIsoString(date) { + var tzo = -date.getTimezoneOffset(), + dif = tzo >= 0 ? "+" : "-", + pad = function (num) { + return (num < 10 ? "0" : "") + num; + }; + + return ( + date.getFullYear() + + "-" + + pad(date.getMonth() + 1) + + "-" + + pad(date.getDate()) + + "T" + + pad(date.getHours()) + + ":" + + pad(date.getMinutes()) + + ":" + + pad(date.getSeconds()) + + dif + + pad(Math.floor(Math.abs(tzo) / 60)) + + ":" + + pad(Math.abs(tzo) % 60) + ); +} + +/** + * Transforme un temps numérique en une date moment.tz + * @param {number} nb + * @returns {moment.tz} Une date formée du temps donné et de la date courante + */ +function numberTimeToDate(nb) { + time = toTime(nb).replace("h", ":"); + date = document.querySelector("#tl_date").value; + + datetime = `${date}T${time}`; + + return moment.tz(datetime, TIMEZONE); +} + +// <<== Gestion des assiduités ==>> + +/** + * Récupère les assiduités des étudiants + * en fonction de : + * - du semestre + * - de la date courant et du jour précédent. + * @param {boolean} clear vidage de l'objet "assiduites" ou non + * @returns {object} l'objets Assiduités { : [,]} + */ +function getAssiduitesFromEtuds(clear, has_formsemestre = true) { + const etudIds = Object.keys(etuds).join(","); + const formsemestre_id = has_formsemestre + ? `formsemestre_id=${getFormSemestreId()}&` + : ""; + + const date_debut = toIsoString(getPrevDate()); + const date_fin = toIsoString(getNextDate()); + + if (clear) { + assiduites = {}; + } + + const url_api = + getUrl() + + `/api/assiduites/group/query?date_debut=${formsemestre_id}${date_debut}&date_fin=${date_fin}&etudids=${etudIds}`; + sync_get(url_api, (data, status) => { + if (status === "success") { + const dataKeys = Object.keys(data); + dataKeys.forEach((key) => { + assiduites[key] = data[key]; + }); + } + }); + return assiduites; +} + +/** + * Création d'une assiduité pour un étudiant + * @param {String} etat l'état de l'étudiant + * @param {Number | String} etudid l'identifiant de l'étudiant + * + * TODO : Rendre asynchrone + */ +function createAssiduite(etat, etudid) { + const tlTimes = getTimeLineTimes(); + const assiduite = { + date_debut: tlTimes.deb.format(), + date_fin: tlTimes.fin.format(), + etat: etat, + }; + + const moduleimpl = getModuleImplId(); + + if (moduleimpl !== null) { + assiduite["moduleimpl_id"] = moduleimpl; + } + + const path = getUrl() + `/api/assiduite/${etudid}/create`; + sync_post( + path, + [assiduite], + (data, status) => { + //success + if (data.success.length > 0) { + let obj = data.success["0"].assiduite_id; + } + }, + (data, status) => { + //error + console.error(data, status); + } + ); +} + +/** + * Suppression d'une assiduité + * @param {String | Number} assiduite_id l'identifiant de l'assiduité + * TODO : Rendre asynchrone + */ +function deleteAssiduite(assiduite_id) { + const path = getUrl() + `/api/assiduite/delete`; + sync_post( + path, + [assiduite_id], + (data, status) => { + //success + if (data.success.length > 0) { + let obj = data.success["0"].assiduite_id; + } + }, + (data, status) => { + //error + console.error(data, status); + } + ); +} + +/** + * + * @param {String | Number} assiduite_id l'identifiant d'une assiduité + * @param {String} etat l'état à modifier + * @returns {boolean} si l'édition a fonctionné + * TODO : Rendre asynchrone + */ +function editAssiduite(assiduite_id, etat) { + const assiduite = { + etat: etat, + moduleimpl_id: getModuleImplId(), + }; + const path = getUrl() + `/api/assiduite/${assiduite_id}/edit`; + let bool = false; + sync_post( + path, + assiduite, + (data, status) => { + bool = true; + }, + (data, status) => { + //error + console.error(data, status); + } + ); + + return bool; +} + +/** + * Récupération des assiduités conflictuelles avec la période de la time line + * @param {String | Number} etudid identifiant de l'étudiant + * @returns {Array[Assiduité]} un tableau d'assiduité + */ +function getAssiduitesConflict(etudid) { + const etudAssiduites = assiduites[etudid]; + if (!etudAssiduites) { + return []; + } + const period = getTimeLineTimes(); + return etudAssiduites.filter((assi) => { + const interval = { + deb: moment.tz(assi.date_debut, TIMEZONE), + fin: moment.tz(assi.date_fin, TIMEZONE), + }; + return hasTimeConflict(period, interval); + }); +} + +/** + * Récupération de la dernière assiduité du jour précédent + * @param {String | Number} etudid l'identifiant de l'étudiant + * @returns {Assiduité} la dernière assiduité du jour précédent + */ +function getLastAssiduiteOfPrevDate(etudid) { + const etudAssiduites = assiduites[etudid]; + if (!etudAssiduites) { + return ""; + } + const period = { + deb: moment.tz(getPrevDate(), TIMEZONE), + fin: moment.tz(getDate(), TIMEZONE), + }; + const prevAssiduites = etudAssiduites + .filter((assi) => { + const interval = { + deb: moment.tz(assi.date_debut, TIMEZONE), + fin: moment.tz(assi.date_fin, TIMEZONE), + }; + + return hasTimeConflict(period, interval); + }) + .sort((a, b) => { + const a_fin = moment.tz(a.date_fin, TIMEZONE); + const b_fin = moment.tz(b.date_fin, TIMEZONE); + return b_fin < a_fin; + }); + + if (prevAssiduites.length < 1) { + return null; + } + + return prevAssiduites.pop(); +} + +/** + * Récupération de l'état appointé + * @param {HTMLFieldSetElement} field le conteneur des boutons d'assiduité d'une ligne étudiant + * @returns {String} l'état appointé : ('present','absent','retard', 'remove') + * + * état = 'remove' si le clic désélectionne une assiduité appointée + */ +function getAssiduiteValue(field) { + const checkboxs = Array.from(field.children); + let value = "remove"; + checkboxs.forEach((chbox) => { + if (chbox.checked) { + value = chbox.value; + } + }); + + return value; +} + +/** + * Mise à jour des assiduités d'un étudiant + * @param {String | Number} etudid identifiant de l'étudiant + */ +function actualizeEtudAssiduite(etudid, has_formsemestre = true) { + const formsemestre_id = has_formsemestre + ? `formsemestre_id=${getFormSemestreId()}&` + : ""; + const date_debut = toIsoString(getPrevDate()); + const date_fin = toIsoString(getNextDate()); + + const url_api = + getUrl() + + `/api/assiduites/${etudid}/query?${formsemestre_id}date_debut=${date_debut}&date_fin=${date_fin}`; + + sync_get(url_api, (data, status) => { + if (status === "success") { + assiduites[etudid] = data; + } + }); +} + +/** + * Déclenchement d'une action après appuie sur un bouton d'assiduité + * @param {HTMLInputElement} element Bouton d'assiduité appuyé + */ +function assiduiteAction(element) { + const field = element.parentElement; + + const type = field.getAttribute("type"); + const etudid = parseInt(field.getAttribute("etudid")); + const assiduite_id = parseInt(field.getAttribute("assiduite_id")); + const etat = getAssiduiteValue(field); + + // Cas de l'action de masse -> peuplement des queues + if (currentMassAction) { + switch (type) { + case "création": + addToMassActionQueue("creer", { etat: etat, etudid: etudid }); + break; + case "édition": + if (etat === "remove") { + addToMassActionQueue("supprimer", assiduite_id); + } else { + addToMassActionQueue("editer", { + etat: etat, + assiduite_id: assiduite_id, + }); + } + break; + } + } else { + // Cas normal -> mise à jour en base + switch (type) { + case "création": + createAssiduite(etat, etudid); + break; + case "édition": + if (etat === "remove") { + deleteAssiduite(assiduite_id); + } else { + editAssiduite(assiduite_id, etat); + } + break; + case "conflit": + openModal(assiduites[etudid]); + break; + } + + if (type != "conflit") { + document + .querySelector(".toast-holder") + .appendChild( + generateToast( + document.createTextNode("L'assiduité a bien été enregistrée.") + ) + ); + } + + actualizeEtud(etudid, !isSingleEtud); + } +} + +// <<== Gestion de l'affichage des barres étudiant ==>> + +/** + * Génère l'HTML lié à la barre d'un étudiant + * @param {Etudiant} etud représentation objet d'un étudiant + * @param {Number} index l'index de l'étudiant dans la liste + * @param {AssiduitéMod} assiduite Objet représentant l'état de l'étudiant pour la période de la timeline + * @returns {String} l'HTML généré + */ +function generateEtudRow( + etud, + index, + assiduite = { + etatAssiduite: "", + type: "création", + id: -1, + date_debut: null, + date_fin: null, + prevAssiduites: "", + } +) { + // Génération des boutons du choix de l'assiduité + let assi = ""; + ["present", "retard", "absent"].forEach((abs) => { + if (abs.toLowerCase() === assiduite.etatAssiduite.toLowerCase()) { + assi += ``; + } else { + assi += ``; + } + }); + const conflit = assiduite.type == "conflit" ? "conflit" : ""; + const pdp_url = `${getUrl()}/api/etudiant/etudid/${etud.id}/photo?size=small`; + const HTML = `
    + +
    ${index}
    +
    + + + +
    + +

    ${etud.nom}

    +
    ${etud.prenom}
    + +
    + +
    +
    +
    + 13h +
    +
    +
    + + ${assi} + +
    + + +
    `; + + return HTML; +} + +/** + * Insertion de la ligne étudiant + * @param {Etudiant} etud l'objet représentant un étudiant + * @param {Number} index le n° de l'étudiant dans la liste des étudiants + * @param {boolean} output ajout automatique dans la page ou non (default : Non) + * @returns {String} HTML si output sinon rien + */ +function insertEtudRow(etud, index, output = false) { + const etudHolder = document.querySelector(".etud_holder"); + const conflict = getAssiduitesConflict(etud.id); + const prevAssiduite = getLastAssiduiteOfPrevDate(etud.id); + let assiduite = { + etatAssiduite: "", + type: "création", + id: -1, + date_debut: null, + date_fin: null, + prevAssiduites: prevAssiduite, + }; + + if (conflict.length > 0) { + assiduite.etatAssiduite = conflict[0].etat; + + assiduite.id = conflict[0].assiduite_id; + assiduite.date_debut = conflict[0].date_debut; + assiduite.date_fin = conflict[0].date_fin; + if (isConflictSameAsTimeLine(conflict[0])) { + assiduite.type = "édition"; + } else { + assiduite.type = "conflit"; + } + } + let row = generateEtudRow(etud, index, assiduite); + + if (output) { + return row; + } + etudHolder.insertAdjacentHTML("beforeend", row); + + row = document.getElementById(`etud_row_${etud.id}`); + const prev = row.querySelector("#prevDateAssi"); + setupAssiduiteBuble(prev, prevAssiduite); + const bar = row.querySelector(".assiduites_bar"); + + bar.appendChild(createMiniTimeline(assiduites[etud.id])); + + if (!verifyDateInSemester()) { + row.querySelector(".btns_field.single").setAttribute("disabled", "true"); + } +} + +/** + * Création de la minitiline d'un étudiant + * @param {Array[Assiduité]} assiduitesArray + * @returns {HTMLElement} l'élément correspondant à la mini timeline + */ +function createMiniTimeline(assiduitesArray) { + const dateiso = document.getElementById("tl_date").value; + const timeline = document.createElement("div"); + timeline.className = "mini-timeline"; + if (isSingleEtud()) { + timeline.classList.add("single"); + } + const timelineDate = moment(dateiso).startOf("day"); + const dayStart = timelineDate.clone().add(8, "hours"); + const dayEnd = timelineDate.clone().add(18, "hours"); + const dayDuration = moment.duration(dayEnd.diff(dayStart)).asMinutes(); + + assiduitesArray.forEach((assiduité) => { + const startDate = moment(assiduité.date_debut); + const endDate = moment(assiduité.date_fin); + + if (startDate.isBefore(dayStart)) { + startDate.startOf("day").add(8, "hours"); + } + + if (endDate.isAfter(dayEnd)) { + endDate.startOf("day").add(18, "hours"); + } + + const block = document.createElement("div"); + block.className = "mini-timeline-block"; + + const startOffset = moment.duration(startDate.diff(dayStart)).asMinutes(); + const duration = moment.duration(endDate.diff(startDate)).asMinutes(); + const leftPercentage = (startOffset / dayDuration) * 100; + const widthPercentage = (duration / dayDuration) * 100; + + block.style.left = `${leftPercentage}%`; + block.style.width = `${widthPercentage}%`; + + if (isSingleEtud()) { + block.addEventListener("click", () => { + let deb = startDate.hours() + startDate.minutes() / 60; + let fin = endDate.hours() + endDate.minutes() / 60; + deb = Math.max(8, deb); + fin = Math.min(18, fin); + + setPeriodValues(deb, fin); + updateSelectedSelect(getCurrentAssiduiteModuleImplId()); + }); + } + + //ajouter affichage assiduites on over + setupAssiduiteBuble(block, assiduité); + + switch (assiduité.etat) { + case "PRESENT": + block.classList.add("present"); + break; + case "RETARD": + block.classList.add("retard"); + break; + case "ABSENT": + block.classList.add("absent"); + break; + default: + block.style.backgroundColor = "white"; + } + + timeline.appendChild(block); + }); + + return timeline; +} + +/** + * Ajout de la visualisation des assiduités de la mini timeline + * @param {HTMLElement} el l'élément survollé + * @param {Assiduité} assiduite l'assiduité représentée par l'élément + */ +function setupAssiduiteBuble(el, assiduite) { + if (!assiduite) return; + el.addEventListener("mouseenter", (event) => { + const bubble = document.querySelector(".assiduite-bubble"); + bubble.className = "assiduite-bubble"; + bubble.classList.add("is-active", assiduite.etat.toLowerCase()); + + bubble.innerHTML = ""; + + const idDiv = document.createElement("div"); + idDiv.className = "assiduite-id"; + idDiv.textContent = `ID: ${assiduite.assiduite_id}`; + bubble.appendChild(idDiv); + + const periodDivDeb = document.createElement("div"); + periodDivDeb.className = "assiduite-period"; + periodDivDeb.textContent = `${formatDateModal(assiduite.date_debut)}`; + bubble.appendChild(periodDivDeb); + const periodDivFin = document.createElement("div"); + periodDivFin.className = "assiduite-period"; + periodDivFin.textContent = `${formatDateModal(assiduite.date_fin)}`; + bubble.appendChild(periodDivFin); + + const stateDiv = document.createElement("div"); + stateDiv.className = "assiduite-state"; + stateDiv.textContent = `État: ${assiduite.etat.capitalize()}`; + bubble.appendChild(stateDiv); + + const userIdDiv = document.createElement("div"); + userIdDiv.className = "assiduite-user_id"; + userIdDiv.textContent = `saisi le ${formatDateModal( + assiduite.entry_date, + "à" + )} \npar ${getUserFromId(assiduite.user_id)}`; + bubble.appendChild(userIdDiv); + + bubble.style.left = `${event.clientX - bubble.offsetWidth / 2}px`; + bubble.style.top = `${event.clientY + 20}px`; + }); + el.addEventListener("mouseout", () => { + const bubble = document.querySelector(".assiduite-bubble"); + bubble.classList.remove("is-active"); + }); +} + +/** + * Mise à jour d'une ligne étudiant + * @param {String | Number} etudid l'identifiant de l'étudiant + */ +function actualizeEtud(etudid) { + actualizeEtudAssiduite(etudid, !isSingleEtud()); + //Actualize row + const etudHolder = document.querySelector(".etud_holder"); + const ancient_row = document.getElementById(`etud_row_${etudid}`); + + let new_row = document.createElement("div"); + new_row.innerHTML = insertEtudRow( + etuds[etudid], + ancient_row.querySelector(".index").textContent, + true + ); + setupCheckBox(new_row.firstElementChild); + const bar = new_row.firstElementChild.querySelector(".assiduites_bar"); + bar.appendChild(createMiniTimeline(assiduites[etudid])); + const prev = new_row.firstElementChild.querySelector("#prevDateAssi"); + if (isSingleEtud()) { + prev.classList.add("single"); + } + setupAssiduiteBuble(prev, getLastAssiduiteOfPrevDate(etudid)); + etudHolder.replaceChild(new_row.firstElementChild, ancient_row); +} + +/** + * Génération de toutes les lignes étudiant + */ +function generateAllEtudRow() { + if (isSingleEtud()) { + actualizeEtud(etudid); + return; + } + + if (!document.querySelector(".selectors")?.disabled) { + return; + } + + document.querySelector(".etud_holder").innerHTML = ""; + etuds_ids = Object.keys(etuds).sort((a, b) => + etuds[a].nom > etuds[b].nom ? 1 : etuds[b].nom > etuds[a].nom ? -1 : 0 + ); + + for (let i = 0; i < etuds_ids.length; i++) { + const etud = etuds[etuds_ids[i]]; + insertEtudRow(etud, i + 1); + } + + setupCheckBox(); +} + +// <== Gestion du modal de conflit ==> +/** + * Mise à jour du modal de conflit + * @param {Array[Assiduité]} assiduiteList Liste des assiduités de l'étudiant + */ +function refreshModal(assiduiteList) { + const tlTime = getTimeLineTimes(); + + renderTimeline(assiduiteList, { + date_debut: tlTime.deb, + date_fin: tlTime.fin, + }); +} +/** + * Ouverture du modal de conflit + * @param {Array[Assiduité]} assiduiteList Liste des assiduités de l'étudiant + */ +function openModal(assiduiteList) { + modal.style.display = "block"; + + const tlTime = getTimeLineTimes(); + + renderTimeline(assiduiteList, { + date_debut: tlTime.deb, + date_fin: tlTime.fin, + }); +} + +/** + * Fermeture du modal de conflit + */ +function closeModal() { + modal.style.display = "none"; +} + +/** + * Génération du modal + * @param {Array[Assiduité]} assiduites la liste des assiduités à afficher + * @param {Période} specialAssiduite Une assiduité représentant la période conflictuelle + */ +function renderTimeline(assiduites, specialAssiduite) { + const timeLabels = document.querySelector(".time-labels"); + const assiduitesContainer = document.querySelector(".assiduites-container"); + + timeLabels.innerHTML = ""; + assiduitesContainer.innerHTML = '
    '; + + // Ajout des labels d'heure sur la frise chronologique + // TODO permettre la modification des bornes (8 et 18) + for (let i = 8; i <= 18; i++) { + const timeLabel = document.createElement("div"); + timeLabel.className = "time-label"; + timeLabel.textContent = i < 10 ? `0${i}:00` : `${i}:00`; + timeLabels.appendChild(timeLabel); + } + + //Placement de la période conflictuelle sur la timeline + const specialAssiduiteEl = document.querySelector(".assiduite-special"); + specialAssiduiteEl.style.width = getWidth( + specialAssiduite.date_debut, + specialAssiduite.date_fin + ); + specialAssiduiteEl.style.left = getLeftPosition(specialAssiduite.date_debut); + specialAssiduiteEl.style.top = "0"; + specialAssiduiteEl.style.zIndex = "0"; // Place l'assiduité spéciale en arrière-plan + assiduitesContainer.appendChild(specialAssiduiteEl); + + //Placement des assiduités sur la timeline + assiduites.forEach((assiduite) => { + const el = document.createElement("div"); + el.className = "assiduite"; + el.style.backgroundColor = getColor(assiduite.etat); + el.style.width = getWidth(assiduite.date_debut, assiduite.date_fin); + el.style.left = getLeftPosition(assiduite.date_debut); + el.style.top = "10px"; + el.setAttribute("data-id", assiduite.assiduite_id); + el.addEventListener("click", () => selectAssiduite(assiduite)); + + // Ajout des informations dans la visualisation d'une assiduité + const infoContainer = document.createElement("div"); + infoContainer.className = "assiduite-info"; + + const idDiv = document.createElement("div"); + idDiv.className = "assiduite-id"; + idDiv.textContent = `ID: ${assiduite.assiduite_id}`; + infoContainer.appendChild(idDiv); + + const periodDivDeb = document.createElement("div"); + periodDivDeb.className = "assiduite-period"; + periodDivDeb.textContent = `${formatDateModal(assiduite.date_debut)}`; + infoContainer.appendChild(periodDivDeb); + const periodDivFin = document.createElement("div"); + periodDivFin.className = "assiduite-period"; + periodDivFin.textContent = `${formatDateModal(assiduite.date_fin)}`; + infoContainer.appendChild(periodDivFin); + + const stateDiv = document.createElement("div"); + stateDiv.className = "assiduite-state"; + stateDiv.textContent = `État: ${assiduite.etat.capitalize()}`; + infoContainer.appendChild(stateDiv); + + const userIdDiv = document.createElement("div"); + userIdDiv.className = "assiduite-user_id"; + userIdDiv.textContent = `saisi le ${formatDateModal( + assiduite.entry_date, + "à" + )} \npar ${getUserFromId(assiduite.user_id)}`; + infoContainer.appendChild(userIdDiv); + + el.appendChild(infoContainer); + assiduitesContainer.appendChild(el); + }); +} + +/** + * Transformation d'une date de début en position sur la timeline + * @param {String} start + * @returns {String} un déplacement par rapport à la gauche en % + */ +function getLeftPosition(start) { + const startTime = new moment.tz(start, TIMEZONE); + const startMins = (startTime.hours() - 8) * 60 + startTime.minutes(); + return (startMins / (18 * 60 - 8 * 60)) * 100 + "%"; +} + +/** + * Ajustement de l'espacement vertical entre les assiduités superposées + * @param {HTMLElement} container le conteneur des assiduités + * @param {String} start la date début de l'assiduité à placer + * @param {String} end la date de fin de l'assiduité à placer + * @returns {String} La position en px + */ +function getTopPosition(container, start, end) { + const overlaps = (a, b) => { + return a.start < b.end && a.end > b.start; + }; + + const startTime = new moment.tz(start, TIMEZONE); + const endTime = new moment.tz(end, TIMEZONE); + const assiduiteDuration = { start: startTime, end: endTime }; + + let position = 0; + let hasOverlap = true; + + while (hasOverlap) { + hasOverlap = false; + Array.from(container.children).some((el) => { + const elStart = new moment.tz(el.getAttribute("data-start")); + const elEnd = new moment.tz(el.getAttribute("data-end")); + const elDuration = { start: elStart, end: elEnd }; + + if (overlaps(assiduiteDuration, elDuration)) { + position += 25; // Pour ajuster l'espacement vertical entre les assiduités superposées + hasOverlap = true; + return true; + } + return false; + }); + } + return position + "px"; +} + +/** + * Transformation d'un état en couleur + * @param {String} state l'état + * @returns {String} la couleur correspondant à l'état + */ +function getColor(state) { + switch (state) { + case "PRESENT": + return "#37f05f"; + case "ABSENT": + return "#ec5c49"; + case "RETARD": + return "#ecb52a"; + default: + return "gray"; + } +} + +/** + * Calcule de la largeur de l'assiduité sur la timeline + * @param {String} start date iso de début + * @param {String} end date iso de fin + * @returns {String} la taille en % + */ +function getWidth(start, end) { + const startTime = new moment.tz(start, TIMEZONE); + const endTime = new moment.tz(end, TIMEZONE); + const duration = (endTime - startTime) / 1000 / 60; + return (duration / (18 * 60 - 8 * 60)) * 100 + "%"; +} + +/** + * Sélection d'une assiduité sur la timeline + * @param {Assiduité} assiduite l'assiduité sélectionnée + */ +function selectAssiduite(assiduite) { + // Désélectionner l'assiduité précédemment sélectionnée + if (selectedAssiduite) { + const prevSelectedEl = document.querySelector( + `.assiduite[data-id="${selectedAssiduite.assiduite_id}"]` + ); + if (prevSelectedEl) { + prevSelectedEl.classList.remove("selected"); + } + } + + // Sélectionner la nouvelle assiduité + selectedAssiduite = assiduite; + const selectedEl = document.querySelector( + `.assiduite[data-id="${assiduite.assiduite_id}"]` + ); + if (selectedEl) { + selectedEl.classList.add("selected"); + } + + //Mise à jour de la partie information du modal + const selectedModal = document.querySelector(".modal-assiduite-content"); + + selectedModal.classList.add("show"); + + document.getElementById("modal-assiduite-id").textContent = + assiduite.assiduite_id; + document.getElementById( + "modal-assiduite-user" + ).textContent = `saisi le ${formatDateModal( + assiduite.entry_date, + "à" + )} \npar ${getUserFromId(assiduite.user_id)}`; + document.getElementById("modal-assiduite-module").textContent = + assiduite.moduleimpl_id; + document.getElementById("modal-assiduite-deb").textContent = formatDateModal( + assiduite.date_debut + ); + document.getElementById("modal-assiduite-fin").textContent = formatDateModal( + assiduite.date_fin + ); + document.getElementById("modal-assiduite-etat").textContent = + assiduite.etat.capitalize(); + + //Activation des boutons d'actions de conflit + deleteBtn.disabled = false; + splitBtn.disabled = false; + editBtn.disabled = false; +} +/** + * Suppression de l'assiduité sélectionnée + */ +function deleteAssiduiteModal() { + if (!selectedAssiduite) return; + deleteAssiduite(selectedAssiduite.assiduite_id); + actualizeEtud(selectedAssiduite.etudid); + refreshModal(assiduites[selectedAssiduite.etudid]); + + // Désélection de l'assiduité + resetSelection(); +} + +/** + * Division d'une assiduité + * @param {Assiduité} assiduite l'assiduité sélectionnée + */ +function splitAssiduiteModal(assiduite) { + //Préparation du prompt + const htmlPrompt = `Entrez l'heure de séparation (HH:mm) : + `; + + const fieldSet = document.createElement("fieldset"); + fieldSet.classList.add("fieldsplit"); + fieldSet.innerHTML = htmlPrompt; + + //Callback de division + const success = () => { + const separatorTime = document.getElementById("promptTime").value; + const dateString = + document.querySelector("#tl_date").value + `T${separatorTime}`; + const separtorDate = new moment.tz(dateString, TIMEZONE); + + const assiduite_debut = new moment.tz(assiduite.date_debut, TIMEZONE); + const assiduite_fin = new moment.tz(assiduite.date_fin, TIMEZONE); + + if ( + separtorDate.isAfter(assiduite_debut) && + separtorDate.isBefore(assiduite_fin) + ) { + const assiduite_avant = { + etat: assiduite.etat, + date_debut: assiduite_debut.format(), + date_fin: separtorDate.format(), + }; + + const assiduite_apres = { + etat: assiduite.etat, + date_debut: separtorDate.format(), + date_fin: assiduite_fin.format(), + }; + + if (assiduite.moduleimpl_id) { + assiduite_apres["moduleimpl_id"] = assiduite.moduleimpl_id; + assiduite_avant["moduleimpl_id"] = assiduite.moduleimpl_id; + } + + deleteAssiduite(assiduite.assiduite_id); + + const path = getUrl() + `/api/assiduite/${assiduite.etudid}/create`; + sync_post( + path, + [assiduite_avant, assiduite_apres], + (data, status) => { + //success + }, + (data, status) => { + //error + console.error(data, status); + } + ); + + actualizeEtud(assiduite.etudid); + refreshModal(assiduites[assiduite.etudid]); + resetSelection(); + } else { + const att = document.createTextNode( + "L'heure de séparation doit être compris dans la période de l'assiduité sélectionnée." + ); + + openAlertModal("Attention", att, "", "#ecb52a"); + } + }; + + openPromptModal("Entrée demandée", fieldSet, success, () => {}, "#37f05f"); +} +/** + * Modification d'une assiduité conflictuelle + * @param {Assiduité} selectedAssiduite l'assiduité sélectionnée + */ +function editAssiduiteModal(selectedAssiduite) { + if (!selectedAssiduite) return; + + //Préparation du modal d'édition + const htmlPrompt = `Entrez l'état de l'assiduité : + `; + + const fieldSet = document.createElement("fieldset"); + fieldSet.classList.add("fieldsplit"); + fieldSet.innerHTML = htmlPrompt; + + //Callback d'action d'édition + const success = () => { + const newState = document.getElementById("promptSelect").value; + if (!["present", "absent", "retard"].includes(newState.toLowerCase())) { + const att = document.createTextNode( + "L'état doit être 'present', 'absent' ou 'retard'." + ); + openAlertModal("Attention", att, "", "#ecb52a"); + return; + } + + // Actualiser l'affichage + + editAssiduite(selectedAssiduite.assiduite_id, newState); + actualizeEtud(selectedAssiduite.etudid); + refreshModal(assiduites[selectedAssiduite.etudid]); + + // Désélection de l'assiduité + resetSelection(); + }; + + //Affichage du prompt + openPromptModal("Entrée demandée", fieldSet, success, () => {}, "#37f05f"); +} + +/** + * Remise à zéro de la sélection + * Désactivation des boutons d'actions de conflit + */ +function resetSelection() { + selectedAssiduite = null; + deleteBtn.disabled = true; + splitBtn.disabled = true; + editBtn.disabled = true; + + document.querySelector(".modal-assiduite-content").classList.remove("show"); +} +/** + * Ajout des évents sur les boutons du modal + */ +window.onload = () => { + modal = document.getElementById("myModal"); + closeBtn = document.querySelector(".close"); + timeline = document.getElementById("timeline"); + deleteBtn = document.getElementById("delete"); + splitBtn = document.getElementById("split"); + editBtn = document.getElementById("edit"); + selectedAssiduite = null; + + closeBtn?.addEventListener("click", closeModal); + + deleteBtn?.addEventListener("click", deleteAssiduiteModal); + splitBtn?.addEventListener("click", () => { + if (selectedAssiduite) { + splitAssiduiteModal(selectedAssiduite); + } + }); + editBtn.addEventListener("click", () => { + if (selectedAssiduite) { + editAssiduiteModal(selectedAssiduite); + } + }); +}; + +// <<== Gestion de la récupération d'informations ==>> + +/** + * Récupération d'un nom d'utilisateur à partir d'un identifiant + * @param {Number} id identifiant de l'utilisateur + * @returns {String} le nom de l'utilisateur ou son pseudo ou "Non Renseigné" + */ +function getUserFromId(id) { + if (id == "") { + return "Non Renseigné"; + } + + let name = "Non Renseigné"; + + sync_get(`/ScoDoc/api/user/${id}`, (data) => { + if (data.nom != "" && data.prenom != "") { + name = `${data.nom} ${data.prenom}`; + } else { + name = data.user_name; + } + }); + + return name; +} + +/** + * Récupération des ids des groupes + * @returns la liste des ids des groupes + */ +function getGroupIds() { + const btns = document.querySelector(".multiselect-container.dropdown-menu"); + + const groups = Array.from(btns.querySelectorAll(".active")).map((el) => { + return el.querySelector("input").value; + }); + + return groups; +} + +/** + * Récupération du moduleimpl_id + * @returns {String} l'identifiant ou null si inéxistant + */ +function getModuleImplId() { + const val = document.querySelector("#moduleimpl_select")?.value; + return ["", undefined, null].includes(val) ? null : val; +} + +/** + * Récupération de l'id du formsemestre + * @returns {String} l'identifiant du formsemestre + */ +function getFormSemestreId() { + return document.querySelector(".formsemestre_id").textContent; +} + +/** + * Récupère la période du semestre + * @returns {object} période {deb,fin} + */ +function getFormSemestreDates() { + const dateDeb = document.getElementById( + "formsemestre_date_debut" + ).textContent; + const dateFin = document.getElementById("formsemestre_date_fin").textContent; + + return { + deb: dateDeb, + fin: dateFin, + }; +} + +/** + * Récupère un objet étudiant à partir de son id + * @param {Number} etudid + */ +function getSingleEtud(etudid) { + sync_get(getUrl() + `/api/etudiant/etudid/${etudid}`, (data) => { + etuds[etudid] = data; + }); +} + +function isSingleEtud() { + return location.href.includes("SignaleAssiduiteEtud"); +} + +function getCurrentAssiduiteModuleImplId() { + const currentAssiduites = getAssiduitesConflict(etudid); + if (currentAssiduites.length > 0) { + const mod = currentAssiduites[0].moduleimpl_id; + return mod == null ? "" : mod; + } + return ""; +} + +function getCurrentAssiduite(etudid) { + const field = document.querySelector( + `fieldset.btns_field.single[etudid='${etudid}']` + ); + + if (!field) return null; + + const assiduite_id = parseInt(field.getAttribute("assiduite_id")); + const type = field.getAttribute("type"); + + if (type == "edition") { + let assi = null; + assiduites[etudid].forEach((a) => { + if (a.assiduite_id === assiduite_id) { + assi = a; + } + }); + return assi; + } else { + return null; + } +} + +// <<== Gestion de la justification ==>> + +function getJustificatifFromPeriod(date) { + let justifs = []; + sync_get( + getUrl() + + `/api/justificatifs/${etudid}/query?date_debut=${date.deb.format()}&date_fin=${date.fin.format()}`, + (data) => { + justifs = data; + } + ); + + return justifs; +} + +function updateJustifieButton(isJustified, isDisabled = true) { + const btn = document.getElementById("justif-rapide"); + if (isJustified) { + btn.classList.add("justifie"); + } else { + btn.classList.remove("justifie"); + } + + if (isDisabled) { + btn.setAttribute("disabled", "true"); + } else { + btn.removeAttribute("disabled"); + } +} + +function fastJustify(assiduite) { + const period = { + deb: new moment.tz(assiduite.date_debut, TIMEZONE), + fin: new moment.tz(assiduite.date_fin, TIMEZONE), + }; + const justifs = getJustificatifFromPeriod(period); + + if (justifs.length > 0) { + //modifier l'assiduité + } else { + //créer un nouveau justificatif + // Afficher prompt -> demander raison et état + } +} diff --git a/app/static/libjs/moment-timezone.js b/app/static/libjs/moment-timezone.js new file mode 100644 index 00000000..56fc2799 --- /dev/null +++ b/app/static/libjs/moment-timezone.js @@ -0,0 +1,1597 @@ +//! moment-timezone.js +//! version : 0.5.40 +//! Copyright (c) JS Foundation and other contributors +//! license : MIT +//! github.com/moment/moment-timezone + +(function (root, factory) { + "use strict"; + + /*global define*/ + if (typeof module === "object" && module.exports) { + module.exports = factory(require("moment")); // Node + } else if (typeof define === "function" && define.amd) { + define(["moment"], factory); // AMD + } else { + factory(root.moment); // Browser + } +})(this, function (moment) { + "use strict"; + + // Resolves es6 module loading issue + if (moment.version === undefined && moment.default) { + moment = moment.default; + } + + // Do not load moment-timezone a second time. + // if (moment.tz !== undefined) { + // logError('Moment Timezone ' + moment.tz.version + ' was already loaded ' + (moment.tz.dataVersion ? 'with data from ' : 'without any data') + moment.tz.dataVersion); + // return moment; + // } + + var VERSION = "0.5.40", + zones = {}, + links = {}, + countries = {}, + names = {}, + guesses = {}, + cachedGuess; + + if (!moment || typeof moment.version !== "string") { + logError( + "Moment Timezone requires Moment.js. See https://momentjs.com/timezone/docs/#/use-it/browser/" + ); + } + + var momentVersion = moment.version.split("."), + major = +momentVersion[0], + minor = +momentVersion[1]; + + // Moment.js version check + if (major < 2 || (major === 2 && minor < 6)) { + logError( + "Moment Timezone requires Moment.js >= 2.6.0. You are using Moment.js " + + moment.version + + ". See momentjs.com" + ); + } + + /************************************ + Unpacking + ************************************/ + + function charCodeToInt(charCode) { + if (charCode > 96) { + return charCode - 87; + } else if (charCode > 64) { + return charCode - 29; + } + return charCode - 48; + } + + function unpackBase60(string) { + var i = 0, + parts = string.split("."), + whole = parts[0], + fractional = parts[1] || "", + multiplier = 1, + num, + out = 0, + sign = 1; + + // handle negative numbers + if (string.charCodeAt(0) === 45) { + i = 1; + sign = -1; + } + + // handle digits before the decimal + for (i; i < whole.length; i++) { + num = charCodeToInt(whole.charCodeAt(i)); + out = 60 * out + num; + } + + // handle digits after the decimal + for (i = 0; i < fractional.length; i++) { + multiplier = multiplier / 60; + num = charCodeToInt(fractional.charCodeAt(i)); + out += num * multiplier; + } + + return out * sign; + } + + function arrayToInt(array) { + for (var i = 0; i < array.length; i++) { + array[i] = unpackBase60(array[i]); + } + } + + function intToUntil(array, length) { + for (var i = 0; i < length; i++) { + array[i] = Math.round((array[i - 1] || 0) + array[i] * 60000); // minutes to milliseconds + } + + array[length - 1] = Infinity; + } + + function mapIndices(source, indices) { + var out = [], + i; + + for (i = 0; i < indices.length; i++) { + out[i] = source[indices[i]]; + } + + return out; + } + + function unpack(string) { + var data = string.split("|"), + offsets = data[2].split(" "), + indices = data[3].split(""), + untils = data[4].split(" "); + + arrayToInt(offsets); + arrayToInt(indices); + arrayToInt(untils); + + intToUntil(untils, indices.length); + + return { + name: data[0], + abbrs: mapIndices(data[1].split(" "), indices), + offsets: mapIndices(offsets, indices), + untils: untils, + population: data[5] | 0, + }; + } + + /************************************ + Zone object + ************************************/ + + function Zone(packedString) { + if (packedString) { + this._set(unpack(packedString)); + } + } + + Zone.prototype = { + _set: function (unpacked) { + this.name = unpacked.name; + this.abbrs = unpacked.abbrs; + this.untils = unpacked.untils; + this.offsets = unpacked.offsets; + this.population = unpacked.population; + }, + + _index: function (timestamp) { + var target = +timestamp, + untils = this.untils, + i; + + for (i = 0; i < untils.length; i++) { + if (target < untils[i]) { + return i; + } + } + }, + + countries: function () { + var zone_name = this.name; + return Object.keys(countries).filter(function (country_code) { + return countries[country_code].zones.indexOf(zone_name) !== -1; + }); + }, + + parse: function (timestamp) { + var target = +timestamp, + offsets = this.offsets, + untils = this.untils, + max = untils.length - 1, + offset, + offsetNext, + offsetPrev, + i; + + for (i = 0; i < max; i++) { + offset = offsets[i]; + offsetNext = offsets[i + 1]; + offsetPrev = offsets[i ? i - 1 : i]; + + if (offset < offsetNext && tz.moveAmbiguousForward) { + offset = offsetNext; + } else if (offset > offsetPrev && tz.moveInvalidForward) { + offset = offsetPrev; + } + + if (target < untils[i] - offset * 60000) { + return offsets[i]; + } + } + + return offsets[max]; + }, + + abbr: function (mom) { + return this.abbrs[this._index(mom)]; + }, + + offset: function (mom) { + logError("zone.offset has been deprecated in favor of zone.utcOffset"); + return this.offsets[this._index(mom)]; + }, + + utcOffset: function (mom) { + return this.offsets[this._index(mom)]; + }, + }; + + /************************************ + Country object + ************************************/ + + function Country(country_name, zone_names) { + this.name = country_name; + this.zones = zone_names; + } + + /************************************ + Current Timezone + ************************************/ + + function OffsetAt(at) { + var timeString = at.toTimeString(); + var abbr = timeString.match(/\([a-z ]+\)/i); + if (abbr && abbr[0]) { + // 17:56:31 GMT-0600 (CST) + // 17:56:31 GMT-0600 (Central Standard Time) + abbr = abbr[0].match(/[A-Z]/g); + abbr = abbr ? abbr.join("") : undefined; + } else { + // 17:56:31 CST + // 17:56:31 GMT+0800 (台北標準時間) + abbr = timeString.match(/[A-Z]{3,5}/g); + abbr = abbr ? abbr[0] : undefined; + } + + if (abbr === "GMT") { + abbr = undefined; + } + + this.at = +at; + this.abbr = abbr; + this.offset = at.getTimezoneOffset(); + } + + function ZoneScore(zone) { + this.zone = zone; + this.offsetScore = 0; + this.abbrScore = 0; + } + + ZoneScore.prototype.scoreOffsetAt = function (offsetAt) { + this.offsetScore += Math.abs( + this.zone.utcOffset(offsetAt.at) - offsetAt.offset + ); + if (this.zone.abbr(offsetAt.at).replace(/[^A-Z]/g, "") !== offsetAt.abbr) { + this.abbrScore++; + } + }; + + function findChange(low, high) { + var mid, diff; + + while ((diff = (((high.at - low.at) / 12e4) | 0) * 6e4)) { + mid = new OffsetAt(new Date(low.at + diff)); + if (mid.offset === low.offset) { + low = mid; + } else { + high = mid; + } + } + + return low; + } + + function userOffsets() { + var startYear = new Date().getFullYear() - 2, + last = new OffsetAt(new Date(startYear, 0, 1)), + offsets = [last], + change, + next, + i; + + for (i = 1; i < 48; i++) { + next = new OffsetAt(new Date(startYear, i, 1)); + if (next.offset !== last.offset) { + change = findChange(last, next); + offsets.push(change); + offsets.push(new OffsetAt(new Date(change.at + 6e4))); + } + last = next; + } + + for (i = 0; i < 4; i++) { + offsets.push(new OffsetAt(new Date(startYear + i, 0, 1))); + offsets.push(new OffsetAt(new Date(startYear + i, 6, 1))); + } + + return offsets; + } + + function sortZoneScores(a, b) { + if (a.offsetScore !== b.offsetScore) { + return a.offsetScore - b.offsetScore; + } + if (a.abbrScore !== b.abbrScore) { + return a.abbrScore - b.abbrScore; + } + if (a.zone.population !== b.zone.population) { + return b.zone.population - a.zone.population; + } + return b.zone.name.localeCompare(a.zone.name); + } + + function addToGuesses(name, offsets) { + var i, offset; + arrayToInt(offsets); + for (i = 0; i < offsets.length; i++) { + offset = offsets[i]; + guesses[offset] = guesses[offset] || {}; + guesses[offset][name] = true; + } + } + + function guessesForUserOffsets(offsets) { + var offsetsLength = offsets.length, + filteredGuesses = {}, + out = [], + i, + j, + guessesOffset; + + for (i = 0; i < offsetsLength; i++) { + guessesOffset = guesses[offsets[i].offset] || {}; + for (j in guessesOffset) { + if (guessesOffset.hasOwnProperty(j)) { + filteredGuesses[j] = true; + } + } + } + + for (i in filteredGuesses) { + if (filteredGuesses.hasOwnProperty(i)) { + out.push(names[i]); + } + } + + return out; + } + + function rebuildGuess() { + // use Intl API when available and returning valid time zone + try { + var intlName = Intl.DateTimeFormat().resolvedOptions().timeZone; + if (intlName && intlName.length > 3) { + var name = names[normalizeName(intlName)]; + if (name) { + return name; + } + logError( + "Moment Timezone found " + + intlName + + " from the Intl api, but did not have that data loaded." + ); + } + } catch (e) { + // Intl unavailable, fall back to manual guessing. + } + + var offsets = userOffsets(), + offsetsLength = offsets.length, + guesses = guessesForUserOffsets(offsets), + zoneScores = [], + zoneScore, + i, + j; + + for (i = 0; i < guesses.length; i++) { + zoneScore = new ZoneScore(getZone(guesses[i]), offsetsLength); + for (j = 0; j < offsetsLength; j++) { + zoneScore.scoreOffsetAt(offsets[j]); + } + zoneScores.push(zoneScore); + } + + zoneScores.sort(sortZoneScores); + + return zoneScores.length > 0 ? zoneScores[0].zone.name : undefined; + } + + function guess(ignoreCache) { + if (!cachedGuess || ignoreCache) { + cachedGuess = rebuildGuess(); + } + return cachedGuess; + } + + /************************************ + Global Methods + ************************************/ + + function normalizeName(name) { + return (name || "").toLowerCase().replace(/\//g, "_"); + } + + function addZone(packed) { + var i, name, split, normalized; + + if (typeof packed === "string") { + packed = [packed]; + } + + for (i = 0; i < packed.length; i++) { + split = packed[i].split("|"); + name = split[0]; + normalized = normalizeName(name); + zones[normalized] = packed[i]; + names[normalized] = name; + addToGuesses(normalized, split[2].split(" ")); + } + } + + function getZone(name, caller) { + name = normalizeName(name); + + var zone = zones[name]; + var link; + + if (zone instanceof Zone) { + return zone; + } + + if (typeof zone === "string") { + zone = new Zone(zone); + zones[name] = zone; + return zone; + } + + // Pass getZone to prevent recursion more than 1 level deep + if ( + links[name] && + caller !== getZone && + (link = getZone(links[name], getZone)) + ) { + zone = zones[name] = new Zone(); + zone._set(link); + zone.name = names[name]; + return zone; + } + + return null; + } + + function getNames() { + var i, + out = []; + + for (i in names) { + if ( + names.hasOwnProperty(i) && + (zones[i] || zones[links[i]]) && + names[i] + ) { + out.push(names[i]); + } + } + + return out.sort(); + } + + function getCountryNames() { + return Object.keys(countries); + } + + function addLink(aliases) { + var i, alias, normal0, normal1; + + if (typeof aliases === "string") { + aliases = [aliases]; + } + + for (i = 0; i < aliases.length; i++) { + alias = aliases[i].split("|"); + + normal0 = normalizeName(alias[0]); + normal1 = normalizeName(alias[1]); + + links[normal0] = normal1; + names[normal0] = alias[0]; + + links[normal1] = normal0; + names[normal1] = alias[1]; + } + } + + function addCountries(data) { + var i, country_code, country_zones, split; + if (!data || !data.length) return; + for (i = 0; i < data.length; i++) { + split = data[i].split("|"); + country_code = split[0].toUpperCase(); + country_zones = split[1].split(" "); + countries[country_code] = new Country(country_code, country_zones); + } + } + + function getCountry(name) { + name = name.toUpperCase(); + return countries[name] || null; + } + + function zonesForCountry(country, with_offset) { + country = getCountry(country); + + if (!country) return null; + + var zones = country.zones.sort(); + + if (with_offset) { + return zones.map(function (zone_name) { + var zone = getZone(zone_name); + return { + name: zone_name, + offset: zone.utcOffset(new Date()), + }; + }); + } + + return zones; + } + + function loadData(data) { + addZone(data.zones); + addLink(data.links); + addCountries(data.countries); + tz.dataVersion = data.version; + } + + function zoneExists(name) { + if (!zoneExists.didShowError) { + zoneExists.didShowError = true; + logError( + "moment.tz.zoneExists('" + + name + + "') has been deprecated in favor of !moment.tz.zone('" + + name + + "')" + ); + } + return !!getZone(name); + } + + function needsOffset(m) { + var isUnixTimestamp = m._f === "X" || m._f === "x"; + return !!(m._a && m._tzm === undefined && !isUnixTimestamp); + } + + function logError(message) { + if (typeof console !== "undefined" && typeof console.error === "function") { + console.error(message); + } + } + + /************************************ + moment.tz namespace + ************************************/ + + function tz(input) { + var args = Array.prototype.slice.call(arguments, 0, -1), + name = arguments[arguments.length - 1], + zone = getZone(name), + out = moment.utc.apply(null, args); + + if (zone && !moment.isMoment(input) && needsOffset(out)) { + out.add(zone.parse(out), "minutes"); + } + + out.tz(name); + + return out; + } + + tz.version = VERSION; + tz.dataVersion = ""; + tz._zones = zones; + tz._links = links; + tz._names = names; + tz._countries = countries; + tz.add = addZone; + tz.link = addLink; + tz.load = loadData; + tz.zone = getZone; + tz.zoneExists = zoneExists; // deprecated in 0.1.0 + tz.guess = guess; + tz.names = getNames; + tz.Zone = Zone; + tz.unpack = unpack; + tz.unpackBase60 = unpackBase60; + tz.needsOffset = needsOffset; + tz.moveInvalidForward = true; + tz.moveAmbiguousForward = false; + tz.countries = getCountryNames; + tz.zonesForCountry = zonesForCountry; + + /************************************ + Interface with Moment.js + ************************************/ + + var fn = moment.fn; + + moment.tz = tz; + + moment.defaultZone = null; + + moment.updateOffset = function (mom, keepTime) { + var zone = moment.defaultZone, + offset; + + if (mom._z === undefined) { + if (zone && needsOffset(mom) && !mom._isUTC) { + mom._d = moment.utc(mom._a)._d; + mom.utc().add(zone.parse(mom), "minutes"); + } + mom._z = zone; + } + if (mom._z) { + offset = mom._z.utcOffset(mom); + if (Math.abs(offset) < 16) { + offset = offset / 60; + } + if (mom.utcOffset !== undefined) { + var z = mom._z; + mom.utcOffset(-offset, keepTime); + mom._z = z; + } else { + mom.zone(offset, keepTime); + } + } + }; + + fn.tz = function (name, keepTime) { + if (name) { + if (typeof name !== "string") { + throw new Error( + "Time zone name must be a string, got " + + name + + " [" + + typeof name + + "]" + ); + } + this._z = getZone(name); + if (this._z) { + moment.updateOffset(this, keepTime); + } else { + logError( + "Moment Timezone has no data for " + + name + + ". See http://momentjs.com/timezone/docs/#/data-loading/." + ); + } + return this; + } + if (this._z) { + return this._z.name; + } + }; + + function abbrWrap(old) { + return function () { + if (this._z) { + return this._z.abbr(this); + } + return old.call(this); + }; + } + + function resetZoneWrap(old) { + return function () { + this._z = null; + return old.apply(this, arguments); + }; + } + + function resetZoneWrap2(old) { + return function () { + if (arguments.length > 0) this._z = null; + return old.apply(this, arguments); + }; + } + + fn.zoneName = abbrWrap(fn.zoneName); + fn.zoneAbbr = abbrWrap(fn.zoneAbbr); + fn.utc = resetZoneWrap(fn.utc); + fn.local = resetZoneWrap(fn.local); + fn.utcOffset = resetZoneWrap2(fn.utcOffset); + + moment.tz.setDefault = function (name) { + if (major < 2 || (major === 2 && minor < 9)) { + logError( + "Moment Timezone setDefault() requires Moment.js >= 2.9.0. You are using Moment.js " + + moment.version + + "." + ); + } + moment.defaultZone = name ? getZone(name) : null; + return moment; + }; + + // Cloning a moment should include the _z property. + var momentProperties = moment.momentProperties; + if (Object.prototype.toString.call(momentProperties) === "[object Array]") { + // moment 2.8.1+ + momentProperties.push("_z"); + momentProperties.push("_a"); + } else if (momentProperties) { + // moment 2.7.0 + momentProperties._z = null; + } + + loadData({ + version: "2022g", + zones: [ + "Africa/Abidjan|GMT|0|0||48e5", + "Africa/Nairobi|EAT|-30|0||47e5", + "Africa/Algiers|CET|-10|0||26e5", + "Africa/Lagos|WAT|-10|0||17e6", + "Africa/Maputo|CAT|-20|0||26e5", + "Africa/Cairo|EET|-20|0||15e6", + "Africa/Casablanca|+00 +01|0 -10|01010101010101010101010101|1T0q0 mo0 gM0 LA0 WM0 jA0 e00 28M0 e00 2600 gM0 2600 e00 2600 gM0 2600 gM0 2600 e00 2600 gM0 2600 e00 28M0 e00|32e5", + "Europe/Paris|CET CEST|-10 -20|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|11e6", + "Africa/Johannesburg|SAST|-20|0||84e5", + "Africa/Juba|EAT CAT|-30 -20|01|24nx0|", + "Africa/Khartoum|EAT CAT|-30 -20|01|1Usl0|51e5", + "Africa/Sao_Tome|GMT WAT|0 -10|010|1UQN0 2q00|", + "Africa/Windhoek|CAT WAT|-20 -10|010|1T3c0 11B0|32e4", + "America/Adak|HST HDT|a0 90|01010101010101010101010|1ST00 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|326", + "America/Anchorage|AKST AKDT|90 80|01010101010101010101010|1SSX0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|30e4", + "America/Santo_Domingo|AST|40|0||29e5", + "America/Fortaleza|-03|30|0||34e5", + "America/Asuncion|-03 -04|30 40|01010101010101010101010|1T0r0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0 19X0 1fB0 19X0 1fB0 19X0 1ip0 17b0 1ip0 17b0 1ip0 19X0 1fB0|28e5", + "America/Panama|EST|50|0||15e5", + "America/Mexico_City|CST CDT|60 50|0101010101010|1T3k0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|20e6", + "America/Managua|CST|60|0||22e5", + "America/Caracas|-04|40|0||29e5", + "America/Lima|-05|50|0||11e6", + "America/Denver|MST MDT|70 60|01010101010101010101010|1SSV0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|26e5", + "America/Campo_Grande|-03 -04|30 40|010101|1SKr0 1zd0 On0 1HB0 FX0|77e4", + "America/Chicago|CST CDT|60 50|01010101010101010101010|1SSU0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|92e5", + "America/Chihuahua|MST MDT CST|70 60 60|0101010101012|1T3l0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|81e4", + "America/Ciudad_Juarez|MST MDT CST|70 60 60|010101010101201010101010|1SSV0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1wn0 cm0 EP0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|", + "America/Phoenix|MST|70|0||42e5", + "America/Whitehorse|PST PDT MST|80 70 70|010101012|1SSW0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1z90|23e3", + "America/New_York|EST EDT|50 40|01010101010101010101010|1SST0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|21e6", + "America/Los_Angeles|PST PDT|80 70|01010101010101010101010|1SSW0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|15e6", + "America/Halifax|AST ADT|40 30|01010101010101010101010|1SSS0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|39e4", + "America/Godthab|-03 -02|30 20|01010101010101|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0|17e3", + "America/Grand_Turk|AST EDT EST|40 40 50|012121212121212121212|1Vkv0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|37e2", + "America/Havana|CST CDT|50 40|01010101010101010101010|1SSR0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Oo0 1zc0 Rc0 1zc0|21e5", + "America/Mazatlan|MST MDT|70 60|0101010101010|1T3l0 1nX0 11B0 1nX0 14p0 1lb0 14p0 1lb0 14p0 1nX0 11B0 1nX0|44e4", + "America/Metlakatla|AKST AKDT PST|90 80 80|010120101010101010101010|1SSX0 1zb0 Op0 1zb0 uM0 jB0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|14e2", + "America/Miquelon|-03 -02|30 20|01010101010101010101010|1SSR0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|61e2", + "America/Noronha|-02|20|0||30e2", + "America/Ojinaga|MST MDT CST CDT|70 60 60 50|01010101010123232323232|1SSV0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1wn0 Rc0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|23e3", + "America/Santiago|-03 -04|30 40|01010101010101010101010|1Tk30 Ap0 1Nb0 Ap0 1zb0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0|62e5", + "America/Sao_Paulo|-02 -03|20 30|010101|1SKq0 1zd0 On0 1HB0 FX0|20e6", + "Atlantic/Azores|-01 +00|10 0|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|25e4", + "America/St_Johns|NST NDT|3u 2u|01010101010101010101010|1SSRu 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Op0 1zb0 Rd0 1zb0|11e4", + "Antarctica/Casey|+11 +08|-b0 -80|0101010|1Vkh0 1o30 14k0 1kr0 12l0 1o01|10", + "Asia/Bangkok|+07|-70|0||15e6", + "Asia/Vladivostok|+10|-a0|0||60e4", + "Australia/Sydney|AEDT AEST|-b0 -a0|01010101010101010101010|1T340 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|40e5", + "Asia/Tashkent|+05|-50|0||23e5", + "Pacific/Auckland|NZDT NZST|-d0 -c0|01010101010101010101010|1T320 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00|14e5", + "Europe/Istanbul|+03|-30|0||13e6", + "Antarctica/Troll|+00 +02|0 -20|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|40", + "Asia/Dhaka|+06|-60|0||16e6", + "Asia/Amman|EET EEST +03|-20 -30 -30|0101010101012|1T2m0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 LA0 1C00|25e5", + "Asia/Kamchatka|+12|-c0|0||18e4", + "Asia/Dubai|+04|-40|0||39e5", + "Asia/Beirut|EET EEST|-20 -30|01010101010101010101010|1T0m0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0|22e5", + "Asia/Kuala_Lumpur|+08|-80|0||71e5", + "Asia/Kolkata|IST|-5u|0||15e6", + "Asia/Chita|+09|-90|0||33e4", + "Asia/Shanghai|CST|-80|0||23e6", + "Asia/Colombo|+0530|-5u|0||22e5", + "Asia/Damascus|EET EEST +03|-20 -30 -30|0101010101012|1T2m0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0 WN0 1qL0 WN0 1qL0|26e5", + "Asia/Famagusta|+03 EET EEST|-30 -20 -30|0121212121212121212121|1Urd0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|", + "Asia/Gaza|EET EEST|-20 -30|01010101010101010101010|1SXX0 1qL0 WN0 1qL0 11c0 1on0 11B0 1o00 11A0 1qo0 XA0 1qp0 WN0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0 1qL0|18e5", + "Asia/Hong_Kong|HKT|-80|0||73e5", + "Asia/Jakarta|WIB|-70|0||31e6", + "Asia/Jayapura|WIT|-90|0||26e4", + "Asia/Jerusalem|IST IDT|-20 -30|01010101010101010101010|1SXA0 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1rz0 W10 1rz0 W10 1rz0 10N0 1oL0 10N0 1oL0 10N0 1oL0 10N0 1rz0|81e4", + "Asia/Kabul|+0430|-4u|0||46e5", + "Asia/Karachi|PKT|-50|0||24e6", + "Asia/Kathmandu|+0545|-5J|0||12e5", + "Asia/Sakhalin|+11|-b0|0||58e4", + "Asia/Makassar|WITA|-80|0||15e5", + "Asia/Manila|PST|-80|0||24e6", + "Europe/Athens|EET EEST|-20 -30|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|35e5", + "Asia/Pyongyang|KST KST|-8u -90|01|1VGf0|29e5", + "Asia/Qyzylorda|+06 +05|-60 -50|01|1Xei0|73e4", + "Asia/Rangoon|+0630|-6u|0||48e5", + "Asia/Seoul|KST|-90|0||23e6", + "Asia/Tehran|+0330 +0430|-3u -4u|0101010101010|1SWIu 1dz0 1cp0 1dz0 1cp0 1dz0 1cp0 1dz0 1cN0 1dz0 1cp0 1dz0|14e6", + "Asia/Tokyo|JST|-90|0||38e6", + "Europe/Lisbon|WET WEST|0 -10|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|27e5", + "Atlantic/Cape_Verde|-01|10|0||50e4", + "Australia/Adelaide|ACDT ACST|-au -9u|01010101010101010101010|1T34u 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|11e5", + "Australia/Brisbane|AEST|-a0|0||20e5", + "Australia/Darwin|ACST|-9u|0||12e4", + "Australia/Eucla|+0845|-8J|0||368", + "Australia/Lord_Howe|+11 +1030|-b0 -au|01010101010101010101010|1T330 1cMu 1cLu 1fAu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu 1fzu 1cMu 1cLu 1cMu 1cLu 1cMu 1cLu 1cMu|347", + "Australia/Perth|AWST|-80|0||18e5", + "Pacific/Easter|-05 -06|50 60|01010101010101010101010|1Tk30 Ap0 1Nb0 Ap0 1zb0 11B0 1nX0 11B0 1nX0 11B0 1nX0 14p0 1lb0 11B0 1qL0 11B0 1nX0 11B0 1nX0 11B0 1nX0 11B0|30e2", + "Europe/Dublin|GMT IST|0 -10|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|12e5", + "Etc/GMT-1|+01|-10|0||", + "Pacific/Fakaofo|+13|-d0|0||483", + "Pacific/Kiritimati|+14|-e0|0||51e2", + "Etc/GMT-2|+02|-20|0||", + "Pacific/Tahiti|-10|a0|0||18e4", + "Pacific/Niue|-11|b0|0||12e2", + "Etc/GMT+12|-12|c0|0||", + "Pacific/Galapagos|-06|60|0||25e3", + "Etc/GMT+7|-07|70|0||", + "Pacific/Pitcairn|-08|80|0||56", + "Pacific/Gambier|-09|90|0||125", + "Etc/UTC|UTC|0|0||", + "Europe/London|GMT BST|0 -10|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|10e6", + "Europe/Chisinau|EET EEST|-20 -30|01010101010101010101010|1T0o0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|67e4", + "Europe/Moscow|MSK|-30|0||16e6", + "Europe/Volgograd|+03 +04|-30 -40|010|1WQL0 5gn0|10e5", + "Pacific/Honolulu|HST|a0|0||37e4", + "MET|MET MEST|-10 -20|01010101010101010101010|1T0p0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0|", + "Pacific/Chatham|+1345 +1245|-dJ -cJ|01010101010101010101010|1T320 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00 1io0 1a00 1fA0 1a00 1fA0 1a00 1fA0 1a00|600", + "Pacific/Apia|+14 +13|-e0 -d0|0101010101|1T320 1a00 1fA0 1cM0 1fA0 1a00 1fA0 1a00 1fA0|37e3", + "Pacific/Fiji|+13 +12|-d0 -c0|0101010101|1Swe0 1VA0 s00 1VA0 s00 20o0 pc0 2hc0 bc0|88e4", + "Pacific/Guam|ChST|-a0|0||17e4", + "Pacific/Marquesas|-0930|9u|0||86e2", + "Pacific/Pago_Pago|SST|b0|0||37e2", + "Pacific/Norfolk|+11 +12|-b0 -c0|010101010101010101|219P0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1fA0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0 1cM0|25e4", + "Pacific/Tongatapu|+14 +13|-e0 -d0|01|1Swd0|75e3", + ], + links: [ + "Africa/Abidjan|Africa/Accra", + "Africa/Abidjan|Africa/Bamako", + "Africa/Abidjan|Africa/Banjul", + "Africa/Abidjan|Africa/Bissau", + "Africa/Abidjan|Africa/Conakry", + "Africa/Abidjan|Africa/Dakar", + "Africa/Abidjan|Africa/Freetown", + "Africa/Abidjan|Africa/Lome", + "Africa/Abidjan|Africa/Monrovia", + "Africa/Abidjan|Africa/Nouakchott", + "Africa/Abidjan|Africa/Ouagadougou", + "Africa/Abidjan|Africa/Timbuktu", + "Africa/Abidjan|America/Danmarkshavn", + "Africa/Abidjan|Atlantic/Reykjavik", + "Africa/Abidjan|Atlantic/St_Helena", + "Africa/Abidjan|Etc/GMT", + "Africa/Abidjan|Etc/GMT+0", + "Africa/Abidjan|Etc/GMT-0", + "Africa/Abidjan|Etc/GMT0", + "Africa/Abidjan|Etc/Greenwich", + "Africa/Abidjan|GMT", + "Africa/Abidjan|GMT+0", + "Africa/Abidjan|GMT-0", + "Africa/Abidjan|GMT0", + "Africa/Abidjan|Greenwich", + "Africa/Abidjan|Iceland", + "Africa/Algiers|Africa/Tunis", + "Africa/Cairo|Africa/Tripoli", + "Africa/Cairo|Egypt", + "Africa/Cairo|Europe/Kaliningrad", + "Africa/Cairo|Libya", + "Africa/Casablanca|Africa/El_Aaiun", + "Africa/Johannesburg|Africa/Maseru", + "Africa/Johannesburg|Africa/Mbabane", + "Africa/Lagos|Africa/Bangui", + "Africa/Lagos|Africa/Brazzaville", + "Africa/Lagos|Africa/Douala", + "Africa/Lagos|Africa/Kinshasa", + "Africa/Lagos|Africa/Libreville", + "Africa/Lagos|Africa/Luanda", + "Africa/Lagos|Africa/Malabo", + "Africa/Lagos|Africa/Ndjamena", + "Africa/Lagos|Africa/Niamey", + "Africa/Lagos|Africa/Porto-Novo", + "Africa/Maputo|Africa/Blantyre", + "Africa/Maputo|Africa/Bujumbura", + "Africa/Maputo|Africa/Gaborone", + "Africa/Maputo|Africa/Harare", + "Africa/Maputo|Africa/Kigali", + "Africa/Maputo|Africa/Lubumbashi", + "Africa/Maputo|Africa/Lusaka", + "Africa/Nairobi|Africa/Addis_Ababa", + "Africa/Nairobi|Africa/Asmara", + "Africa/Nairobi|Africa/Asmera", + "Africa/Nairobi|Africa/Dar_es_Salaam", + "Africa/Nairobi|Africa/Djibouti", + "Africa/Nairobi|Africa/Kampala", + "Africa/Nairobi|Africa/Mogadishu", + "Africa/Nairobi|Indian/Antananarivo", + "Africa/Nairobi|Indian/Comoro", + "Africa/Nairobi|Indian/Mayotte", + "America/Adak|America/Atka", + "America/Adak|US/Aleutian", + "America/Anchorage|America/Juneau", + "America/Anchorage|America/Nome", + "America/Anchorage|America/Sitka", + "America/Anchorage|America/Yakutat", + "America/Anchorage|US/Alaska", + "America/Campo_Grande|America/Cuiaba", + "America/Caracas|America/Boa_Vista", + "America/Caracas|America/Guyana", + "America/Caracas|America/La_Paz", + "America/Caracas|America/Manaus", + "America/Caracas|America/Porto_Velho", + "America/Caracas|Brazil/West", + "America/Caracas|Etc/GMT+4", + "America/Chicago|America/Indiana/Knox", + "America/Chicago|America/Indiana/Tell_City", + "America/Chicago|America/Knox_IN", + "America/Chicago|America/Matamoros", + "America/Chicago|America/Menominee", + "America/Chicago|America/North_Dakota/Beulah", + "America/Chicago|America/North_Dakota/Center", + "America/Chicago|America/North_Dakota/New_Salem", + "America/Chicago|America/Rainy_River", + "America/Chicago|America/Rankin_Inlet", + "America/Chicago|America/Resolute", + "America/Chicago|America/Winnipeg", + "America/Chicago|CST6CDT", + "America/Chicago|Canada/Central", + "America/Chicago|US/Central", + "America/Chicago|US/Indiana-Starke", + "America/Denver|America/Boise", + "America/Denver|America/Cambridge_Bay", + "America/Denver|America/Edmonton", + "America/Denver|America/Inuvik", + "America/Denver|America/Shiprock", + "America/Denver|America/Yellowknife", + "America/Denver|Canada/Mountain", + "America/Denver|MST7MDT", + "America/Denver|Navajo", + "America/Denver|US/Mountain", + "America/Fortaleza|America/Araguaina", + "America/Fortaleza|America/Argentina/Buenos_Aires", + "America/Fortaleza|America/Argentina/Catamarca", + "America/Fortaleza|America/Argentina/ComodRivadavia", + "America/Fortaleza|America/Argentina/Cordoba", + "America/Fortaleza|America/Argentina/Jujuy", + "America/Fortaleza|America/Argentina/La_Rioja", + "America/Fortaleza|America/Argentina/Mendoza", + "America/Fortaleza|America/Argentina/Rio_Gallegos", + "America/Fortaleza|America/Argentina/Salta", + "America/Fortaleza|America/Argentina/San_Juan", + "America/Fortaleza|America/Argentina/San_Luis", + "America/Fortaleza|America/Argentina/Tucuman", + "America/Fortaleza|America/Argentina/Ushuaia", + "America/Fortaleza|America/Bahia", + "America/Fortaleza|America/Belem", + "America/Fortaleza|America/Buenos_Aires", + "America/Fortaleza|America/Catamarca", + "America/Fortaleza|America/Cayenne", + "America/Fortaleza|America/Cordoba", + "America/Fortaleza|America/Jujuy", + "America/Fortaleza|America/Maceio", + "America/Fortaleza|America/Mendoza", + "America/Fortaleza|America/Montevideo", + "America/Fortaleza|America/Paramaribo", + "America/Fortaleza|America/Punta_Arenas", + "America/Fortaleza|America/Recife", + "America/Fortaleza|America/Rosario", + "America/Fortaleza|America/Santarem", + "America/Fortaleza|Antarctica/Palmer", + "America/Fortaleza|Antarctica/Rothera", + "America/Fortaleza|Atlantic/Stanley", + "America/Fortaleza|Etc/GMT+3", + "America/Godthab|America/Nuuk", + "America/Halifax|America/Glace_Bay", + "America/Halifax|America/Goose_Bay", + "America/Halifax|America/Moncton", + "America/Halifax|America/Thule", + "America/Halifax|Atlantic/Bermuda", + "America/Halifax|Canada/Atlantic", + "America/Havana|Cuba", + "America/Lima|America/Bogota", + "America/Lima|America/Eirunepe", + "America/Lima|America/Guayaquil", + "America/Lima|America/Porto_Acre", + "America/Lima|America/Rio_Branco", + "America/Lima|Brazil/Acre", + "America/Lima|Etc/GMT+5", + "America/Los_Angeles|America/Ensenada", + "America/Los_Angeles|America/Santa_Isabel", + "America/Los_Angeles|America/Tijuana", + "America/Los_Angeles|America/Vancouver", + "America/Los_Angeles|Canada/Pacific", + "America/Los_Angeles|Mexico/BajaNorte", + "America/Los_Angeles|PST8PDT", + "America/Los_Angeles|US/Pacific", + "America/Managua|America/Belize", + "America/Managua|America/Costa_Rica", + "America/Managua|America/El_Salvador", + "America/Managua|America/Guatemala", + "America/Managua|America/Regina", + "America/Managua|America/Swift_Current", + "America/Managua|America/Tegucigalpa", + "America/Managua|Canada/Saskatchewan", + "America/Mazatlan|Mexico/BajaSur", + "America/Mexico_City|America/Bahia_Banderas", + "America/Mexico_City|America/Merida", + "America/Mexico_City|America/Monterrey", + "America/Mexico_City|Mexico/General", + "America/New_York|America/Detroit", + "America/New_York|America/Fort_Wayne", + "America/New_York|America/Indiana/Indianapolis", + "America/New_York|America/Indiana/Marengo", + "America/New_York|America/Indiana/Petersburg", + "America/New_York|America/Indiana/Vevay", + "America/New_York|America/Indiana/Vincennes", + "America/New_York|America/Indiana/Winamac", + "America/New_York|America/Indianapolis", + "America/New_York|America/Iqaluit", + "America/New_York|America/Kentucky/Louisville", + "America/New_York|America/Kentucky/Monticello", + "America/New_York|America/Louisville", + "America/New_York|America/Montreal", + "America/New_York|America/Nassau", + "America/New_York|America/Nipigon", + "America/New_York|America/Pangnirtung", + "America/New_York|America/Port-au-Prince", + "America/New_York|America/Thunder_Bay", + "America/New_York|America/Toronto", + "America/New_York|Canada/Eastern", + "America/New_York|EST5EDT", + "America/New_York|US/East-Indiana", + "America/New_York|US/Eastern", + "America/New_York|US/Michigan", + "America/Noronha|Atlantic/South_Georgia", + "America/Noronha|Brazil/DeNoronha", + "America/Noronha|Etc/GMT+2", + "America/Panama|America/Atikokan", + "America/Panama|America/Cancun", + "America/Panama|America/Cayman", + "America/Panama|America/Coral_Harbour", + "America/Panama|America/Jamaica", + "America/Panama|EST", + "America/Panama|Jamaica", + "America/Phoenix|America/Creston", + "America/Phoenix|America/Dawson_Creek", + "America/Phoenix|America/Fort_Nelson", + "America/Phoenix|America/Hermosillo", + "America/Phoenix|MST", + "America/Phoenix|US/Arizona", + "America/Santiago|Chile/Continental", + "America/Santo_Domingo|America/Anguilla", + "America/Santo_Domingo|America/Antigua", + "America/Santo_Domingo|America/Aruba", + "America/Santo_Domingo|America/Barbados", + "America/Santo_Domingo|America/Blanc-Sablon", + "America/Santo_Domingo|America/Curacao", + "America/Santo_Domingo|America/Dominica", + "America/Santo_Domingo|America/Grenada", + "America/Santo_Domingo|America/Guadeloupe", + "America/Santo_Domingo|America/Kralendijk", + "America/Santo_Domingo|America/Lower_Princes", + "America/Santo_Domingo|America/Marigot", + "America/Santo_Domingo|America/Martinique", + "America/Santo_Domingo|America/Montserrat", + "America/Santo_Domingo|America/Port_of_Spain", + "America/Santo_Domingo|America/Puerto_Rico", + "America/Santo_Domingo|America/St_Barthelemy", + "America/Santo_Domingo|America/St_Kitts", + "America/Santo_Domingo|America/St_Lucia", + "America/Santo_Domingo|America/St_Thomas", + "America/Santo_Domingo|America/St_Vincent", + "America/Santo_Domingo|America/Tortola", + "America/Santo_Domingo|America/Virgin", + "America/Sao_Paulo|Brazil/East", + "America/St_Johns|Canada/Newfoundland", + "America/Whitehorse|America/Dawson", + "America/Whitehorse|Canada/Yukon", + "Asia/Bangkok|Antarctica/Davis", + "Asia/Bangkok|Asia/Barnaul", + "Asia/Bangkok|Asia/Ho_Chi_Minh", + "Asia/Bangkok|Asia/Hovd", + "Asia/Bangkok|Asia/Krasnoyarsk", + "Asia/Bangkok|Asia/Novokuznetsk", + "Asia/Bangkok|Asia/Novosibirsk", + "Asia/Bangkok|Asia/Phnom_Penh", + "Asia/Bangkok|Asia/Saigon", + "Asia/Bangkok|Asia/Tomsk", + "Asia/Bangkok|Asia/Vientiane", + "Asia/Bangkok|Etc/GMT-7", + "Asia/Bangkok|Indian/Christmas", + "Asia/Chita|Asia/Dili", + "Asia/Chita|Asia/Khandyga", + "Asia/Chita|Asia/Yakutsk", + "Asia/Chita|Etc/GMT-9", + "Asia/Chita|Pacific/Palau", + "Asia/Dhaka|Antarctica/Vostok", + "Asia/Dhaka|Asia/Almaty", + "Asia/Dhaka|Asia/Bishkek", + "Asia/Dhaka|Asia/Dacca", + "Asia/Dhaka|Asia/Kashgar", + "Asia/Dhaka|Asia/Omsk", + "Asia/Dhaka|Asia/Qostanay", + "Asia/Dhaka|Asia/Thimbu", + "Asia/Dhaka|Asia/Thimphu", + "Asia/Dhaka|Asia/Urumqi", + "Asia/Dhaka|Etc/GMT-6", + "Asia/Dhaka|Indian/Chagos", + "Asia/Dubai|Asia/Baku", + "Asia/Dubai|Asia/Muscat", + "Asia/Dubai|Asia/Tbilisi", + "Asia/Dubai|Asia/Yerevan", + "Asia/Dubai|Etc/GMT-4", + "Asia/Dubai|Europe/Astrakhan", + "Asia/Dubai|Europe/Samara", + "Asia/Dubai|Europe/Saratov", + "Asia/Dubai|Europe/Ulyanovsk", + "Asia/Dubai|Indian/Mahe", + "Asia/Dubai|Indian/Mauritius", + "Asia/Dubai|Indian/Reunion", + "Asia/Gaza|Asia/Hebron", + "Asia/Hong_Kong|Hongkong", + "Asia/Jakarta|Asia/Pontianak", + "Asia/Jerusalem|Asia/Tel_Aviv", + "Asia/Jerusalem|Israel", + "Asia/Kamchatka|Asia/Anadyr", + "Asia/Kamchatka|Etc/GMT-12", + "Asia/Kamchatka|Kwajalein", + "Asia/Kamchatka|Pacific/Funafuti", + "Asia/Kamchatka|Pacific/Kwajalein", + "Asia/Kamchatka|Pacific/Majuro", + "Asia/Kamchatka|Pacific/Nauru", + "Asia/Kamchatka|Pacific/Tarawa", + "Asia/Kamchatka|Pacific/Wake", + "Asia/Kamchatka|Pacific/Wallis", + "Asia/Kathmandu|Asia/Katmandu", + "Asia/Kolkata|Asia/Calcutta", + "Asia/Kuala_Lumpur|Asia/Brunei", + "Asia/Kuala_Lumpur|Asia/Choibalsan", + "Asia/Kuala_Lumpur|Asia/Irkutsk", + "Asia/Kuala_Lumpur|Asia/Kuching", + "Asia/Kuala_Lumpur|Asia/Singapore", + "Asia/Kuala_Lumpur|Asia/Ulaanbaatar", + "Asia/Kuala_Lumpur|Asia/Ulan_Bator", + "Asia/Kuala_Lumpur|Etc/GMT-8", + "Asia/Kuala_Lumpur|Singapore", + "Asia/Makassar|Asia/Ujung_Pandang", + "Asia/Rangoon|Asia/Yangon", + "Asia/Rangoon|Indian/Cocos", + "Asia/Sakhalin|Asia/Magadan", + "Asia/Sakhalin|Asia/Srednekolymsk", + "Asia/Sakhalin|Etc/GMT-11", + "Asia/Sakhalin|Pacific/Bougainville", + "Asia/Sakhalin|Pacific/Efate", + "Asia/Sakhalin|Pacific/Guadalcanal", + "Asia/Sakhalin|Pacific/Kosrae", + "Asia/Sakhalin|Pacific/Noumea", + "Asia/Sakhalin|Pacific/Pohnpei", + "Asia/Sakhalin|Pacific/Ponape", + "Asia/Seoul|ROK", + "Asia/Shanghai|Asia/Chongqing", + "Asia/Shanghai|Asia/Chungking", + "Asia/Shanghai|Asia/Harbin", + "Asia/Shanghai|Asia/Macao", + "Asia/Shanghai|Asia/Macau", + "Asia/Shanghai|Asia/Taipei", + "Asia/Shanghai|PRC", + "Asia/Shanghai|ROC", + "Asia/Tashkent|Antarctica/Mawson", + "Asia/Tashkent|Asia/Aqtau", + "Asia/Tashkent|Asia/Aqtobe", + "Asia/Tashkent|Asia/Ashgabat", + "Asia/Tashkent|Asia/Ashkhabad", + "Asia/Tashkent|Asia/Atyrau", + "Asia/Tashkent|Asia/Dushanbe", + "Asia/Tashkent|Asia/Oral", + "Asia/Tashkent|Asia/Samarkand", + "Asia/Tashkent|Asia/Yekaterinburg", + "Asia/Tashkent|Etc/GMT-5", + "Asia/Tashkent|Indian/Kerguelen", + "Asia/Tashkent|Indian/Maldives", + "Asia/Tehran|Iran", + "Asia/Tokyo|Japan", + "Asia/Vladivostok|Antarctica/DumontDUrville", + "Asia/Vladivostok|Asia/Ust-Nera", + "Asia/Vladivostok|Etc/GMT-10", + "Asia/Vladivostok|Pacific/Chuuk", + "Asia/Vladivostok|Pacific/Port_Moresby", + "Asia/Vladivostok|Pacific/Truk", + "Asia/Vladivostok|Pacific/Yap", + "Atlantic/Azores|America/Scoresbysund", + "Atlantic/Cape_Verde|Etc/GMT+1", + "Australia/Adelaide|Australia/Broken_Hill", + "Australia/Adelaide|Australia/South", + "Australia/Adelaide|Australia/Yancowinna", + "Australia/Brisbane|Australia/Lindeman", + "Australia/Brisbane|Australia/Queensland", + "Australia/Darwin|Australia/North", + "Australia/Lord_Howe|Australia/LHI", + "Australia/Perth|Australia/West", + "Australia/Sydney|Antarctica/Macquarie", + "Australia/Sydney|Australia/ACT", + "Australia/Sydney|Australia/Canberra", + "Australia/Sydney|Australia/Currie", + "Australia/Sydney|Australia/Hobart", + "Australia/Sydney|Australia/Melbourne", + "Australia/Sydney|Australia/NSW", + "Australia/Sydney|Australia/Tasmania", + "Australia/Sydney|Australia/Victoria", + "Etc/UTC|Etc/UCT", + "Etc/UTC|Etc/Universal", + "Etc/UTC|Etc/Zulu", + "Etc/UTC|UCT", + "Etc/UTC|UTC", + "Etc/UTC|Universal", + "Etc/UTC|Zulu", + "Europe/Athens|Asia/Nicosia", + "Europe/Athens|EET", + "Europe/Athens|Europe/Bucharest", + "Europe/Athens|Europe/Helsinki", + "Europe/Athens|Europe/Kiev", + "Europe/Athens|Europe/Kyiv", + "Europe/Athens|Europe/Mariehamn", + "Europe/Athens|Europe/Nicosia", + "Europe/Athens|Europe/Riga", + "Europe/Athens|Europe/Sofia", + "Europe/Athens|Europe/Tallinn", + "Europe/Athens|Europe/Uzhgorod", + "Europe/Athens|Europe/Vilnius", + "Europe/Athens|Europe/Zaporozhye", + "Europe/Chisinau|Europe/Tiraspol", + "Europe/Dublin|Eire", + "Europe/Istanbul|Antarctica/Syowa", + "Europe/Istanbul|Asia/Aden", + "Europe/Istanbul|Asia/Baghdad", + "Europe/Istanbul|Asia/Bahrain", + "Europe/Istanbul|Asia/Istanbul", + "Europe/Istanbul|Asia/Kuwait", + "Europe/Istanbul|Asia/Qatar", + "Europe/Istanbul|Asia/Riyadh", + "Europe/Istanbul|Etc/GMT-3", + "Europe/Istanbul|Europe/Kirov", + "Europe/Istanbul|Europe/Minsk", + "Europe/Istanbul|Turkey", + "Europe/Lisbon|Atlantic/Canary", + "Europe/Lisbon|Atlantic/Faeroe", + "Europe/Lisbon|Atlantic/Faroe", + "Europe/Lisbon|Atlantic/Madeira", + "Europe/Lisbon|Portugal", + "Europe/Lisbon|WET", + "Europe/London|Europe/Belfast", + "Europe/London|Europe/Guernsey", + "Europe/London|Europe/Isle_of_Man", + "Europe/London|Europe/Jersey", + "Europe/London|GB", + "Europe/London|GB-Eire", + "Europe/Moscow|Europe/Simferopol", + "Europe/Moscow|W-SU", + "Europe/Paris|Africa/Ceuta", + "Europe/Paris|Arctic/Longyearbyen", + "Europe/Paris|Atlantic/Jan_Mayen", + "Europe/Paris|CET", + "Europe/Paris|Europe/Amsterdam", + "Europe/Paris|Europe/Andorra", + "Europe/Paris|Europe/Belgrade", + "Europe/Paris|Europe/Berlin", + "Europe/Paris|Europe/Bratislava", + "Europe/Paris|Europe/Brussels", + "Europe/Paris|Europe/Budapest", + "Europe/Paris|Europe/Busingen", + "Europe/Paris|Europe/Copenhagen", + "Europe/Paris|Europe/Gibraltar", + "Europe/Paris|Europe/Ljubljana", + "Europe/Paris|Europe/Luxembourg", + "Europe/Paris|Europe/Madrid", + "Europe/Paris|Europe/Malta", + "Europe/Paris|Europe/Monaco", + "Europe/Paris|Europe/Oslo", + "Europe/Paris|Europe/Podgorica", + "Europe/Paris|Europe/Prague", + "Europe/Paris|Europe/Rome", + "Europe/Paris|Europe/San_Marino", + "Europe/Paris|Europe/Sarajevo", + "Europe/Paris|Europe/Skopje", + "Europe/Paris|Europe/Stockholm", + "Europe/Paris|Europe/Tirane", + "Europe/Paris|Europe/Vaduz", + "Europe/Paris|Europe/Vatican", + "Europe/Paris|Europe/Vienna", + "Europe/Paris|Europe/Warsaw", + "Europe/Paris|Europe/Zagreb", + "Europe/Paris|Europe/Zurich", + "Europe/Paris|Poland", + "Pacific/Auckland|Antarctica/McMurdo", + "Pacific/Auckland|Antarctica/South_Pole", + "Pacific/Auckland|NZ", + "Pacific/Chatham|NZ-CHAT", + "Pacific/Easter|Chile/EasterIsland", + "Pacific/Fakaofo|Etc/GMT-13", + "Pacific/Fakaofo|Pacific/Enderbury", + "Pacific/Fakaofo|Pacific/Kanton", + "Pacific/Galapagos|Etc/GMT+6", + "Pacific/Gambier|Etc/GMT+9", + "Pacific/Guam|Pacific/Saipan", + "Pacific/Honolulu|HST", + "Pacific/Honolulu|Pacific/Johnston", + "Pacific/Honolulu|US/Hawaii", + "Pacific/Kiritimati|Etc/GMT-14", + "Pacific/Niue|Etc/GMT+11", + "Pacific/Pago_Pago|Pacific/Midway", + "Pacific/Pago_Pago|Pacific/Samoa", + "Pacific/Pago_Pago|US/Samoa", + "Pacific/Pitcairn|Etc/GMT+8", + "Pacific/Tahiti|Etc/GMT+10", + "Pacific/Tahiti|Pacific/Rarotonga", + ], + countries: [ + "AD|Europe/Andorra", + "AE|Asia/Dubai", + "AF|Asia/Kabul", + "AG|America/Puerto_Rico America/Antigua", + "AI|America/Puerto_Rico America/Anguilla", + "AL|Europe/Tirane", + "AM|Asia/Yerevan", + "AO|Africa/Lagos Africa/Luanda", + "AQ|Antarctica/Casey Antarctica/Davis Antarctica/Mawson Antarctica/Palmer Antarctica/Rothera Antarctica/Troll Asia/Urumqi Pacific/Auckland Pacific/Port_Moresby Asia/Riyadh Antarctica/McMurdo Antarctica/DumontDUrville Antarctica/Syowa Antarctica/Vostok", + "AR|America/Argentina/Buenos_Aires America/Argentina/Cordoba America/Argentina/Salta America/Argentina/Jujuy America/Argentina/Tucuman America/Argentina/Catamarca America/Argentina/La_Rioja America/Argentina/San_Juan America/Argentina/Mendoza America/Argentina/San_Luis America/Argentina/Rio_Gallegos America/Argentina/Ushuaia", + "AS|Pacific/Pago_Pago", + "AT|Europe/Vienna", + "AU|Australia/Lord_Howe Antarctica/Macquarie Australia/Hobart Australia/Melbourne Australia/Sydney Australia/Broken_Hill Australia/Brisbane Australia/Lindeman Australia/Adelaide Australia/Darwin Australia/Perth Australia/Eucla", + "AW|America/Puerto_Rico America/Aruba", + "AX|Europe/Helsinki Europe/Mariehamn", + "AZ|Asia/Baku", + "BA|Europe/Belgrade Europe/Sarajevo", + "BB|America/Barbados", + "BD|Asia/Dhaka", + "BE|Europe/Brussels", + "BF|Africa/Abidjan Africa/Ouagadougou", + "BG|Europe/Sofia", + "BH|Asia/Qatar Asia/Bahrain", + "BI|Africa/Maputo Africa/Bujumbura", + "BJ|Africa/Lagos Africa/Porto-Novo", + "BL|America/Puerto_Rico America/St_Barthelemy", + "BM|Atlantic/Bermuda", + "BN|Asia/Kuching Asia/Brunei", + "BO|America/La_Paz", + "BQ|America/Puerto_Rico America/Kralendijk", + "BR|America/Noronha America/Belem America/Fortaleza America/Recife America/Araguaina America/Maceio America/Bahia America/Sao_Paulo America/Campo_Grande America/Cuiaba America/Santarem America/Porto_Velho America/Boa_Vista America/Manaus America/Eirunepe America/Rio_Branco", + "BS|America/Toronto America/Nassau", + "BT|Asia/Thimphu", + "BW|Africa/Maputo Africa/Gaborone", + "BY|Europe/Minsk", + "BZ|America/Belize", + "CA|America/St_Johns America/Halifax America/Glace_Bay America/Moncton America/Goose_Bay America/Toronto America/Iqaluit America/Winnipeg America/Resolute America/Rankin_Inlet America/Regina America/Swift_Current America/Edmonton America/Cambridge_Bay America/Yellowknife America/Inuvik America/Dawson_Creek America/Fort_Nelson America/Whitehorse America/Dawson America/Vancouver America/Panama America/Puerto_Rico America/Phoenix America/Blanc-Sablon America/Atikokan America/Creston", + "CC|Asia/Yangon Indian/Cocos", + "CD|Africa/Maputo Africa/Lagos Africa/Kinshasa Africa/Lubumbashi", + "CF|Africa/Lagos Africa/Bangui", + "CG|Africa/Lagos Africa/Brazzaville", + "CH|Europe/Zurich", + "CI|Africa/Abidjan", + "CK|Pacific/Rarotonga", + "CL|America/Santiago America/Punta_Arenas Pacific/Easter", + "CM|Africa/Lagos Africa/Douala", + "CN|Asia/Shanghai Asia/Urumqi", + "CO|America/Bogota", + "CR|America/Costa_Rica", + "CU|America/Havana", + "CV|Atlantic/Cape_Verde", + "CW|America/Puerto_Rico America/Curacao", + "CX|Asia/Bangkok Indian/Christmas", + "CY|Asia/Nicosia Asia/Famagusta", + "CZ|Europe/Prague", + "DE|Europe/Zurich Europe/Berlin Europe/Busingen", + "DJ|Africa/Nairobi Africa/Djibouti", + "DK|Europe/Berlin Europe/Copenhagen", + "DM|America/Puerto_Rico America/Dominica", + "DO|America/Santo_Domingo", + "DZ|Africa/Algiers", + "EC|America/Guayaquil Pacific/Galapagos", + "EE|Europe/Tallinn", + "EG|Africa/Cairo", + "EH|Africa/El_Aaiun", + "ER|Africa/Nairobi Africa/Asmara", + "ES|Europe/Madrid Africa/Ceuta Atlantic/Canary", + "ET|Africa/Nairobi Africa/Addis_Ababa", + "FI|Europe/Helsinki", + "FJ|Pacific/Fiji", + "FK|Atlantic/Stanley", + "FM|Pacific/Kosrae Pacific/Port_Moresby Pacific/Guadalcanal Pacific/Chuuk Pacific/Pohnpei", + "FO|Atlantic/Faroe", + "FR|Europe/Paris", + "GA|Africa/Lagos Africa/Libreville", + "GB|Europe/London", + "GD|America/Puerto_Rico America/Grenada", + "GE|Asia/Tbilisi", + "GF|America/Cayenne", + "GG|Europe/London Europe/Guernsey", + "GH|Africa/Abidjan Africa/Accra", + "GI|Europe/Gibraltar", + "GL|America/Nuuk America/Danmarkshavn America/Scoresbysund America/Thule", + "GM|Africa/Abidjan Africa/Banjul", + "GN|Africa/Abidjan Africa/Conakry", + "GP|America/Puerto_Rico America/Guadeloupe", + "GQ|Africa/Lagos Africa/Malabo", + "GR|Europe/Athens", + "GS|Atlantic/South_Georgia", + "GT|America/Guatemala", + "GU|Pacific/Guam", + "GW|Africa/Bissau", + "GY|America/Guyana", + "HK|Asia/Hong_Kong", + "HN|America/Tegucigalpa", + "HR|Europe/Belgrade Europe/Zagreb", + "HT|America/Port-au-Prince", + "HU|Europe/Budapest", + "ID|Asia/Jakarta Asia/Pontianak Asia/Makassar Asia/Jayapura", + "IE|Europe/Dublin", + "IL|Asia/Jerusalem", + "IM|Europe/London Europe/Isle_of_Man", + "IN|Asia/Kolkata", + "IO|Indian/Chagos", + "IQ|Asia/Baghdad", + "IR|Asia/Tehran", + "IS|Africa/Abidjan Atlantic/Reykjavik", + "IT|Europe/Rome", + "JE|Europe/London Europe/Jersey", + "JM|America/Jamaica", + "JO|Asia/Amman", + "JP|Asia/Tokyo", + "KE|Africa/Nairobi", + "KG|Asia/Bishkek", + "KH|Asia/Bangkok Asia/Phnom_Penh", + "KI|Pacific/Tarawa Pacific/Kanton Pacific/Kiritimati", + "KM|Africa/Nairobi Indian/Comoro", + "KN|America/Puerto_Rico America/St_Kitts", + "KP|Asia/Pyongyang", + "KR|Asia/Seoul", + "KW|Asia/Riyadh Asia/Kuwait", + "KY|America/Panama America/Cayman", + "KZ|Asia/Almaty Asia/Qyzylorda Asia/Qostanay Asia/Aqtobe Asia/Aqtau Asia/Atyrau Asia/Oral", + "LA|Asia/Bangkok Asia/Vientiane", + "LB|Asia/Beirut", + "LC|America/Puerto_Rico America/St_Lucia", + "LI|Europe/Zurich Europe/Vaduz", + "LK|Asia/Colombo", + "LR|Africa/Monrovia", + "LS|Africa/Johannesburg Africa/Maseru", + "LT|Europe/Vilnius", + "LU|Europe/Brussels Europe/Luxembourg", + "LV|Europe/Riga", + "LY|Africa/Tripoli", + "MA|Africa/Casablanca", + "MC|Europe/Paris Europe/Monaco", + "MD|Europe/Chisinau", + "ME|Europe/Belgrade Europe/Podgorica", + "MF|America/Puerto_Rico America/Marigot", + "MG|Africa/Nairobi Indian/Antananarivo", + "MH|Pacific/Tarawa Pacific/Kwajalein Pacific/Majuro", + "MK|Europe/Belgrade Europe/Skopje", + "ML|Africa/Abidjan Africa/Bamako", + "MM|Asia/Yangon", + "MN|Asia/Ulaanbaatar Asia/Hovd Asia/Choibalsan", + "MO|Asia/Macau", + "MP|Pacific/Guam Pacific/Saipan", + "MQ|America/Martinique", + "MR|Africa/Abidjan Africa/Nouakchott", + "MS|America/Puerto_Rico America/Montserrat", + "MT|Europe/Malta", + "MU|Indian/Mauritius", + "MV|Indian/Maldives", + "MW|Africa/Maputo Africa/Blantyre", + "MX|America/Mexico_City America/Cancun America/Merida America/Monterrey America/Matamoros America/Chihuahua America/Ciudad_Juarez America/Ojinaga America/Mazatlan America/Bahia_Banderas America/Hermosillo America/Tijuana", + "MY|Asia/Kuching Asia/Singapore Asia/Kuala_Lumpur", + "MZ|Africa/Maputo", + "NA|Africa/Windhoek", + "NC|Pacific/Noumea", + "NE|Africa/Lagos Africa/Niamey", + "NF|Pacific/Norfolk", + "NG|Africa/Lagos", + "NI|America/Managua", + "NL|Europe/Brussels Europe/Amsterdam", + "NO|Europe/Berlin Europe/Oslo", + "NP|Asia/Kathmandu", + "NR|Pacific/Nauru", + "NU|Pacific/Niue", + "NZ|Pacific/Auckland Pacific/Chatham", + "OM|Asia/Dubai Asia/Muscat", + "PA|America/Panama", + "PE|America/Lima", + "PF|Pacific/Tahiti Pacific/Marquesas Pacific/Gambier", + "PG|Pacific/Port_Moresby Pacific/Bougainville", + "PH|Asia/Manila", + "PK|Asia/Karachi", + "PL|Europe/Warsaw", + "PM|America/Miquelon", + "PN|Pacific/Pitcairn", + "PR|America/Puerto_Rico", + "PS|Asia/Gaza Asia/Hebron", + "PT|Europe/Lisbon Atlantic/Madeira Atlantic/Azores", + "PW|Pacific/Palau", + "PY|America/Asuncion", + "QA|Asia/Qatar", + "RE|Asia/Dubai Indian/Reunion", + "RO|Europe/Bucharest", + "RS|Europe/Belgrade", + "RU|Europe/Kaliningrad Europe/Moscow Europe/Simferopol Europe/Kirov Europe/Volgograd Europe/Astrakhan Europe/Saratov Europe/Ulyanovsk Europe/Samara Asia/Yekaterinburg Asia/Omsk Asia/Novosibirsk Asia/Barnaul Asia/Tomsk Asia/Novokuznetsk Asia/Krasnoyarsk Asia/Irkutsk Asia/Chita Asia/Yakutsk Asia/Khandyga Asia/Vladivostok Asia/Ust-Nera Asia/Magadan Asia/Sakhalin Asia/Srednekolymsk Asia/Kamchatka Asia/Anadyr", + "RW|Africa/Maputo Africa/Kigali", + "SA|Asia/Riyadh", + "SB|Pacific/Guadalcanal", + "SC|Asia/Dubai Indian/Mahe", + "SD|Africa/Khartoum", + "SE|Europe/Berlin Europe/Stockholm", + "SG|Asia/Singapore", + "SH|Africa/Abidjan Atlantic/St_Helena", + "SI|Europe/Belgrade Europe/Ljubljana", + "SJ|Europe/Berlin Arctic/Longyearbyen", + "SK|Europe/Prague Europe/Bratislava", + "SL|Africa/Abidjan Africa/Freetown", + "SM|Europe/Rome Europe/San_Marino", + "SN|Africa/Abidjan Africa/Dakar", + "SO|Africa/Nairobi Africa/Mogadishu", + "SR|America/Paramaribo", + "SS|Africa/Juba", + "ST|Africa/Sao_Tome", + "SV|America/El_Salvador", + "SX|America/Puerto_Rico America/Lower_Princes", + "SY|Asia/Damascus", + "SZ|Africa/Johannesburg Africa/Mbabane", + "TC|America/Grand_Turk", + "TD|Africa/Ndjamena", + "TF|Asia/Dubai Indian/Maldives Indian/Kerguelen", + "TG|Africa/Abidjan Africa/Lome", + "TH|Asia/Bangkok", + "TJ|Asia/Dushanbe", + "TK|Pacific/Fakaofo", + "TL|Asia/Dili", + "TM|Asia/Ashgabat", + "TN|Africa/Tunis", + "TO|Pacific/Tongatapu", + "TR|Europe/Istanbul", + "TT|America/Puerto_Rico America/Port_of_Spain", + "TV|Pacific/Tarawa Pacific/Funafuti", + "TW|Asia/Taipei", + "TZ|Africa/Nairobi Africa/Dar_es_Salaam", + "UA|Europe/Simferopol Europe/Kyiv", + "UG|Africa/Nairobi Africa/Kampala", + "UM|Pacific/Pago_Pago Pacific/Tarawa Pacific/Honolulu Pacific/Midway Pacific/Wake", + "US|America/New_York America/Detroit America/Kentucky/Louisville America/Kentucky/Monticello America/Indiana/Indianapolis America/Indiana/Vincennes America/Indiana/Winamac America/Indiana/Marengo America/Indiana/Petersburg America/Indiana/Vevay America/Chicago America/Indiana/Tell_City America/Indiana/Knox America/Menominee America/North_Dakota/Center America/North_Dakota/New_Salem America/North_Dakota/Beulah America/Denver America/Boise America/Phoenix America/Los_Angeles America/Anchorage America/Juneau America/Sitka America/Metlakatla America/Yakutat America/Nome America/Adak Pacific/Honolulu", + "UY|America/Montevideo", + "UZ|Asia/Samarkand Asia/Tashkent", + "VA|Europe/Rome Europe/Vatican", + "VC|America/Puerto_Rico America/St_Vincent", + "VE|America/Caracas", + "VG|America/Puerto_Rico America/Tortola", + "VI|America/Puerto_Rico America/St_Thomas", + "VN|Asia/Bangkok Asia/Ho_Chi_Minh", + "VU|Pacific/Efate", + "WF|Pacific/Tarawa Pacific/Wallis", + "WS|Pacific/Apia", + "YE|Asia/Riyadh Asia/Aden", + "YT|Africa/Nairobi Indian/Mayotte", + "ZA|Africa/Johannesburg", + "ZM|Africa/Maputo Africa/Lusaka", + "ZW|Africa/Maputo Africa/Harare", + ], + }); + + return moment; +}); diff --git a/app/static/libjs/moment.new.min.js b/app/static/libjs/moment.new.min.js new file mode 100644 index 00000000..d63167a8 --- /dev/null +++ b/app/static/libjs/moment.new.min.js @@ -0,0 +1,3309 @@ +!(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"; + var H; + function f() { + return H.apply(null, arguments); + } + function a(e) { + return ( + e instanceof Array || + "[object Array]" === Object.prototype.toString.call(e) + ); + } + function F(e) { + return null != e && "[object Object]" === Object.prototype.toString.call(e); + } + function c(e, t) { + return Object.prototype.hasOwnProperty.call(e, t); + } + function L(e) { + if (Object.getOwnPropertyNames) + return 0 === Object.getOwnPropertyNames(e).length; + for (var t in e) if (c(e, t)) return; + return 1; + } + function o(e) { + return void 0 === e; + } + function u(e) { + return ( + "number" == typeof e || + "[object Number]" === Object.prototype.toString.call(e) + ); + } + function V(e) { + return ( + e instanceof Date || "[object Date]" === Object.prototype.toString.call(e) + ); + } + function G(e, t) { + for (var n = [], s = e.length, i = 0; i < s; ++i) n.push(t(e[i], i)); + return n; + } + function E(e, t) { + for (var n in t) c(t, n) && (e[n] = t[n]); + return ( + c(t, "toString") && (e.toString = t.toString), + c(t, "valueOf") && (e.valueOf = t.valueOf), + e + ); + } + function l(e, t, n, s) { + return Pt(e, t, n, s, !0).utc(); + } + function m(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 A(e) { + if (null == e._isValid) { + var t = m(e), + n = j.call(t.parsedDateParts, function (e) { + return null != e; + }), + n = + !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 && + (n = + n && + 0 === t.charsLeftOver && + 0 === t.unusedTokens.length && + void 0 === t.bigHour), + null != Object.isFrozen && Object.isFrozen(e)) + ) + return n; + e._isValid = n; + } + return e._isValid; + } + function I(e) { + var t = l(NaN); + return null != e ? E(m(t), e) : (m(t).userInvalidated = !0), t; + } + var j = + Array.prototype.some || + function (e) { + for (var t = Object(this), n = t.length >>> 0, s = 0; s < n; s++) + if (s in t && e.call(this, t[s], s, t)) return !0; + return !1; + }, + Z = (f.momentProperties = []), + z = !1; + function $(e, t) { + var n, + s, + i, + r = Z.length; + if ( + (o(t._isAMomentObject) || (e._isAMomentObject = t._isAMomentObject), + o(t._i) || (e._i = t._i), + o(t._f) || (e._f = t._f), + o(t._l) || (e._l = t._l), + o(t._strict) || (e._strict = t._strict), + o(t._tzm) || (e._tzm = t._tzm), + o(t._isUTC) || (e._isUTC = t._isUTC), + o(t._offset) || (e._offset = t._offset), + o(t._pf) || (e._pf = m(t)), + o(t._locale) || (e._locale = t._locale), + 0 < r) + ) + for (n = 0; n < r; n++) o((i = t[(s = Z[n])])) || (e[s] = i); + return e; + } + function q(e) { + $(this, e), + (this._d = new Date(null != e._d ? e._d.getTime() : NaN)), + this.isValid() || (this._d = new Date(NaN)), + !1 === z && ((z = !0), f.updateOffset(this), (z = !1)); + } + function h(e) { + return e instanceof q || (null != e && null != e._isAMomentObject); + } + function B(e) { + !1 === f.suppressDeprecationWarnings && + "undefined" != typeof console && + console.warn && + console.warn("Deprecation warning: " + e); + } + function e(r, a) { + var o = !0; + return E(function () { + if ((null != f.deprecationHandler && f.deprecationHandler(null, r), o)) { + for (var e, t, n = [], s = arguments.length, i = 0; i < s; i++) { + if (((e = ""), "object" == typeof arguments[i])) { + for (t in ((e += "\n[" + i + "] "), arguments[0])) + c(arguments[0], t) && (e += t + ": " + arguments[0][t] + ", "); + e = e.slice(0, -2); + } else e = arguments[i]; + n.push(e); + } + B( + r + + "\nArguments: " + + Array.prototype.slice.call(n).join("") + + "\n" + + new Error().stack + ), + (o = !1); + } + return a.apply(this, arguments); + }, a); + } + var J = {}; + function Q(e, t) { + null != f.deprecationHandler && f.deprecationHandler(e, t), + J[e] || (B(t), (J[e] = !0)); + } + function d(e) { + return ( + ("undefined" != typeof Function && e instanceof Function) || + "[object Function]" === Object.prototype.toString.call(e) + ); + } + function X(e, t) { + var n, + s = E({}, e); + for (n in t) + c(t, n) && + (F(e[n]) && F(t[n]) + ? ((s[n] = {}), E(s[n], e[n]), E(s[n], t[n])) + : null != t[n] + ? (s[n] = t[n]) + : delete s[n]); + for (n in e) c(e, n) && !c(t, n) && F(e[n]) && (s[n] = E({}, s[n])); + return s; + } + function K(e) { + null != e && this.set(e); + } + (f.suppressDeprecationWarnings = !1), (f.deprecationHandler = null); + var ee = + Object.keys || + function (e) { + var t, + n = []; + for (t in e) c(e, t) && n.push(t); + return n; + }; + function r(e, t, n) { + var s = "" + Math.abs(e); + return ( + (0 <= e ? (n ? "+" : "") : "-") + + Math.pow(10, Math.max(0, t - s.length)) + .toString() + .substr(1) + + s + ); + } + var te = + /(\[[^\[]*\])|(\\)?([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, + ne = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g, + se = {}, + ie = {}; + function s(e, t, n, s) { + var i = + "string" == typeof s + ? function () { + return this[s](); + } + : s; + e && (ie[e] = i), + t && + (ie[t[0]] = function () { + return r(i.apply(this, arguments), t[1], t[2]); + }), + n && + (ie[n] = function () { + return this.localeData().ordinal(i.apply(this, arguments), e); + }); + } + function re(e, t) { + return e.isValid() + ? ((t = ae(t, e.localeData())), + (se[t] = + se[t] || + (function (s) { + for (var e, i = s.match(te), t = 0, r = i.length; t < r; t++) + ie[i[t]] + ? (i[t] = ie[i[t]]) + : (i[t] = (e = i[t]).match(/\[[\s\S]/) + ? e.replace(/^\[|\]$/g, "") + : e.replace(/\\/g, "")); + return function (e) { + for (var t = "", n = 0; n < r; n++) + t += d(i[n]) ? i[n].call(e, s) : i[n]; + return t; + }; + })(t)), + se[t](e)) + : e.localeData().invalidDate(); + } + function ae(e, t) { + var n = 5; + function s(e) { + return t.longDateFormat(e) || e; + } + for (ne.lastIndex = 0; 0 <= n && ne.test(e); ) + (e = e.replace(ne, s)), (ne.lastIndex = 0), --n; + return e; + } + var oe = {}; + function t(e, t) { + var n = e.toLowerCase(); + oe[n] = oe[n + "s"] = oe[t] = e; + } + function _(e) { + return "string" == typeof e ? oe[e] || oe[e.toLowerCase()] : void 0; + } + function ue(e) { + var t, + n, + s = {}; + for (n in e) c(e, n) && (t = _(n)) && (s[t] = e[n]); + return s; + } + var le = {}; + function n(e, t) { + le[e] = t; + } + function he(e) { + return (e % 4 == 0 && e % 100 != 0) || e % 400 == 0; + } + function y(e) { + return e < 0 ? Math.ceil(e) || 0 : Math.floor(e); + } + function g(e) { + var e = +e, + t = 0; + return (t = 0 != e && isFinite(e) ? y(e) : t); + } + function de(t, n) { + return function (e) { + return null != e + ? (fe(this, t, e), f.updateOffset(this, n), this) + : ce(this, t); + }; + } + function ce(e, t) { + return e.isValid() ? e._d["get" + (e._isUTC ? "UTC" : "") + t]() : NaN; + } + function fe(e, t, n) { + e.isValid() && + !isNaN(n) && + ("FullYear" === t && he(e.year()) && 1 === e.month() && 29 === e.date() + ? ((n = g(n)), + e._d["set" + (e._isUTC ? "UTC" : "") + t]( + n, + e.month(), + We(n, e.month()) + )) + : e._d["set" + (e._isUTC ? "UTC" : "") + t](n)); + } + var i = /\d/, + w = /\d\d/, + me = /\d{3}/, + _e = /\d{4}/, + ye = /[+-]?\d{6}/, + p = /\d\d?/, + ge = /\d\d\d\d?/, + we = /\d\d\d\d\d\d?/, + pe = /\d{1,3}/, + ve = /\d{1,4}/, + ke = /[+-]?\d{1,6}/, + Me = /\d+/, + De = /[+-]?\d+/, + Se = /Z|[+-]\d\d:?\d\d/gi, + Ye = /Z|[+-]\d\d(?::?\d\d)?/gi, + v = + /[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 k(e, n, s) { + be[e] = d(n) + ? n + : function (e, t) { + return e && s ? s : n; + }; + } + function Oe(e, t) { + return c(be, e) + ? be[e](t._strict, t._locale) + : new RegExp( + M( + e + .replace("\\", "") + .replace( + /\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, + function (e, t, n, s, i) { + return t || n || s || i; + } + ) + ) + ); + } + function M(e) { + return e.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); + } + var be = {}, + xe = {}; + function D(e, n) { + var t, + s, + i = n; + for ( + "string" == typeof e && (e = [e]), + u(n) && + (i = function (e, t) { + t[n] = g(e); + }), + s = e.length, + t = 0; + t < s; + t++ + ) + xe[e[t]] = i; + } + function Te(e, i) { + D(e, function (e, t, n, s) { + (n._w = n._w || {}), i(e, n._w, n, s); + }); + } + var S, + Y = 0, + O = 1, + b = 2, + x = 3, + T = 4, + N = 5, + Ne = 6, + Pe = 7, + Re = 8; + function We(e, t) { + if (isNaN(e) || isNaN(t)) return NaN; + var n = ((t % (n = 12)) + n) % n; + return (e += (t - n) / 12), 1 == n ? (he(e) ? 29 : 28) : 31 - ((n % 7) % 2); + } + (S = + Array.prototype.indexOf || + function (e) { + for (var t = 0; t < this.length; ++t) if (this[t] === e) return t; + return -1; + }), + s("M", ["MM", 2], "Mo", function () { + return this.month() + 1; + }), + s("MMM", 0, 0, function (e) { + return this.localeData().monthsShort(this, e); + }), + s("MMMM", 0, 0, function (e) { + return this.localeData().months(this, e); + }), + t("month", "M"), + n("month", 8), + k("M", p), + k("MM", p, w), + k("MMM", function (e, t) { + return t.monthsShortRegex(e); + }), + k("MMMM", function (e, t) { + return t.monthsRegex(e); + }), + D(["M", "MM"], function (e, t) { + t[O] = g(e) - 1; + }), + D(["MMM", "MMMM"], function (e, t, n, s) { + s = n._locale.monthsParse(e, s, n._strict); + null != s ? (t[O] = s) : (m(n).invalidMonth = e); + }); + var Ce = + "January_February_March_April_May_June_July_August_September_October_November_December".split( + "_" + ), + Ue = "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), + He = /D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/, + Fe = v, + Le = v; + function Ve(e, t) { + var n; + if (e.isValid()) { + if ("string" == typeof t) + if (/^\d+$/.test(t)) t = g(t); + else if (!u((t = e.localeData().monthsParse(t)))) return; + (n = Math.min(e.date(), We(e.year(), t))), + e._d["set" + (e._isUTC ? "UTC" : "") + "Month"](t, n); + } + } + function Ge(e) { + return null != e + ? (Ve(this, e), f.updateOffset(this, !0), this) + : ce(this, "Month"); + } + function Ee() { + function e(e, t) { + return t.length - e.length; + } + for (var t, n = [], s = [], i = [], r = 0; r < 12; r++) + (t = l([2e3, r])), + n.push(this.monthsShort(t, "")), + s.push(this.months(t, "")), + i.push(this.months(t, "")), + i.push(this.monthsShort(t, "")); + for (n.sort(e), s.sort(e), i.sort(e), r = 0; r < 12; r++) + (n[r] = M(n[r])), (s[r] = M(s[r])); + for (r = 0; r < 24; r++) i[r] = M(i[r]); + (this._monthsRegex = new RegExp("^(" + i.join("|") + ")", "i")), + (this._monthsShortRegex = this._monthsRegex), + (this._monthsStrictRegex = new RegExp("^(" + s.join("|") + ")", "i")), + (this._monthsShortStrictRegex = new RegExp( + "^(" + n.join("|") + ")", + "i" + )); + } + function Ae(e) { + return he(e) ? 366 : 365; + } + s("Y", 0, 0, function () { + var e = this.year(); + return e <= 9999 ? r(e, 4) : "+" + e; + }), + s(0, ["YY", 2], 0, function () { + return this.year() % 100; + }), + s(0, ["YYYY", 4], 0, "year"), + s(0, ["YYYYY", 5], 0, "year"), + s(0, ["YYYYYY", 6, !0], 0, "year"), + t("year", "y"), + n("year", 1), + k("Y", De), + k("YY", p, w), + k("YYYY", ve, _e), + k("YYYYY", ke, ye), + k("YYYYYY", ke, ye), + D(["YYYYY", "YYYYYY"], Y), + D("YYYY", function (e, t) { + t[Y] = 2 === e.length ? f.parseTwoDigitYear(e) : g(e); + }), + D("YY", function (e, t) { + t[Y] = f.parseTwoDigitYear(e); + }), + D("Y", function (e, t) { + t[Y] = parseInt(e, 10); + }), + (f.parseTwoDigitYear = function (e) { + return g(e) + (68 < g(e) ? 1900 : 2e3); + }); + var Ie = de("FullYear", !0); + function je(e, t, n, s, i, r, a) { + var o; + return ( + e < 100 && 0 <= e + ? ((o = new Date(e + 400, t, n, s, i, r, a)), + isFinite(o.getFullYear()) && o.setFullYear(e)) + : (o = new Date(e, t, n, s, i, r, a)), + o + ); + } + function Ze(e) { + var t; + return ( + e < 100 && 0 <= e + ? (((t = Array.prototype.slice.call(arguments))[0] = e + 400), + (t = new Date(Date.UTC.apply(null, t))), + isFinite(t.getUTCFullYear()) && t.setUTCFullYear(e)) + : (t = new Date(Date.UTC.apply(null, arguments))), + t + ); + } + function ze(e, t, n) { + n = 7 + t - n; + return n - ((7 + Ze(e, 0, n).getUTCDay() - t) % 7) - 1; + } + function $e(e, t, n, s, i) { + var r, + t = 1 + 7 * (t - 1) + ((7 + n - s) % 7) + ze(e, s, i), + n = + t <= 0 + ? Ae((r = e - 1)) + t + : t > Ae(e) + ? ((r = e + 1), t - Ae(e)) + : ((r = e), t); + return { year: r, dayOfYear: n }; + } + function qe(e, t, n) { + var s, + i, + r = ze(e.year(), t, n), + r = Math.floor((e.dayOfYear() - r - 1) / 7) + 1; + return ( + r < 1 + ? (s = r + P((i = e.year() - 1), t, n)) + : r > P(e.year(), t, n) + ? ((s = r - P(e.year(), t, n)), (i = e.year() + 1)) + : ((i = e.year()), (s = r)), + { week: s, year: i } + ); + } + function P(e, t, n) { + var s = ze(e, t, n), + t = ze(e + 1, t, n); + return (Ae(e) - s + t) / 7; + } + s("w", ["ww", 2], "wo", "week"), + s("W", ["WW", 2], "Wo", "isoWeek"), + t("week", "w"), + t("isoWeek", "W"), + n("week", 5), + n("isoWeek", 5), + k("w", p), + k("ww", p, w), + k("W", p), + k("WW", p, w), + Te(["w", "ww", "W", "WW"], function (e, t, n, s) { + t[s.substr(0, 1)] = g(e); + }); + function Be(e, t) { + return e.slice(t, 7).concat(e.slice(0, t)); + } + s("d", 0, "do", "day"), + s("dd", 0, 0, function (e) { + return this.localeData().weekdaysMin(this, e); + }), + s("ddd", 0, 0, function (e) { + return this.localeData().weekdaysShort(this, e); + }), + s("dddd", 0, 0, function (e) { + return this.localeData().weekdays(this, e); + }), + s("e", 0, 0, "weekday"), + s("E", 0, 0, "isoWeekday"), + t("day", "d"), + t("weekday", "e"), + t("isoWeekday", "E"), + n("day", 11), + n("weekday", 11), + n("isoWeekday", 11), + k("d", p), + k("e", p), + k("E", p), + k("dd", function (e, t) { + return t.weekdaysMinRegex(e); + }), + k("ddd", function (e, t) { + return t.weekdaysShortRegex(e); + }), + k("dddd", function (e, t) { + return t.weekdaysRegex(e); + }), + Te(["dd", "ddd", "dddd"], function (e, t, n, s) { + s = n._locale.weekdaysParse(e, s, n._strict); + null != s ? (t.d = s) : (m(n).invalidWeekday = e); + }), + Te(["d", "e", "E"], function (e, t, n, s) { + t[s] = g(e); + }); + var Je = "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split( + "_" + ), + Qe = "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), + Xe = "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), + Ke = v, + et = v, + tt = v; + function nt() { + function e(e, t) { + return t.length - e.length; + } + for (var t, n, s, i = [], r = [], a = [], o = [], u = 0; u < 7; u++) + (s = l([2e3, 1]).day(u)), + (t = M(this.weekdaysMin(s, ""))), + (n = M(this.weekdaysShort(s, ""))), + (s = M(this.weekdays(s, ""))), + i.push(t), + r.push(n), + a.push(s), + o.push(t), + o.push(n), + o.push(s); + i.sort(e), + r.sort(e), + a.sort(e), + o.sort(e), + (this._weekdaysRegex = new RegExp("^(" + o.join("|") + ")", "i")), + (this._weekdaysShortRegex = this._weekdaysRegex), + (this._weekdaysMinRegex = this._weekdaysRegex), + (this._weekdaysStrictRegex = new RegExp("^(" + a.join("|") + ")", "i")), + (this._weekdaysShortStrictRegex = new RegExp( + "^(" + r.join("|") + ")", + "i" + )), + (this._weekdaysMinStrictRegex = new RegExp( + "^(" + i.join("|") + ")", + "i" + )); + } + function st() { + return this.hours() % 12 || 12; + } + function it(e, t) { + s(e, 0, 0, function () { + return this.localeData().meridiem(this.hours(), this.minutes(), t); + }); + } + function rt(e, t) { + return t._meridiemParse; + } + s("H", ["HH", 2], 0, "hour"), + s("h", ["hh", 2], 0, st), + s("k", ["kk", 2], 0, function () { + return this.hours() || 24; + }), + s("hmm", 0, 0, function () { + return "" + st.apply(this) + r(this.minutes(), 2); + }), + s("hmmss", 0, 0, function () { + return "" + st.apply(this) + r(this.minutes(), 2) + r(this.seconds(), 2); + }), + s("Hmm", 0, 0, function () { + return "" + this.hours() + r(this.minutes(), 2); + }), + s("Hmmss", 0, 0, function () { + return "" + this.hours() + r(this.minutes(), 2) + r(this.seconds(), 2); + }), + it("a", !0), + it("A", !1), + t("hour", "h"), + n("hour", 13), + k("a", rt), + k("A", rt), + k("H", p), + k("h", p), + k("k", p), + k("HH", p, w), + k("hh", p, w), + k("kk", p, w), + k("hmm", ge), + k("hmmss", we), + k("Hmm", ge), + k("Hmmss", we), + D(["H", "HH"], x), + D(["k", "kk"], function (e, t, n) { + e = g(e); + t[x] = 24 === e ? 0 : e; + }), + D(["a", "A"], function (e, t, n) { + (n._isPm = n._locale.isPM(e)), (n._meridiem = e); + }), + D(["h", "hh"], function (e, t, n) { + (t[x] = g(e)), (m(n).bigHour = !0); + }), + D("hmm", function (e, t, n) { + var s = e.length - 2; + (t[x] = g(e.substr(0, s))), (t[T] = g(e.substr(s))), (m(n).bigHour = !0); + }), + D("hmmss", function (e, t, n) { + var s = e.length - 4, + i = e.length - 2; + (t[x] = g(e.substr(0, s))), + (t[T] = g(e.substr(s, 2))), + (t[N] = g(e.substr(i))), + (m(n).bigHour = !0); + }), + D("Hmm", function (e, t, n) { + var s = e.length - 2; + (t[x] = g(e.substr(0, s))), (t[T] = g(e.substr(s))); + }), + D("Hmmss", function (e, t, n) { + var s = e.length - 4, + i = e.length - 2; + (t[x] = g(e.substr(0, s))), + (t[T] = g(e.substr(s, 2))), + (t[N] = g(e.substr(i))); + }); + v = de("Hours", !0); + var at, + ot = { + 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: Ce, + monthsShort: Ue, + week: { dow: 0, doy: 6 }, + weekdays: Je, + weekdaysMin: Xe, + weekdaysShort: Qe, + meridiemParse: /[ap]\.?m?\.?/i, + }, + R = {}, + ut = {}; + function lt(e) { + return e && e.toLowerCase().replace("_", "-"); + } + function ht(e) { + for (var t, n, s, i, r = 0; r < e.length; ) { + for ( + t = (i = lt(e[r]).split("-")).length, + n = (n = lt(e[r + 1])) ? n.split("-") : null; + 0 < t; + + ) { + if ((s = dt(i.slice(0, t).join("-")))) return s; + if ( + n && + n.length >= t && + (function (e, t) { + for (var n = Math.min(e.length, t.length), s = 0; s < n; s += 1) + if (e[s] !== t[s]) return s; + return n; + })(i, n) >= + t - 1 + ) + break; + t--; + } + r++; + } + return at; + } + function dt(t) { + var e; + if ( + void 0 === R[t] && + "undefined" != typeof module && + module && + module.exports && + null != t.match("^[^/\\\\]*$") + ) + try { + (e = at._abbr), require("./locale/" + t), ct(e); + } catch (e) { + R[t] = null; + } + return R[t]; + } + function ct(e, t) { + return ( + e && + ((t = o(t) ? mt(e) : ft(e, t)) + ? (at = t) + : "undefined" != typeof console && + console.warn && + console.warn( + "Locale " + e + " not found. Did you forget to load it?" + )), + at._abbr + ); + } + function ft(e, t) { + if (null === t) return delete R[e], null; + var n, + s = ot; + if (((t.abbr = e), null != R[e])) + Q( + "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 = R[e]._config); + else if (null != t.parentLocale) + if (null != R[t.parentLocale]) s = R[t.parentLocale]._config; + else { + if (null == (n = dt(t.parentLocale))) + return ( + ut[t.parentLocale] || (ut[t.parentLocale] = []), + ut[t.parentLocale].push({ name: e, config: t }), + null + ); + s = n._config; + } + return ( + (R[e] = new K(X(s, t))), + ut[e] && + ut[e].forEach(function (e) { + ft(e.name, e.config); + }), + ct(e), + R[e] + ); + } + function mt(e) { + var t; + if (!(e = e && e._locale && e._locale._abbr ? e._locale._abbr : e)) + return at; + if (!a(e)) { + if ((t = dt(e))) return t; + e = [e]; + } + return ht(e); + } + function _t(e) { + var t = e._a; + return ( + t && + -2 === m(e).overflow && + ((t = + t[O] < 0 || 11 < t[O] + ? O + : t[b] < 1 || t[b] > We(t[Y], t[O]) + ? b + : t[x] < 0 || + 24 < t[x] || + (24 === t[x] && (0 !== t[T] || 0 !== t[N] || 0 !== t[Ne])) + ? x + : t[T] < 0 || 59 < t[T] + ? T + : t[N] < 0 || 59 < t[N] + ? N + : t[Ne] < 0 || 999 < t[Ne] + ? Ne + : -1), + m(e)._overflowDayOfYear && (t < Y || b < t) && (t = b), + m(e)._overflowWeeks && -1 === t && (t = Pe), + m(e)._overflowWeekday && -1 === t && (t = Re), + (m(e).overflow = t)), + e + ); + } + var yt = + /^\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)?)?$/, + gt = + /^\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)?)?$/, + wt = /Z|[+-]\d\d(?::?\d\d)?/, + pt = [ + ["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], + ], + vt = [ + ["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/], + ], + kt = /^\/?Date\((-?\d+)/i, + Mt = + /^(?:(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}))$/, + Dt = { + UT: 0, + GMT: 0, + EDT: -240, + EST: -300, + CDT: -300, + CST: -360, + MDT: -360, + MST: -420, + PDT: -420, + PST: -480, + }; + function St(e) { + var t, + n, + s, + i, + r, + a, + o = e._i, + u = yt.exec(o) || gt.exec(o), + o = pt.length, + l = vt.length; + if (u) { + for (m(e).iso = !0, t = 0, n = o; t < n; t++) + if (pt[t][1].exec(u[1])) { + (i = pt[t][0]), (s = !1 !== pt[t][2]); + break; + } + if (null == i) e._isValid = !1; + else { + if (u[3]) { + for (t = 0, n = l; t < n; t++) + if (vt[t][1].exec(u[3])) { + r = (u[2] || " ") + vt[t][0]; + break; + } + if (null == r) return void (e._isValid = !1); + } + if (s || null == r) { + if (u[4]) { + if (!wt.exec(u[4])) return void (e._isValid = !1); + a = "Z"; + } + (e._f = i + (r || "") + (a || "")), Tt(e); + } else e._isValid = !1; + } + } else e._isValid = !1; + } + function Yt(e, t, n, s, i, r) { + e = [ + (function (e) { + e = parseInt(e, 10); + { + if (e <= 49) return 2e3 + e; + if (e <= 999) return 1900 + e; + } + return e; + })(e), + Ue.indexOf(t), + parseInt(n, 10), + parseInt(s, 10), + parseInt(i, 10), + ]; + return r && e.push(parseInt(r, 10)), e; + } + function Ot(e) { + var t, + n, + s, + i, + r = Mt.exec( + e._i + .replace(/\([^()]*\)|[\n\t]/g, " ") + .replace(/(\s\s+)/g, " ") + .replace(/^\s\s*/, "") + .replace(/\s\s*$/, "") + ); + r + ? ((t = Yt(r[4], r[3], r[2], r[5], r[6], r[7])), + (n = r[1]), + (s = t), + (i = e), + n && Qe.indexOf(n) !== new Date(s[0], s[1], s[2]).getDay() + ? ((m(i).weekdayMismatch = !0), (i._isValid = !1)) + : ((e._a = t), + (e._tzm = + ((n = r[8]), + (s = r[9]), + (i = r[10]), + n + ? Dt[n] + : s + ? 0 + : 60 * (((n = parseInt(i, 10)) - (s = n % 100)) / 100) + s)), + (e._d = Ze.apply(null, e._a)), + e._d.setUTCMinutes(e._d.getUTCMinutes() - e._tzm), + (m(e).rfc2822 = !0))) + : (e._isValid = !1); + } + function bt(e, t, n) { + return null != e ? e : null != t ? t : n; + } + function xt(e) { + var t, + n, + s, + i, + r, + a, + o, + u, + l, + h, + d, + c = []; + if (!e._d) { + for ( + s = e, + i = new Date(f.now()), + n = s._useUTC + ? [i.getUTCFullYear(), i.getUTCMonth(), i.getUTCDate()] + : [i.getFullYear(), i.getMonth(), i.getDate()], + e._w && + null == e._a[b] && + null == e._a[O] && + (null != (i = (s = e)._w).GG || null != i.W || null != i.E + ? ((u = 1), + (l = 4), + (r = bt(i.GG, s._a[Y], qe(W(), 1, 4).year)), + (a = bt(i.W, 1)), + ((o = bt(i.E, 1)) < 1 || 7 < o) && (h = !0)) + : ((u = s._locale._week.dow), + (l = s._locale._week.doy), + (d = qe(W(), u, l)), + (r = bt(i.gg, s._a[Y], d.year)), + (a = bt(i.w, d.week)), + null != i.d + ? ((o = i.d) < 0 || 6 < o) && (h = !0) + : null != i.e + ? ((o = i.e + u), (i.e < 0 || 6 < i.e) && (h = !0)) + : (o = u)), + a < 1 || a > P(r, u, l) + ? (m(s)._overflowWeeks = !0) + : null != h + ? (m(s)._overflowWeekday = !0) + : ((d = $e(r, a, o, u, l)), + (s._a[Y] = d.year), + (s._dayOfYear = d.dayOfYear))), + null != e._dayOfYear && + ((i = bt(e._a[Y], n[Y])), + (e._dayOfYear > Ae(i) || 0 === e._dayOfYear) && + (m(e)._overflowDayOfYear = !0), + (h = Ze(i, 0, e._dayOfYear)), + (e._a[O] = h.getUTCMonth()), + (e._a[b] = h.getUTCDate())), + t = 0; + t < 3 && null == e._a[t]; + ++t + ) + e._a[t] = c[t] = n[t]; + for (; t < 7; t++) + e._a[t] = c[t] = null == e._a[t] ? (2 === t ? 1 : 0) : e._a[t]; + 24 === e._a[x] && + 0 === e._a[T] && + 0 === e._a[N] && + 0 === e._a[Ne] && + ((e._nextDay = !0), (e._a[x] = 0)), + (e._d = (e._useUTC ? Ze : je).apply(null, c)), + (r = e._useUTC ? e._d.getUTCDay() : e._d.getDay()), + null != e._tzm && e._d.setUTCMinutes(e._d.getUTCMinutes() - e._tzm), + e._nextDay && (e._a[x] = 24), + e._w && + void 0 !== e._w.d && + e._w.d !== r && + (m(e).weekdayMismatch = !0); + } + } + function Tt(e) { + if (e._f === f.ISO_8601) St(e); + else if (e._f === f.RFC_2822) Ot(e); + else { + (e._a = []), (m(e).empty = !0); + for ( + var t, + n, + s, + i, + r, + a = "" + e._i, + o = a.length, + u = 0, + l = ae(e._f, e._locale).match(te) || [], + h = l.length, + d = 0; + d < h; + d++ + ) + (n = l[d]), + (t = (a.match(Oe(n, e)) || [])[0]) && + (0 < (s = a.substr(0, a.indexOf(t))).length && + m(e).unusedInput.push(s), + (a = a.slice(a.indexOf(t) + t.length)), + (u += t.length)), + ie[n] + ? (t ? (m(e).empty = !1) : m(e).unusedTokens.push(n), + (s = n), + (r = e), + null != (i = t) && c(xe, s) && xe[s](i, r._a, r, s)) + : e._strict && !t && m(e).unusedTokens.push(n); + (m(e).charsLeftOver = o - u), + 0 < a.length && m(e).unusedInput.push(a), + e._a[x] <= 12 && + !0 === m(e).bigHour && + 0 < e._a[x] && + (m(e).bigHour = void 0), + (m(e).parsedDateParts = e._a.slice(0)), + (m(e).meridiem = e._meridiem), + (e._a[x] = (function (e, t, n) { + if (null == n) return t; + return null != e.meridiemHour + ? e.meridiemHour(t, n) + : null != e.isPM + ? ((e = e.isPM(n)) && t < 12 && (t += 12), + (t = e || 12 !== t ? t : 0)) + : t; + })(e._locale, e._a[x], e._meridiem)), + null !== (o = m(e).era) && + (e._a[Y] = e._locale.erasConvertYear(o, e._a[Y])), + xt(e), + _t(e); + } + } + function Nt(e) { + var t, + n, + s, + i = e._i, + r = e._f; + if ( + ((e._locale = e._locale || mt(e._l)), + null === i || (void 0 === r && "" === i)) + ) + return I({ nullInput: !0 }); + if (("string" == typeof i && (e._i = i = e._locale.preparse(i)), h(i))) + return new q(_t(i)); + if (V(i)) e._d = i; + else if (a(r)) + !(function (e) { + var t, + n, + s, + i, + r, + a, + o = !1, + u = e._f.length; + if (0 === u) return (m(e).invalidFormat = !0), (e._d = new Date(NaN)); + for (i = 0; i < u; i++) + (r = 0), + (a = !1), + (t = $({}, e)), + null != e._useUTC && (t._useUTC = e._useUTC), + (t._f = e._f[i]), + Tt(t), + A(t) && (a = !0), + (r = (r += m(t).charsLeftOver) + 10 * m(t).unusedTokens.length), + (m(t).score = r), + o + ? r < s && ((s = r), (n = t)) + : (null == s || r < s || a) && ((s = r), (n = t), a && (o = !0)); + E(e, n || t); + })(e); + else if (r) Tt(e); + else if (o((r = (i = e)._i))) i._d = new Date(f.now()); + else + V(r) + ? (i._d = new Date(r.valueOf())) + : "string" == typeof r + ? ((n = i), + null !== (t = kt.exec(n._i)) + ? (n._d = new Date(+t[1])) + : (St(n), + !1 === n._isValid && + (delete n._isValid, + Ot(n), + !1 === n._isValid && + (delete n._isValid, + n._strict + ? (n._isValid = !1) + : f.createFromInputFallback(n))))) + : a(r) + ? ((i._a = G(r.slice(0), function (e) { + return parseInt(e, 10); + })), + xt(i)) + : F(r) + ? (t = i)._d || + ((s = void 0 === (n = ue(t._i)).day ? n.date : n.day), + (t._a = G( + [n.year, n.month, s, n.hour, n.minute, n.second, n.millisecond], + function (e) { + return e && parseInt(e, 10); + } + )), + xt(t)) + : u(r) + ? (i._d = new Date(r)) + : f.createFromInputFallback(i); + return A(e) || (e._d = null), e; + } + function Pt(e, t, n, s, i) { + var r = {}; + return ( + (!0 !== t && !1 !== t) || ((s = t), (t = void 0)), + (!0 !== n && !1 !== n) || ((s = n), (n = void 0)), + ((F(e) && L(e)) || (a(e) && 0 === e.length)) && (e = void 0), + (r._isAMomentObject = !0), + (r._useUTC = r._isUTC = i), + (r._l = n), + (r._i = e), + (r._f = t), + (r._strict = s), + (i = new q(_t(Nt((i = r)))))._nextDay && + (i.add(1, "d"), (i._nextDay = void 0)), + i + ); + } + function W(e, t, n, s) { + return Pt(e, t, n, s, !1); + } + (f.createFromInputFallback = e( + "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" : "")); + } + )), + (f.ISO_8601 = function () {}), + (f.RFC_2822 = function () {}); + (ge = e( + "moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/", + function () { + var e = W.apply(null, arguments); + return this.isValid() && e.isValid() ? (e < this ? this : e) : I(); + } + )), + (we = e( + "moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/", + function () { + var e = W.apply(null, arguments); + return this.isValid() && e.isValid() ? (this < e ? this : e) : I(); + } + )); + function Rt(e, t) { + var n, s; + if (!(t = 1 === t.length && a(t[0]) ? t[0] : t).length) return W(); + for (n = t[0], s = 1; s < t.length; ++s) + (t[s].isValid() && !t[s][e](n)) || (n = t[s]); + return n; + } + var Wt = [ + "year", + "quarter", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + ]; + function Ct(e) { + var e = ue(e), + t = e.year || 0, + n = e.quarter || 0, + s = e.month || 0, + i = e.week || e.isoWeek || 0, + r = e.day || 0, + a = e.hour || 0, + o = e.minute || 0, + u = e.second || 0, + l = e.millisecond || 0; + (this._isValid = (function (e) { + var t, + n, + s = !1, + i = Wt.length; + for (t in e) + if (c(e, t) && (-1 === S.call(Wt, t) || (null != e[t] && isNaN(e[t])))) + return !1; + for (n = 0; n < i; ++n) + if (e[Wt[n]]) { + if (s) return !1; + parseFloat(e[Wt[n]]) !== g(e[Wt[n]]) && (s = !0); + } + return !0; + })(e)), + (this._milliseconds = +l + 1e3 * u + 6e4 * o + 1e3 * a * 60 * 60), + (this._days = +r + 7 * i), + (this._months = +s + 3 * n + 12 * t), + (this._data = {}), + (this._locale = mt()), + this._bubble(); + } + function Ut(e) { + return e instanceof Ct; + } + function Ht(e) { + return e < 0 ? -1 * Math.round(-1 * e) : Math.round(e); + } + function Ft(e, n) { + s(e, 0, 0, function () { + var e = this.utcOffset(), + t = "+"; + return ( + e < 0 && ((e = -e), (t = "-")), + t + r(~~(e / 60), 2) + n + r(~~e % 60, 2) + ); + }); + } + Ft("Z", ":"), + Ft("ZZ", ""), + k("Z", Ye), + k("ZZ", Ye), + D(["Z", "ZZ"], function (e, t, n) { + (n._useUTC = !0), (n._tzm = Vt(Ye, e)); + }); + var Lt = /([\+\-]|\d\d)/gi; + function Vt(e, t) { + var t = (t || "").match(e); + return null === t + ? null + : 0 === + (t = + 60 * + (e = ((t[t.length - 1] || []) + "").match(Lt) || ["-", 0, 0])[1] + + g(e[2])) + ? 0 + : "+" === e[0] + ? t + : -t; + } + function Gt(e, t) { + var n; + return t._isUTC + ? ((t = t.clone()), + (n = (h(e) || V(e) ? e : W(e)).valueOf() - t.valueOf()), + t._d.setTime(t._d.valueOf() + n), + f.updateOffset(t, !1), + t) + : W(e).local(); + } + function Et(e) { + return -Math.round(e._d.getTimezoneOffset()); + } + function At() { + return !!this.isValid() && this._isUTC && 0 === this._offset; + } + f.updateOffset = function () {}; + var It = /^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/, + jt = + /^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/; + function C(e, t) { + var n, + s = e, + i = null; + return ( + Ut(e) + ? (s = { ms: e._milliseconds, d: e._days, M: e._months }) + : u(e) || !isNaN(+e) + ? ((s = {}), t ? (s[t] = +e) : (s.milliseconds = +e)) + : (i = It.exec(e)) + ? ((n = "-" === i[1] ? -1 : 1), + (s = { + y: 0, + d: g(i[b]) * n, + h: g(i[x]) * n, + m: g(i[T]) * n, + s: g(i[N]) * n, + ms: g(Ht(1e3 * i[Ne])) * n, + })) + : (i = jt.exec(e)) + ? ((n = "-" === i[1] ? -1 : 1), + (s = { + y: Zt(i[2], n), + M: Zt(i[3], n), + w: Zt(i[4], n), + d: Zt(i[5], n), + h: Zt(i[6], n), + m: Zt(i[7], n), + s: Zt(i[8], n), + })) + : null == s + ? (s = {}) + : "object" == typeof s && + ("from" in s || "to" in s) && + ((t = (function (e, t) { + var n; + if (!e.isValid() || !t.isValid()) + return { milliseconds: 0, months: 0 }; + (t = Gt(t, e)), + e.isBefore(t) + ? (n = zt(e, t)) + : (((n = zt(t, e)).milliseconds = -n.milliseconds), + (n.months = -n.months)); + return n; + })(W(s.from), W(s.to))), + ((s = {}).ms = t.milliseconds), + (s.M = t.months)), + (i = new Ct(s)), + Ut(e) && c(e, "_locale") && (i._locale = e._locale), + Ut(e) && c(e, "_isValid") && (i._isValid = e._isValid), + i + ); + } + function Zt(e, t) { + e = e && parseFloat(e.replace(",", ".")); + return (isNaN(e) ? 0 : e) * t; + } + function zt(e, t) { + var n = {}; + return ( + (n.months = t.month() - e.month() + 12 * (t.year() - e.year())), + e.clone().add(n.months, "M").isAfter(t) && --n.months, + (n.milliseconds = +t - +e.clone().add(n.months, "M")), + n + ); + } + function $t(s, i) { + return function (e, t) { + var n; + return ( + null === t || + isNaN(+t) || + (Q( + i, + "moment()." + + i + + "(period, number) is deprecated. Please use moment()." + + i + + "(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info." + ), + (n = e), + (e = t), + (t = n)), + qt(this, C(e, t), s), + this + ); + }; + } + function qt(e, t, n, s) { + var i = t._milliseconds, + r = Ht(t._days), + t = Ht(t._months); + e.isValid() && + ((s = null == s || s), + t && Ve(e, ce(e, "Month") + t * n), + r && fe(e, "Date", ce(e, "Date") + r * n), + i && e._d.setTime(e._d.valueOf() + i * n), + s && f.updateOffset(e, r || t)); + } + (C.fn = Ct.prototype), + (C.invalid = function () { + return C(NaN); + }); + (Ce = $t(1, "add")), (Je = $t(-1, "subtract")); + function Bt(e) { + return "string" == typeof e || e instanceof String; + } + function Jt(e) { + return ( + h(e) || + V(e) || + Bt(e) || + u(e) || + (function (t) { + var e = a(t), + n = !1; + e && + (n = + 0 === + t.filter(function (e) { + return !u(e) && Bt(t); + }).length); + return e && n; + })(e) || + (function (e) { + var t, + n, + s = F(e) && !L(e), + i = !1, + r = [ + "years", + "year", + "y", + "months", + "month", + "M", + "days", + "day", + "d", + "dates", + "date", + "D", + "hours", + "hour", + "h", + "minutes", + "minute", + "m", + "seconds", + "second", + "s", + "milliseconds", + "millisecond", + "ms", + ], + a = r.length; + for (t = 0; t < a; t += 1) (n = r[t]), (i = i || c(e, n)); + return s && i; + })(e) || + null == e + ); + } + function Qt(e, t) { + if (e.date() < t.date()) return -Qt(t, e); + var n = 12 * (t.year() - e.year()) + (t.month() - e.month()), + s = e.clone().add(n, "months"), + t = + t - s < 0 + ? (t - s) / (s - e.clone().add(n - 1, "months")) + : (t - s) / (e.clone().add(1 + n, "months") - s); + return -(n + t) || 0; + } + function Xt(e) { + return void 0 === e + ? this._locale._abbr + : (null != (e = mt(e)) && (this._locale = e), this); + } + (f.defaultFormat = "YYYY-MM-DDTHH:mm:ssZ"), + (f.defaultFormatUtc = "YYYY-MM-DDTHH:mm:ss[Z]"); + Xe = e( + "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 Kt() { + return this._locale; + } + var en = 126227808e5; + function tn(e, t) { + return ((e % t) + t) % t; + } + function nn(e, t, n) { + return e < 100 && 0 <= e + ? new Date(e + 400, t, n) - en + : new Date(e, t, n).valueOf(); + } + function sn(e, t, n) { + return e < 100 && 0 <= e ? Date.UTC(e + 400, t, n) - en : Date.UTC(e, t, n); + } + function rn(e, t) { + return t.erasAbbrRegex(e); + } + function an() { + for ( + var e = [], t = [], n = [], s = [], i = this.eras(), r = 0, a = i.length; + r < a; + ++r + ) + t.push(M(i[r].name)), + e.push(M(i[r].abbr)), + n.push(M(i[r].narrow)), + s.push(M(i[r].name)), + s.push(M(i[r].abbr)), + s.push(M(i[r].narrow)); + (this._erasRegex = new RegExp("^(" + s.join("|") + ")", "i")), + (this._erasNameRegex = new RegExp("^(" + t.join("|") + ")", "i")), + (this._erasAbbrRegex = new RegExp("^(" + e.join("|") + ")", "i")), + (this._erasNarrowRegex = new RegExp("^(" + n.join("|") + ")", "i")); + } + function on(e, t) { + s(0, [e, e.length], 0, t); + } + function un(e, t, n, s, i) { + var r; + return null == e + ? qe(this, s, i).year + : ((r = P(e, s, i)), + function (e, t, n, s, i) { + (e = $e(e, t, n, s, i)), (t = Ze(e.year, 0, e.dayOfYear)); + return ( + this.year(t.getUTCFullYear()), + this.month(t.getUTCMonth()), + this.date(t.getUTCDate()), + this + ); + }.call(this, e, (t = r < t ? r : t), n, s, i)); + } + s("N", 0, 0, "eraAbbr"), + s("NN", 0, 0, "eraAbbr"), + s("NNN", 0, 0, "eraAbbr"), + s("NNNN", 0, 0, "eraName"), + s("NNNNN", 0, 0, "eraNarrow"), + s("y", ["y", 1], "yo", "eraYear"), + s("y", ["yy", 2], 0, "eraYear"), + s("y", ["yyy", 3], 0, "eraYear"), + s("y", ["yyyy", 4], 0, "eraYear"), + k("N", rn), + k("NN", rn), + k("NNN", rn), + k("NNNN", function (e, t) { + return t.erasNameRegex(e); + }), + k("NNNNN", function (e, t) { + return t.erasNarrowRegex(e); + }), + D(["N", "NN", "NNN", "NNNN", "NNNNN"], function (e, t, n, s) { + s = n._locale.erasParse(e, s, n._strict); + s ? (m(n).era = s) : (m(n).invalidEra = e); + }), + k("y", Me), + k("yy", Me), + k("yyy", Me), + k("yyyy", Me), + k("yo", function (e, t) { + return t._eraYearOrdinalRegex || Me; + }), + D(["y", "yy", "yyy", "yyyy"], Y), + D(["yo"], function (e, t, n, s) { + var i; + n._locale._eraYearOrdinalRegex && + (i = e.match(n._locale._eraYearOrdinalRegex)), + n._locale.eraYearOrdinalParse + ? (t[Y] = n._locale.eraYearOrdinalParse(e, i)) + : (t[Y] = parseInt(e, 10)); + }), + s(0, ["gg", 2], 0, function () { + return this.weekYear() % 100; + }), + s(0, ["GG", 2], 0, function () { + return this.isoWeekYear() % 100; + }), + on("gggg", "weekYear"), + on("ggggg", "weekYear"), + on("GGGG", "isoWeekYear"), + on("GGGGG", "isoWeekYear"), + t("weekYear", "gg"), + t("isoWeekYear", "GG"), + n("weekYear", 1), + n("isoWeekYear", 1), + k("G", De), + k("g", De), + k("GG", p, w), + k("gg", p, w), + k("GGGG", ve, _e), + k("gggg", ve, _e), + k("GGGGG", ke, ye), + k("ggggg", ke, ye), + Te(["gggg", "ggggg", "GGGG", "GGGGG"], function (e, t, n, s) { + t[s.substr(0, 2)] = g(e); + }), + Te(["gg", "GG"], function (e, t, n, s) { + t[s] = f.parseTwoDigitYear(e); + }), + s("Q", 0, "Qo", "quarter"), + t("quarter", "Q"), + n("quarter", 7), + k("Q", i), + D("Q", function (e, t) { + t[O] = 3 * (g(e) - 1); + }), + s("D", ["DD", 2], "Do", "date"), + t("date", "D"), + n("date", 9), + k("D", p), + k("DD", p, w), + k("Do", function (e, t) { + return e + ? t._dayOfMonthOrdinalParse || t._ordinalParse + : t._dayOfMonthOrdinalParseLenient; + }), + D(["D", "DD"], b), + D("Do", function (e, t) { + t[b] = g(e.match(p)[0]); + }); + ve = de("Date", !0); + s("DDD", ["DDDD", 3], "DDDo", "dayOfYear"), + t("dayOfYear", "DDD"), + n("dayOfYear", 4), + k("DDD", pe), + k("DDDD", me), + D(["DDD", "DDDD"], function (e, t, n) { + n._dayOfYear = g(e); + }), + s("m", ["mm", 2], 0, "minute"), + t("minute", "m"), + n("minute", 14), + k("m", p), + k("mm", p, w), + D(["m", "mm"], T); + var ln, + _e = de("Minutes", !1), + ke = + (s("s", ["ss", 2], 0, "second"), + t("second", "s"), + n("second", 15), + k("s", p), + k("ss", p, w), + D(["s", "ss"], N), + de("Seconds", !1)); + for ( + s("S", 0, 0, function () { + return ~~(this.millisecond() / 100); + }), + s(0, ["SS", 2], 0, function () { + return ~~(this.millisecond() / 10); + }), + s(0, ["SSS", 3], 0, "millisecond"), + s(0, ["SSSS", 4], 0, function () { + return 10 * this.millisecond(); + }), + s(0, ["SSSSS", 5], 0, function () { + return 100 * this.millisecond(); + }), + s(0, ["SSSSSS", 6], 0, function () { + return 1e3 * this.millisecond(); + }), + s(0, ["SSSSSSS", 7], 0, function () { + return 1e4 * this.millisecond(); + }), + s(0, ["SSSSSSSS", 8], 0, function () { + return 1e5 * this.millisecond(); + }), + s(0, ["SSSSSSSSS", 9], 0, function () { + return 1e6 * this.millisecond(); + }), + t("millisecond", "ms"), + n("millisecond", 16), + k("S", pe, i), + k("SS", pe, w), + k("SSS", pe, me), + ln = "SSSS"; + ln.length <= 9; + ln += "S" + ) + k(ln, Me); + function hn(e, t) { + t[Ne] = g(1e3 * ("0." + e)); + } + for (ln = "S"; ln.length <= 9; ln += "S") D(ln, hn); + (ye = de("Milliseconds", !1)), + s("z", 0, 0, "zoneAbbr"), + s("zz", 0, 0, "zoneName"); + i = q.prototype; + function dn(e) { + return e; + } + (i.add = Ce), + (i.calendar = function (e, t) { + 1 === arguments.length && + (arguments[0] + ? Jt(arguments[0]) + ? ((e = arguments[0]), (t = void 0)) + : (function (e) { + for ( + var t = F(e) && !L(e), + n = !1, + s = [ + "sameDay", + "nextDay", + "lastDay", + "nextWeek", + "lastWeek", + "sameElse", + ], + i = 0; + i < s.length; + i += 1 + ) + n = n || c(e, s[i]); + return t && n; + })(arguments[0]) && ((t = arguments[0]), (e = void 0)) + : (t = e = void 0)); + var e = e || W(), + n = Gt(e, this).startOf("day"), + n = f.calendarFormat(this, n) || "sameElse", + t = t && (d(t[n]) ? t[n].call(this, e) : t[n]); + return this.format(t || this.localeData().calendar(n, this, W(e))); + }), + (i.clone = function () { + return new q(this); + }), + (i.diff = function (e, t, n) { + var s, i, r; + if (!this.isValid()) return NaN; + if (!(s = Gt(e, this)).isValid()) return NaN; + switch (((i = 6e4 * (s.utcOffset() - this.utcOffset())), (t = _(t)))) { + case "year": + r = Qt(this, s) / 12; + break; + case "month": + r = Qt(this, s); + break; + case "quarter": + r = Qt(this, s) / 3; + break; + case "second": + r = (this - s) / 1e3; + break; + case "minute": + r = (this - s) / 6e4; + break; + case "hour": + r = (this - s) / 36e5; + break; + case "day": + r = (this - s - i) / 864e5; + break; + case "week": + r = (this - s - i) / 6048e5; + break; + default: + r = this - s; + } + return n ? r : y(r); + }), + (i.endOf = function (e) { + var t, n; + if (void 0 === (e = _(e)) || "millisecond" === e || !this.isValid()) + return this; + switch (((n = this._isUTC ? sn : nn), e)) { + case "year": + t = n(this.year() + 1, 0, 1) - 1; + break; + case "quarter": + t = n(this.year(), this.month() - (this.month() % 3) + 3, 1) - 1; + break; + case "month": + t = n(this.year(), this.month() + 1, 1) - 1; + break; + case "week": + t = + n(this.year(), this.month(), this.date() - this.weekday() + 7) - 1; + break; + case "isoWeek": + t = + n( + this.year(), + this.month(), + this.date() - (this.isoWeekday() - 1) + 7 + ) - 1; + break; + case "day": + case "date": + t = n(this.year(), this.month(), this.date() + 1) - 1; + break; + case "hour": + (t = this._d.valueOf()), + (t += + 36e5 - + tn(t + (this._isUTC ? 0 : 6e4 * this.utcOffset()), 36e5) - + 1); + break; + case "minute": + (t = this._d.valueOf()), (t += 6e4 - tn(t, 6e4) - 1); + break; + case "second": + (t = this._d.valueOf()), (t += 1e3 - tn(t, 1e3) - 1); + } + return this._d.setTime(t), f.updateOffset(this, !0), this; + }), + (i.format = function (e) { + return ( + (e = e || (this.isUtc() ? f.defaultFormatUtc : f.defaultFormat)), + (e = re(this, e)), + this.localeData().postformat(e) + ); + }), + (i.from = function (e, t) { + return this.isValid() && ((h(e) && e.isValid()) || W(e).isValid()) + ? C({ to: this, from: e }).locale(this.locale()).humanize(!t) + : this.localeData().invalidDate(); + }), + (i.fromNow = function (e) { + return this.from(W(), e); + }), + (i.to = function (e, t) { + return this.isValid() && ((h(e) && e.isValid()) || W(e).isValid()) + ? C({ from: this, to: e }).locale(this.locale()).humanize(!t) + : this.localeData().invalidDate(); + }), + (i.toNow = function (e) { + return this.to(W(), e); + }), + (i.get = function (e) { + return d(this[(e = _(e))]) ? this[e]() : this; + }), + (i.invalidAt = function () { + return m(this).overflow; + }), + (i.isAfter = function (e, t) { + return ( + (e = h(e) ? e : W(e)), + !(!this.isValid() || !e.isValid()) && + ("millisecond" === (t = _(t) || "millisecond") + ? this.valueOf() > e.valueOf() + : e.valueOf() < this.clone().startOf(t).valueOf()) + ); + }), + (i.isBefore = function (e, t) { + return ( + (e = h(e) ? e : W(e)), + !(!this.isValid() || !e.isValid()) && + ("millisecond" === (t = _(t) || "millisecond") + ? this.valueOf() < e.valueOf() + : this.clone().endOf(t).valueOf() < e.valueOf()) + ); + }), + (i.isBetween = function (e, t, n, s) { + return ( + (e = h(e) ? e : W(e)), + (t = h(t) ? t : W(t)), + !!(this.isValid() && e.isValid() && t.isValid()) && + ("(" === (s = s || "()")[0] + ? this.isAfter(e, n) + : !this.isBefore(e, n)) && + (")" === s[1] ? this.isBefore(t, n) : !this.isAfter(t, n)) + ); + }), + (i.isSame = function (e, t) { + var e = h(e) ? e : W(e); + return ( + !(!this.isValid() || !e.isValid()) && + ("millisecond" === (t = _(t) || "millisecond") + ? this.valueOf() === e.valueOf() + : ((e = e.valueOf()), + this.clone().startOf(t).valueOf() <= e && + e <= this.clone().endOf(t).valueOf())) + ); + }), + (i.isSameOrAfter = function (e, t) { + return this.isSame(e, t) || this.isAfter(e, t); + }), + (i.isSameOrBefore = function (e, t) { + return this.isSame(e, t) || this.isBefore(e, t); + }), + (i.isValid = function () { + return A(this); + }), + (i.lang = Xe), + (i.locale = Xt), + (i.localeData = Kt), + (i.max = we), + (i.min = ge), + (i.parsingFlags = function () { + return E({}, m(this)); + }), + (i.set = function (e, t) { + if ("object" == typeof e) + for ( + var n = (function (e) { + var t, + n = []; + for (t in e) c(e, t) && n.push({ unit: t, priority: le[t] }); + return ( + n.sort(function (e, t) { + return e.priority - t.priority; + }), + n + ); + })((e = ue(e))), + s = n.length, + i = 0; + i < s; + i++ + ) + this[n[i].unit](e[n[i].unit]); + else if (d(this[(e = _(e))])) return this[e](t); + return this; + }), + (i.startOf = function (e) { + var t, n; + if (void 0 === (e = _(e)) || "millisecond" === e || !this.isValid()) + return this; + switch (((n = this._isUTC ? sn : nn), e)) { + case "year": + t = n(this.year(), 0, 1); + break; + case "quarter": + t = n(this.year(), this.month() - (this.month() % 3), 1); + break; + case "month": + t = n(this.year(), this.month(), 1); + break; + case "week": + t = n(this.year(), this.month(), this.date() - this.weekday()); + break; + case "isoWeek": + t = n( + this.year(), + this.month(), + this.date() - (this.isoWeekday() - 1) + ); + break; + case "day": + case "date": + t = n(this.year(), this.month(), this.date()); + break; + case "hour": + (t = this._d.valueOf()), + (t -= tn(t + (this._isUTC ? 0 : 6e4 * this.utcOffset()), 36e5)); + break; + case "minute": + (t = this._d.valueOf()), (t -= tn(t, 6e4)); + break; + case "second": + (t = this._d.valueOf()), (t -= tn(t, 1e3)); + } + return this._d.setTime(t), f.updateOffset(this, !0), this; + }), + (i.subtract = Je), + (i.toArray = function () { + var e = this; + return [ + e.year(), + e.month(), + e.date(), + e.hour(), + e.minute(), + e.second(), + e.millisecond(), + ]; + }), + (i.toObject = function () { + var e = this; + return { + years: e.year(), + months: e.month(), + date: e.date(), + hours: e.hours(), + minutes: e.minutes(), + seconds: e.seconds(), + milliseconds: e.milliseconds(), + }; + }), + (i.toDate = function () { + return new Date(this.valueOf()); + }), + (i.toISOString = function (e) { + if (!this.isValid()) return null; + var t = (e = !0 !== e) ? this.clone().utc() : this; + return t.year() < 0 || 9999 < t.year() + ? re( + t, + e + ? "YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]" + : "YYYYYY-MM-DD[T]HH:mm:ss.SSSZ" + ) + : d(Date.prototype.toISOString) + ? e + ? this.toDate().toISOString() + : new Date(this.valueOf() + 60 * this.utcOffset() * 1e3) + .toISOString() + .replace("Z", re(t, "Z")) + : re( + t, + e ? "YYYY-MM-DD[T]HH:mm:ss.SSS[Z]" : "YYYY-MM-DD[T]HH:mm:ss.SSSZ" + ); + }), + (i.inspect = function () { + if (!this.isValid()) return "moment.invalid(/* " + this._i + " */)"; + var e, + t = "moment", + n = ""; + return ( + this.isLocal() || + ((t = 0 === this.utcOffset() ? "moment.utc" : "moment.parseZone"), + (n = "Z")), + (t = "[" + t + '("]'), + (e = 0 <= this.year() && this.year() <= 9999 ? "YYYY" : "YYYYYY"), + this.format(t + e + "-MM-DD[T]HH:mm:ss.SSS" + (n + '[")]')) + ); + }), + "undefined" != typeof Symbol && + null != Symbol.for && + (i[Symbol.for("nodejs.util.inspect.custom")] = function () { + return "Moment<" + this.format() + ">"; + }), + (i.toJSON = function () { + return this.isValid() ? this.toISOString() : null; + }), + (i.toString = function () { + return this.clone() + .locale("en") + .format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ"); + }), + (i.unix = function () { + return Math.floor(this.valueOf() / 1e3); + }), + (i.valueOf = function () { + return this._d.valueOf() - 6e4 * (this._offset || 0); + }), + (i.creationData = function () { + return { + input: this._i, + format: this._f, + locale: this._locale, + isUTC: this._isUTC, + strict: this._strict, + }; + }), + (i.eraName = function () { + for ( + var e, t = this.localeData().eras(), n = 0, s = t.length; + n < s; + ++n + ) { + if ( + ((e = this.clone().startOf("day").valueOf()), + t[n].since <= e && e <= t[n].until) + ) + return t[n].name; + if (t[n].until <= e && e <= t[n].since) return t[n].name; + } + return ""; + }), + (i.eraNarrow = function () { + for ( + var e, t = this.localeData().eras(), n = 0, s = t.length; + n < s; + ++n + ) { + if ( + ((e = this.clone().startOf("day").valueOf()), + t[n].since <= e && e <= t[n].until) + ) + return t[n].narrow; + if (t[n].until <= e && e <= t[n].since) return t[n].narrow; + } + return ""; + }), + (i.eraAbbr = function () { + for ( + var e, t = this.localeData().eras(), n = 0, s = t.length; + n < s; + ++n + ) { + if ( + ((e = this.clone().startOf("day").valueOf()), + t[n].since <= e && e <= t[n].until) + ) + return t[n].abbr; + if (t[n].until <= e && e <= t[n].since) return t[n].abbr; + } + return ""; + }), + (i.eraYear = function () { + for ( + var e, t, n = this.localeData().eras(), s = 0, i = n.length; + s < i; + ++s + ) + if ( + ((e = n[s].since <= n[s].until ? 1 : -1), + (t = this.clone().startOf("day").valueOf()), + (n[s].since <= t && t <= n[s].until) || + (n[s].until <= t && t <= n[s].since)) + ) + return (this.year() - f(n[s].since).year()) * e + n[s].offset; + return this.year(); + }), + (i.year = Ie), + (i.isLeapYear = function () { + return he(this.year()); + }), + (i.weekYear = function (e) { + return un.call( + this, + e, + this.week(), + this.weekday(), + this.localeData()._week.dow, + this.localeData()._week.doy + ); + }), + (i.isoWeekYear = function (e) { + return un.call(this, e, this.isoWeek(), this.isoWeekday(), 1, 4); + }), + (i.quarter = i.quarters = + function (e) { + return null == e + ? Math.ceil((this.month() + 1) / 3) + : this.month(3 * (e - 1) + (this.month() % 3)); + }), + (i.month = Ge), + (i.daysInMonth = function () { + return We(this.year(), this.month()); + }), + (i.week = i.weeks = + function (e) { + var t = this.localeData().week(this); + return null == e ? t : this.add(7 * (e - t), "d"); + }), + (i.isoWeek = i.isoWeeks = + function (e) { + var t = qe(this, 1, 4).week; + return null == e ? t : this.add(7 * (e - t), "d"); + }), + (i.weeksInYear = function () { + var e = this.localeData()._week; + return P(this.year(), e.dow, e.doy); + }), + (i.weeksInWeekYear = function () { + var e = this.localeData()._week; + return P(this.weekYear(), e.dow, e.doy); + }), + (i.isoWeeksInYear = function () { + return P(this.year(), 1, 4); + }), + (i.isoWeeksInISOWeekYear = function () { + return P(this.isoWeekYear(), 1, 4); + }), + (i.date = ve), + (i.day = i.days = + function (e) { + if (!this.isValid()) return null != e ? this : NaN; + var t, + n, + s = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); + return null != e + ? ((t = e), + (n = this.localeData()), + (e = + "string" != typeof t + ? t + : isNaN(t) + ? "number" == typeof (t = n.weekdaysParse(t)) + ? t + : null + : parseInt(t, 10)), + this.add(e - s, "d")) + : s; + }), + (i.weekday = function (e) { + if (!this.isValid()) return null != e ? this : NaN; + var t = (this.day() + 7 - this.localeData()._week.dow) % 7; + return null == e ? t : this.add(e - t, "d"); + }), + (i.isoWeekday = function (e) { + return this.isValid() + ? null != e + ? ((t = e), + (n = this.localeData()), + (n = + "string" == typeof t + ? n.weekdaysParse(t) % 7 || 7 + : isNaN(t) + ? null + : t), + this.day(this.day() % 7 ? n : n - 7)) + : this.day() || 7 + : null != e + ? this + : NaN; + var t, n; + }), + (i.dayOfYear = function (e) { + var t = + Math.round( + (this.clone().startOf("day") - this.clone().startOf("year")) / 864e5 + ) + 1; + return null == e ? t : this.add(e - t, "d"); + }), + (i.hour = i.hours = v), + (i.minute = i.minutes = _e), + (i.second = i.seconds = ke), + (i.millisecond = i.milliseconds = ye), + (i.utcOffset = function (e, t, n) { + var s, + i = this._offset || 0; + if (!this.isValid()) return null != e ? this : NaN; + if (null == e) return this._isUTC ? i : Et(this); + if ("string" == typeof e) { + if (null === (e = Vt(Ye, e))) return this; + } else Math.abs(e) < 16 && !n && (e *= 60); + return ( + !this._isUTC && t && (s = Et(this)), + (this._offset = e), + (this._isUTC = !0), + null != s && this.add(s, "m"), + i !== e && + (!t || this._changeInProgress + ? qt(this, C(e - i, "m"), 1, !1) + : this._changeInProgress || + ((this._changeInProgress = !0), + f.updateOffset(this, !0), + (this._changeInProgress = null))), + this + ); + }), + (i.utc = function (e) { + return this.utcOffset(0, e); + }), + (i.local = function (e) { + return ( + this._isUTC && + (this.utcOffset(0, e), + (this._isUTC = !1), + e && this.subtract(Et(this), "m")), + this + ); + }), + (i.parseZone = function () { + var e; + return ( + null != this._tzm + ? this.utcOffset(this._tzm, !1, !0) + : "string" == typeof this._i && + (null != (e = Vt(Se, this._i)) + ? this.utcOffset(e) + : this.utcOffset(0, !0)), + this + ); + }), + (i.hasAlignedHourOffset = function (e) { + return ( + !!this.isValid() && + ((e = e ? W(e).utcOffset() : 0), (this.utcOffset() - e) % 60 == 0) + ); + }), + (i.isDST = function () { + return ( + this.utcOffset() > this.clone().month(0).utcOffset() || + this.utcOffset() > this.clone().month(5).utcOffset() + ); + }), + (i.isLocal = function () { + return !!this.isValid() && !this._isUTC; + }), + (i.isUtcOffset = function () { + return !!this.isValid() && this._isUTC; + }), + (i.isUtc = At), + (i.isUTC = At), + (i.zoneAbbr = function () { + return this._isUTC ? "UTC" : ""; + }), + (i.zoneName = function () { + return this._isUTC ? "Coordinated Universal Time" : ""; + }), + (i.dates = e("dates accessor is deprecated. Use date instead.", ve)), + (i.months = e("months accessor is deprecated. Use month instead", Ge)), + (i.years = e("years accessor is deprecated. Use year instead", Ie)), + (i.zone = e( + "moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/", + function (e, t) { + return null != e + ? (this.utcOffset((e = "string" != typeof e ? -e : e), t), this) + : -this.utcOffset(); + } + )), + (i.isDSTShifted = e( + "isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information", + function () { + if (!o(this._isDSTShifted)) return this._isDSTShifted; + var e, + t = {}; + return ( + $(t, this), + (t = Nt(t))._a + ? ((e = (t._isUTC ? l : W)(t._a)), + (this._isDSTShifted = + this.isValid() && + 0 < + (function (e, t, n) { + for ( + var s = Math.min(e.length, t.length), + i = Math.abs(e.length - t.length), + r = 0, + a = 0; + a < s; + a++ + ) + ((n && e[a] !== t[a]) || (!n && g(e[a]) !== g(t[a]))) && + r++; + return r + i; + })(t._a, e.toArray()))) + : (this._isDSTShifted = !1), + this._isDSTShifted + ); + } + )); + w = K.prototype; + function cn(e, t, n, s) { + var i = mt(), + s = l().set(s, t); + return i[n](s, e); + } + function fn(e, t, n) { + if ((u(e) && ((t = e), (e = void 0)), (e = e || ""), null != t)) + return cn(e, t, n, "month"); + for (var s = [], i = 0; i < 12; i++) s[i] = cn(e, i, n, "month"); + return s; + } + function mn(e, t, n, s) { + t = + ("boolean" == typeof e + ? u(t) && ((n = t), (t = void 0)) + : ((t = e), (e = !1), u((n = t)) && ((n = t), (t = void 0))), + t || ""); + var i, + r = mt(), + a = e ? r._week.dow : 0, + o = []; + if (null != n) return cn(t, (n + a) % 7, s, "day"); + for (i = 0; i < 7; i++) o[i] = cn(t, (i + a) % 7, s, "day"); + return o; + } + (w.calendar = function (e, t, n) { + return d((e = this._calendar[e] || this._calendar.sameElse)) + ? e.call(t, n) + : e; + }), + (w.longDateFormat = function (e) { + var t = this._longDateFormat[e], + n = this._longDateFormat[e.toUpperCase()]; + return t || !n + ? t + : ((this._longDateFormat[e] = n + .match(te) + .map(function (e) { + return "MMMM" === e || "MM" === e || "DD" === e || "dddd" === e + ? e.slice(1) + : e; + }) + .join("")), + this._longDateFormat[e]); + }), + (w.invalidDate = function () { + return this._invalidDate; + }), + (w.ordinal = function (e) { + return this._ordinal.replace("%d", e); + }), + (w.preparse = dn), + (w.postformat = dn), + (w.relativeTime = function (e, t, n, s) { + var i = this._relativeTime[n]; + return d(i) ? i(e, t, n, s) : i.replace(/%d/i, e); + }), + (w.pastFuture = function (e, t) { + return d((e = this._relativeTime[0 < e ? "future" : "past"])) + ? e(t) + : e.replace(/%s/i, t); + }), + (w.set = function (e) { + var t, n; + for (n in e) + c(e, n) && (d((t = e[n])) ? (this[n] = t) : (this["_" + n] = t)); + (this._config = e), + (this._dayOfMonthOrdinalParseLenient = new RegExp( + (this._dayOfMonthOrdinalParse.source || this._ordinalParse.source) + + "|" + + /\d{1,2}/.source + )); + }), + (w.eras = function (e, t) { + for ( + var n, s = this._eras || mt("en")._eras, i = 0, r = s.length; + i < r; + ++i + ) + switch ( + ("string" == typeof s[i].since && + ((n = f(s[i].since).startOf("day")), (s[i].since = n.valueOf())), + typeof s[i].until) + ) { + case "undefined": + s[i].until = 1 / 0; + break; + case "string": + (n = f(s[i].until).startOf("day").valueOf()), + (s[i].until = n.valueOf()); + } + return s; + }), + (w.erasParse = function (e, t, n) { + var s, + i, + r, + a, + o, + u = this.eras(); + for (e = e.toUpperCase(), s = 0, i = u.length; s < i; ++s) + if ( + ((r = u[s].name.toUpperCase()), + (a = u[s].abbr.toUpperCase()), + (o = u[s].narrow.toUpperCase()), + n) + ) + switch (t) { + case "N": + case "NN": + case "NNN": + if (a === e) return u[s]; + break; + case "NNNN": + if (r === e) return u[s]; + break; + case "NNNNN": + if (o === e) return u[s]; + } + else if (0 <= [r, a, o].indexOf(e)) return u[s]; + }), + (w.erasConvertYear = function (e, t) { + var n = e.since <= e.until ? 1 : -1; + return void 0 === t + ? f(e.since).year() + : f(e.since).year() + (t - e.offset) * n; + }), + (w.erasAbbrRegex = function (e) { + return ( + c(this, "_erasAbbrRegex") || an.call(this), + e ? this._erasAbbrRegex : this._erasRegex + ); + }), + (w.erasNameRegex = function (e) { + return ( + c(this, "_erasNameRegex") || an.call(this), + e ? this._erasNameRegex : this._erasRegex + ); + }), + (w.erasNarrowRegex = function (e) { + return ( + c(this, "_erasNarrowRegex") || an.call(this), + e ? this._erasNarrowRegex : this._erasRegex + ); + }), + (w.months = function (e, t) { + return e + ? (a(this._months) + ? this._months + : this._months[ + (this._months.isFormat || He).test(t) ? "format" : "standalone" + ])[e.month()] + : a(this._months) + ? this._months + : this._months.standalone; + }), + (w.monthsShort = function (e, t) { + return e + ? (a(this._monthsShort) + ? this._monthsShort + : this._monthsShort[He.test(t) ? "format" : "standalone"])[ + e.month() + ] + : a(this._monthsShort) + ? this._monthsShort + : this._monthsShort.standalone; + }), + (w.monthsParse = function (e, t, n) { + var s, i; + if (this._monthsParseExact) + return function (e, t, n) { + var s, + i, + r, + e = e.toLocaleLowerCase(); + if (!this._monthsParse) + for ( + this._monthsParse = [], + this._longMonthsParse = [], + this._shortMonthsParse = [], + s = 0; + s < 12; + ++s + ) + (r = l([2e3, s])), + (this._shortMonthsParse[s] = this.monthsShort( + r, + "" + ).toLocaleLowerCase()), + (this._longMonthsParse[s] = this.months( + r, + "" + ).toLocaleLowerCase()); + return n + ? "MMM" === t + ? -1 !== (i = S.call(this._shortMonthsParse, e)) + ? i + : null + : -1 !== (i = S.call(this._longMonthsParse, e)) + ? i + : null + : "MMM" === t + ? -1 !== (i = S.call(this._shortMonthsParse, e)) || + -1 !== (i = S.call(this._longMonthsParse, e)) + ? i + : null + : -1 !== (i = S.call(this._longMonthsParse, e)) || + -1 !== (i = S.call(this._shortMonthsParse, e)) + ? i + : null; + }.call(this, e, t, n); + for ( + this._monthsParse || + ((this._monthsParse = []), + (this._longMonthsParse = []), + (this._shortMonthsParse = [])), + s = 0; + s < 12; + s++ + ) { + if ( + ((i = l([2e3, s])), + n && + !this._longMonthsParse[s] && + ((this._longMonthsParse[s] = new RegExp( + "^" + this.months(i, "").replace(".", "") + "$", + "i" + )), + (this._shortMonthsParse[s] = new RegExp( + "^" + this.monthsShort(i, "").replace(".", "") + "$", + "i" + ))), + n || + this._monthsParse[s] || + ((i = "^" + this.months(i, "") + "|^" + this.monthsShort(i, "")), + (this._monthsParse[s] = new RegExp(i.replace(".", ""), "i"))), + n && "MMMM" === t && this._longMonthsParse[s].test(e)) + ) + return s; + if (n && "MMM" === t && this._shortMonthsParse[s].test(e)) return s; + if (!n && this._monthsParse[s].test(e)) return s; + } + }), + (w.monthsRegex = function (e) { + return this._monthsParseExact + ? (c(this, "_monthsRegex") || Ee.call(this), + e ? this._monthsStrictRegex : this._monthsRegex) + : (c(this, "_monthsRegex") || (this._monthsRegex = Le), + this._monthsStrictRegex && e + ? this._monthsStrictRegex + : this._monthsRegex); + }), + (w.monthsShortRegex = function (e) { + return this._monthsParseExact + ? (c(this, "_monthsRegex") || Ee.call(this), + e ? this._monthsShortStrictRegex : this._monthsShortRegex) + : (c(this, "_monthsShortRegex") || (this._monthsShortRegex = Fe), + this._monthsShortStrictRegex && e + ? this._monthsShortStrictRegex + : this._monthsShortRegex); + }), + (w.week = function (e) { + return qe(e, this._week.dow, this._week.doy).week; + }), + (w.firstDayOfYear = function () { + return this._week.doy; + }), + (w.firstDayOfWeek = function () { + return this._week.dow; + }), + (w.weekdays = function (e, t) { + return ( + (t = a(this._weekdays) + ? this._weekdays + : this._weekdays[ + e && !0 !== e && this._weekdays.isFormat.test(t) + ? "format" + : "standalone" + ]), + !0 === e ? Be(t, this._week.dow) : e ? t[e.day()] : t + ); + }), + (w.weekdaysMin = function (e) { + return !0 === e + ? Be(this._weekdaysMin, this._week.dow) + : e + ? this._weekdaysMin[e.day()] + : this._weekdaysMin; + }), + (w.weekdaysShort = function (e) { + return !0 === e + ? Be(this._weekdaysShort, this._week.dow) + : e + ? this._weekdaysShort[e.day()] + : this._weekdaysShort; + }), + (w.weekdaysParse = function (e, t, n) { + var s, i; + if (this._weekdaysParseExact) + return function (e, t, n) { + var s, + i, + r, + e = e.toLocaleLowerCase(); + if (!this._weekdaysParse) + for ( + this._weekdaysParse = [], + this._shortWeekdaysParse = [], + this._minWeekdaysParse = [], + s = 0; + s < 7; + ++s + ) + (r = l([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 = S.call(this._weekdaysParse, e)) + ? i + : null + : "ddd" === t + ? -1 !== (i = S.call(this._shortWeekdaysParse, e)) + ? i + : null + : -1 !== (i = S.call(this._minWeekdaysParse, e)) + ? i + : null + : "dddd" === t + ? -1 !== (i = S.call(this._weekdaysParse, e)) || + -1 !== (i = S.call(this._shortWeekdaysParse, e)) || + -1 !== (i = S.call(this._minWeekdaysParse, e)) + ? i + : null + : "ddd" === t + ? -1 !== (i = S.call(this._shortWeekdaysParse, e)) || + -1 !== (i = S.call(this._weekdaysParse, e)) || + -1 !== (i = S.call(this._minWeekdaysParse, e)) + ? i + : null + : -1 !== (i = S.call(this._minWeekdaysParse, e)) || + -1 !== (i = S.call(this._weekdaysParse, e)) || + -1 !== (i = S.call(this._shortWeekdaysParse, e)) + ? i + : null; + }.call(this, e, t, n); + for ( + this._weekdaysParse || + ((this._weekdaysParse = []), + (this._minWeekdaysParse = []), + (this._shortWeekdaysParse = []), + (this._fullWeekdaysParse = [])), + s = 0; + s < 7; + s++ + ) { + if ( + ((i = l([2e3, 1]).day(s)), + n && + !this._fullWeekdaysParse[s] && + ((this._fullWeekdaysParse[s] = new RegExp( + "^" + this.weekdays(i, "").replace(".", "\\.?") + "$", + "i" + )), + (this._shortWeekdaysParse[s] = new RegExp( + "^" + this.weekdaysShort(i, "").replace(".", "\\.?") + "$", + "i" + )), + (this._minWeekdaysParse[s] = new RegExp( + "^" + this.weekdaysMin(i, "").replace(".", "\\.?") + "$", + "i" + ))), + this._weekdaysParse[s] || + ((i = + "^" + + this.weekdays(i, "") + + "|^" + + this.weekdaysShort(i, "") + + "|^" + + this.weekdaysMin(i, "")), + (this._weekdaysParse[s] = new RegExp(i.replace(".", ""), "i"))), + n && "dddd" === t && this._fullWeekdaysParse[s].test(e)) + ) + return s; + if (n && "ddd" === t && this._shortWeekdaysParse[s].test(e)) return s; + if (n && "dd" === t && this._minWeekdaysParse[s].test(e)) return s; + if (!n && this._weekdaysParse[s].test(e)) return s; + } + }), + (w.weekdaysRegex = function (e) { + return this._weekdaysParseExact + ? (c(this, "_weekdaysRegex") || nt.call(this), + e ? this._weekdaysStrictRegex : this._weekdaysRegex) + : (c(this, "_weekdaysRegex") || (this._weekdaysRegex = Ke), + this._weekdaysStrictRegex && e + ? this._weekdaysStrictRegex + : this._weekdaysRegex); + }), + (w.weekdaysShortRegex = function (e) { + return this._weekdaysParseExact + ? (c(this, "_weekdaysRegex") || nt.call(this), + e ? this._weekdaysShortStrictRegex : this._weekdaysShortRegex) + : (c(this, "_weekdaysShortRegex") || (this._weekdaysShortRegex = et), + this._weekdaysShortStrictRegex && e + ? this._weekdaysShortStrictRegex + : this._weekdaysShortRegex); + }), + (w.weekdaysMinRegex = function (e) { + return this._weekdaysParseExact + ? (c(this, "_weekdaysRegex") || nt.call(this), + e ? this._weekdaysMinStrictRegex : this._weekdaysMinRegex) + : (c(this, "_weekdaysMinRegex") || (this._weekdaysMinRegex = tt), + this._weekdaysMinStrictRegex && e + ? this._weekdaysMinStrictRegex + : this._weekdaysMinRegex); + }), + (w.isPM = function (e) { + return "p" === (e + "").toLowerCase().charAt(0); + }), + (w.meridiem = function (e, t, n) { + return 11 < e ? (n ? "pm" : "PM") : n ? "am" : "AM"; + }), + ct("en", { + eras: [ + { + since: "0001-01-01", + until: 1 / 0, + 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; + return ( + e + + (1 === g((e % 100) / 10) + ? "th" + : 1 == t + ? "st" + : 2 == t + ? "nd" + : 3 == t + ? "rd" + : "th") + ); + }, + }), + (f.lang = e("moment.lang is deprecated. Use moment.locale instead.", ct)), + (f.langData = e( + "moment.langData is deprecated. Use moment.localeData instead.", + mt + )); + var _n = Math.abs; + function yn(e, t, n, s) { + t = C(t, n); + return ( + (e._milliseconds += s * t._milliseconds), + (e._days += s * t._days), + (e._months += s * t._months), + e._bubble() + ); + } + function gn(e) { + return e < 0 ? Math.floor(e) : Math.ceil(e); + } + function wn(e) { + return (4800 * e) / 146097; + } + function pn(e) { + return (146097 * e) / 4800; + } + function vn(e) { + return function () { + return this.as(e); + }; + } + (pe = vn("ms")), + (me = vn("s")), + (Ce = vn("m")), + (we = vn("h")), + (ge = vn("d")), + (Je = vn("w")), + (v = vn("M")), + (_e = vn("Q")), + (ke = vn("y")); + function kn(e) { + return function () { + return this.isValid() ? this._data[e] : NaN; + }; + } + var ye = kn("milliseconds"), + ve = kn("seconds"), + Ie = kn("minutes"), + w = kn("hours"), + Mn = kn("days"), + Dn = kn("months"), + Sn = kn("years"); + var Yn = Math.round, + On = { ss: 44, s: 45, m: 45, h: 22, d: 26, w: null, M: 11 }; + function bn(e, t, n, s) { + var i = C(e).abs(), + r = Yn(i.as("s")), + a = Yn(i.as("m")), + o = Yn(i.as("h")), + u = Yn(i.as("d")), + l = Yn(i.as("M")), + h = Yn(i.as("w")), + i = Yn(i.as("y")), + r = + (r <= n.ss ? ["s", r] : r < n.s && ["ss", r]) || + (a <= 1 && ["m"]) || + (a < n.m && ["mm", a]) || + (o <= 1 && ["h"]) || + (o < n.h && ["hh", o]) || + (u <= 1 && ["d"]) || + (u < n.d && ["dd", u]); + return ( + ((r = (r = + null != n.w ? r || (h <= 1 && ["w"]) || (h < n.w && ["ww", h]) : r) || + (l <= 1 && ["M"]) || + (l < n.M && ["MM", l]) || + (i <= 1 && ["y"]) || ["yy", i])[2] = t), + (r[3] = 0 < +e), + (r[4] = s), + function (e, t, n, s, i) { + return i.relativeTime(t || 1, !!n, e, s); + }.apply(null, r) + ); + } + var xn = Math.abs; + function Tn(e) { + return (0 < e) - (e < 0) || +e; + } + function Nn() { + if (!this.isValid()) return this.localeData().invalidDate(); + var e, + t, + n, + s, + i, + r, + a, + o = xn(this._milliseconds) / 1e3, + u = xn(this._days), + l = xn(this._months), + h = this.asSeconds(); + return h + ? ((e = y(o / 60)), + (t = y(e / 60)), + (o %= 60), + (e %= 60), + (n = y(l / 12)), + (l %= 12), + (s = o ? o.toFixed(3).replace(/\.?0+$/, "") : ""), + (i = Tn(this._months) !== Tn(h) ? "-" : ""), + (r = Tn(this._days) !== Tn(h) ? "-" : ""), + (a = Tn(this._milliseconds) !== Tn(h) ? "-" : ""), + (h < 0 ? "-" : "") + + "P" + + (n ? i + n + "Y" : "") + + (l ? i + l + "M" : "") + + (u ? r + u + "D" : "") + + (t || e || o ? "T" : "") + + (t ? a + t + "H" : "") + + (e ? a + e + "M" : "") + + (o ? a + s + "S" : "")) + : "P0D"; + } + var U = Ct.prototype; + return ( + (U.isValid = function () { + return this._isValid; + }), + (U.abs = function () { + var e = this._data; + return ( + (this._milliseconds = _n(this._milliseconds)), + (this._days = _n(this._days)), + (this._months = _n(this._months)), + (e.milliseconds = _n(e.milliseconds)), + (e.seconds = _n(e.seconds)), + (e.minutes = _n(e.minutes)), + (e.hours = _n(e.hours)), + (e.months = _n(e.months)), + (e.years = _n(e.years)), + this + ); + }), + (U.add = function (e, t) { + return yn(this, e, t, 1); + }), + (U.subtract = function (e, t) { + return yn(this, e, t, -1); + }), + (U.as = function (e) { + if (!this.isValid()) return NaN; + var t, + n, + s = this._milliseconds; + if ("month" === (e = _(e)) || "quarter" === e || "year" === e) + switch (((t = this._days + s / 864e5), (n = this._months + wn(t)), e)) { + case "month": + return n; + case "quarter": + return n / 3; + case "year": + return n / 12; + } + else + switch (((t = this._days + Math.round(pn(this._months))), e)) { + case "week": + return t / 7 + s / 6048e5; + case "day": + return t + s / 864e5; + case "hour": + return 24 * t + s / 36e5; + case "minute": + return 1440 * t + s / 6e4; + case "second": + return 86400 * t + s / 1e3; + case "millisecond": + return Math.floor(864e5 * t) + s; + default: + throw new Error("Unknown unit " + e); + } + }), + (U.asMilliseconds = pe), + (U.asSeconds = me), + (U.asMinutes = Ce), + (U.asHours = we), + (U.asDays = ge), + (U.asWeeks = Je), + (U.asMonths = v), + (U.asQuarters = _e), + (U.asYears = ke), + (U.valueOf = function () { + return this.isValid() + ? this._milliseconds + + 864e5 * this._days + + (this._months % 12) * 2592e6 + + 31536e6 * g(this._months / 12) + : NaN; + }), + (U._bubble = function () { + var e = this._milliseconds, + t = this._days, + n = this._months, + s = this._data; + return ( + (0 <= e && 0 <= t && 0 <= n) || + (e <= 0 && t <= 0 && n <= 0) || + ((e += 864e5 * gn(pn(n) + t)), (n = t = 0)), + (s.milliseconds = e % 1e3), + (e = y(e / 1e3)), + (s.seconds = e % 60), + (e = y(e / 60)), + (s.minutes = e % 60), + (e = y(e / 60)), + (s.hours = e % 24), + (t += y(e / 24)), + (n += e = y(wn(t))), + (t -= gn(pn(e))), + (e = y(n / 12)), + (n %= 12), + (s.days = t), + (s.months = n), + (s.years = e), + this + ); + }), + (U.clone = function () { + return C(this); + }), + (U.get = function (e) { + return (e = _(e)), this.isValid() ? this[e + "s"]() : NaN; + }), + (U.milliseconds = ye), + (U.seconds = ve), + (U.minutes = Ie), + (U.hours = w), + (U.days = Mn), + (U.weeks = function () { + return y(this.days() / 7); + }), + (U.months = Dn), + (U.years = Sn), + (U.humanize = function (e, t) { + if (!this.isValid()) return this.localeData().invalidDate(); + var n = !1, + s = On; + return ( + "object" == typeof e && ((t = e), (e = !1)), + "boolean" == typeof e && (n = e), + "object" == typeof t && + ((s = Object.assign({}, On, t)), + null != t.s && null == t.ss && (s.ss = t.s - 1)), + (e = this.localeData()), + (t = bn(this, !n, s, e)), + n && (t = e.pastFuture(+this, t)), + e.postformat(t) + ); + }), + (U.toISOString = Nn), + (U.toString = Nn), + (U.toJSON = Nn), + (U.locale = Xt), + (U.localeData = Kt), + (U.toIsoString = e( + "toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)", + Nn + )), + (U.lang = Xe), + s("X", 0, 0, "unix"), + s("x", 0, 0, "valueOf"), + k("x", De), + k("X", /[+-]?\d+(\.\d{1,3})?/), + D("X", function (e, t, n) { + n._d = new Date(1e3 * parseFloat(e)); + }), + D("x", function (e, t, n) { + n._d = new Date(g(e)); + }), + (f.version = "2.29.4"), + (H = W), + (f.fn = i), + (f.min = function () { + return Rt("isBefore", [].slice.call(arguments, 0)); + }), + (f.max = function () { + return Rt("isAfter", [].slice.call(arguments, 0)); + }), + (f.now = function () { + return Date.now ? Date.now() : +new Date(); + }), + (f.utc = l), + (f.unix = function (e) { + return W(1e3 * e); + }), + (f.months = function (e, t) { + return fn(e, t, "months"); + }), + (f.isDate = V), + (f.locale = ct), + (f.invalid = I), + (f.duration = C), + (f.isMoment = h), + (f.weekdays = function (e, t, n) { + return mn(e, t, n, "weekdays"); + }), + (f.parseZone = function () { + return W.apply(null, arguments).parseZone(); + }), + (f.localeData = mt), + (f.isDuration = Ut), + (f.monthsShort = function (e, t) { + return fn(e, t, "monthsShort"); + }), + (f.weekdaysMin = function (e, t, n) { + return mn(e, t, n, "weekdaysMin"); + }), + (f.defineLocale = ft), + (f.updateLocale = function (e, t) { + var n, s; + return ( + null != t + ? ((s = ot), + null != R[e] && null != R[e].parentLocale + ? R[e].set(X(R[e]._config, t)) + : ((t = X((s = null != (n = dt(e)) ? n._config : s), t)), + null == n && (t.abbr = e), + ((s = new K(t)).parentLocale = R[e]), + (R[e] = s)), + ct(e)) + : null != R[e] && + (null != R[e].parentLocale + ? ((R[e] = R[e].parentLocale), e === ct() && ct(e)) + : null != R[e] && delete R[e]), + R[e] + ); + }), + (f.locales = function () { + return ee(R); + }), + (f.weekdaysShort = function (e, t, n) { + return mn(e, t, n, "weekdaysShort"); + }), + (f.normalizeUnits = _), + (f.relativeTimeRounding = function (e) { + return void 0 === e ? Yn : "function" == typeof e && ((Yn = e), !0); + }), + (f.relativeTimeThreshold = function (e, t) { + return ( + void 0 !== On[e] && + (void 0 === t ? On[e] : ((On[e] = t), "s" === e && (On.ss = t - 1), !0)) + ); + }), + (f.calendarFormat = function (e, t) { + return (e = e.diff(t, "days", !0)) < -6 + ? "sameElse" + : e < -1 + ? "lastWeek" + : e < 0 + ? "lastDay" + : e < 1 + ? "sameDay" + : e < 2 + ? "nextDay" + : e < 7 + ? "nextWeek" + : "sameElse"; + }), + (f.prototype = i), + (f.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", + }), + f + ); +}); diff --git a/app/templates/assiduites/alert.j2 b/app/templates/assiduites/alert.j2 new file mode 100644 index 00000000..03073e54 --- /dev/null +++ b/app/templates/assiduites/alert.j2 @@ -0,0 +1,156 @@ +{% block alertmodal %} +
    + + +
    +
    + × +

    alertModal Header

    +
    +
    +

    Some text in the alertModal Body

    +

    Some other text...

    +
    + +
    + +
    + + + +{% endblock alertmodal %} \ No newline at end of file diff --git a/app/templates/assiduites/minitimeline.j2 b/app/templates/assiduites/minitimeline.j2 new file mode 100644 index 00000000..4800e71a --- /dev/null +++ b/app/templates/assiduites/minitimeline.j2 @@ -0,0 +1,105 @@ +
    + +
    + + + + \ No newline at end of file diff --git a/app/templates/assiduites/moduleimpl_dynamic_selector.j2 b/app/templates/assiduites/moduleimpl_dynamic_selector.j2 new file mode 100644 index 00000000..3fcf07d5 --- /dev/null +++ b/app/templates/assiduites/moduleimpl_dynamic_selector.j2 @@ -0,0 +1,113 @@ + + + + + + \ No newline at end of file diff --git a/app/templates/assiduites/moduleimpl_selector.j2 b/app/templates/assiduites/moduleimpl_selector.j2 new file mode 100644 index 00000000..de740684 --- /dev/null +++ b/app/templates/assiduites/moduleimpl_selector.j2 @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/app/templates/assiduites/prompt.j2 b/app/templates/assiduites/prompt.j2 new file mode 100644 index 00000000..95c0fc27 --- /dev/null +++ b/app/templates/assiduites/prompt.j2 @@ -0,0 +1,211 @@ +{% block promptModal %} +
    + + +
    +
    + × +

    promptModal Header

    +
    +
    +

    Some text in the promptModal Body

    +

    Some other text...

    +
    + +
    + +
    + + + +{% endblock promptModal %} \ No newline at end of file diff --git a/app/templates/assiduites/signal_assiduites_etud.j2 b/app/templates/assiduites/signal_assiduites_etud.j2 new file mode 100644 index 00000000..00b4bd21 --- /dev/null +++ b/app/templates/assiduites/signal_assiduites_etud.j2 @@ -0,0 +1,99 @@ +{# -*- mode: jinja-html -*- #} + +{% include "assiduites/toast.j2" %} +
    + {% block content %} +

    Signalement de l'assiduité de {{sco.etud.nomprenom}}

    + +
    + Date: + +
    + + {% include "assiduites/timeline.j2" %} + + +
    + {% include "assiduites/moduleimpl_dynamic_selector.j2" %} + +
    + +
    + + + +
    + +
    +
    +
    +
    +
    + + + +
    +
    +
    + + {% include "assiduites/alert.j2" %} + {% include "assiduites/prompt.j2" %} + + + + + + + {% endblock %} + +
    \ No newline at end of file diff --git a/app/templates/assiduites/signal_assiduites_group.j2 b/app/templates/assiduites/signal_assiduites_group.j2 new file mode 100644 index 00000000..582aa058 --- /dev/null +++ b/app/templates/assiduites/signal_assiduites_group.j2 @@ -0,0 +1,76 @@ +{% include "assiduites/toast.j2" %} +
    + +
    + + {{formsemestre_id}} + {{formsemestre_date_debut}} + {{formsemestre_date_fin}} + +
    + +

    + Saisie des assiduités {{gr_tit|safe}} {{sem}} +

    + +
    +
    Groupes : {{grp|safe}}
    + +
    Modules :{{moduleimpl_select|safe}}
    + +
    + Date: + +
    + + +
    + + {{timeline|safe}} + +
    +
    + + +
    +
    +
    + + {% include "assiduites/alert.j2" %} + {% include "assiduites/prompt.j2" %} + + +
    \ No newline at end of file diff --git a/app/templates/assiduites/timeline.j2 b/app/templates/assiduites/timeline.j2 new file mode 100644 index 00000000..7f24b4dd --- /dev/null +++ b/app/templates/assiduites/timeline.j2 @@ -0,0 +1,212 @@ +
    +
    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/app/templates/assiduites/toast.j2 b/app/templates/assiduites/toast.j2 new file mode 100644 index 00000000..6fc7af9d --- /dev/null +++ b/app/templates/assiduites/toast.j2 @@ -0,0 +1,88 @@ +
    +
    + + + + \ No newline at end of file diff --git a/app/templates/sidebar.j2 b/app/templates/sidebar.j2 old mode 100644 new mode 100755 index ba851377..d0413807 --- a/app/templates/sidebar.j2 +++ b/app/templates/sidebar.j2 @@ -60,7 +60,7 @@ {% endif %}
      {% if current_user.has_permission(sco.Permission.ScoAbsChange) %} -
    • Ajouter
    • Justifier
    • diff --git a/app/views/__init__.py b/app/views/__init__.py index 9d342dbb..89d3418b 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -23,6 +23,7 @@ scolar_bp = Blueprint("scolar", __name__) notes_bp = Blueprint("notes", __name__) users_bp = Blueprint("users", __name__) absences_bp = Blueprint("absences", __name__) +assiduites_bp = Blueprint("assiduites", __name__) # Cette fonction est bien appelée avant toutes les requêtes @@ -107,6 +108,7 @@ class ScoData: from app.views import ( absences, + assiduites, but_formation, notes_formsemestre, notes, diff --git a/app/views/assiduites.py b/app/views/assiduites.py new file mode 100644 index 00000000..83bf22b5 --- /dev/null +++ b/app/views/assiduites.py @@ -0,0 +1,377 @@ +import datetime + +from flask import g, request, render_template + +from flask import abort, url_for + +from app.comp import res_sem +from app.comp.res_compat import NotesTableCompat +from app.decorators import ( + scodoc, + permission_required, +) +from app.models import FormSemestre, Identite +from app.views import assiduites_bp as bp +from app.views import ScoData + +# --------------- +from app.scodoc.sco_permissions import Permission +from app.scodoc import html_sco_header +from app.scodoc import sco_moduleimpl +from app.scodoc import sco_preferences +from app.scodoc import sco_groups_view +from app.scodoc import sco_etud +from app.scodoc import sco_find_etud +from flask_login import current_user + + +CSSSTYLES = html_sco_header.BOOTSTRAP_MULTISELECT_CSS + +# --- UTILS --- + + +class HTMLElement: + """""" + + +class HTMLElement: + """Représentation d'un HTMLElement version Python""" + + def __init__(self, tag: str, *attr, **kattr) -> None: + self.tag: str = tag + self.children: list[HTMLElement] = [] + self.self_close: bool = kattr.get("self_close", False) + self.text_content: str = kattr.get("text_content", "") + self.key_attributes: dict[str, any] = kattr + self.attributes: list[str] = list(attr) + + def add(self, *child: HTMLElement) -> None: + """add child element to self""" + for kid in child: + self.children.append(kid) + + def remove(self, child: HTMLElement) -> None: + """Remove child element from self""" + if child in self.children: + self.children.remove(child) + + def __str__(self) -> str: + attr: list[str] = self.attributes + + for att, val in self.key_attributes.items(): + if att in ("self_close", "text_content"): + continue + + if att != "cls": + attr.append(f'{att}="{val}"') + else: + attr.append(f'class="{val}"') + + if not self.self_close: + head: str = f"<{self.tag} {' '.join(attr)}>{self.text_content}" + body: str = "\n".join(map(str, self.children)) + foot: str = f"" + return head + body + foot + return f"<{self.tag} {' '.join(attr)}/>" + + def __add__(self, other: str): + return str(self) + other + + def __radd__(self, other: str): + return other + str(self) + + +class HTMLStringElement(HTMLElement): + """Utilisation d'une chaine de caracètres pour représenter un element""" + + def __init__(self, text: str) -> None: + self.text: str = text + HTMLElement.__init__(self, "textnode") + + def __str__(self) -> str: + return self.text + + +class HTMLBuilder: + def __init__(self, *content: HTMLElement or str) -> None: + self.content: list[HTMLElement or str] = list(content) + + def add(self, *element: HTMLElement or str): + self.content.extend(element) + + def remove(self, element: HTMLElement or str): + if element in self.content: + self.content.remove(element) + + def __str__(self) -> str: + return "\n".join(map(str, self.content)) + + def build(self) -> str: + return self.__str__() + + +# -------------------------------------------------------------------- +# +# Assiduités (/ScoDoc//Scolarite/Assiduites/...) +# +# -------------------------------------------------------------------- + + +@bp.route("/") +@bp.route("/index_html") +@scodoc +@permission_required(Permission.ScoView) +def index_html(): + """Gestionnaire assiduités, page principale""" + + H = [ + html_sco_header.sco_header( + page_title="Saisie des assiduités", + cssstyles=["css/calabs.css"], + javascripts=["js/calabs.js"], + ), + """

      Traitement des assiduités

      +

      + Pour saisir des assiduités ou consulter les états, il est recommandé par passer par + le semestre concerné (saisie par jours nommés ou par semaines). +

      + """, + ] + H.append( + """

      Pour signaler, annuler ou justifier une assiduité pour un seul étudiant, + choisissez d'abord concerné:

      """ + ) + H.append(sco_find_etud.form_search_etud()) + if current_user.has_permission( + Permission.ScoAbsChange + ) and sco_preferences.get_preference("handle_billets_abs"): + H.append( + f""" +

      Billets d'absence

      + + """ + ) + H.append(html_sco_header.sco_footer()) + return "\n".join(H) + + +@bp.route("/SignaleAssiduiteEtud") +@scodoc +@permission_required(Permission.ScoAbsChange) +def signal_assiduites_etud(): + """ + signal_assiduites_etud Saisie de l'assiduité d'un étudiant + + Args: + etudid (int): l'identifiant de l'étudiant + + Returns: + str: l'html généré + """ + + etudid = request.args.get("etudid", -1) + etud: Identite = Identite.query.get_or_404(etudid) + if etud.dept_id != g.scodoc_dept_id: + abort(404, "étudiant inexistant dans ce département") + + header: str = html_sco_header.sco_header( + page_title="Saisie Assiduités", + init_qtip=True, + javascripts=[ + "js/assiduites.js", + "libjs/moment.new.min.js", + "libjs/moment-timezone.js", + ], + cssstyles=CSSSTYLES + + [ + "css/assiduites.css", + ], + ) + + return HTMLBuilder( + header, + render_template("assiduites/minitimeline.j2"), + render_template( + "assiduites/signal_assiduites_etud.j2", + sco=ScoData(etud), + date=datetime.date.today().isoformat(), + ), + ).build() + + +@bp.route("/SignalAssiduiteGr") +@scodoc +@permission_required(Permission.ScoAbsChange) +def signal_assiduites_group(): + """ + signal_assiduites_group Saisie des assiduités des groupes pour le jour donnée + + Returns: + str: l'html généré + """ + formsemestre_id: int = request.args.get("formsemestre_id", -1) + moduleimpl_id: int = request.args.get("moduleimpl_id") + group_ids: list[int] = request.args.get("group_ids", None) + + formsemestre_id: int = request.args.get("formsemestre_id", -1) + moduleimpl_id: int = request.args.get("moduleimpl_id") + date: str = request.args.get("jour", datetime.date.today().isoformat()) + group_ids: list[int] = request.args.get("group_ids", None) + + if group_ids is None: + group_ids = [] + else: + group_ids = group_ids.split(",") + map(str, group_ids) + + # Vérification du moduleimpl_id + try: + moduleimpl_id = int(moduleimpl_id) + except (TypeError, ValueError): + moduleimpl_id = None + # Vérification du formsemestre_id + try: + formsemestre_id = int(formsemestre_id) + except (TypeError, ValueError): + formsemestre_id = None + + groups_infos = sco_groups_view.DisplayedGroupsInfos( + group_ids, moduleimpl_id=moduleimpl_id, formsemestre_id=formsemestre_id + ) + + # Aucun étudiant WIP + if not groups_infos.members: + return ( + html_sco_header.sco_header(page_title="Saisie journalière des Assiduités") + + "

      Aucun étudiant !

      " + + html_sco_header.sco_footer() + ) + + # --- URL DEFAULT --- + + base_url: str = f"SignalAssiduiteGr?date={date}&{groups_infos.groups_query_args}" + + # --- Filtrage par formsemestre --- + formsemestre_id = groups_infos.formsemestre_id + formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) + if formsemestre.dept_id != g.scodoc_dept_id: + abort(404, "groupes inexistants dans ce département") + + require_module = sco_preferences.get_preference( + "abs_require_module", formsemestre_id + ) + + etuds = [ + sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0] + for m in groups_infos.members + ] + + # --- Restriction en fonction du moduleimpl_id --- + if moduleimpl_id: + mod_inscrits = { + x["etudid"] + for x in sco_moduleimpl.do_moduleimpl_inscription_list( + moduleimpl_id=moduleimpl_id + ) + } + etuds_inscrits_module = [e for e in etuds if e["etudid"] in mod_inscrits] + if etuds_inscrits_module: + etuds = etuds_inscrits_module + else: + # Si aucun etudiant n'est inscrit au module choisi... + moduleimpl_id = None + + # --- Génération de l'HTML --- + sem = formsemestre.to_dict() + + if groups_infos.tous_les_etuds_du_sem: + gr_tit = "en" + else: + if len(groups_infos.group_ids) > 1: + grp = "des groupes" + else: + grp = "du groupe" + gr_tit = ( + grp + ' ' + groups_infos.groups_titles + "" + ) + + header: str = html_sco_header.sco_header( + page_title="Saisie journalière des assiduités", + init_qtip=True, + javascripts=html_sco_header.BOOTSTRAP_MULTISELECT_JS + + [ + # Voir fonctionnement JS + "js/etud_info.js", + "js/abs_ajax.js", + "js/groups_view.js", + "js/assiduites.js", + "libjs/moment.new.min.js", + "libjs/moment-timezone.js", + ], + cssstyles=CSSSTYLES + + [ + "css/assiduites.css", + ], + no_side_bar=1, + ) + + return HTMLBuilder( + header, + render_template("assiduites/minitimeline.j2"), + render_template( + "assiduites/signal_assiduites_group.j2", + gr_tit=gr_tit, + sem=sem["titre_num"], + date=date, + formsemestre_id=formsemestre_id, + grp=sco_groups_view.menu_groups_choice(groups_infos), + moduleimpl_select=_module_selector(formsemestre, moduleimpl_id), + timeline=_timeline(), + formsemestre_date_debut=str(formsemestre.date_debut), + formsemestre_date_fin=str(formsemestre.date_fin), + ), + html_sco_header.sco_footer(), + ).build() + + +def _module_selector( + formsemestre: FormSemestre, moduleimpl_id: int = None +) -> HTMLElement: + """ + _module_selector Génère un HTMLSelectElement à partir des moduleimpl du formsemestre + + Args: + formsemestre (FormSemestre): Le formsemestre d'où les moduleimpls seront pris. + + Returns: + str: La représentation str d'un HTMLSelectElement + """ + + ntc: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) + + modimpls_list: list[dict] = [] + ues = ntc.get_ues_stat_dict() + for ue in ues: + modimpls_list += ntc.get_modimpls_dict(ue_id=ue["ue_id"]) + + selected = moduleimpl_id is not None + + modules = [] + + for modimpl in modimpls_list: + modname: str = ( + (modimpl["module"]["code"] or "") + + " " + + (modimpl["module"]["abbrev"] or modimpl["module"]["titre"] or "") + ) + modules.append({"moduleimpl_id": modimpl["moduleimpl_id"], "name": modname}) + + return render_template( + "assiduites/moduleimpl_selector.j2", selected=selected, modules=modules + ) + + +def _timeline() -> HTMLElement: + return render_template("assiduites/timeline.j2")