From 6839831af54cd9302119bcc3bffafe10475958d4 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Mon, 3 Aug 2026 14:31:25 +0200 Subject: refactor project structure * merge sites into single project root * fix name of mass-graduation site * enable typescript support --- .gitignore | 2 + Makefile | 22 + assets/user-profile-icon.svg | 14 + cockpit.css | 232 +++++ cockpit.html | 150 +++ cockpit/.gitignore | 2 - cockpit/Makefile | 1 - cockpit/assets/user-profile-icon.svg | 14 - cockpit/index.css | 232 ----- cockpit/index.html | 150 --- cockpit/js/cockpit.js | 366 ------- cockpit/package-lock.json | 1860 ---------------------------------- cockpit/package.json | 16 - cockpit/webpack.config.js | 44 - common.mk | 18 - js/config.js | 0 js/database.js | 160 --- js/graduation-common.js | 396 -------- js/graduation-mock.js | 30 - js/graduation-prod.js | 15 - js/group.js | 57 -- js/mock.js | 104 -- js/selection.js | 170 ---- js/student-common.js | 115 --- js/student-mock.js | 9 - js/student-prod.js | 2 - js/utils-common.js | 20 - js/utils-mock.js | 41 - js/utils-prod.js | 36 - mass-graduation.css | 186 ++++ mass-graduation.html | 46 + mass-graducation/.gitignore | 2 - mass-graducation/Makefile | 1 - mass-graducation/Readme.md | 3 - mass-graducation/index.css | 186 ---- mass-graducation/index.html | 46 - mass-graducation/package-lock.json | 1860 ---------------------------------- mass-graducation/package.json | 16 - mass-graducation/src/main.js | 357 ------- mass-graducation/webpack.config.js | 44 - package-lock.json | 1754 ++++++++++++++++++++++++++++++++ package.json | 15 + src/admin-tools.js | 57 ++ src/cockpit.js | 366 +++++++ src/config.js | 0 src/database.js | 160 +++ src/graduation-common.js | 396 ++++++++ src/graduation-mock.js | 30 + src/graduation-prod.js | 15 + src/group.js | 57 ++ src/mass-graduation.js | 357 +++++++ src/mock.js | 104 ++ src/selection.js | 170 ++++ src/student-common.js | 115 +++ src/student-mock.js | 9 + src/student-prod.js | 2 + src/utils-common.ts | 25 + src/utils-mock.js | 41 + src/utils-prod.js | 36 + tsconfig.json | 15 + webpack.config.js | 48 + 61 files changed, 4424 insertions(+), 6373 deletions(-) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 assets/user-profile-icon.svg create mode 100644 cockpit.css create mode 100644 cockpit.html delete mode 100644 cockpit/.gitignore delete mode 100644 cockpit/Makefile delete mode 100644 cockpit/assets/user-profile-icon.svg delete mode 100644 cockpit/index.css delete mode 100644 cockpit/index.html delete mode 100644 cockpit/js/cockpit.js delete mode 100644 cockpit/package-lock.json delete mode 100644 cockpit/package.json delete mode 100644 cockpit/webpack.config.js delete mode 100644 common.mk delete mode 100644 js/config.js delete mode 100644 js/database.js delete mode 100644 js/graduation-common.js delete mode 100644 js/graduation-mock.js delete mode 100644 js/graduation-prod.js delete mode 100644 js/group.js delete mode 100644 js/mock.js delete mode 100644 js/selection.js delete mode 100644 js/student-common.js delete mode 100644 js/student-mock.js delete mode 100644 js/student-prod.js delete mode 100644 js/utils-common.js delete mode 100644 js/utils-mock.js delete mode 100644 js/utils-prod.js create mode 100644 mass-graduation.css create mode 100644 mass-graduation.html delete mode 100644 mass-graducation/.gitignore delete mode 100644 mass-graducation/Makefile delete mode 100644 mass-graducation/Readme.md delete mode 100644 mass-graducation/index.css delete mode 100644 mass-graducation/index.html delete mode 100644 mass-graducation/package-lock.json delete mode 100644 mass-graducation/package.json delete mode 100644 mass-graducation/src/main.js delete mode 100644 mass-graducation/webpack.config.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/admin-tools.js create mode 100644 src/cockpit.js create mode 100644 src/config.js create mode 100644 src/database.js create mode 100644 src/graduation-common.js create mode 100644 src/graduation-mock.js create mode 100644 src/graduation-prod.js create mode 100644 src/group.js create mode 100644 src/mass-graduation.js create mode 100644 src/mock.js create mode 100644 src/selection.js create mode 100644 src/student-common.js create mode 100644 src/student-mock.js create mode 100644 src/student-prod.js create mode 100644 src/utils-common.ts create mode 100644 src/utils-mock.js create mode 100644 src/utils-prod.js create mode 100644 tsconfig.json create mode 100644 webpack.config.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1eae0cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d89e3a6 --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +HTML := cockpit.html mass-graduation.html admin-tools.html +SITES := $(addprefix dist/,$(HTML)) +JS := $(SITES:.html=.js) + +dist/%.html: %.html %.css dist/%.js + echo | pandoc --template $< --embed-resources --standalone > $@ + +dist/%.js: $(wildcard src/*.js) + npm run build:dev + +.PHONY: mock dev prod +mock: + npm run build:mock + $(MAKE) $(SITES) + +dev: + npm run build:dev + $(MAKE) $(SITES) + +prod: + npm run build + $(MAKE) $(SITES) diff --git a/assets/user-profile-icon.svg b/assets/user-profile-icon.svg new file mode 100644 index 0000000..ae16831 --- /dev/null +++ b/assets/user-profile-icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/cockpit.css b/cockpit.css new file mode 100644 index 0000000..5e020a6 --- /dev/null +++ b/cockpit.css @@ -0,0 +1,232 @@ +#student-selection-container { + margin: 0.2rem; +} + +#student-selection-search-container { + position: relative; +} + +#student-selection { + box-sizing: border-box; + width: 100%; + padding: 0.75rem 2.5rem 0.75rem 1rem; + margin-bottom: 1.5rem; + border: 1px solid #ddd; + border-radius: 5pt; + font-size: 1.5rem; +} + +#student-selection-clear { + position: absolute; + right: 0.75rem; + transform: translateY(-175%); + border: none; + background: transparent; + font-size: 1.875rem; + cursor: pointer; + color: #888; +} + +#student-selection-clear:hover { + color: #333; +} + +#student-selection:focus { + border-color: #0077ff; +} + +#student-selection-loader { + --size: 0.5px; + width: calc(48 * var(--size)); + height: calc(48 * var(--size)); + border: calc(5 * var(--size)) solid black; + border-bottom-color: transparent; + border-radius: 50%; + display: inline; + position: absolute; + top: 20px; + right: 0.75rem; + box-sizing: border-box; + animation: rotation 1s linear infinite; +} + +@keyframes rotation { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +#student-selection-list { + list-style: none; +} + +#student-selection-list li { + padding: 0.75rem 1rem; + border-bottom: 1px solid #eee; + cursor: pointer; + transition: background 0.2s; +} + +#student-selection-list li:hover { + background: #f0f8ff; +} + +#student-selection-list li.active { + background: #0077ff; + color: white; +} + +#student-selection-list li > u.student-selection-list-highlight { + font-weight: bold; +} + +#student-selection-noresults { + color: red; + text-align: center; + margin-top: 1rem; +} + +.student-info-banner { + display: flex; +} + +#student-info-icons { + width: 20%; + position: relative; +} + +#student-info-icon { + margin: 0.2rem; + clip-path: circle(); + width: 100%; +} + +#student-level-icon { + width: 30%; + position: absolute; + top: 70%; + left: 70%; + border: solid 1px; + border-radius: 50%; + background: white; +} + +#student-info-name-container { + flex: 50%; + margin: 0.2rem; +} + +#student-info-name { + font-size: 1.2rem; +} + +#student-info-tag { + border-radius: 5pt; + background: gray; + font-size: 0.8rem; + padding: 3pt; + display: none; + width: min-content; +} + +#student-info-coach-container { + margin: 0.2rem; + flex: 0 1 20%; + flex-direction: column; +} + +#student-observation { + margin-left: 2rem; + margin-bottom: 1rem; +} + +.student-info-mid-element { + padding: 5pt; + border: black solid 0.2pt; + border-radius: 5pt; + margin: 0.2rem; + text-align: center; + hyphens: auto; + flex: 1; +} + +.student-info-points { + display: block; + font-size: 2rem; + margin-bottom: 5px; +} + +#student-info-green { + color: green; +} + +#student-info-orange { + color: orange; +} + +#student-info-red { + color: red; +} + +#student-info-level { + font-size: 1.2rem; + display: block; +} + +#student-info-point-reason-container { + margin: 0.2rem; +} + +#student-info-point-reason { + box-sizing: border-box; + padding: 10px; + width: 100%; + font-size: 0.8rem; + letter-spacing: 1px; + line-height: 1.5; + border-radius: 5px; + border: 1px solid #cccccc; +} + +#student-info-point-reason-submit { + position: absolute; + right: 0.75rem; + transform: translateY(-175%); + border: none; + background: transparent; + font-size: 1.875rem; + cursor: pointer; + color: #888; +} + +#student-info-stickers-container { + flex: 70%; +} + +#student-info-bonus-container { + flex: 30%; +} + +.student-info-bottom-element { + padding: 5pt; + border: black solid 0.2pt; + border-radius: 5pt; + margin: 0.2rem; + height: 5rem; +} + +#student-info-bonus-amount { + font-size: 2rem; +} + +.graduation-dropdown > summary { + background: lightgray; + border: black solid 0.2pt; + border-radius: 10px; + padding: 0.25rem; + margin-top: 1rem; + margin-bottom: 1rem; +} diff --git a/cockpit.html b/cockpit.html new file mode 100644 index 0000000..e7aef02 --- /dev/null +++ b/cockpit.html @@ -0,0 +1,150 @@ + + + + + Cockpit + + + + + + +
+
+ + + +
+ + +
+ + + + diff --git a/cockpit/.gitignore b/cockpit/.gitignore deleted file mode 100644 index 1eae0cf..0000000 --- a/cockpit/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -dist/ -node_modules/ diff --git a/cockpit/Makefile b/cockpit/Makefile deleted file mode 100644 index 23f8a8f..0000000 --- a/cockpit/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../common.mk diff --git a/cockpit/assets/user-profile-icon.svg b/cockpit/assets/user-profile-icon.svg deleted file mode 100644 index ae16831..0000000 --- a/cockpit/assets/user-profile-icon.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/cockpit/index.css b/cockpit/index.css deleted file mode 100644 index 5e020a6..0000000 --- a/cockpit/index.css +++ /dev/null @@ -1,232 +0,0 @@ -#student-selection-container { - margin: 0.2rem; -} - -#student-selection-search-container { - position: relative; -} - -#student-selection { - box-sizing: border-box; - width: 100%; - padding: 0.75rem 2.5rem 0.75rem 1rem; - margin-bottom: 1.5rem; - border: 1px solid #ddd; - border-radius: 5pt; - font-size: 1.5rem; -} - -#student-selection-clear { - position: absolute; - right: 0.75rem; - transform: translateY(-175%); - border: none; - background: transparent; - font-size: 1.875rem; - cursor: pointer; - color: #888; -} - -#student-selection-clear:hover { - color: #333; -} - -#student-selection:focus { - border-color: #0077ff; -} - -#student-selection-loader { - --size: 0.5px; - width: calc(48 * var(--size)); - height: calc(48 * var(--size)); - border: calc(5 * var(--size)) solid black; - border-bottom-color: transparent; - border-radius: 50%; - display: inline; - position: absolute; - top: 20px; - right: 0.75rem; - box-sizing: border-box; - animation: rotation 1s linear infinite; -} - -@keyframes rotation { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} - -#student-selection-list { - list-style: none; -} - -#student-selection-list li { - padding: 0.75rem 1rem; - border-bottom: 1px solid #eee; - cursor: pointer; - transition: background 0.2s; -} - -#student-selection-list li:hover { - background: #f0f8ff; -} - -#student-selection-list li.active { - background: #0077ff; - color: white; -} - -#student-selection-list li > u.student-selection-list-highlight { - font-weight: bold; -} - -#student-selection-noresults { - color: red; - text-align: center; - margin-top: 1rem; -} - -.student-info-banner { - display: flex; -} - -#student-info-icons { - width: 20%; - position: relative; -} - -#student-info-icon { - margin: 0.2rem; - clip-path: circle(); - width: 100%; -} - -#student-level-icon { - width: 30%; - position: absolute; - top: 70%; - left: 70%; - border: solid 1px; - border-radius: 50%; - background: white; -} - -#student-info-name-container { - flex: 50%; - margin: 0.2rem; -} - -#student-info-name { - font-size: 1.2rem; -} - -#student-info-tag { - border-radius: 5pt; - background: gray; - font-size: 0.8rem; - padding: 3pt; - display: none; - width: min-content; -} - -#student-info-coach-container { - margin: 0.2rem; - flex: 0 1 20%; - flex-direction: column; -} - -#student-observation { - margin-left: 2rem; - margin-bottom: 1rem; -} - -.student-info-mid-element { - padding: 5pt; - border: black solid 0.2pt; - border-radius: 5pt; - margin: 0.2rem; - text-align: center; - hyphens: auto; - flex: 1; -} - -.student-info-points { - display: block; - font-size: 2rem; - margin-bottom: 5px; -} - -#student-info-green { - color: green; -} - -#student-info-orange { - color: orange; -} - -#student-info-red { - color: red; -} - -#student-info-level { - font-size: 1.2rem; - display: block; -} - -#student-info-point-reason-container { - margin: 0.2rem; -} - -#student-info-point-reason { - box-sizing: border-box; - padding: 10px; - width: 100%; - font-size: 0.8rem; - letter-spacing: 1px; - line-height: 1.5; - border-radius: 5px; - border: 1px solid #cccccc; -} - -#student-info-point-reason-submit { - position: absolute; - right: 0.75rem; - transform: translateY(-175%); - border: none; - background: transparent; - font-size: 1.875rem; - cursor: pointer; - color: #888; -} - -#student-info-stickers-container { - flex: 70%; -} - -#student-info-bonus-container { - flex: 30%; -} - -.student-info-bottom-element { - padding: 5pt; - border: black solid 0.2pt; - border-radius: 5pt; - margin: 0.2rem; - height: 5rem; -} - -#student-info-bonus-amount { - font-size: 2rem; -} - -.graduation-dropdown > summary { - background: lightgray; - border: black solid 0.2pt; - border-radius: 10px; - padding: 0.25rem; - margin-top: 1rem; - margin-bottom: 1rem; -} diff --git a/cockpit/index.html b/cockpit/index.html deleted file mode 100644 index 0607967..0000000 --- a/cockpit/index.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - - Cockpit - - - - - - -
-
- - - -
- - -
- - - - diff --git a/cockpit/js/cockpit.js b/cockpit/js/cockpit.js deleted file mode 100644 index fb3b539..0000000 --- a/cockpit/js/cockpit.js +++ /dev/null @@ -1,366 +0,0 @@ -// import { - // fetchSubjectsMock as fetchSubjects, - // fetchProgressMock as fetchProgress, -// } from "../../js/mock.js"; - -import { - OBSERVATION_TYPES, - LEVEL_NAMES, - GraduationChange, - fetchGraduation, - fetchPublicGraduation, - submitGraduationChange -} from "../../js/graduation-ENV.js"; - -import { fetchStudent, fetchStudents, Student } from "../../js/student-ENV.js"; -import { - isTeacher as getIsTeacher, - getName, - getUserId, - DATE_FMT, - DATETIME_FMT, -} from "../../js/utils-ENV.js"; -import { initSelection } from "../../js/selection.js"; - -let isTeacher; -let name; -let student; - -async function loadStudent(studentToLoad) { - student = studentToLoad; - await fetchAndUpdateStudentInfo(); - // updateStudentCalendar(); - // updateStudentProgress(); - updateStudentGraduation(); -} - -function initStudentSelection() { - initSelection( - "student-selection", - fetchStudents, - (student) => `${student.name} ${student.getGroup()}`, - loadStudent - ); -} - -function pointClickHandler(color) { - const textareaDiv = document.getElementById("student-info-point-reason-container"); - const textarea = document.getElementById("student-info-point-reason"); - const submitBtn = document.getElementById("student-info-point-reason-submit"); - - // TODO(muhq): Prefill the textarea with common reasons - switch (color) { - case "green": - textarea.style.background = "rgb(from green r g b / 20%)" - textarea.value = ""; - break; - case "orange": - textarea.style.background = "rgb(from orange r g b / 20%)" - textarea.value = ""; - break; - case "red": - textarea.style.background = "rgb(from red r g b / 20%)" - textarea.value = ""; - break; - } - textareaDiv.style.display = "block"; - textarea.focus(); - - const coach = name; - - let submit = function() { - let change; - switch (color) { - case "green": - change = GraduationChange.newGreenPointChange(textarea.value, coach); - break; - case "orange": - change = GraduationChange.newOrangePointChange(textarea.value, coach); - break; - case "red": - change = GraduationChange.newRedPointChange(textarea.value, coach); - break; - default: - alert("Invalid color"); - break; - } - submitGraduationChange(student, change); - textareaDiv.style.display = "none"; - updateStudentInfo(); - student.fetchGraduation().then(() => updateStudentGraduation() ); - } - // Use onEVENT to override any previous handler - textarea.onkeydown = function(e) { - if (e.shiftKey && e.key === "Enter") { - submit(); - } - if (e.key === "Escape") { - textareaDiv.style.display = "none"; - } - }; - submitBtn.onclick = submit; -} - -async function fetchAndUpdateStudentInfo() { - if (isTeacher) - student.graduation = await fetchGraduation(student); - else - student.graduation = await fetchPublicGraduation(student); - - updateStudentInfo(); -} - -let DEFAULT_STUDENT_ICON; - -function updateStudentInfo() { - // Reset the student icon - document.getElementById("student-info-icon").src = DEFAULT_STUDENT_ICON; - - // Try to replace the generic icon with the user's icon - var img = document.createElement('img'); - img.onload = function() { - document.getElementById("student-info-icon").src = img.src; - img = img.onload = null; - }; - student.getIconUrl().then( (url) => img.src = url ); - - document.getElementById("student-info-name").innerHTML = `${student.name}`; - - if (student.graduation) { - const tag = document.getElementById("student-info-tag"); - tag.innerHTML = student.graduation.getLevelName(); - tag.style.display = "block"; - - document.getElementById("student-info-points-green").innerHTML = student.graduation.green; - document.getElementById("student-info-points-orange").innerHTML = student.graduation.orange; - document.getElementById("student-info-points-red").innerHTML = student.graduation.red; - - if (isTeacher) { - document.getElementById("student-info-green").addEventListener('click', - () => pointClickHandler("green")); - document.getElementById("student-info-orange").addEventListener('click', - () => pointClickHandler("orange")); - document.getElementById("student-info-red").addEventListener('click', - () => pointClickHandler("red")); - } - - renderGraduationLevel(student.graduation); - - // renderGraduationStickers(student.graduation, document.getElementById("student-info-stickers")); - // renderGraduationBonus(student.graduation, document.getElementById("student-info-stickers")); - - renderObservationStatus(student.graduation, document.getElementById("student-observation")); - } - - document.getElementById("student-content").style.display = "block"; -} - -function renderGraduationLevel(graduation) { - const label = document.getElementById("student-info-level"); - label.innerHTML = graduation.getLevelName(); - const icon = document.getElementById("student-level-icon"); - icon.src = graduation.getIcon(); -} - -// function renderGraduationStickers(graduation, node) { - // node.innerHTML = ""; -// } - -// function renderGraduationBonus(graduation, node) { - // node.innerHTML = ""; -// } - -function renderObservationStatus(graduation, node) { - node.innerHTML = ""; - - let status; - if (isTeacher || graduation.isObserved() || graduation.observationRequested) { - status = document.createElement("span"); - status.classList.add("student-observation-status"); - if (graduation.isObserved()) { - status.innerHTML = '👁 Ich befinde mich in der Beobachtungsphase zum Levelaufstieg.'; - } else if (graduation.observationRequested) { - status.innerHTML = 'Beobachtungsphase noch nicht begonnen.'; - } else { - status.innerHTML = "Nicht in Beobachtung."; - } - } else { - status = document.createElement("button"); - status.classList.add("student-request-observation-button"); - // Level is 1-based - status.innerHTML = `Ich will ${LEVEL_NAMES[graduation.level]} werden`; - status.addEventListener("click", async function() { - await graduation.requestObservation(); - // Rerender the observation status - renderObservationStatus(graduation, node); - }); - } - node.appendChild(status); -} - -// function newProgressBar(subject) { - // let bar = document.createElement("div"); - // fetchProgress(subject).then((progress) => { - // bar.progress = progress; - // }); - - // let s1 = document.createElement("div"); - // s1.onhover - - // bar.appendChild(s1); - // let s2 = document.createElement("div"); - // bar.appendChild(s2); - // let s3 = document.createElement("div"); - // bar.appendChild(s3); - // return bar; -// } - -// let selectedSubjects; -// async function updateStudentProgress() { - // selectedSubjects = await fetchSubjects(student); - - // let prog = document.getElementById("progress"); - // for(const subj of selectedSubjects) { - // let subjDiv = document.createElement("div"); - // subjDiv.innerHTML = subj; - // subjDiv.appendChild(newProgressBar(subj)) - // prog.appendChild(subjDiv); - // } -// } - -// async function updateStudentCalendar() { - // let cal = document.getElementById("calendar"); -// } - -async function updateStudentGraduation() { - if (!isTeacher) return; - console.assert(student.graduation, "Student has no graduation"); - const graduation = student.graduation; - - const changesTbl = document.getElementById("graduation-changes"); - changesTbl.innerHTML = ""; - for (const change of graduation.changes.filter( - (ch) => ["Grün", "Rot", "Orange"].includes(ch.change) )) - { - const changeTr = document.createElement("tr"); - for (const key in change) { - if (key == "json" || key == "id" || key == "name") continue; - - if (key == "change") { - switch(change.change) { - case "Grün": - changeTr.style.background = "green"; - break; - case "Rot": - changeTr.style.background = "red"; - break; - case "Orange": - changeTr.style.background = "orange"; - break; - default: - alert(`unexpected change: ${change.change}`); - } - continue; - } - - const value = change[key]; - const fmt = key == "date" ? DATETIME_FMT.format(value) : value; - - const changeTd = document.createElement("td"); - changeTd.innerHTML = fmt; - changeTd.dataset.key = key; - changeTr.appendChild(changeTd); - } - changesTbl.appendChild(changeTr); - } - - const observtionsTbl = document.getElementById("graduation-current-observation-period"); - observtionsTbl.innerHTML = ""; - if (graduation.observation) { - const sums = graduation.observation.getObservationsSums(); - for (let i = 0; i < OBSERVATION_TYPES.length; i++) { - const tr = document.createElement("tr"); - const typeTd = document.createElement("td"); - typeTd.innerHTML = OBSERVATION_TYPES[i]; - tr.appendChild(typeTd); - - const yesTd = document.createElement("td"); - yesTd.innerHTML = sums[i].yes; - tr.appendChild(yesTd); - - const noTd = document.createElement("td"); - noTd.innerHTML = sums[i].no; - tr.appendChild(noTd); - - observtionsTbl.appendChild(tr); - } - - const endDiv = document.getElementById("graduation-current-observation-period-end"); - if (!graduation.observation.ready()) { - endDiv.style.display = "none"; - return; - } - - endDiv.style.display = "block"; - - const reason = document.getElementById("graduation-current-observation-period-end-reason").value; - const successBtn = document.getElementById("graduation-current-observation-period-end-success"); - successBtn.addEventListener("click", () => { - submitGraduationChange(graduation.student, GraduationChange.newObservationEnd(reason, true)); - }); - const failureBtn = document.getElementById("graduation-current-observation-period-end-failure"); - failureBtn.addEventListener("click", () => { - submitGraduationChange(graduation.student, GraduationChange.newObservationEnd(reason, false)); - }); - } - - const pastObservtionsTbl = document.getElementById("graduation-observation-periods"); - pastObservtionsTbl.innerHTML = ""; - if (graduation.pastObservations.length > 0) { - for (const period of graduation.pastObservations) { - const tr = document.createElement("tr"); - const periodTd = document.createElement("td"); - const startDate = DATE_FMT.format(period.start); - const endDate = DATE_FMT.format(period.end); - periodTd.innerHTML = `${startDate} - ${endDate}`; - tr.appendChild(periodTd); - - const successTd = document.createElement("td"); - if (period.success) { - successTd.innerHTML = "Graduierung erfolgreich" - } else { - successTd.innerHTML = "Graduierung nicht erfolgreich" - } - tr.appendChild(successTd); - - pastObservtionsTbl.appendChild(tr); - } - } -} - -async function initCockpit() { - if (isTeacher) { - initStudentSelection(); - const params = new URLSearchParams(location.search); - const userId = params.get("userToLoad"); - if (userId) { - student = await fetchStudent(userId); - await loadStudent(student); - } - } else { - // Remove all elements only suitable for teachers - for(const elem of document.querySelectorAll("[data-availability=if-lehrer]")) { - elem.remove(); - } - - const student = await Student.fetchById(getUserId()); - await loadStudent(student); - } -} - -window.onload = function() { - isTeacher = getIsTeacher(); - DEFAULT_STUDENT_ICON = document.getElementById("student-info-icon").src; - name = getName(); - initCockpit(); -} diff --git a/cockpit/package-lock.json b/cockpit/package-lock.json deleted file mode 100644 index 6ba8178..0000000 --- a/cockpit/package-lock.json +++ /dev/null @@ -1,1860 +0,0 @@ -{ - "name": "cockpit", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "cockpit", - "version": "1.0.0", - "devDependencies": { - "html-webpack-plugin": "^5.6.7", - "webpack": "^5.107.2", - "webpack-cli": "^7.0.2" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz", - "integrity": "sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.17.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", - "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, - "node_modules/ajv": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.32", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", - "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true, - "license": "ISC" - }, - "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001793", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", - "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-select/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/css-select/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/css-select/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/css-select/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "license": "MIT", - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.361", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", - "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==", - "dev": true, - "license": "ISC" - }, - "node_modules/enhanced-resolve": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz", - "integrity": "sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/envinfo": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", - "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", - "dev": true, - "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.6.7", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.7.tgz", - "integrity": "sha512-md+vXtdCAe60s1k6AU3dUyMJnDxUyQAwfwPKoLisvgUF1IXjtlLsk2se54+qfL9Mdm26bbwvjJybpNx48NKRLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.20.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/html-webpack-plugin/node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-webpack-plugin/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/html-webpack-plugin/node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loader-runner": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", - "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-releases": { - "version": "2.0.46", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", - "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/renderkid/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", - "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.0.tgz", - "integrity": "sha512-Eum+5ajkaOhf5KbM26osvv21kLD7BaGqQ1UA4Ami4arYwylmGUQTgHFpHDdmJod1q4QXa66p0to/FBKID+J1vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@minify-html/node": { - "optional": true - }, - "@swc/core": { - "optional": true - }, - "@swc/css": { - "optional": true - }, - "@swc/html": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "cssnano": { - "optional": true - }, - "csso": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "html-minifier-terser": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "postcss": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, - "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack": { - "version": "5.107.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.2.tgz", - "integrity": "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.16.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.22.0", - "es-module-lexer": "^2.1.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "loader-runner": "^4.3.2", - "mime-db": "^1.54.0", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.3", - "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.5.0", - "watchpack": "^2.5.1", - "webpack-sources": "^3.5.0" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.0.2.tgz", - "integrity": "sha512-dB0R4T+C/8YuvM+fabdvil6QE44/ChDXikV5lOOkrUeCkW5hTJv2pGLE3keh+D5hjYw8icBaJkZzpFoaHV4T+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@discoveryjs/json-ext": "^1.0.0", - "commander": "^14.0.3", - "cross-spawn": "^7.0.6", - "envinfo": "^7.14.0", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^6.0.1" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.101.0", - "webpack-bundle-analyzer": "^4.0.0 || ^5.0.0", - "webpack-dev-server": "^5.0.0" - }, - "peerDependenciesMeta": { - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/webpack-merge": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", - "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", - "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true, - "license": "MIT" - } - } -} diff --git a/cockpit/package.json b/cockpit/package.json deleted file mode 100644 index 65daf01..0000000 --- a/cockpit/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": "1.0.0", - "description": "", - "name": "cockpit", - "scripts": { - "build": "webpack --mode=production --config-node-env=production", - "build:dev": "webpack --mode=development", - "build:mock": "webpack --mode=development --config-node-env=mock", - "watch": "webpack --watch" - }, - "devDependencies": { - "html-webpack-plugin": "^5.6.7", - "webpack": "^5.107.2", - "webpack-cli": "^7.0.2" - } -} diff --git a/cockpit/webpack.config.js b/cockpit/webpack.config.js deleted file mode 100644 index 88aad41..0000000 --- a/cockpit/webpack.config.js +++ /dev/null @@ -1,44 +0,0 @@ -const webpack = require('webpack'); -const path = require('path'); - -const isProduction = process.env.NODE_ENV === 'production'; -const isMock = process.env.NODE_ENV === 'mock'; - -const config = { - entry: './js/cockpit.js', - output: { - path: path.resolve(__dirname, 'dist'), - }, - module: { - rules: [ - { - test: /\.png/, - type: 'asset/inline', - }, - ], - }, - plugins: [ - new webpack.NormalModuleReplacementPlugin( - /-ENV.js/, - (resource) => { - resource.request = resource.request.replace( - /-ENV/, - `-${isMock ? "mock" : "prod"}`, - ); - - if (resource.createData) { - resource.createData.request = resource.request; - } - } - ) - ], -}; - -module.exports = () => { - if (isProduction) { - config.mode = 'production'; - } else { - config.mode = 'development'; - } - return config; -}; diff --git a/common.mk b/common.mk deleted file mode 100644 index 47a3fe9..0000000 --- a/common.mk +++ /dev/null @@ -1,18 +0,0 @@ -dist/index.html: index.html index.css dist/main.js - echo | pandoc --template $< --embed-resources --standalone > $@ - -dist/main.js: $(wildcard js/*.js) - npm run build:dev - -.PHONY: mock dev prod -mock: - npm run build:mock - $(MAKE) dist/index.html - -dev: - npm run build:dev - $(MAKE) dist/index.html - -prod: - npm run build - $(MAKE) dist/index.html diff --git a/js/config.js b/js/config.js deleted file mode 100644 index e69de29..0000000 diff --git a/js/database.js b/js/database.js deleted file mode 100644 index e929896..0000000 --- a/js/database.js +++ /dev/null @@ -1,160 +0,0 @@ -import { getCourseId } from "./utils-ENV.js"; - -const DB_TEMPLATE_LIST_HEADER = ` - - - - - - - -`; -const DB_TEMPLATE_LIST_ENTRY = ` - - - - - -`; -const DB_TEMPLATE_LIST_FOOTER = ` - -
Json
##timeadded####user##[[Json]]
-`; - -let studentDbs = populateStudentDbs(); - -const ID_QUERY_PARAM_REGEX = /[?&]id=(\d+)/ - -async function populateStudentDbs() { - const courseUrl = `https://lernplattform.bycs.de/course/view.php?id=${getCourseId()}`; - const courseRes = await fetch(courseUrl); - const courseDoc = new DOMParser().parseFromString(await courseRes.text(), 'text/html'); - - let dbs = new Map(); - for (const link of courseDoc.querySelectorAll("div.activityname a.aalink")) { - if (link.textContent.trim() === "Submit Datenbank") { - const dbId = link.href.match(ID_QUERY_PARAM_REGEX)[1] - dbs.set("submit", new DB(dbId)); - continue; - } - - const match = link.textContent.match(/(\d+)(Shadow|Public|Submit)/); - if (!match) continue; - const studentId = match[1]; - const dbType = match[2]; - const dbId = link.href.match(ID_QUERY_PARAM_REGEX)[1] - - if (!dbs.has(studentId)) { - dbs.set(studentId, {}); - } - - dbs.get(studentId)[dbType.toLowerCase()] = new DB(dbId); - } - - return dbs; -} - -async function getSubmitDb() { - const dbs = await studentDbs; - return dbs.get("submit"); -} - -class DB { - constructor(id) { - this.id = id; - } - - async fetchEntries(params) { - return loadFromDatabaseActivity(this.id, params) - } - - async saveEntry(json) { - return addEntryToDatabaseActivity(this.id, json); - } - - static async getStudentDbs(student) { - const dbs = await studentDbs; - return dbs.get(student.id); - } -} - -async function loadFromDatabaseActivity(id, params) { - let dbUrl = `${window.location.origin}/mod/data/view.php?id=${id}&mode=list&perpage=1000`; - // TODO(muhq): Why does this not include entries? - if (params) { - dbUrl += "&filter=1&advanced=1"; - for (const param in params) { - dbUrl += `&${param}=${encodeURIComponent(params[param])}` - } - } - - const dbRes = await fetch(encodeURI(dbUrl)); - const dbDoc = new DOMParser().parseFromString(await dbRes.text(), 'text/html'); - let data = []; - dbDoc.querySelectorAll('.db-row').forEach(row => { - let entry = {}; - row.querySelectorAll('.db-cell').forEach(field => { - const key = field.dataset.key; - let value; - // Moodle includes the full date in the title attribute of the included span - if (key === "date") { - value = field.querySelector("span").title; - } else { - value = field.innerText.trim(); - } - entry[key] = value; - }); - data.push(entry); - }); - return data; -} - -async function loadFromStudentDatabase(student, type) { - const dbs = await DB.getStudentDbs(student); - const db = dbs[type]; - return await db.fetchEntries() -} - -async function addEntryToDatabaseActivity(id, json) { - const url = `/mod/data/edit.php?id=${id}`; - const editRes = await fetch(url); - const editDoc = new DOMParser().parseFromString(await editRes.text(), 'text/html'); - const form = editDoc.querySelector('div[role="main"] > form'); - - const formData = new FormData(form); - - for (const key of formData.keys()) { - if (key.startsWith("field_")) { - formData.set(key, json); - // We found our json key - break; - } - } - formData.set("saveandview", "Speichern"); - - return await fetch("/mod/data/edit.php", { - method: "POST", - // Set the FormData instance as the request body - body: formData, - }); -} - -async function addEntryToStudentDatabase(student, type, json) { - let db; - // global submit database - if (type === "submit") { - db = await getSubmitDb(); - // individuell databases - } else { - const dbs = await DB.getStudentDbs(student); - db = dbs[type]; - } - await db.saveEntry(json); -} - -export { - DB, - getSubmitDb, - loadFromDatabaseActivity, loadFromStudentDatabase, - addEntryToDatabaseActivity, addEntryToStudentDatabase, -}; diff --git a/js/graduation-common.js b/js/graduation-common.js deleted file mode 100644 index 0c28465..0000000 --- a/js/graduation-common.js +++ /dev/null @@ -1,396 +0,0 @@ -import { getSubmitDb, loadFromStudentDatabase, addEntryToStudentDatabase } from "./database.js" - -import NeulingPng from "../assets/Neuling.png"; -import EntdeckerPng from "../assets/Entdecker.png"; -import NavigatorPng from "../assets/Navigator.png"; -import PioneerPng from "../assets/Pioneer.png"; - -const LEVEL_NAMES = ["Neuling", "Entdecker", "Navigator", "Pionieer"]; - -const LEVEL_IMAGES = [ - new URL(NeulingPng), - new URL(EntdeckerPng), - new URL(NavigatorPng), - new URL(PioneerPng), -]; - -class GraduationChange { - constructor(date, change, reason, coach, context) { - this.date = date; - this.change = change; - this.reason = reason; - this.coach = coach; - this.context = context; - } - - isPublic() { - return [ - "Grün", "Orange", "Rot", - "ObservationStart", "ObservationEnd", - "LevelChange" - ].includes(this.change); - } - - publicDBFields () { - return { - "change": this.change, - "date": this.date, - }; - } - - dbFields () { - return { - // We include the date in the json because Moodle reports the timeadded - // locale dependent. - "date": this.date, - "change": this.change, - "reason": this.reason, - "context": this.context, - }; - } - - static fromDBEntry(e) { - const {coach, json} = e; - const change = JSON.parse(json); - return new GraduationChange(new Date(change.date), change.change, change.reason, coach, change.context); - } - - static newObservationRequest() { - return new GraduationChange(new Date(), "ObservationRequest", "", "", ""); - } - - static newObservationStart() { - return new GraduationChange(new Date(), "ObservationStart", "", "", ""); - } - - static newObservationEnd(reason, success) { - return new GraduationChange(new Date(), "ObservationEnd", reason, "", JSON.stringify(success)); - } - - static newObservationChange(observation) { - return new GraduationChange(observation.date, "Observation", "", "", JSON.stringify(observation.status)); - } - - static newGreenPointChange(reason, coach) { - return new GraduationChange(new Date(), "Grün", reason, coach, ""); - } - - static newOrangePointChange(reason, coach) { - return new GraduationChange(new Date(), "Orange", reason, coach, ""); - } - - static newRedPointChange(reason, coach) { - return new GraduationChange(new Date(), "Rot", reason, coach, ""); - } -} - -class Graduation { - constructor(student, green, orange, red, level, stickers, observationRequested, observation, pastObservations, changes) { - this.student = student; - this.green = green; - this.orange = orange; - this.red = red; - this.level = level; - this.stickers = stickers; - this.observationRequested = observationRequested; - this.observation = observation; - this.pastObservations = pastObservations; - this.changes = changes; - - student.graduation = this; - } - - static from(student, changes) { - let green = 0; - let orange = 0; - let red = 0; - let level = 1; - let openObservationRequest = null; - let observationPeriod = null; - let pastObservations = []; - let stickers = []; - - // The parsing logic assumes that changes are ordered chronologically - changes.sort((a, b) => a.date - b.date); - - for (const change of changes) { - switch (change.change) { - case "Grün": - green++; - break; - - case "Orange": - orange++; - break; - - case "Rot": - red++; - break; - - case "ObservationRequest": - openObservationRequest = change.date; - break; - - case "ObservationStart": - console.assert(openObservationRequest, "No request for observation start"); - observationPeriod = new ObservationPeriod(change.date); - openObservationRequest = null; - break; - - case "ObservationEnd": - console.assert(observationPeriod, "No active observation period"); - observationPeriod.finishPeriod(change.context.success); - if (change.context.success) { - level++; - } - pastObservations.push(observationPeriod); - observationPeriod = null; - break; - - case "Observation": - console.assert(observationPeriod, "No active observation period"); - const observation = Observation.fromChange(change); - observationPeriod.addObservation(observation); - break; - - case "LevelChange": - level = change.context.level; - break; - - default: - alert(`Unexpected change: ${change}`); - } - } - return new Graduation(student, green, orange, red, level, - stickers, - openObservationRequest, observationPeriod, pastObservations, - changes); - } - - addChange(change) { - // TODO(muhq): Implement incremental addition - let changes = this.changes; - changes.push(change); - // This updates the student.graduation reference as well - Graduation.from(this.student, changes); - } - - /** - * Request observation - */ - async requestObservation() { - const request = GraduationChange.newObservationRequest(); - this.observationRequested = request.date; - await addEntryToStudentDatabase(this.student, "submit", JSON.stringify(request.publicDBFields())); - } - - /** - * Start an observation - */ - async startObservation() { - if (!this.observationRequested) { - console.assert(this.observationRequested, "No observation requested"); - return; - } - this.observationRequested = null; - this.observation = new ObservationPeriod(new Date()); - return submitGraduationChange(this.student, GraduationChange.newObservationStart()); - } - - /** - * End an observation - */ - async endObservation(reason, success) { - if (!this.observation) { - console.assert(this.observation, "No active observation"); - return; - } - this.observation.end = new Date(); - this.pastObservations.push(this.observation); - this.observation = null; - return submitGraduationChange(this.student, GraduationChange.newObservationEnd(reason, success)); - } - - /** - * Return if the student is in observation. - */ - isObserved() { - return Boolean(this.observation); - } - - /** - * Return the URI of the icon representing the graduation level. - */ - getIcon() { - return LEVEL_IMAGES[this.level - 1]; - } - - /** - * Return the name of the current graduation level. - * @return {String} The name of the current level. - */ - getLevelName() { - return LEVEL_NAMES[this.level - 1]; - } -} - -class ObservationPeriod { - constructor(start) { - this.start = start; - this.observations = []; - } - - finishPeriod(success) { - this.end = new Date(); - this.success = success; - } - - ended() { - return Boolean(this.end); - } - - ready() { - if (this.observations.length < 10) - return false; - - // at least 10 observation on different days - let days = new Map(); - for (const observation of this.observations) { - const d = observation.date; - days.set(`${d.getFullYear()}${d.getMonth()}${d.getDate()}`, true); - } - return days.size >= 10; - } - - getObservationsSums() { - let sums = Array(OBSERVATION_TYPES.length).fill().map(() => ({"yes":0, "no":0})); - for (const observation of this.observations) { - for (let i = 0; i < sums.length; i++) { - if (observation.status[i]) { - if (observation.status[i] === "yes") - sums[i].yes++; - else - sums[i].no++; - } - } - } - return sums; - } - - addObservation(observation) { - this.observations.push(observation); - } -} - -const OBSERVATION_TYPES = [ - "Arbeitet konzentriert an Aufgaben", - "Arbeitet zunehmend selbstständig", - "Führt das Logbuch zuverlässig", - "Bringt Material mit", - "Hält Regeln ein", - "Geht verantwortungsvoll mit Materialien und LernRaum um", - "Nimmt Rückmeldungen an und setzt sie um", -]; - -class Observation { - constructor(date, status) { - console.assert(status.length == OBSERVATION_TYPES.length, "invalid observation status"); - this.date = date; - this.status = status; - } - - static fromChange(change) { - return new Observation(change.date, JSON.parse(change.context)) - } -} - -function changesFromDbEntries(entries) { - let changes = []; - for (const e of entries) { - const gradChange = GraduationChange.fromDBEntry(e); - changes.push(gradChange); - } - return changes; -} - -async function fetchGraduationChangesFromDb(student, type) { - const data = await loadFromStudentDatabase(student, type); - return changesFromDbEntries(data); -} - -const submitEntriesPromise = async function() { - const db = await getSubmitDb(); - const entries = await db.fetchEntries(); - return changesFromDbEntries(entries); -}(); - -async function addSubmittedChanges(student, changes) { - let submitEntries = await submitEntriesPromise; - for (const e of submitEntries.filter( (c) => c.coach === student.name )) { - changes.push(e); - } - return changes; -} - -async function fetchGraduationChanges(student) { - let changes = await fetchGraduationChangesFromDb(student, "shadow"); - return addSubmittedChanges(student, changes); -} - -async function fetchPublicGraduationChanges(student) { - let changes = await fetchGraduationChangesFromDb(student, "public"); - return addSubmittedChanges(student, changes); -} - -async function fetchGraduation(student) { - const changes = await fetchGraduationChanges(student); - return Graduation.from(student, changes); -} - -async function fetchPublicGraduation(student) { - const changes = await fetchPublicGraduationChanges(student); - return Graduation.from(student, changes); -} - -async function submitGraduationChange(student, change) { - student.graduation.addChange(change); - - // Submit changes concurrently - let promises = []; - - if (change.isPublic()) { - promises.push( - // TODO(muhq): strip non public fields from changes - addEntryToStudentDatabase(student, "public", JSON.stringify(change.publicDBFields())) - ); - } - - promises.push( - addEntryToStudentDatabase(student, "shadow", JSON.stringify(change.dbFields())) - ); - - await Promise.all(promises); -} - -async function submitObservation(student, observation) { - console.assert(student.graduation.observation, "no active observation"); - student.graduation.observation.addObservation(observation); - await submitGraduationChange(student, GraduationChange.newObservationChange(observation)); -} - -export { - LEVEL_NAMES, - LEVEL_IMAGES, - GraduationChange, - Graduation, - fetchGraduation, - fetchPublicGraduation, - fetchGraduationChanges, - fetchPublicGraduationChanges, - submitGraduationChange, - submitObservation, - - OBSERVATION_TYPES, - Observation, - ObservationPeriod, -}; diff --git a/js/graduation-mock.js b/js/graduation-mock.js deleted file mode 100644 index 1c84433..0000000 --- a/js/graduation-mock.js +++ /dev/null @@ -1,30 +0,0 @@ -import { - LEVEL_NAMES, - GraduationChange, - Graduation, - - OBSERVATION_TYPES, - Observation, - ObservationPeriod, -} from "./graduation-common.js"; - -import { - fetchGraduationMock as fetchGraduation, - fetchPublicGraduationMock as fetchPublicGraduation, - submitGraduationChangeMock as submitGraduationChange, - submitObservationMock as submitObservation -} from "./mock.js"; - -export { - LEVEL_NAMES, - GraduationChange, - Graduation, - fetchGraduation, - fetchPublicGraduation, - submitGraduationChange, - submitObservation, - - OBSERVATION_TYPES, - Observation, - ObservationPeriod, -}; diff --git a/js/graduation-prod.js b/js/graduation-prod.js deleted file mode 100644 index 0e86956..0000000 --- a/js/graduation-prod.js +++ /dev/null @@ -1,15 +0,0 @@ -export { - LEVEL_NAMES, - GraduationChange, - Graduation, - fetchGraduation, - fetchPublicGraduation, - fetchGraduationChanges, - fetchPublicGraduationChanges, - submitGraduationChange, - submitObservation, - - OBSERVATION_TYPES, - Observation, - ObservationPeriod, -} from "./graduation-common.js"; diff --git a/js/group.js b/js/group.js deleted file mode 100644 index ba5e1a0..0000000 --- a/js/group.js +++ /dev/null @@ -1,57 +0,0 @@ -import { getCourseParticipantsUrl } from "./utils-ENV.js"; -import { fetchStudents } from "./student-ENV.js"; - -/** - * Class to represent a Group of Students. - */ -class Group { - constructor(name, students) { - this.name = name; - if (!students) { - this.students = []; - } - } - - setStudents(students) { - this.students = students; - } - - addStudent(student) { - this.students.push(student); - } - - async getStudents() { - return this.students; - } -} - -let groups = new Map(); - -function buildGroups(students) { - for (const student of students) { - let group = groups.get(student.group); - - if (!group) { - group = new Group(student.group); - groups.set(student.group, group); - } - - group.addStudent(student); - } -} - -/** - * Fetch all Groups and return them as a map. - */ -async function fetchGroups() { - if (groups.size) { - return groups; - } - - // Fetch all students and build the groups from them - const students = await fetchStudents(); - buildGroups(students); - return groups; -} - -export { Group, buildGroups, fetchGroups }; diff --git a/js/mock.js b/js/mock.js deleted file mode 100644 index 91b1525..0000000 --- a/js/mock.js +++ /dev/null @@ -1,104 +0,0 @@ -import { Graduation, GraduationChange } from "./graduation-common.js"; -import { Student } from "./student-common.js"; -import { buildGroups } from "./group.js"; - -const students = [ - new Student(1, 'foo bar', 'test'), - new Student(2, 'maxi mustermensch', 'test'), - new Student(3, 'b b b', '5a') -]; - -// Build the groups from our mock students -buildGroups(students); - -function fetchStudentsMock() { - return new Promise((resolve) => { resolve(students); }); -} - -function fetchSubjectsMock() { - return new Promise((resolve) => { - resolve(['Mathe', 'Deutsch', 'Englisch']); - }); -} - -function fetchProgressMock(subject) { - return new Promise((resolve) => { - if (subject === "Mathe") { - resolve([40, 10, 3]); - } else if (subject === "Deutsch") { - resolve([10, 2, 0]); - } else if (subject === "Englisch") { - resolve([50, 25, 10]); - } else { - resolve([0, 0, 0]); - } - }); -} - -const graduationChanges = {}; -for (const s of students) { - switch (s.name) { - case "foo bar": - graduationChanges[s.name] = [ - GraduationChange.newGreenPointChange("Gute Arbeit", "Florian Fischer"), - GraduationChange.newOrangePointChange("", "Florian Fischer"), - ]; - break; - case "maxi mustermensch": - graduationChanges[s.name] = [ - GraduationChange.newGreenPointChange("", "Florian Fischer"), - GraduationChange.newGreenPointChange("", "Florian Fischer"), - new GraduationChange(new Date(), "ObservationStart", "", "Florian Fischer", ""), - ]; - break; - case "b b b": - graduationChanges[s.name] = [ - GraduationChange.newRedPointChange("", "Florian Fischer"), - ]; - } - // Build the student's graduation - Graduation.from(s, graduationChanges[s.name]); -} - -function fetchGraduationMock(s) { - return new Promise((resolve) => { - const changes = graduationChanges[s.name]; - if (!changes) { - resolve(null); - } else { - resolve(Graduation.from(s, changes)); - } - }); -} - -function fetchPublicGraduationMock(s) { - return new Promise((resolve) => { - const changes = graduationChanges[s.name]; - if (!changes) { - resolve(null); - } else { - resolve(Graduation.from(s, changes.filter((change) => change.isPublic()))); - } - }); -} - -function submitGraduationChangeMock(s, c) { - console.assert(s.graduation, "no graduation") - s.graduation.addChange(c); -} - -function submitObservationMock(s, o) { - console.assert(s.graduation, "no graduation") - console.assert(s.graduation.observation, "no observation period") - s.graduation.observation.addObservation(o); -} - -export { - fetchStudentsMock, - fetchSubjectsMock, - fetchProgressMock, - fetchGraduationMock, - fetchPublicGraduationMock, - submitGraduationChangeMock, - submitObservationMock, -}; diff --git a/js/selection.js b/js/selection.js deleted file mode 100644 index eda20ae..0000000 --- a/js/selection.js +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Initialises a selection based on existing HTML elements. - * For HTML elements are required: - * - A text input with id $idPrefix - * - A list with id ${idPrefix}-list - * - An element shown if no results are available with id ${idPrefix}-noresults - * - A button to clear the input with id ${idPrefix}-clear - * - * @param {string} idPrefix - The id prefix of the four HTML elements. - * @param {async function} fetchCandidates - The async function to retrieve the selectable candidates. - * @param {function} fmtCandidate - The function to format the displayed HTML for each candidate. - * @param {function} onSelection - The function to call when a candidate is selected. - */ -async function initSelection(idPrefix, fetchCandidates, fmtCandidate, onSelection) { - const sel = document.getElementById(idPrefix); - const list = document.getElementById(`${idPrefix}-list`); - const noResults = document.getElementById(`${idPrefix}-noresults`); - const clearBtn = document.getElementById(`${idPrefix}-clear`); - const loader = document.getElementById(`${idPrefix}-loader`); - - loader.style.display = "unset"; - const candidates = await fetchCandidates(); - loader.style.display = "none"; - - let items = []; - - let currentFocus = -1; - let visibleItems = []; - function setActive() { - visibleItems.forEach(item => item.classList.remove("active")); - if (currentFocus >= 0 && visibleItems[currentFocus]) { - visibleItems[currentFocus].classList.add("active"); - } - } - - for (const candidate of candidates) { - let item = document.createElement("li"); - item.candidate = candidate; - item.innerHTML = fmtCandidate(candidate); - item.style.display = 'none'; - - item.addEventListener('click', function() { - sel.value = ""; - sel.dispatchEvent(new Event('keyup')); - onSelection(candidate); - }); - - list.appendChild(item); - items.push(item); - } - - sel.addEventListener('keyup', function(e) { - if ((["ArrowDown", "ArrowUp", "Enter"]).includes(e.key)) - return; - - const filter = sel.value.toLowerCase(); - visibleItems = []; - for (const item of items) { - const itemText = item.textContent.trim().toLowerCase(); - - if (itemText.indexOf(filter) > -1 && filter !== "") { - item.style.display = ""; - visibleItems.push(item); - - // Highlight matched text - const regex = new RegExp(`(${filter})`, 'gi'); - item.innerHTML = item.textContent.replace( - regex, - "$1" - ); - } else { - item.style.display = "none"; - } - } - - // Show the clear button if there is an input - clearBtn.style.display = sel.value ? "block" : "none"; - - // Select the first or none if there are no items visible - currentFocus = visibleItems.length ? 0 : -1; - setActive(visibleItems); - noResults.style.display = visibleItems.length || filter === "" ? "none" : "block"; - }); - - clearBtn.addEventListener('click', function() { - sel.value = ""; - sel.dispatchEvent(new Event('keyup')); // reset list - sel.focus(); - }); - - sel.addEventListener('keydown', function(e) { - if (e.key === "ArrowDown" || e.key === "ArrowUp") { - if(e.key === "ArrowDown") - currentFocus = (currentFocus + 1) % visibleItems.length; - else - currentFocus = (currentFocus - 1 + visibleItems.length) % visibleItems.length; - setActive(); - e.preventDefault(); - } else if (e.key === "Enter") { - if (currentFocus > -1 && visibleItems[currentFocus]) { - - const visibleItem = visibleItems[currentFocus]; - sel.value = ""; - sel.dispatchEvent(new Event('keyup')); - onSelection(visibleItem.candidate); - } - e.preventDefault(); - } - }); -} - -function pointClickHandler(color) { - const textareaDiv = document.getElementById("student-info-point-reason-container"); - const textarea = document.getElementById("student-info-point-reason"); - const submitBtn = document.getElementById("student-info-point-reason-submit"); - - // TODO(muhq): Prefill the textarea with common reasons - switch (color) { - case "green": - textarea.style.background = "rgb(from green r g b / 20%)" - textarea.value = ""; - break; - case "orange": - textarea.style.background = "rgb(from orange r g b / 20%)" - textarea.value = ""; - break; - case "red": - textarea.style.background = "rgb(from red r g b / 20%)" - textarea.value = ""; - break; - } - textareaDiv.style.display = "block"; - textarea.focus(); - - // Can not use string interpolation because of Moodle's firstname filter. - const coach = firstname + " " + lastname; - - let submit = function() { - let change; - switch (color) { - case "green": - change = GraduationChange.newGreenPointChange(student, textarea.value, coach); - break; - case "orange": - change = GraduationChange.newOrangePointChange(student, textarea.value, coach); - break; - case "red": - change = GraduationChange.newRedPointChange(student, textarea.value, coach); - break; - default: - alert("Invalid color"); - break; - } - submitGraduationChange(student, change); - textareaDiv.style.display = "none"; - updateStudentInfo(); - } - // Use onEVENT to override any previous handler - textarea.onkeydown = function(e) { - if (e.shiftKey && e.key === "Enter") { - submit(); - } - if (e.key === "Escape") { - textareaDiv.style.display = "none"; - } - }; - submitBtn.onclick = submit; -} - -export { initSelection }; diff --git a/js/student-common.js b/js/student-common.js deleted file mode 100644 index 2dfa35d..0000000 --- a/js/student-common.js +++ /dev/null @@ -1,115 +0,0 @@ -import { getCourseId, getUserIconUrl, getCourseParticipantsUrl } from "./utils-ENV.js"; -import { fetchGraduation } from "./graduation-common.js"; -/** - * Class to represent Students. - */ -class Student { - graduation = null; - - constructor(id, name, group) { - this.id = id; - this.name = name; - this.group = group; - } - - async getIconUrl() { - return await getUserIconUrl(this.id); - } - - getGroup() { - return this.group; - } - - async getGraduation() { - if(!this.graduation) { - // fetchGraduation(this) sets this.graduation - await fetchGraduation(this); - } - return this.graduation; - } - - static async fetchById(id) { - let name = ""; - let group = ""; - - const res = await fetch(`/user/view.php?id=${id}&course=${getCourseId()}`); - const doc = new DOMParser().parseFromString(await res.text(), "text/html"); - - name = doc.querySelector("div.page-header-headings > h2").textContent; - - const cards = doc.querySelectorAll("div.card-body dl"); - for (const card of cards) { - if (card.querySelector("dt").textContent !== "Gruppe") - continue; - group = card.querySelector("dd > a").textContent; - break; - } - - return new Student(id, name, group); - } -} - -// Cache the fetched students. -let students = []; - -const idRegex = /id=(\d+)/; -async function _fetchStudents() { - const res = await fetch(getCourseParticipantsUrl()); - const doc = new DOMParser().parseFromString(await res.text(), "text/html"); - const table = doc.querySelector("tbody"); - let fetchedStudents = []; - table.querySelectorAll("tr").forEach(row => { - if (row.classList.contains("emptyrow")) - return; - - const roleCell = row.querySelector(".c4"); - // TODO(muhq): Is this locale dependent? - if (roleCell.innerHTML.includes("Lehrer")) { - return; - } - - const nameCell = row.querySelector(".c1"); - let name; - if (nameCell.firstChild.firstChild.tagName == "SPAN") - name = nameCell.firstChild.firstChild.title; - else - name = nameCell.innerText; - - const profileUrl = nameCell.querySelector("a.aabtn").href; - const match = idRegex.exec(profileUrl); - const id = match[1]; - - const groupCell = row.querySelector(".c5"); - const group = groupCell.querySelector("a.quickeditlink").textContent; - - fetchedStudents.push(new Student(id, name, group)); - }); - return fetchedStudents; -} - -async function fetchStudents(idFilter, nameFilter, groupFilter) { - if (!students.size) { - const studentsPromise = _fetchStudents() - const storedStudents = localStorage.getItem("students"); - if (storedStudents) { - studentsPromise.then((fetched) => localStorage.setItem("students", JSON.stringify(fetched))); - for (const o of JSON.parse(storedStudents)) { - students.push(new Student(o.id, o.name, o.group)); - } - } else { - students = await studentsPromise; - localStorage.setItem("students", JSON.stringify(students)); - } - } - - return students.filter((s) => (!idFilter || s.id === idFilter) && - (!nameFilter || s.name.includes(nameFilter)) && - (!groupFilter || s.group.includes(groupFilter))); -} - -async function fetchStudent(idFilter, nameFilter, groupFilter) { - const students = await fetchStudents(idFilter, nameFilter, groupFilter); - return students[0]; -} - -export { Student, fetchStudents, fetchStudent }; diff --git a/js/student-mock.js b/js/student-mock.js deleted file mode 100644 index 7c79e12..0000000 --- a/js/student-mock.js +++ /dev/null @@ -1,9 +0,0 @@ -import { Student } from "./student-common.js"; -import { fetchStudentsMock as fetchStudents } from "./mock.js"; - -async function fetchStudent(idFilter, nameFilter, groupFilter) { - const students = await fetchStudents(idFilter, nameFilter, groupFilter); - return students[0]; -} - -export { Student, fetchStudents, fetchStudent }; diff --git a/js/student-prod.js b/js/student-prod.js deleted file mode 100644 index 9c8d7cb..0000000 --- a/js/student-prod.js +++ /dev/null @@ -1,2 +0,0 @@ -import { Student, fetchStudent, fetchStudents } from "./student-common.js"; -export { Student, fetchStudents, fetchStudent }; diff --git a/js/utils-common.js b/js/utils-common.js deleted file mode 100644 index 8e4cfac..0000000 --- a/js/utils-common.js +++ /dev/null @@ -1,20 +0,0 @@ -export const DATE_FMT = new Intl.DateTimeFormat("de-DE", { month: "numeric", day: "numeric", year: "numeric"}); - -export const DATETIME_FMT = new Intl.DateTimeFormat("de-DE", { month: "numeric", day: "numeric", year: "numeric", hour: "numeric", minute: "numeric" }); - -export function dateStringNow() { - return DATE_FMT.format(new Date()); -} - -export function datetimeStringNow() { - return DATETIME_FMT.format(new Date()); -} - -// TODO(muhq): Cache the retrieved URL -export async function getUserIconUrl(userId) { - console.assert(userId, "getUserIconUrl needs a userId"); - const userRes = await fetch(`https://lernplattform.bycs.de/user/profile.php?id=${userId}`) - const userDoc = new DOMParser().parseFromString(await userRes.text(), 'text/html') - const userpicture = userDoc.querySelector(".userpicture"); - return userpicture ? userpicture.src : ""; -} diff --git a/js/utils-mock.js b/js/utils-mock.js deleted file mode 100644 index da2f94b..0000000 --- a/js/utils-mock.js +++ /dev/null @@ -1,41 +0,0 @@ -import { fetchStudent } from "./student-ENV.js"; -export { DATE_FMT, DATETIME_FMT, dateStringNow, datetimeStringNow } from "./utils-common.js"; - -export function isTeacher() { - return !(new URLSearchParams(document.location.search)).has("student"); -} - -export function getFirstAndLastname() { - let firstname = ""; - let lastname = ""; - - const params = new URLSearchParams(document.location.search); - if (params.has("firstname")) - firstname = params.get("firstname") - if (params.has("lastname")) - lastname = params.get("lastname") - - return [firstname, lastname]; -} - -export function getName() { - let name = ""; - - const params = new URLSearchParams(document.location.search); - if (params.has("firstname")) - name = params.get("firstname") - if (params.has("lastname")) - name += params.get("lastname") - - return name; -} - -export function getUserId() { - const name = getName(); - - return fetchStudent(null, name).id; -} - -export function getCourseId() { - return "1337"; -} diff --git a/js/utils-prod.js b/js/utils-prod.js deleted file mode 100644 index ddde8a9..0000000 --- a/js/utils-prod.js +++ /dev/null @@ -1,36 +0,0 @@ -export { DATE_FMT, DATETIME_FMT, dateStringNow, datetimeStringNow, getUserIconUrl } from "./utils-common.js"; - -export function isTeacher() { - return Boolean(document.querySelector('.editmode-switch-form')) - || document.body.classList.contains('editing'); -} - -export function getFirstAndLastname() { - let firstname = ""; - let lastname = ""; - - const autoNameDiv = document.getElementById("auto-name"); - firstname = autoNameDiv.children[0].innerHTML; - lastname = autoNameDiv.children[1].innerHTML; - autoNameDiv.remove(); - - return [firstname, lastname]; -} - -export function getName() { - return document.querySelector("bycs-topbar").shadowRoot.querySelector("div.user-name").textContent; -} - -export function getUserId() { - // M is exported by Moodle - return String(M.cfg.userId); -} - -export function getCourseId() { - // M is exported by Moodle - return String(M.cfg.courseId); -} - -export function getCourseParticipantsUrl() { - return `https://lernplattform.bycs.de/user/index.php?id=${getCourseId()}&perpage=5000`; -} diff --git a/mass-graduation.css b/mass-graduation.css new file mode 100644 index 0000000..5de091b --- /dev/null +++ b/mass-graduation.css @@ -0,0 +1,186 @@ +#group-selection-container { + margin: 0.2rem; +} + +#group-selection-search-container { + position: relative; +} + +#group-selection { + box-sizing: border-box; + width: 100%; + padding: 0.75rem 2.5rem 0.75rem 1rem; + margin-bottom: 1.5rem; + border: 1px solid #ddd; + border-radius: 5pt; + font-size: 1.5rem; +} + +#group-selection-clear { + position: absolute; + right: 0.75rem; + transform: translateY(-175%); + border: none; + background: transparent; + font-size: 1.875rem; + cursor: pointer; + color: #888; +} + +#group-selection-clear:hover { + color: #333; +} + +#group-selection:focus { + border-color: #0077ff; +} + +#group-selection-loader { + --size: 0.5px; + width: calc(48 * var(--size)); + height: calc(48 * var(--size)); + border: calc(5 * var(--size)) solid black; + border-bottom-color: transparent; + border-radius: 50%; + display: inline; + position: absolute; + top: 20px; + right: 0.75rem; + box-sizing: border-box; + animation: rotation 1s linear infinite; +} + +@keyframes rotation { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +#group-selection-list { + list-style: none; +} + +#group-selection-list li { + padding: 0.75rem 1rem; + border-bottom: 1px solid #eee; + cursor: pointer; + transition: background 0.2s; +} + +#group-selection-list li:hover { + background: #f0f8ff; +} + +#group-selection-list li.active { + background: #0077ff; + color: white; +} + +#group-selection-list li > u.group-selection-list-highlight { + font-weight: bold; +} + +#group-selection-noresults { + color: red; + text-align: center; + margin-top: 1rem; +} + +#group-content { +} + +#group-members { + padding-left: 0; +} + +.group-member { + display: grid; + grid-template-columns: 30px 200px 30px repeat(3, 40px) auto 20px; +} + +.group-member-icon { + width: 30px; + height: 30px; + align-self: center; +} + +.group-member-name { + align-self: center; +} + +.group-member-observation-icon { + border: none; + background: none; +} + +.group-member-green, .group-member-orange, .group-member-red { + margin: 5px; +} + +.group-member-green { + background: green; + margin: 1px; +} +.group-member-orange { + background: orange; + margin: 1px; +} +.group-member-red { + background: red; + margin: 1px; +} + +#group-member-point-reason { + box-sizing: border-box; + padding: 10px; + width: 50%; + font-size: 0.8rem; + letter-spacing: 1px; + line-height: 1.5; + border-radius: 5px; + border: 1px solid #cccccc; +} + +.group-member-print { + background: none; + border: none; + font-size: larger; +} + +.group-member-observation-container { + background: lightgray; + width: 100%; +} + +.group-member-observation-table { + background: lightgray; + +} +.group-member-observation-table-description { + max-width: 70%; +} + +.group-member-observation-table-yes, +.group-member-observation-table-no, +.group-member-observation-table-maybe { + width: 10%; + text-align: center; +} + +.submit-mass-graduation-inprogress { + --size: 0.5px; + width: calc(48 * var(--size)); + height: calc(48 * var(--size)); + border: calc(5 * var(--size)) solid black; + border-bottom-color: transparent; + border-radius: 50%; + display: inline; + position: absolute; + margin-left: 0.25rem; + margin-top: 0.25rem; + box-sizing: border-box; + animation: rotation 1s linear infinite; +} diff --git a/mass-graduation.html b/mass-graduation.html new file mode 100644 index 0000000..16c50a0 --- /dev/null +++ b/mass-graduation.html @@ -0,0 +1,46 @@ + + + + + Graduation + + + + + +
+
+ + + +
+ + +
+ + + + diff --git a/mass-graducation/.gitignore b/mass-graducation/.gitignore deleted file mode 100644 index 1eae0cf..0000000 --- a/mass-graducation/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -dist/ -node_modules/ diff --git a/mass-graducation/Makefile b/mass-graducation/Makefile deleted file mode 100644 index 97a781f..0000000 --- a/mass-graducation/Makefile +++ /dev/null @@ -1 +0,0 @@ -include ../common.mk \ No newline at end of file diff --git a/mass-graducation/Readme.md b/mass-graducation/Readme.md deleted file mode 100644 index 986f4a4..0000000 --- a/mass-graducation/Readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# mass graduation - -A simple web site to add graduation changes for whole user groups. diff --git a/mass-graducation/index.css b/mass-graducation/index.css deleted file mode 100644 index 5de091b..0000000 --- a/mass-graducation/index.css +++ /dev/null @@ -1,186 +0,0 @@ -#group-selection-container { - margin: 0.2rem; -} - -#group-selection-search-container { - position: relative; -} - -#group-selection { - box-sizing: border-box; - width: 100%; - padding: 0.75rem 2.5rem 0.75rem 1rem; - margin-bottom: 1.5rem; - border: 1px solid #ddd; - border-radius: 5pt; - font-size: 1.5rem; -} - -#group-selection-clear { - position: absolute; - right: 0.75rem; - transform: translateY(-175%); - border: none; - background: transparent; - font-size: 1.875rem; - cursor: pointer; - color: #888; -} - -#group-selection-clear:hover { - color: #333; -} - -#group-selection:focus { - border-color: #0077ff; -} - -#group-selection-loader { - --size: 0.5px; - width: calc(48 * var(--size)); - height: calc(48 * var(--size)); - border: calc(5 * var(--size)) solid black; - border-bottom-color: transparent; - border-radius: 50%; - display: inline; - position: absolute; - top: 20px; - right: 0.75rem; - box-sizing: border-box; - animation: rotation 1s linear infinite; -} - -@keyframes rotation { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} - -#group-selection-list { - list-style: none; -} - -#group-selection-list li { - padding: 0.75rem 1rem; - border-bottom: 1px solid #eee; - cursor: pointer; - transition: background 0.2s; -} - -#group-selection-list li:hover { - background: #f0f8ff; -} - -#group-selection-list li.active { - background: #0077ff; - color: white; -} - -#group-selection-list li > u.group-selection-list-highlight { - font-weight: bold; -} - -#group-selection-noresults { - color: red; - text-align: center; - margin-top: 1rem; -} - -#group-content { -} - -#group-members { - padding-left: 0; -} - -.group-member { - display: grid; - grid-template-columns: 30px 200px 30px repeat(3, 40px) auto 20px; -} - -.group-member-icon { - width: 30px; - height: 30px; - align-self: center; -} - -.group-member-name { - align-self: center; -} - -.group-member-observation-icon { - border: none; - background: none; -} - -.group-member-green, .group-member-orange, .group-member-red { - margin: 5px; -} - -.group-member-green { - background: green; - margin: 1px; -} -.group-member-orange { - background: orange; - margin: 1px; -} -.group-member-red { - background: red; - margin: 1px; -} - -#group-member-point-reason { - box-sizing: border-box; - padding: 10px; - width: 50%; - font-size: 0.8rem; - letter-spacing: 1px; - line-height: 1.5; - border-radius: 5px; - border: 1px solid #cccccc; -} - -.group-member-print { - background: none; - border: none; - font-size: larger; -} - -.group-member-observation-container { - background: lightgray; - width: 100%; -} - -.group-member-observation-table { - background: lightgray; - -} -.group-member-observation-table-description { - max-width: 70%; -} - -.group-member-observation-table-yes, -.group-member-observation-table-no, -.group-member-observation-table-maybe { - width: 10%; - text-align: center; -} - -.submit-mass-graduation-inprogress { - --size: 0.5px; - width: calc(48 * var(--size)); - height: calc(48 * var(--size)); - border: calc(5 * var(--size)) solid black; - border-bottom-color: transparent; - border-radius: 50%; - display: inline; - position: absolute; - margin-left: 0.25rem; - margin-top: 0.25rem; - box-sizing: border-box; - animation: rotation 1s linear infinite; -} diff --git a/mass-graducation/index.html b/mass-graducation/index.html deleted file mode 100644 index 481be41..0000000 --- a/mass-graducation/index.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - Graduation - - - - - -
-
- - - -
- - -
- - - - diff --git a/mass-graducation/package-lock.json b/mass-graducation/package-lock.json deleted file mode 100644 index 18aa850..0000000 --- a/mass-graducation/package-lock.json +++ /dev/null @@ -1,1860 +0,0 @@ -{ - "name": "mass-graduation", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "mass-graduation", - "version": "1.0.0", - "devDependencies": { - "html-webpack-plugin": "^5.6.7", - "webpack": "^5.107.2", - "webpack-cli": "^7.0.2" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz", - "integrity": "sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.17.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", - "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, - "node_modules/ajv": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.32", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", - "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true, - "license": "ISC" - }, - "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001793", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", - "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-select/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/css-select/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/css-select/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/css-select/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", - "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "license": "MIT", - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.361", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", - "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==", - "dev": true, - "license": "ISC" - }, - "node_modules/enhanced-resolve": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz", - "integrity": "sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/envinfo": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", - "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", - "dev": true, - "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.6.7", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.7.tgz", - "integrity": "sha512-md+vXtdCAe60s1k6AU3dUyMJnDxUyQAwfwPKoLisvgUF1IXjtlLsk2se54+qfL9Mdm26bbwvjJybpNx48NKRLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.20.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/html-webpack-plugin/node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-webpack-plugin/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/html-webpack-plugin/node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loader-runner": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", - "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-releases": { - "version": "2.0.46", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", - "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/renderkid/node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/renderkid/node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", - "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.0.tgz", - "integrity": "sha512-Eum+5ajkaOhf5KbM26osvv21kLD7BaGqQ1UA4Ami4arYwylmGUQTgHFpHDdmJod1q4QXa66p0to/FBKID+J1vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@minify-html/node": { - "optional": true - }, - "@swc/core": { - "optional": true - }, - "@swc/css": { - "optional": true - }, - "@swc/html": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "cssnano": { - "optional": true - }, - "csso": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "html-minifier-terser": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "postcss": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, - "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack": { - "version": "5.107.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.2.tgz", - "integrity": "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.16.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.22.0", - "es-module-lexer": "^2.1.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "loader-runner": "^4.3.2", - "mime-db": "^1.54.0", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.3", - "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.5.0", - "watchpack": "^2.5.1", - "webpack-sources": "^3.5.0" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.0.2.tgz", - "integrity": "sha512-dB0R4T+C/8YuvM+fabdvil6QE44/ChDXikV5lOOkrUeCkW5hTJv2pGLE3keh+D5hjYw8icBaJkZzpFoaHV4T+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@discoveryjs/json-ext": "^1.0.0", - "commander": "^14.0.3", - "cross-spawn": "^7.0.6", - "envinfo": "^7.14.0", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^6.0.1" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=20.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.101.0", - "webpack-bundle-analyzer": "^4.0.0 || ^5.0.0", - "webpack-dev-server": "^5.0.0" - }, - "peerDependenciesMeta": { - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/webpack-merge": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", - "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", - "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true, - "license": "MIT" - } - } -} diff --git a/mass-graducation/package.json b/mass-graducation/package.json deleted file mode 100644 index b9f7781..0000000 --- a/mass-graducation/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": "1.0.0", - "description": "", - "name": "mass-graduation", - "scripts": { - "build": "webpack --mode=production --config-node-env=production", - "build:dev": "webpack --mode=development", - "build:mock": "webpack --mode=development --config-node-env=mock", - "watch": "webpack --watch" - }, - "devDependencies": { - "html-webpack-plugin": "^5.6.7", - "webpack": "^5.107.2", - "webpack-cli": "^7.0.2" - } -} diff --git a/mass-graducation/src/main.js b/mass-graducation/src/main.js deleted file mode 100644 index 3ed011e..0000000 --- a/mass-graducation/src/main.js +++ /dev/null @@ -1,357 +0,0 @@ -import { - OBSERVATION_TYPES, - GraduationChange, - submitGraduationChange, - Observation, - submitObservation -} from "../../js/graduation-ENV.js"; - -import { fetchGroups } from "../../js/group.js"; -import { initSelection } from "../../js/selection.js"; -import { getName, DATETIME_FMT, datetimeStringNow } from "../../js/utils-ENV.js"; - -let group; -let name; - -function toggle(btn, others) { - if (btn.innerHTML === "X") { - btn.innerHTML = "o"; - } else { - btn.innerHTML = "X"; - if (others) { - for (const other of others) - other.innerHTML = "o"; - } - } - - document.getElementById("submit-mass-graduation").style.display = "inline"; - document.getElementById("submit-mass-graduation-state").innerHTML = ""; -} - -function observationClickHandler(li) { - let observationDiv = li.observationDiv; - // Build the observation container - if(!observationDiv) { - li.style.background = "lightgray"; - - observationDiv = document.createElement("div"); - // Remember the observation div for this student - li.observationDiv = observationDiv; - observationDiv.id = `${li.student.name}-observation-container`; - observationDiv.classList.add("group-member-observation-container"); - li.appendChild(observationDiv); - - const obsTbl = document.createElement("table"); - obsTbl.classList.add("group-member-observation-table"); - observationDiv.appendChild(obsTbl); - - const obsTh = document.createElement("thead"); - obsTh.innerHTML = ` - - - Ja - Nein - Kann ich nicht beurteilen -`; - - obsTbl.appendChild(obsTh); - - const obsTbdy = document.createElement("tbody"); - obsTbl.appendChild(obsTbdy); - - for (const obsType of OBSERVATION_TYPES) { - const obsTr = document.createElement("tr"); - - const obsDesc = document.createElement("td"); - obsDesc.innerHTML = obsType; - obsDesc.classList.add("group-member-observation-table-description"); - obsTr.appendChild(obsDesc); - - const obsYes = document.createElement("td"); - obsYes.innerHTML = "o"; - obsYes.dataset.yes = "true"; - obsYes.classList.add("group-member-observation-table-yes"); - obsTr.appendChild(obsYes); - - const obsNo = document.createElement("td"); - obsNo.innerHTML = "o"; - obsNo.dataset.no = "true"; - obsNo.classList.add("group-member-observation-table-no"); - obsTr.appendChild(obsNo); - - const obsMaybe = document.createElement("td"); - obsMaybe.innerHTML = "X"; - obsMaybe.dataset.maybe = "true"; - obsMaybe.classList.add("group-member-observation-table-maybe"); - obsTr.appendChild(obsMaybe); - - obsYes.addEventListener("click", () => { - toggle(obsYes, [obsNo, obsMaybe]); - }); - obsNo.addEventListener("click", () => { - toggle(obsNo, [obsYes, obsMaybe]); - }); - obsMaybe.addEventListener("click", () => { - toggle(obsMaybe, [obsYes, obsNo]); - }); - - obsTbdy.appendChild(obsTr); - } - // Toggle the observation container visibility - } else { - if (observationDiv.style.display === "none") { - observationDiv.style.display = "block"; - li.style.background = "lightgray"; - - } else { - observationDiv.style.display = "none"; - li.style.background = "unset"; - } - } -} - -async function loadGroup(selected) { - group = selected; - const students = await group.getStudents(); - let graduationPromises = []; - document.getElementById("group-content").style.display = "block"; - - const ul = document.getElementById("group-members"); - // Clear group list - ul.innerHTML = ""; - - for (const student of students) { - const graduationPromise = student.getGraduation(); - - const li = document.createElement("li"); - li.classList.add("group-member") - // Remember the student of the entry - li.student = student; - - const i = document.createElement("img"); - i.classList.add("group-member-icon") - graduationPromise.then((graduation) => { - i.src = graduation.getIcon(); - }); - li.appendChild(i); - - const n = document.createElement("span"); - n.classList.add("group-member-name") - n.innerHTML = `${student.name}`; - li.appendChild(n); - - const ob = document.createElement("button"); - ob.classList.add("group-member-observation-icon") - - graduationPromise.then((graduation) => { - if (!graduation.isObserved()) { - return; - } - ob.innerHTML = "👁" - ob.addEventListener("click", () => observationClickHandler(li) ); - }); - graduationPromises.push(graduationPromise); - - li.appendChild(ob); - - for (const col of ["green", "orange", "red"]) { - const b = document.createElement("button"); - b.classList.add(`group-member-${col}`) - b.innerHTML = "o"; - b.addEventListener("click", () => toggle(b) ); - li.appendChild(b); - } - - const textarea = document.createElement("textarea"); - textarea.classList.add("group-member-point-reason"); - li.appendChild(textarea); - - const p = document.createElement("button"); - p.classList.add("group-member-print") - p.innerHTML = "🖶"; - p.addEventListener("click", () => printNote(li) ); - li.appendChild(p); - - ul.appendChild(li); - } - - const graduations = await Promise.all(graduationPromises); - let observations = graduations.filter( - (g) => g.observationRequested || (g.observation && !g.observation.end) - ); - - const observationReuqestsContainer = document.getElementById("observation-requests-container"); - - if (observations.length == 0) { - observationReuqestsContainer.style.display = "none"; - return; - } else { - observationReuqestsContainer.style.display = "unset"; - } - - const observationReuqestsNode = document.getElementById("observation-requests"); - observationReuqestsNode.innerHTML = ""; - observations.sort((g1, g2) => { - let g1D, g2D; - g1D = g1.observationRequested ? g1.observationRequested : g1.observation.start; - g2D = g2.observationRequested ? g2.observationRequested : g2.observation.start; - return g1D - g2D; - }); - - for (const graduation of observations) { - const row = document.createElement("tr"); - const iconCell = document.createElement("td"); - const icon = document.createElement("img"); - icon.src = graduation.getIcon(); - row.appendChild(iconCell); - - const nameCell = document.createElement("td"); - nameCell.innerHTML = `${graduation.student.name}`; - row.appendChild(nameCell); - - const dateCell = document.createElement("td"); - let date; - if (graduation.observationRequested) { - date = graduation.observationRequested; - } else { - date = graduation.observation.start; - } - dateCell.innerHTML = DATETIME_FMT.format(date); - row.appendChild(dateCell); - - const btnCell = document.createElement("td"); - if (graduation.observation && !graduation.observation.ready()) { - btnCell.innerHTML = "In Beobachtungsphase"; - } else { - const startBtn = document.createElement("button"); - if (graduation.observationRequested) { - startBtn.addEventListener("click", () => { - graduation.startObservation(); - //TODO(muhq): Add observation icon-button. - startBtn.remove(); - btnCell.innerHTML = "In Beobachtungsphase"; - }); - startBtn.innerHTML = `Beobachtungsphase beginnen`; - startBtn.classList.add("start-observation"); - } else { - startBtn.addEventListener("click", () => { - window.open(`/mod/page/view.php?id=93017047&userToLoad=${graduation.student.id}`, '_blank'); - }); - startBtn.innerHTML = `Graduierung abschließen`; - startBtn.classList.add("end-observation"); - } - btnCell.appendChild(startBtn); - } - row.appendChild(btnCell); - - observationReuqestsNode.appendChild(row); - } -} - -function submitMassGraduation() { - const state = document.getElementById("submit-mass-graduation-state"); - state.innerHTML = ""; - state.classList.add("submit-mass-graduation-inprogress"); - state.style.display = "unset"; - - // TODO(muhq): await graduation promises - - let promises = []; - for(const li of document.querySelectorAll("li.group-member")) { - const reason = li.querySelector(".group-member-point-reason").value; - const student = li.student; - if (li.querySelector(".group-member-green").innerHTML === "X") { - promises.push(submitGraduationChange(student, GraduationChange.newGreenPointChange(reason, name))); - } - if (li.querySelector(".group-member-orange").innerHTML === "X") { - promises.push(submitGraduationChange(student, GraduationChange.newOrangePointChange(reason, name))); - } - if (li.querySelector(".group-member-red").innerHTML === "X") { - promises.push(submitGraduationChange(student, GraduationChange.newRedPointChange(reason, name))); - } - if (li.observationDiv) { - let status = []; - for (const row of li.observationDiv.querySelectorAll("tbody > tr")) { - if (row.querySelector('td[data-yes="true"]').innerHTML === "X") - status.push("yes"); - else if (row.querySelector('td[data-no="true"]').innerHTML === "X") - status.push("no"); - else - status.push(""); - } - promises.push(submitObservation(student, new Observation(new Date(), status))); - } - } - - Promise.all(promises).then(() => { - state.style.display = "inline"; - state.innerHTML = "✓"; - state.style.color = "green"; - for (const li of document.querySelectorAll("li.group-member")) { - if (!li.observationDiv) - continue; - li.observationDiv.remove(); - li.observationDiv = null; - } - }).catch((error) => { - state.style.display = "inline"; - state.innerHTML = "X"; - state.style.color = "red"; - console.log(error); - }).finally(() => { - state.classList.remove("submit-mass-graduation-inprogress"); - }); -} - -let prePrintBody = null; -function printNote(li) { - // save the current DOM - prePrintBody = document.body; - - // prepare the printable red point notice - const printBdy = document.createElement("body"); - const reason = li.querySelector(".group-member-point-reason").value; - const dateStr = datetimeStringNow(); - let pointType = ""; - if (li.querySelector(".group-member-orange").innerHTML === "X") { - pointType = "Oranger Punkt"; - } - if (li.querySelector(".group-member-red").innerHTML === "X") { - pointType = "Roter Punkt"; - } - - printBdy.innerHTML = ` -

Dokuzettel

-

${pointType}

-

Schüler*in: ${li.student.name}

-

Datum: ${dateStr}

-

Lehrkraft: ${name}

-

Klassenleitung: TODO

-

Begründung

-

${reason}

-`; - - document.body = printBdy; - print(); - // Restore the DOM after printing the red point note - document.body = prePrintBody; -} - -async function init() { - document.getElementById("submit-mass-graduation").addEventListener("click", submitMassGraduation); - name = getName(); - - initSelection( - "group-selection", - async function() { return (await fetchGroups()).values(); }, - (group) => `${group.name}`, - loadGroup, - ); -} - -if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", init); -} else { - init(); -} - diff --git a/mass-graducation/webpack.config.js b/mass-graducation/webpack.config.js deleted file mode 100644 index fae04c6..0000000 --- a/mass-graducation/webpack.config.js +++ /dev/null @@ -1,44 +0,0 @@ -const webpack = require('webpack'); -const path = require('path'); - -const isProduction = process.env.NODE_ENV === 'production'; -const isMock = process.env.NODE_ENV === 'mock'; - -const config = { - entry: './src/main.js', - output: { - path: path.resolve(__dirname, 'dist'), - }, - module: { - rules: [ - { - test: /\.png/, - type: 'asset/inline', - }, - ], - }, - plugins: [ - new webpack.NormalModuleReplacementPlugin( - /-ENV.js/, - (resource) => { - resource.request = resource.request.replace( - /-ENV/, - `-${isMock ? "mock" : "prod"}`, - ); - - if (resource.createData) { - resource.createData.request = resource.request; - } - } - ) - ], -}; - -module.exports = () => { - if (isProduction) { - config.mode = 'production'; - } else { - config.mode = 'development'; - } - return config; -}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..dad02f9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1754 @@ +{ + "name": "ByCS-SOL", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ByCS-SOL", + "version": "1.0.0", + "devDependencies": { + "typescript": "^7.0.2", + "webpack": "^5.107.2", + "webpack-cli": "^7.0.2" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-1.1.0.tgz", + "integrity": "sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.1.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.2.tgz", + "integrity": "sha512-Vu4a5UFA9rIIFJ7rB/Vaafh9lrCQszopTCx6KjFboXTGQbPNasehVR5TEiithSDGyd1DEiUByggTZsg8jukeIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@typescript/typescript-aix-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", + "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", + "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-darwin-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", + "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", + "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-freebsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", + "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", + "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", + "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-loong64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", + "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-mips64el": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", + "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-ppc64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", + "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-riscv64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", + "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-s390x": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", + "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-linux-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", + "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", + "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-netbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", + "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", + "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-openbsd-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", + "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-sunos-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", + "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-arm64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", + "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@typescript/typescript-win32-x64": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", + "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.11", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.11.tgz", + "integrity": "sha512-/yImnXwyTvgMkhgekLHok/Rx5vO6E0BmStWlSqKWMVm2a2ITuZ1Tn+9bgLS+gZRdZmWtd8nxuhHpdmCUOWsTQQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.399", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.399.tgz", + "integrity": "sha512-lEcqhErbHjXRvd41rnWLpzbyU/IXfIYo7QwaFWmxGeLiLyY2TBCdHnWY88vB+p3ubnihRypDm66panXl7TylLA==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.24.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", + "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/envinfo": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimizer-webpack-plugin": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@minify-html/node": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "@swc/css": { + "optional": true + }, + "@swc/html": { + "optional": true + }, + "clean-css": { + "optional": true + }, + "cssnano": { + "optional": true + }, + "csso": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "html-minifier-terser": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "postcss": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.49.0.tgz", + "integrity": "sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typescript": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", + "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc" + }, + "engines": { + "node": ">=16.20.0" + }, + "optionalDependencies": { + "@typescript/typescript-aix-ppc64": "7.0.2", + "@typescript/typescript-darwin-arm64": "7.0.2", + "@typescript/typescript-darwin-x64": "7.0.2", + "@typescript/typescript-freebsd-arm64": "7.0.2", + "@typescript/typescript-freebsd-x64": "7.0.2", + "@typescript/typescript-linux-arm": "7.0.2", + "@typescript/typescript-linux-arm64": "7.0.2", + "@typescript/typescript-linux-loong64": "7.0.2", + "@typescript/typescript-linux-mips64el": "7.0.2", + "@typescript/typescript-linux-ppc64": "7.0.2", + "@typescript/typescript-linux-riscv64": "7.0.2", + "@typescript/typescript-linux-s390x": "7.0.2", + "@typescript/typescript-linux-x64": "7.0.2", + "@typescript/typescript-netbsd-arm64": "7.0.2", + "@typescript/typescript-netbsd-x64": "7.0.2", + "@typescript/typescript-openbsd-arm64": "7.0.2", + "@typescript/typescript-openbsd-x64": "7.0.2", + "@typescript/typescript-sunos-x64": "7.0.2", + "@typescript/typescript-win32-arm64": "7.0.2", + "@typescript/typescript-win32-x64": "7.0.2" + } + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/watchpack": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.2.tgz", + "integrity": "sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.109.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.109.2.tgz", + "integrity": "sha512-U9/cvLzxObKNEZ9+TtdqrHM5/9z3lgl2c+c4BzbqGxFQvQvBAq87yql5A8pQ+rrMbS496MZJeF5enVBndIy2hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.16.0", + "browserslist": "^4.28.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.24.4", + "es-module-lexer": "^2.1.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "graceful-fs": "^4.2.11", + "mime-db": "^1.54.0", + "minimizer-webpack-plugin": "^5.6.1", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "watchpack": "^2.5.2", + "webpack-sources": "^3.5.1" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-7.2.2.tgz", + "integrity": "sha512-lD0pALneslq8FfV+rwvm1BMW0AFAJrHHhNupAGN4asYjMvqrtRsenU4iKpiBo09gS4ntMxKGUxl9jhTEzVt0oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^1.1.0", + "commander": "^14.0.3", + "cross-spawn": "^7.0.6", + "envinfo": "^7.21.0", + "import-local": "^3.2.0", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^6.0.1" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "js-yaml": "^4.0.0 || ^5.0.0", + "json5": "^2.2.3", + "toml": "^3.0.0 || ^4.0.0", + "webpack": "^5.101.0", + "webpack-bundle-analyzer": "^4.0.0 || ^5.0.0", + "webpack-dev-server": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "js-yaml": { + "optional": true + }, + "json5": { + "optional": true + }, + "toml": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.1.tgz", + "integrity": "sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..aa3ccad --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "version": "1.0.0", + "description": "", + "name": "ByCS-SOL", + "scripts": { + "build": "tsc && webpack --mode=production --config-node-env=production", + "build:dev": "tsc && webpack --mode=development", + "build:mock": "tsc && webpack --mode=development --config-node-env=mock" + }, + "devDependencies": { + "typescript": "^7.0.2", + "webpack": "^5.107.2", + "webpack-cli": "^7.0.2" + } +} diff --git a/src/admin-tools.js b/src/admin-tools.js new file mode 100644 index 0000000..b0cf80d --- /dev/null +++ b/src/admin-tools.js @@ -0,0 +1,57 @@ +import { DB } from "./database.js"; +import { fetchStudents } from "./student-ENV.js"; + +let outputNode; +function output(msg) { + if (outputNode.style.display === "none") { + outputNode.style.display = "block"; + } + const line = document.createElement("div"); + line.innerHTML = msg; + line.classList.add("admin-tools-output-line"); + outputNode.appendChild(line); +} + +const studentsPromise = fetchStudents(); + +async function createGraduationDbs() { + const students = await studentsPromise; + + for (const student of students) { + if (!DB.getStudentDbs(student)) { + } + } +} + +async function init() { + outputNode = document.getElementById("admin-tools-output"); + const students = await studentsPromise; + + output("Suche nach Datenbanken ..."); + let missing = 0; + for (const student of students) { + const dbs = await DB.getStudentDbs(student); + if (!dbs) { + output(`\t${student.name}: Datenbanken fehlen`); + missing += 3; + continue; + } + for (const type of ["shadow", "public", "submit"]) { + if (!dbs[type]) { + output(`\t${student.name} ${student.group}: ${type} Datenbank fehlt`); + missing++; + } + } + } + if (missing) { + output(`${missing} von ${students.length * 3} Datenbanken fehlen!`); + } else { + output("Alle Datenbanken vorhanden!"); + } +} + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); +} else { + init(); +} diff --git a/src/cockpit.js b/src/cockpit.js new file mode 100644 index 0000000..bd4c860 --- /dev/null +++ b/src/cockpit.js @@ -0,0 +1,366 @@ +// import { + // fetchSubjectsMock as fetchSubjects, + // fetchProgressMock as fetchProgress, +// } from "../../js/mock.js"; + +import { + OBSERVATION_TYPES, + LEVEL_NAMES, + GraduationChange, + fetchGraduation, + fetchPublicGraduation, + submitGraduationChange +} from "./graduation-ENV.js"; + +import { fetchStudent, fetchStudents, Student } from "./student-ENV.js"; +import { + isTeacher as getIsTeacher, + getName, + getUserId, + DATE_FMT, + DATETIME_FMT, +} from "./utils-ENV.js"; +import { initSelection } from "./selection.js"; + +let isTeacher; +let name; +let student; + +async function loadStudent(studentToLoad) { + student = studentToLoad; + await fetchAndUpdateStudentInfo(); + // updateStudentCalendar(); + // updateStudentProgress(); + updateStudentGraduation(); +} + +function initStudentSelection() { + initSelection( + "student-selection", + fetchStudents, + (student) => `${student.name} ${student.getGroup()}`, + loadStudent + ); +} + +function pointClickHandler(color) { + const textareaDiv = document.getElementById("student-info-point-reason-container"); + const textarea = document.getElementById("student-info-point-reason"); + const submitBtn = document.getElementById("student-info-point-reason-submit"); + + // TODO(muhq): Prefill the textarea with common reasons + switch (color) { + case "green": + textarea.style.background = "rgb(from green r g b / 20%)" + textarea.value = ""; + break; + case "orange": + textarea.style.background = "rgb(from orange r g b / 20%)" + textarea.value = ""; + break; + case "red": + textarea.style.background = "rgb(from red r g b / 20%)" + textarea.value = ""; + break; + } + textareaDiv.style.display = "block"; + textarea.focus(); + + const coach = name; + + let submit = function() { + let change; + switch (color) { + case "green": + change = GraduationChange.newGreenPointChange(textarea.value, coach); + break; + case "orange": + change = GraduationChange.newOrangePointChange(textarea.value, coach); + break; + case "red": + change = GraduationChange.newRedPointChange(textarea.value, coach); + break; + default: + alert("Invalid color"); + break; + } + submitGraduationChange(student, change); + textareaDiv.style.display = "none"; + updateStudentInfo(); + student.fetchGraduation().then(() => updateStudentGraduation() ); + } + // Use onEVENT to override any previous handler + textarea.onkeydown = function(e) { + if (e.shiftKey && e.key === "Enter") { + submit(); + } + if (e.key === "Escape") { + textareaDiv.style.display = "none"; + } + }; + submitBtn.onclick = submit; +} + +async function fetchAndUpdateStudentInfo() { + if (isTeacher) + student.graduation = await fetchGraduation(student); + else + student.graduation = await fetchPublicGraduation(student); + + updateStudentInfo(); +} + +let DEFAULT_STUDENT_ICON; + +function updateStudentInfo() { + // Reset the student icon + document.getElementById("student-info-icon").src = DEFAULT_STUDENT_ICON; + + // Try to replace the generic icon with the user's icon + var img = document.createElement('img'); + img.onload = function() { + document.getElementById("student-info-icon").src = img.src; + img = img.onload = null; + }; + student.getIconUrl().then( (url) => img.src = url ); + + document.getElementById("student-info-name").innerHTML = `${student.name}`; + + if (student.graduation) { + const tag = document.getElementById("student-info-tag"); + tag.innerHTML = student.graduation.getLevelName(); + tag.style.display = "block"; + + document.getElementById("student-info-points-green").innerHTML = student.graduation.green; + document.getElementById("student-info-points-orange").innerHTML = student.graduation.orange; + document.getElementById("student-info-points-red").innerHTML = student.graduation.red; + + if (isTeacher) { + document.getElementById("student-info-green").addEventListener('click', + () => pointClickHandler("green")); + document.getElementById("student-info-orange").addEventListener('click', + () => pointClickHandler("orange")); + document.getElementById("student-info-red").addEventListener('click', + () => pointClickHandler("red")); + } + + renderGraduationLevel(student.graduation); + + // renderGraduationStickers(student.graduation, document.getElementById("student-info-stickers")); + // renderGraduationBonus(student.graduation, document.getElementById("student-info-stickers")); + + renderObservationStatus(student.graduation, document.getElementById("student-observation")); + } + + document.getElementById("student-content").style.display = "block"; +} + +function renderGraduationLevel(graduation) { + const label = document.getElementById("student-info-level"); + label.innerHTML = graduation.getLevelName(); + const icon = document.getElementById("student-level-icon"); + icon.src = graduation.getIcon(); +} + +// function renderGraduationStickers(graduation, node) { + // node.innerHTML = ""; +// } + +// function renderGraduationBonus(graduation, node) { + // node.innerHTML = ""; +// } + +function renderObservationStatus(graduation, node) { + node.innerHTML = ""; + + let status; + if (isTeacher || graduation.isObserved() || graduation.observationRequested) { + status = document.createElement("span"); + status.classList.add("student-observation-status"); + if (graduation.isObserved()) { + status.innerHTML = '👁 Ich befinde mich in der Beobachtungsphase zum Levelaufstieg.'; + } else if (graduation.observationRequested) { + status.innerHTML = 'Beobachtungsphase noch nicht begonnen.'; + } else { + status.innerHTML = "Nicht in Beobachtung."; + } + } else { + status = document.createElement("button"); + status.classList.add("student-request-observation-button"); + // Level is 1-based + status.innerHTML = `Ich will ${LEVEL_NAMES[graduation.level]} werden`; + status.addEventListener("click", async function() { + await graduation.requestObservation(); + // Rerender the observation status + renderObservationStatus(graduation, node); + }); + } + node.appendChild(status); +} + +// function newProgressBar(subject) { + // let bar = document.createElement("div"); + // fetchProgress(subject).then((progress) => { + // bar.progress = progress; + // }); + + // let s1 = document.createElement("div"); + // s1.onhover + + // bar.appendChild(s1); + // let s2 = document.createElement("div"); + // bar.appendChild(s2); + // let s3 = document.createElement("div"); + // bar.appendChild(s3); + // return bar; +// } + +// let selectedSubjects; +// async function updateStudentProgress() { + // selectedSubjects = await fetchSubjects(student); + + // let prog = document.getElementById("progress"); + // for(const subj of selectedSubjects) { + // let subjDiv = document.createElement("div"); + // subjDiv.innerHTML = subj; + // subjDiv.appendChild(newProgressBar(subj)) + // prog.appendChild(subjDiv); + // } +// } + +// async function updateStudentCalendar() { + // let cal = document.getElementById("calendar"); +// } + +async function updateStudentGraduation() { + if (!isTeacher) return; + console.assert(student.graduation, "Student has no graduation"); + const graduation = student.graduation; + + const changesTbl = document.getElementById("graduation-changes"); + changesTbl.innerHTML = ""; + for (const change of graduation.changes.filter( + (ch) => ["Grün", "Rot", "Orange"].includes(ch.change) )) + { + const changeTr = document.createElement("tr"); + for (const key in change) { + if (key == "json" || key == "id" || key == "name") continue; + + if (key == "change") { + switch(change.change) { + case "Grün": + changeTr.style.background = "green"; + break; + case "Rot": + changeTr.style.background = "red"; + break; + case "Orange": + changeTr.style.background = "orange"; + break; + default: + alert(`unexpected change: ${change.change}`); + } + continue; + } + + const value = change[key]; + const fmt = key == "date" ? DATETIME_FMT.format(value) : value; + + const changeTd = document.createElement("td"); + changeTd.innerHTML = fmt; + changeTd.dataset.key = key; + changeTr.appendChild(changeTd); + } + changesTbl.appendChild(changeTr); + } + + const observtionsTbl = document.getElementById("graduation-current-observation-period"); + observtionsTbl.innerHTML = ""; + if (graduation.observation) { + const sums = graduation.observation.getObservationsSums(); + for (let i = 0; i < OBSERVATION_TYPES.length; i++) { + const tr = document.createElement("tr"); + const typeTd = document.createElement("td"); + typeTd.innerHTML = OBSERVATION_TYPES[i]; + tr.appendChild(typeTd); + + const yesTd = document.createElement("td"); + yesTd.innerHTML = sums[i].yes; + tr.appendChild(yesTd); + + const noTd = document.createElement("td"); + noTd.innerHTML = sums[i].no; + tr.appendChild(noTd); + + observtionsTbl.appendChild(tr); + } + + const endDiv = document.getElementById("graduation-current-observation-period-end"); + if (!graduation.observation.ready()) { + endDiv.style.display = "none"; + return; + } + + endDiv.style.display = "block"; + + const reason = document.getElementById("graduation-current-observation-period-end-reason").value; + const successBtn = document.getElementById("graduation-current-observation-period-end-success"); + successBtn.addEventListener("click", () => { + submitGraduationChange(graduation.student, GraduationChange.newObservationEnd(reason, true)); + }); + const failureBtn = document.getElementById("graduation-current-observation-period-end-failure"); + failureBtn.addEventListener("click", () => { + submitGraduationChange(graduation.student, GraduationChange.newObservationEnd(reason, false)); + }); + } + + const pastObservtionsTbl = document.getElementById("graduation-observation-periods"); + pastObservtionsTbl.innerHTML = ""; + if (graduation.pastObservations.length > 0) { + for (const period of graduation.pastObservations) { + const tr = document.createElement("tr"); + const periodTd = document.createElement("td"); + const startDate = DATE_FMT.format(period.start); + const endDate = DATE_FMT.format(period.end); + periodTd.innerHTML = `${startDate} - ${endDate}`; + tr.appendChild(periodTd); + + const successTd = document.createElement("td"); + if (period.success) { + successTd.innerHTML = "Graduierung erfolgreich" + } else { + successTd.innerHTML = "Graduierung nicht erfolgreich" + } + tr.appendChild(successTd); + + pastObservtionsTbl.appendChild(tr); + } + } +} + +async function initCockpit() { + if (isTeacher) { + initStudentSelection(); + const params = new URLSearchParams(location.search); + const userId = params.get("userToLoad"); + if (userId) { + student = await fetchStudent(userId); + await loadStudent(student); + } + } else { + // Remove all elements only suitable for teachers + for(const elem of document.querySelectorAll("[data-availability=if-lehrer]")) { + elem.remove(); + } + + const student = await Student.fetchById(getUserId()); + await loadStudent(student); + } +} + +window.onload = function() { + isTeacher = getIsTeacher(); + DEFAULT_STUDENT_ICON = document.getElementById("student-info-icon").src; + name = getName(); + initCockpit(); +} diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..e69de29 diff --git a/src/database.js b/src/database.js new file mode 100644 index 0000000..e929896 --- /dev/null +++ b/src/database.js @@ -0,0 +1,160 @@ +import { getCourseId } from "./utils-ENV.js"; + +const DB_TEMPLATE_LIST_HEADER = ` + + + + + + + +`; +const DB_TEMPLATE_LIST_ENTRY = ` + + + + + +`; +const DB_TEMPLATE_LIST_FOOTER = ` + +
Json
##timeadded####user##[[Json]]
+`; + +let studentDbs = populateStudentDbs(); + +const ID_QUERY_PARAM_REGEX = /[?&]id=(\d+)/ + +async function populateStudentDbs() { + const courseUrl = `https://lernplattform.bycs.de/course/view.php?id=${getCourseId()}`; + const courseRes = await fetch(courseUrl); + const courseDoc = new DOMParser().parseFromString(await courseRes.text(), 'text/html'); + + let dbs = new Map(); + for (const link of courseDoc.querySelectorAll("div.activityname a.aalink")) { + if (link.textContent.trim() === "Submit Datenbank") { + const dbId = link.href.match(ID_QUERY_PARAM_REGEX)[1] + dbs.set("submit", new DB(dbId)); + continue; + } + + const match = link.textContent.match(/(\d+)(Shadow|Public|Submit)/); + if (!match) continue; + const studentId = match[1]; + const dbType = match[2]; + const dbId = link.href.match(ID_QUERY_PARAM_REGEX)[1] + + if (!dbs.has(studentId)) { + dbs.set(studentId, {}); + } + + dbs.get(studentId)[dbType.toLowerCase()] = new DB(dbId); + } + + return dbs; +} + +async function getSubmitDb() { + const dbs = await studentDbs; + return dbs.get("submit"); +} + +class DB { + constructor(id) { + this.id = id; + } + + async fetchEntries(params) { + return loadFromDatabaseActivity(this.id, params) + } + + async saveEntry(json) { + return addEntryToDatabaseActivity(this.id, json); + } + + static async getStudentDbs(student) { + const dbs = await studentDbs; + return dbs.get(student.id); + } +} + +async function loadFromDatabaseActivity(id, params) { + let dbUrl = `${window.location.origin}/mod/data/view.php?id=${id}&mode=list&perpage=1000`; + // TODO(muhq): Why does this not include entries? + if (params) { + dbUrl += "&filter=1&advanced=1"; + for (const param in params) { + dbUrl += `&${param}=${encodeURIComponent(params[param])}` + } + } + + const dbRes = await fetch(encodeURI(dbUrl)); + const dbDoc = new DOMParser().parseFromString(await dbRes.text(), 'text/html'); + let data = []; + dbDoc.querySelectorAll('.db-row').forEach(row => { + let entry = {}; + row.querySelectorAll('.db-cell').forEach(field => { + const key = field.dataset.key; + let value; + // Moodle includes the full date in the title attribute of the included span + if (key === "date") { + value = field.querySelector("span").title; + } else { + value = field.innerText.trim(); + } + entry[key] = value; + }); + data.push(entry); + }); + return data; +} + +async function loadFromStudentDatabase(student, type) { + const dbs = await DB.getStudentDbs(student); + const db = dbs[type]; + return await db.fetchEntries() +} + +async function addEntryToDatabaseActivity(id, json) { + const url = `/mod/data/edit.php?id=${id}`; + const editRes = await fetch(url); + const editDoc = new DOMParser().parseFromString(await editRes.text(), 'text/html'); + const form = editDoc.querySelector('div[role="main"] > form'); + + const formData = new FormData(form); + + for (const key of formData.keys()) { + if (key.startsWith("field_")) { + formData.set(key, json); + // We found our json key + break; + } + } + formData.set("saveandview", "Speichern"); + + return await fetch("/mod/data/edit.php", { + method: "POST", + // Set the FormData instance as the request body + body: formData, + }); +} + +async function addEntryToStudentDatabase(student, type, json) { + let db; + // global submit database + if (type === "submit") { + db = await getSubmitDb(); + // individuell databases + } else { + const dbs = await DB.getStudentDbs(student); + db = dbs[type]; + } + await db.saveEntry(json); +} + +export { + DB, + getSubmitDb, + loadFromDatabaseActivity, loadFromStudentDatabase, + addEntryToDatabaseActivity, addEntryToStudentDatabase, +}; diff --git a/src/graduation-common.js b/src/graduation-common.js new file mode 100644 index 0000000..0c28465 --- /dev/null +++ b/src/graduation-common.js @@ -0,0 +1,396 @@ +import { getSubmitDb, loadFromStudentDatabase, addEntryToStudentDatabase } from "./database.js" + +import NeulingPng from "../assets/Neuling.png"; +import EntdeckerPng from "../assets/Entdecker.png"; +import NavigatorPng from "../assets/Navigator.png"; +import PioneerPng from "../assets/Pioneer.png"; + +const LEVEL_NAMES = ["Neuling", "Entdecker", "Navigator", "Pionieer"]; + +const LEVEL_IMAGES = [ + new URL(NeulingPng), + new URL(EntdeckerPng), + new URL(NavigatorPng), + new URL(PioneerPng), +]; + +class GraduationChange { + constructor(date, change, reason, coach, context) { + this.date = date; + this.change = change; + this.reason = reason; + this.coach = coach; + this.context = context; + } + + isPublic() { + return [ + "Grün", "Orange", "Rot", + "ObservationStart", "ObservationEnd", + "LevelChange" + ].includes(this.change); + } + + publicDBFields () { + return { + "change": this.change, + "date": this.date, + }; + } + + dbFields () { + return { + // We include the date in the json because Moodle reports the timeadded + // locale dependent. + "date": this.date, + "change": this.change, + "reason": this.reason, + "context": this.context, + }; + } + + static fromDBEntry(e) { + const {coach, json} = e; + const change = JSON.parse(json); + return new GraduationChange(new Date(change.date), change.change, change.reason, coach, change.context); + } + + static newObservationRequest() { + return new GraduationChange(new Date(), "ObservationRequest", "", "", ""); + } + + static newObservationStart() { + return new GraduationChange(new Date(), "ObservationStart", "", "", ""); + } + + static newObservationEnd(reason, success) { + return new GraduationChange(new Date(), "ObservationEnd", reason, "", JSON.stringify(success)); + } + + static newObservationChange(observation) { + return new GraduationChange(observation.date, "Observation", "", "", JSON.stringify(observation.status)); + } + + static newGreenPointChange(reason, coach) { + return new GraduationChange(new Date(), "Grün", reason, coach, ""); + } + + static newOrangePointChange(reason, coach) { + return new GraduationChange(new Date(), "Orange", reason, coach, ""); + } + + static newRedPointChange(reason, coach) { + return new GraduationChange(new Date(), "Rot", reason, coach, ""); + } +} + +class Graduation { + constructor(student, green, orange, red, level, stickers, observationRequested, observation, pastObservations, changes) { + this.student = student; + this.green = green; + this.orange = orange; + this.red = red; + this.level = level; + this.stickers = stickers; + this.observationRequested = observationRequested; + this.observation = observation; + this.pastObservations = pastObservations; + this.changes = changes; + + student.graduation = this; + } + + static from(student, changes) { + let green = 0; + let orange = 0; + let red = 0; + let level = 1; + let openObservationRequest = null; + let observationPeriod = null; + let pastObservations = []; + let stickers = []; + + // The parsing logic assumes that changes are ordered chronologically + changes.sort((a, b) => a.date - b.date); + + for (const change of changes) { + switch (change.change) { + case "Grün": + green++; + break; + + case "Orange": + orange++; + break; + + case "Rot": + red++; + break; + + case "ObservationRequest": + openObservationRequest = change.date; + break; + + case "ObservationStart": + console.assert(openObservationRequest, "No request for observation start"); + observationPeriod = new ObservationPeriod(change.date); + openObservationRequest = null; + break; + + case "ObservationEnd": + console.assert(observationPeriod, "No active observation period"); + observationPeriod.finishPeriod(change.context.success); + if (change.context.success) { + level++; + } + pastObservations.push(observationPeriod); + observationPeriod = null; + break; + + case "Observation": + console.assert(observationPeriod, "No active observation period"); + const observation = Observation.fromChange(change); + observationPeriod.addObservation(observation); + break; + + case "LevelChange": + level = change.context.level; + break; + + default: + alert(`Unexpected change: ${change}`); + } + } + return new Graduation(student, green, orange, red, level, + stickers, + openObservationRequest, observationPeriod, pastObservations, + changes); + } + + addChange(change) { + // TODO(muhq): Implement incremental addition + let changes = this.changes; + changes.push(change); + // This updates the student.graduation reference as well + Graduation.from(this.student, changes); + } + + /** + * Request observation + */ + async requestObservation() { + const request = GraduationChange.newObservationRequest(); + this.observationRequested = request.date; + await addEntryToStudentDatabase(this.student, "submit", JSON.stringify(request.publicDBFields())); + } + + /** + * Start an observation + */ + async startObservation() { + if (!this.observationRequested) { + console.assert(this.observationRequested, "No observation requested"); + return; + } + this.observationRequested = null; + this.observation = new ObservationPeriod(new Date()); + return submitGraduationChange(this.student, GraduationChange.newObservationStart()); + } + + /** + * End an observation + */ + async endObservation(reason, success) { + if (!this.observation) { + console.assert(this.observation, "No active observation"); + return; + } + this.observation.end = new Date(); + this.pastObservations.push(this.observation); + this.observation = null; + return submitGraduationChange(this.student, GraduationChange.newObservationEnd(reason, success)); + } + + /** + * Return if the student is in observation. + */ + isObserved() { + return Boolean(this.observation); + } + + /** + * Return the URI of the icon representing the graduation level. + */ + getIcon() { + return LEVEL_IMAGES[this.level - 1]; + } + + /** + * Return the name of the current graduation level. + * @return {String} The name of the current level. + */ + getLevelName() { + return LEVEL_NAMES[this.level - 1]; + } +} + +class ObservationPeriod { + constructor(start) { + this.start = start; + this.observations = []; + } + + finishPeriod(success) { + this.end = new Date(); + this.success = success; + } + + ended() { + return Boolean(this.end); + } + + ready() { + if (this.observations.length < 10) + return false; + + // at least 10 observation on different days + let days = new Map(); + for (const observation of this.observations) { + const d = observation.date; + days.set(`${d.getFullYear()}${d.getMonth()}${d.getDate()}`, true); + } + return days.size >= 10; + } + + getObservationsSums() { + let sums = Array(OBSERVATION_TYPES.length).fill().map(() => ({"yes":0, "no":0})); + for (const observation of this.observations) { + for (let i = 0; i < sums.length; i++) { + if (observation.status[i]) { + if (observation.status[i] === "yes") + sums[i].yes++; + else + sums[i].no++; + } + } + } + return sums; + } + + addObservation(observation) { + this.observations.push(observation); + } +} + +const OBSERVATION_TYPES = [ + "Arbeitet konzentriert an Aufgaben", + "Arbeitet zunehmend selbstständig", + "Führt das Logbuch zuverlässig", + "Bringt Material mit", + "Hält Regeln ein", + "Geht verantwortungsvoll mit Materialien und LernRaum um", + "Nimmt Rückmeldungen an und setzt sie um", +]; + +class Observation { + constructor(date, status) { + console.assert(status.length == OBSERVATION_TYPES.length, "invalid observation status"); + this.date = date; + this.status = status; + } + + static fromChange(change) { + return new Observation(change.date, JSON.parse(change.context)) + } +} + +function changesFromDbEntries(entries) { + let changes = []; + for (const e of entries) { + const gradChange = GraduationChange.fromDBEntry(e); + changes.push(gradChange); + } + return changes; +} + +async function fetchGraduationChangesFromDb(student, type) { + const data = await loadFromStudentDatabase(student, type); + return changesFromDbEntries(data); +} + +const submitEntriesPromise = async function() { + const db = await getSubmitDb(); + const entries = await db.fetchEntries(); + return changesFromDbEntries(entries); +}(); + +async function addSubmittedChanges(student, changes) { + let submitEntries = await submitEntriesPromise; + for (const e of submitEntries.filter( (c) => c.coach === student.name )) { + changes.push(e); + } + return changes; +} + +async function fetchGraduationChanges(student) { + let changes = await fetchGraduationChangesFromDb(student, "shadow"); + return addSubmittedChanges(student, changes); +} + +async function fetchPublicGraduationChanges(student) { + let changes = await fetchGraduationChangesFromDb(student, "public"); + return addSubmittedChanges(student, changes); +} + +async function fetchGraduation(student) { + const changes = await fetchGraduationChanges(student); + return Graduation.from(student, changes); +} + +async function fetchPublicGraduation(student) { + const changes = await fetchPublicGraduationChanges(student); + return Graduation.from(student, changes); +} + +async function submitGraduationChange(student, change) { + student.graduation.addChange(change); + + // Submit changes concurrently + let promises = []; + + if (change.isPublic()) { + promises.push( + // TODO(muhq): strip non public fields from changes + addEntryToStudentDatabase(student, "public", JSON.stringify(change.publicDBFields())) + ); + } + + promises.push( + addEntryToStudentDatabase(student, "shadow", JSON.stringify(change.dbFields())) + ); + + await Promise.all(promises); +} + +async function submitObservation(student, observation) { + console.assert(student.graduation.observation, "no active observation"); + student.graduation.observation.addObservation(observation); + await submitGraduationChange(student, GraduationChange.newObservationChange(observation)); +} + +export { + LEVEL_NAMES, + LEVEL_IMAGES, + GraduationChange, + Graduation, + fetchGraduation, + fetchPublicGraduation, + fetchGraduationChanges, + fetchPublicGraduationChanges, + submitGraduationChange, + submitObservation, + + OBSERVATION_TYPES, + Observation, + ObservationPeriod, +}; diff --git a/src/graduation-mock.js b/src/graduation-mock.js new file mode 100644 index 0000000..1c84433 --- /dev/null +++ b/src/graduation-mock.js @@ -0,0 +1,30 @@ +import { + LEVEL_NAMES, + GraduationChange, + Graduation, + + OBSERVATION_TYPES, + Observation, + ObservationPeriod, +} from "./graduation-common.js"; + +import { + fetchGraduationMock as fetchGraduation, + fetchPublicGraduationMock as fetchPublicGraduation, + submitGraduationChangeMock as submitGraduationChange, + submitObservationMock as submitObservation +} from "./mock.js"; + +export { + LEVEL_NAMES, + GraduationChange, + Graduation, + fetchGraduation, + fetchPublicGraduation, + submitGraduationChange, + submitObservation, + + OBSERVATION_TYPES, + Observation, + ObservationPeriod, +}; diff --git a/src/graduation-prod.js b/src/graduation-prod.js new file mode 100644 index 0000000..0e86956 --- /dev/null +++ b/src/graduation-prod.js @@ -0,0 +1,15 @@ +export { + LEVEL_NAMES, + GraduationChange, + Graduation, + fetchGraduation, + fetchPublicGraduation, + fetchGraduationChanges, + fetchPublicGraduationChanges, + submitGraduationChange, + submitObservation, + + OBSERVATION_TYPES, + Observation, + ObservationPeriod, +} from "./graduation-common.js"; diff --git a/src/group.js b/src/group.js new file mode 100644 index 0000000..ba5e1a0 --- /dev/null +++ b/src/group.js @@ -0,0 +1,57 @@ +import { getCourseParticipantsUrl } from "./utils-ENV.js"; +import { fetchStudents } from "./student-ENV.js"; + +/** + * Class to represent a Group of Students. + */ +class Group { + constructor(name, students) { + this.name = name; + if (!students) { + this.students = []; + } + } + + setStudents(students) { + this.students = students; + } + + addStudent(student) { + this.students.push(student); + } + + async getStudents() { + return this.students; + } +} + +let groups = new Map(); + +function buildGroups(students) { + for (const student of students) { + let group = groups.get(student.group); + + if (!group) { + group = new Group(student.group); + groups.set(student.group, group); + } + + group.addStudent(student); + } +} + +/** + * Fetch all Groups and return them as a map. + */ +async function fetchGroups() { + if (groups.size) { + return groups; + } + + // Fetch all students and build the groups from them + const students = await fetchStudents(); + buildGroups(students); + return groups; +} + +export { Group, buildGroups, fetchGroups }; diff --git a/src/mass-graduation.js b/src/mass-graduation.js new file mode 100644 index 0000000..3cbf32a --- /dev/null +++ b/src/mass-graduation.js @@ -0,0 +1,357 @@ +import { + OBSERVATION_TYPES, + GraduationChange, + submitGraduationChange, + Observation, + submitObservation +} from "./graduation-ENV.js"; + +import { fetchGroups } from "./group.js"; +import { initSelection } from "./selection.js"; +import { getName, DATETIME_FMT, datetimeStringNow } from "./utils-ENV.js"; + +let group; +let name; + +function toggle(btn, others) { + if (btn.innerHTML === "X") { + btn.innerHTML = "o"; + } else { + btn.innerHTML = "X"; + if (others) { + for (const other of others) + other.innerHTML = "o"; + } + } + + document.getElementById("submit-mass-graduation").style.display = "inline"; + document.getElementById("submit-mass-graduation-state").innerHTML = ""; +} + +function observationClickHandler(li) { + let observationDiv = li.observationDiv; + // Build the observation container + if(!observationDiv) { + li.style.background = "lightgray"; + + observationDiv = document.createElement("div"); + // Remember the observation div for this student + li.observationDiv = observationDiv; + observationDiv.id = `${li.student.name}-observation-container`; + observationDiv.classList.add("group-member-observation-container"); + li.appendChild(observationDiv); + + const obsTbl = document.createElement("table"); + obsTbl.classList.add("group-member-observation-table"); + observationDiv.appendChild(obsTbl); + + const obsTh = document.createElement("thead"); + obsTh.innerHTML = ` + + + Ja + Nein + Kann ich nicht beurteilen +`; + + obsTbl.appendChild(obsTh); + + const obsTbdy = document.createElement("tbody"); + obsTbl.appendChild(obsTbdy); + + for (const obsType of OBSERVATION_TYPES) { + const obsTr = document.createElement("tr"); + + const obsDesc = document.createElement("td"); + obsDesc.innerHTML = obsType; + obsDesc.classList.add("group-member-observation-table-description"); + obsTr.appendChild(obsDesc); + + const obsYes = document.createElement("td"); + obsYes.innerHTML = "o"; + obsYes.dataset.yes = "true"; + obsYes.classList.add("group-member-observation-table-yes"); + obsTr.appendChild(obsYes); + + const obsNo = document.createElement("td"); + obsNo.innerHTML = "o"; + obsNo.dataset.no = "true"; + obsNo.classList.add("group-member-observation-table-no"); + obsTr.appendChild(obsNo); + + const obsMaybe = document.createElement("td"); + obsMaybe.innerHTML = "X"; + obsMaybe.dataset.maybe = "true"; + obsMaybe.classList.add("group-member-observation-table-maybe"); + obsTr.appendChild(obsMaybe); + + obsYes.addEventListener("click", () => { + toggle(obsYes, [obsNo, obsMaybe]); + }); + obsNo.addEventListener("click", () => { + toggle(obsNo, [obsYes, obsMaybe]); + }); + obsMaybe.addEventListener("click", () => { + toggle(obsMaybe, [obsYes, obsNo]); + }); + + obsTbdy.appendChild(obsTr); + } + // Toggle the observation container visibility + } else { + if (observationDiv.style.display === "none") { + observationDiv.style.display = "block"; + li.style.background = "lightgray"; + + } else { + observationDiv.style.display = "none"; + li.style.background = "unset"; + } + } +} + +async function loadGroup(selected) { + group = selected; + const students = await group.getStudents(); + let graduationPromises = []; + document.getElementById("group-content").style.display = "block"; + + const ul = document.getElementById("group-members"); + // Clear group list + ul.innerHTML = ""; + + for (const student of students) { + const graduationPromise = student.getGraduation(); + + const li = document.createElement("li"); + li.classList.add("group-member") + // Remember the student of the entry + li.student = student; + + const i = document.createElement("img"); + i.classList.add("group-member-icon") + graduationPromise.then((graduation) => { + i.src = graduation.getIcon(); + }); + li.appendChild(i); + + const n = document.createElement("span"); + n.classList.add("group-member-name") + n.innerHTML = `${student.name}`; + li.appendChild(n); + + const ob = document.createElement("button"); + ob.classList.add("group-member-observation-icon") + + graduationPromise.then((graduation) => { + if (!graduation.isObserved()) { + return; + } + ob.innerHTML = "👁" + ob.addEventListener("click", () => observationClickHandler(li) ); + }); + graduationPromises.push(graduationPromise); + + li.appendChild(ob); + + for (const col of ["green", "orange", "red"]) { + const b = document.createElement("button"); + b.classList.add(`group-member-${col}`) + b.innerHTML = "o"; + b.addEventListener("click", () => toggle(b) ); + li.appendChild(b); + } + + const textarea = document.createElement("textarea"); + textarea.classList.add("group-member-point-reason"); + li.appendChild(textarea); + + const p = document.createElement("button"); + p.classList.add("group-member-print") + p.innerHTML = "🖶"; + p.addEventListener("click", () => printNote(li) ); + li.appendChild(p); + + ul.appendChild(li); + } + + const graduations = await Promise.all(graduationPromises); + let observations = graduations.filter( + (g) => g.observationRequested || (g.observation && !g.observation.end) + ); + + const observationReuqestsContainer = document.getElementById("observation-requests-container"); + + if (observations.length == 0) { + observationReuqestsContainer.style.display = "none"; + return; + } else { + observationReuqestsContainer.style.display = "unset"; + } + + const observationReuqestsNode = document.getElementById("observation-requests"); + observationReuqestsNode.innerHTML = ""; + observations.sort((g1, g2) => { + let g1D, g2D; + g1D = g1.observationRequested ? g1.observationRequested : g1.observation.start; + g2D = g2.observationRequested ? g2.observationRequested : g2.observation.start; + return g1D - g2D; + }); + + for (const graduation of observations) { + const row = document.createElement("tr"); + const iconCell = document.createElement("td"); + const icon = document.createElement("img"); + icon.src = graduation.getIcon(); + row.appendChild(iconCell); + + const nameCell = document.createElement("td"); + nameCell.innerHTML = `${graduation.student.name}`; + row.appendChild(nameCell); + + const dateCell = document.createElement("td"); + let date; + if (graduation.observationRequested) { + date = graduation.observationRequested; + } else { + date = graduation.observation.start; + } + dateCell.innerHTML = DATETIME_FMT.format(date); + row.appendChild(dateCell); + + const btnCell = document.createElement("td"); + if (graduation.observation && !graduation.observation.ready()) { + btnCell.innerHTML = "In Beobachtungsphase"; + } else { + const startBtn = document.createElement("button"); + if (graduation.observationRequested) { + startBtn.addEventListener("click", () => { + graduation.startObservation(); + //TODO(muhq): Add observation icon-button. + startBtn.remove(); + btnCell.innerHTML = "In Beobachtungsphase"; + }); + startBtn.innerHTML = `Beobachtungsphase beginnen`; + startBtn.classList.add("start-observation"); + } else { + startBtn.addEventListener("click", () => { + window.open(`/mod/page/view.php?id=93017047&userToLoad=${graduation.student.id}`, '_blank'); + }); + startBtn.innerHTML = `Graduierung abschließen`; + startBtn.classList.add("end-observation"); + } + btnCell.appendChild(startBtn); + } + row.appendChild(btnCell); + + observationReuqestsNode.appendChild(row); + } +} + +function submitMassGraduation() { + const state = document.getElementById("submit-mass-graduation-state"); + state.innerHTML = ""; + state.classList.add("submit-mass-graduation-inprogress"); + state.style.display = "unset"; + + // TODO(muhq): await graduation promises + + let promises = []; + for(const li of document.querySelectorAll("li.group-member")) { + const reason = li.querySelector(".group-member-point-reason").value; + const student = li.student; + if (li.querySelector(".group-member-green").innerHTML === "X") { + promises.push(submitGraduationChange(student, GraduationChange.newGreenPointChange(reason, name))); + } + if (li.querySelector(".group-member-orange").innerHTML === "X") { + promises.push(submitGraduationChange(student, GraduationChange.newOrangePointChange(reason, name))); + } + if (li.querySelector(".group-member-red").innerHTML === "X") { + promises.push(submitGraduationChange(student, GraduationChange.newRedPointChange(reason, name))); + } + if (li.observationDiv) { + let status = []; + for (const row of li.observationDiv.querySelectorAll("tbody > tr")) { + if (row.querySelector('td[data-yes="true"]').innerHTML === "X") + status.push("yes"); + else if (row.querySelector('td[data-no="true"]').innerHTML === "X") + status.push("no"); + else + status.push(""); + } + promises.push(submitObservation(student, new Observation(new Date(), status))); + } + } + + Promise.all(promises).then(() => { + state.style.display = "inline"; + state.innerHTML = "✓"; + state.style.color = "green"; + for (const li of document.querySelectorAll("li.group-member")) { + if (!li.observationDiv) + continue; + li.observationDiv.remove(); + li.observationDiv = null; + } + }).catch((error) => { + state.style.display = "inline"; + state.innerHTML = "X"; + state.style.color = "red"; + console.log(error); + }).finally(() => { + state.classList.remove("submit-mass-graduation-inprogress"); + }); +} + +let prePrintBody = null; +function printNote(li) { + // save the current DOM + prePrintBody = document.body; + + // prepare the printable red point notice + const printBdy = document.createElement("body"); + const reason = li.querySelector(".group-member-point-reason").value; + const dateStr = datetimeStringNow(); + let pointType = ""; + if (li.querySelector(".group-member-orange").innerHTML === "X") { + pointType = "Oranger Punkt"; + } + if (li.querySelector(".group-member-red").innerHTML === "X") { + pointType = "Roter Punkt"; + } + + printBdy.innerHTML = ` +

Dokuzettel

+

${pointType}

+

Schüler*in: ${li.student.name}

+

Datum: ${dateStr}

+

Lehrkraft: ${name}

+

Klassenleitung: TODO

+

Begründung

+

${reason}

+`; + + document.body = printBdy; + print(); + // Restore the DOM after printing the red point note + document.body = prePrintBody; +} + +async function init() { + document.getElementById("submit-mass-graduation").addEventListener("click", submitMassGraduation); + name = getName(); + + initSelection( + "group-selection", + async function() { return (await fetchGroups()).values(); }, + (group) => `${group.name}`, + loadGroup, + ); +} + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); +} else { + init(); +} + diff --git a/src/mock.js b/src/mock.js new file mode 100644 index 0000000..91b1525 --- /dev/null +++ b/src/mock.js @@ -0,0 +1,104 @@ +import { Graduation, GraduationChange } from "./graduation-common.js"; +import { Student } from "./student-common.js"; +import { buildGroups } from "./group.js"; + +const students = [ + new Student(1, 'foo bar', 'test'), + new Student(2, 'maxi mustermensch', 'test'), + new Student(3, 'b b b', '5a') +]; + +// Build the groups from our mock students +buildGroups(students); + +function fetchStudentsMock() { + return new Promise((resolve) => { resolve(students); }); +} + +function fetchSubjectsMock() { + return new Promise((resolve) => { + resolve(['Mathe', 'Deutsch', 'Englisch']); + }); +} + +function fetchProgressMock(subject) { + return new Promise((resolve) => { + if (subject === "Mathe") { + resolve([40, 10, 3]); + } else if (subject === "Deutsch") { + resolve([10, 2, 0]); + } else if (subject === "Englisch") { + resolve([50, 25, 10]); + } else { + resolve([0, 0, 0]); + } + }); +} + +const graduationChanges = {}; +for (const s of students) { + switch (s.name) { + case "foo bar": + graduationChanges[s.name] = [ + GraduationChange.newGreenPointChange("Gute Arbeit", "Florian Fischer"), + GraduationChange.newOrangePointChange("", "Florian Fischer"), + ]; + break; + case "maxi mustermensch": + graduationChanges[s.name] = [ + GraduationChange.newGreenPointChange("", "Florian Fischer"), + GraduationChange.newGreenPointChange("", "Florian Fischer"), + new GraduationChange(new Date(), "ObservationStart", "", "Florian Fischer", ""), + ]; + break; + case "b b b": + graduationChanges[s.name] = [ + GraduationChange.newRedPointChange("", "Florian Fischer"), + ]; + } + // Build the student's graduation + Graduation.from(s, graduationChanges[s.name]); +} + +function fetchGraduationMock(s) { + return new Promise((resolve) => { + const changes = graduationChanges[s.name]; + if (!changes) { + resolve(null); + } else { + resolve(Graduation.from(s, changes)); + } + }); +} + +function fetchPublicGraduationMock(s) { + return new Promise((resolve) => { + const changes = graduationChanges[s.name]; + if (!changes) { + resolve(null); + } else { + resolve(Graduation.from(s, changes.filter((change) => change.isPublic()))); + } + }); +} + +function submitGraduationChangeMock(s, c) { + console.assert(s.graduation, "no graduation") + s.graduation.addChange(c); +} + +function submitObservationMock(s, o) { + console.assert(s.graduation, "no graduation") + console.assert(s.graduation.observation, "no observation period") + s.graduation.observation.addObservation(o); +} + +export { + fetchStudentsMock, + fetchSubjectsMock, + fetchProgressMock, + fetchGraduationMock, + fetchPublicGraduationMock, + submitGraduationChangeMock, + submitObservationMock, +}; diff --git a/src/selection.js b/src/selection.js new file mode 100644 index 0000000..eda20ae --- /dev/null +++ b/src/selection.js @@ -0,0 +1,170 @@ +/** + * Initialises a selection based on existing HTML elements. + * For HTML elements are required: + * - A text input with id $idPrefix + * - A list with id ${idPrefix}-list + * - An element shown if no results are available with id ${idPrefix}-noresults + * - A button to clear the input with id ${idPrefix}-clear + * + * @param {string} idPrefix - The id prefix of the four HTML elements. + * @param {async function} fetchCandidates - The async function to retrieve the selectable candidates. + * @param {function} fmtCandidate - The function to format the displayed HTML for each candidate. + * @param {function} onSelection - The function to call when a candidate is selected. + */ +async function initSelection(idPrefix, fetchCandidates, fmtCandidate, onSelection) { + const sel = document.getElementById(idPrefix); + const list = document.getElementById(`${idPrefix}-list`); + const noResults = document.getElementById(`${idPrefix}-noresults`); + const clearBtn = document.getElementById(`${idPrefix}-clear`); + const loader = document.getElementById(`${idPrefix}-loader`); + + loader.style.display = "unset"; + const candidates = await fetchCandidates(); + loader.style.display = "none"; + + let items = []; + + let currentFocus = -1; + let visibleItems = []; + function setActive() { + visibleItems.forEach(item => item.classList.remove("active")); + if (currentFocus >= 0 && visibleItems[currentFocus]) { + visibleItems[currentFocus].classList.add("active"); + } + } + + for (const candidate of candidates) { + let item = document.createElement("li"); + item.candidate = candidate; + item.innerHTML = fmtCandidate(candidate); + item.style.display = 'none'; + + item.addEventListener('click', function() { + sel.value = ""; + sel.dispatchEvent(new Event('keyup')); + onSelection(candidate); + }); + + list.appendChild(item); + items.push(item); + } + + sel.addEventListener('keyup', function(e) { + if ((["ArrowDown", "ArrowUp", "Enter"]).includes(e.key)) + return; + + const filter = sel.value.toLowerCase(); + visibleItems = []; + for (const item of items) { + const itemText = item.textContent.trim().toLowerCase(); + + if (itemText.indexOf(filter) > -1 && filter !== "") { + item.style.display = ""; + visibleItems.push(item); + + // Highlight matched text + const regex = new RegExp(`(${filter})`, 'gi'); + item.innerHTML = item.textContent.replace( + regex, + "$1" + ); + } else { + item.style.display = "none"; + } + } + + // Show the clear button if there is an input + clearBtn.style.display = sel.value ? "block" : "none"; + + // Select the first or none if there are no items visible + currentFocus = visibleItems.length ? 0 : -1; + setActive(visibleItems); + noResults.style.display = visibleItems.length || filter === "" ? "none" : "block"; + }); + + clearBtn.addEventListener('click', function() { + sel.value = ""; + sel.dispatchEvent(new Event('keyup')); // reset list + sel.focus(); + }); + + sel.addEventListener('keydown', function(e) { + if (e.key === "ArrowDown" || e.key === "ArrowUp") { + if(e.key === "ArrowDown") + currentFocus = (currentFocus + 1) % visibleItems.length; + else + currentFocus = (currentFocus - 1 + visibleItems.length) % visibleItems.length; + setActive(); + e.preventDefault(); + } else if (e.key === "Enter") { + if (currentFocus > -1 && visibleItems[currentFocus]) { + + const visibleItem = visibleItems[currentFocus]; + sel.value = ""; + sel.dispatchEvent(new Event('keyup')); + onSelection(visibleItem.candidate); + } + e.preventDefault(); + } + }); +} + +function pointClickHandler(color) { + const textareaDiv = document.getElementById("student-info-point-reason-container"); + const textarea = document.getElementById("student-info-point-reason"); + const submitBtn = document.getElementById("student-info-point-reason-submit"); + + // TODO(muhq): Prefill the textarea with common reasons + switch (color) { + case "green": + textarea.style.background = "rgb(from green r g b / 20%)" + textarea.value = ""; + break; + case "orange": + textarea.style.background = "rgb(from orange r g b / 20%)" + textarea.value = ""; + break; + case "red": + textarea.style.background = "rgb(from red r g b / 20%)" + textarea.value = ""; + break; + } + textareaDiv.style.display = "block"; + textarea.focus(); + + // Can not use string interpolation because of Moodle's firstname filter. + const coach = firstname + " " + lastname; + + let submit = function() { + let change; + switch (color) { + case "green": + change = GraduationChange.newGreenPointChange(student, textarea.value, coach); + break; + case "orange": + change = GraduationChange.newOrangePointChange(student, textarea.value, coach); + break; + case "red": + change = GraduationChange.newRedPointChange(student, textarea.value, coach); + break; + default: + alert("Invalid color"); + break; + } + submitGraduationChange(student, change); + textareaDiv.style.display = "none"; + updateStudentInfo(); + } + // Use onEVENT to override any previous handler + textarea.onkeydown = function(e) { + if (e.shiftKey && e.key === "Enter") { + submit(); + } + if (e.key === "Escape") { + textareaDiv.style.display = "none"; + } + }; + submitBtn.onclick = submit; +} + +export { initSelection }; diff --git a/src/student-common.js b/src/student-common.js new file mode 100644 index 0000000..2dfa35d --- /dev/null +++ b/src/student-common.js @@ -0,0 +1,115 @@ +import { getCourseId, getUserIconUrl, getCourseParticipantsUrl } from "./utils-ENV.js"; +import { fetchGraduation } from "./graduation-common.js"; +/** + * Class to represent Students. + */ +class Student { + graduation = null; + + constructor(id, name, group) { + this.id = id; + this.name = name; + this.group = group; + } + + async getIconUrl() { + return await getUserIconUrl(this.id); + } + + getGroup() { + return this.group; + } + + async getGraduation() { + if(!this.graduation) { + // fetchGraduation(this) sets this.graduation + await fetchGraduation(this); + } + return this.graduation; + } + + static async fetchById(id) { + let name = ""; + let group = ""; + + const res = await fetch(`/user/view.php?id=${id}&course=${getCourseId()}`); + const doc = new DOMParser().parseFromString(await res.text(), "text/html"); + + name = doc.querySelector("div.page-header-headings > h2").textContent; + + const cards = doc.querySelectorAll("div.card-body dl"); + for (const card of cards) { + if (card.querySelector("dt").textContent !== "Gruppe") + continue; + group = card.querySelector("dd > a").textContent; + break; + } + + return new Student(id, name, group); + } +} + +// Cache the fetched students. +let students = []; + +const idRegex = /id=(\d+)/; +async function _fetchStudents() { + const res = await fetch(getCourseParticipantsUrl()); + const doc = new DOMParser().parseFromString(await res.text(), "text/html"); + const table = doc.querySelector("tbody"); + let fetchedStudents = []; + table.querySelectorAll("tr").forEach(row => { + if (row.classList.contains("emptyrow")) + return; + + const roleCell = row.querySelector(".c4"); + // TODO(muhq): Is this locale dependent? + if (roleCell.innerHTML.includes("Lehrer")) { + return; + } + + const nameCell = row.querySelector(".c1"); + let name; + if (nameCell.firstChild.firstChild.tagName == "SPAN") + name = nameCell.firstChild.firstChild.title; + else + name = nameCell.innerText; + + const profileUrl = nameCell.querySelector("a.aabtn").href; + const match = idRegex.exec(profileUrl); + const id = match[1]; + + const groupCell = row.querySelector(".c5"); + const group = groupCell.querySelector("a.quickeditlink").textContent; + + fetchedStudents.push(new Student(id, name, group)); + }); + return fetchedStudents; +} + +async function fetchStudents(idFilter, nameFilter, groupFilter) { + if (!students.size) { + const studentsPromise = _fetchStudents() + const storedStudents = localStorage.getItem("students"); + if (storedStudents) { + studentsPromise.then((fetched) => localStorage.setItem("students", JSON.stringify(fetched))); + for (const o of JSON.parse(storedStudents)) { + students.push(new Student(o.id, o.name, o.group)); + } + } else { + students = await studentsPromise; + localStorage.setItem("students", JSON.stringify(students)); + } + } + + return students.filter((s) => (!idFilter || s.id === idFilter) && + (!nameFilter || s.name.includes(nameFilter)) && + (!groupFilter || s.group.includes(groupFilter))); +} + +async function fetchStudent(idFilter, nameFilter, groupFilter) { + const students = await fetchStudents(idFilter, nameFilter, groupFilter); + return students[0]; +} + +export { Student, fetchStudents, fetchStudent }; diff --git a/src/student-mock.js b/src/student-mock.js new file mode 100644 index 0000000..7c79e12 --- /dev/null +++ b/src/student-mock.js @@ -0,0 +1,9 @@ +import { Student } from "./student-common.js"; +import { fetchStudentsMock as fetchStudents } from "./mock.js"; + +async function fetchStudent(idFilter, nameFilter, groupFilter) { + const students = await fetchStudents(idFilter, nameFilter, groupFilter); + return students[0]; +} + +export { Student, fetchStudents, fetchStudent }; diff --git a/src/student-prod.js b/src/student-prod.js new file mode 100644 index 0000000..9c8d7cb --- /dev/null +++ b/src/student-prod.js @@ -0,0 +1,2 @@ +import { Student, fetchStudent, fetchStudents } from "./student-common.js"; +export { Student, fetchStudents, fetchStudent }; diff --git a/src/utils-common.ts b/src/utils-common.ts new file mode 100644 index 0000000..89a8ddd --- /dev/null +++ b/src/utils-common.ts @@ -0,0 +1,25 @@ +"use strict"; + +export const DATE_FMT = new Intl.DateTimeFormat("de-DE", { month: "numeric", day: "numeric", year: "numeric"}); + +export const DATETIME_FMT = new Intl.DateTimeFormat("de-DE", { month: "numeric", day: "numeric", year: "numeric", hour: "numeric", minute: "numeric" }); + +export function dateStringNow() { + return DATE_FMT.format(new Date()); +} + +export function datetimeStringNow() { + return DATETIME_FMT.format(new Date()); +} + +// TODO(muhq): Cache the retrieved URL +export async function getUserIconUrl(userId: number) { + const userRes = await fetch(`https://lernplattform.bycs.de/user/profile.php?id=${userId}`) + const userDoc = new DOMParser().parseFromString(await userRes.text(), 'text/html') + const userpicture = userDoc.querySelector(".userpicture"); + if (userpicture === null) { + return ""; + } + + return (userpicture as HTMLImageElement).src; +} diff --git a/src/utils-mock.js b/src/utils-mock.js new file mode 100644 index 0000000..e64b59d --- /dev/null +++ b/src/utils-mock.js @@ -0,0 +1,41 @@ +import { fetchStudent } from "./student-ENV.js"; +export { DATE_FMT, DATETIME_FMT, dateStringNow, datetimeStringNow } from "../dist/src/utils-common.js"; + +export function isTeacher() { + return !(new URLSearchParams(document.location.search)).has("student"); +} + +export function getFirstAndLastname() { + let firstname = ""; + let lastname = ""; + + const params = new URLSearchParams(document.location.search); + if (params.has("firstname")) + firstname = params.get("firstname") + if (params.has("lastname")) + lastname = params.get("lastname") + + return [firstname, lastname]; +} + +export function getName() { + let name = ""; + + const params = new URLSearchParams(document.location.search); + if (params.has("firstname")) + name = params.get("firstname") + if (params.has("lastname")) + name += params.get("lastname") + + return name; +} + +export function getUserId() { + const name = getName(); + + return fetchStudent(null, name).id; +} + +export function getCourseId() { + return "1337"; +} diff --git a/src/utils-prod.js b/src/utils-prod.js new file mode 100644 index 0000000..efcd8e3 --- /dev/null +++ b/src/utils-prod.js @@ -0,0 +1,36 @@ +export { DATE_FMT, DATETIME_FMT, dateStringNow, datetimeStringNow, getUserIconUrl } from "./utils-common.ts"; + +export function isTeacher() { + return Boolean(document.querySelector('.editmode-switch-form')) + || document.body.classList.contains('editing'); +} + +export function getFirstAndLastname() { + let firstname = ""; + let lastname = ""; + + const autoNameDiv = document.getElementById("auto-name"); + firstname = autoNameDiv.children[0].innerHTML; + lastname = autoNameDiv.children[1].innerHTML; + autoNameDiv.remove(); + + return [firstname, lastname]; +} + +export function getName() { + return document.querySelector("bycs-topbar").shadowRoot.querySelector("div.user-name").textContent; +} + +export function getUserId() { + // M is exported by Moodle + return String(M.cfg.userId); +} + +export function getCourseId() { + // M is exported by Moodle + return String(M.cfg.courseId); +} + +export function getCourseParticipantsUrl() { + return `https://lernplattform.bycs.de/user/index.php?id=${getCourseId()}&perpage=5000`; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c1335af --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "rootDir": "./", + "outDir": "./dist/", + "noImplicitAny": true, + "module": "esnext", + "moduleResolution": "bundler", + "target": "esnext", + "esModuleInterop": true, + "allowJs": true, + "strict": true + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules"] +} \ No newline at end of file diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..081a2a1 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,48 @@ +const webpack = require('webpack'); +const path = require('path'); + +const isProduction = process.env.NODE_ENV === 'production'; +const isMock = process.env.NODE_ENV === 'mock'; + +const config = { + entry: { + 'cockpit': './src/cockpit.js', + 'mass-graduation': './src/mass-graduation.js', + 'admin-tools': './src/admin-tools.js', + }, + output: { + path: path.resolve(__dirname, 'dist'), + }, + module: { + rules: [ + { + test: /\.png/, + type: 'asset/inline', + }, + ], + }, + plugins: [ + new webpack.NormalModuleReplacementPlugin( + /-ENV.js/, + (resource) => { + resource.request = resource.request.replace( + /-ENV/, + `-${isMock ? "mock" : "prod"}`, + ); + + if (resource.createData) { + resource.createData.request = resource.request; + } + } + ) + ], +}; + +module.exports = () => { + if (isProduction) { + config.mode = 'production'; + } else { + config.mode = 'development'; + } + return config; +}; -- cgit v1.2.3