feature: homework tool
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var COURSE_CHOICES = [
|
||||
{ edition: "N", label: "N-Kurs", detail: "N", color: "#47ABE8" },
|
||||
{ edition: "NE", label: "E-Kurs", detail: "N + E", color: "#FE756C" },
|
||||
{ edition: "NEA", label: "A-Kurs", detail: "N + E + A", color: "#3BB583" },
|
||||
{ edition: "E", label: "Aufstockung N → E", detail: "E", color: "#FE756C" },
|
||||
{ edition: "EA", label: "Aufstockung N → A", detail: "E + A", color: "#3BB583" },
|
||||
{ edition: "A", label: "Aufstockung E → A", detail: "A", color: "#3BB583" }
|
||||
];
|
||||
|
||||
function renderMathSoon() {
|
||||
Vue.nextTick(function () {
|
||||
if (typeof window.renderFiftyOhmMath !== "function") return;
|
||||
window.renderFiftyOhmMath(document.getElementById("homework-generator"));
|
||||
});
|
||||
}
|
||||
|
||||
function copyText(text) {
|
||||
if (navigator.clipboard && window.isSecureContext) return navigator.clipboard.writeText(text);
|
||||
var input = document.createElement("textarea");
|
||||
input.value = text;
|
||||
input.setAttribute("readonly", "");
|
||||
input.style.position = "fixed";
|
||||
input.style.opacity = "0";
|
||||
document.body.appendChild(input);
|
||||
input.select();
|
||||
var copied = document.execCommand("copy");
|
||||
input.remove();
|
||||
return copied ? Promise.resolve() : Promise.reject(new Error("Kopieren nicht möglich"));
|
||||
}
|
||||
|
||||
var app = Vue.createApp({
|
||||
setup: function () {
|
||||
var ref = Vue.ref;
|
||||
var computed = Vue.computed;
|
||||
var reactive = Vue.reactive;
|
||||
|
||||
var loading = ref(true);
|
||||
var loadError = ref("");
|
||||
var step = ref(1);
|
||||
var catalog = ref({});
|
||||
var questionIndex = ref({});
|
||||
var questionsBySection = ref({});
|
||||
var selectedCourse = ref(null);
|
||||
var toc = ref(null);
|
||||
var selectedChapterIds = ref([]);
|
||||
var selectedSectionIds = ref([]);
|
||||
var questionSelection = reactive({});
|
||||
var generatedUrl = ref("");
|
||||
var copyMessage = ref("");
|
||||
var tocCache = {};
|
||||
|
||||
var stepLabels = [
|
||||
{ number: 1, label: "Kurs" },
|
||||
{ number: 2, label: "Kapitel" },
|
||||
{ number: 3, label: "Sektionen" },
|
||||
{ number: 4, label: "Fragen" },
|
||||
{ number: 5, label: "Link" }
|
||||
];
|
||||
|
||||
function sectionQuestionIds(section) {
|
||||
if (!selectedCourse.value) return [];
|
||||
return questionsBySection.value[selectedCourse.value.edition + ":" + section.ident] || [];
|
||||
}
|
||||
|
||||
function chapterQuestionCount(chapter) {
|
||||
return chapter.sections.reduce(function (sum, section) {
|
||||
return sum + sectionQuestionIds(section).length;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
var selectedChapters = computed(function () {
|
||||
if (!toc.value) return [];
|
||||
return toc.value.chapters.filter(function (chapter) {
|
||||
return selectedChapterIds.value.indexOf(chapter.ident) !== -1;
|
||||
});
|
||||
});
|
||||
|
||||
var selectedSectionGroups = computed(function () {
|
||||
var groups = [];
|
||||
selectedChapters.value.forEach(function (chapter) {
|
||||
chapter.sections.forEach(function (section) {
|
||||
if (selectedSectionIds.value.indexOf(section.ident) === -1) return;
|
||||
groups.push({ chapter: chapter, section: section, questionIds: sectionQuestionIds(section) });
|
||||
});
|
||||
});
|
||||
return groups;
|
||||
});
|
||||
|
||||
var orderedQuestions = computed(function () {
|
||||
return selectedSectionGroups.value.reduce(function (result, group) {
|
||||
return result.concat(group.questionIds);
|
||||
}, []);
|
||||
});
|
||||
|
||||
var selectedQuestionIds = computed(function () {
|
||||
return orderedQuestions.value.filter(function (questionId) { return questionSelection[questionId]; });
|
||||
});
|
||||
|
||||
function buildSectionIndex() {
|
||||
var result = {};
|
||||
Object.keys(questionIndex.value).forEach(function (questionId) {
|
||||
var entry = questionIndex.value[questionId];
|
||||
(entry.editions || []).forEach(function (edition) {
|
||||
var key = edition + ":" + entry.section;
|
||||
if (!result[key]) result[key] = [];
|
||||
result[key].push(questionId);
|
||||
});
|
||||
});
|
||||
Object.keys(result).forEach(function (key) {
|
||||
result[key].sort(function (left, right) {
|
||||
return left.localeCompare(right, "de", { numeric: true });
|
||||
});
|
||||
});
|
||||
questionsBySection.value = result;
|
||||
}
|
||||
|
||||
function goToStep(number) {
|
||||
step.value = number;
|
||||
copyMessage.value = "";
|
||||
if (number === 4) renderMathSoon();
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
}
|
||||
|
||||
function selectCourse(course) {
|
||||
selectedCourse.value = course;
|
||||
selectedChapterIds.value = [];
|
||||
selectedSectionIds.value = [];
|
||||
generatedUrl.value = "";
|
||||
if (tocCache[course.edition]) {
|
||||
toc.value = tocCache[course.edition];
|
||||
goToStep(2);
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
fetch("assets/toc/" + course.edition + ".json")
|
||||
.then(function (response) {
|
||||
if (!response.ok) throw new Error("Inhaltsverzeichnis nicht gefunden (HTTP " + response.status + ").");
|
||||
return response.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
tocCache[course.edition] = data;
|
||||
toc.value = data;
|
||||
loading.value = false;
|
||||
goToStep(2);
|
||||
})
|
||||
.catch(function (error) {
|
||||
loadError.value = error.message || "Unbekannter Ladefehler";
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function openSectionSelection() {
|
||||
var preselectedSections = [];
|
||||
selectedChapters.value.forEach(function (chapter) {
|
||||
chapter.sections.forEach(function (section) {
|
||||
if (sectionQuestionIds(section).length > 0) preselectedSections.push(section.ident);
|
||||
});
|
||||
});
|
||||
selectedSectionIds.value = preselectedSections;
|
||||
goToStep(3);
|
||||
}
|
||||
|
||||
function openQuestionSelection() {
|
||||
orderedQuestions.value.forEach(function (questionId) { questionSelection[questionId] = true; });
|
||||
goToStep(4);
|
||||
}
|
||||
|
||||
function setAllQuestions(selected) {
|
||||
orderedQuestions.value.forEach(function (questionId) { questionSelection[questionId] = selected; });
|
||||
}
|
||||
|
||||
function generateLink() {
|
||||
var url = new URL("simulation.html", window.location.href);
|
||||
url.search = "?homework=" + selectedQuestionIds.value.join("+");
|
||||
generatedUrl.value = url.toString();
|
||||
goToStep(5);
|
||||
}
|
||||
|
||||
function copyLink() {
|
||||
copyText(generatedUrl.value)
|
||||
.then(function () { copyMessage.value = "Der Hausaufgabenlink wurde kopiert."; })
|
||||
.catch(function () { copyMessage.value = "Der Link konnte nicht automatisch kopiert werden. Bitte kopiere ihn aus dem Feld."; });
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
fetch("assets/fragenkatalog.json"),
|
||||
fetch("assets/metadata.json"),
|
||||
fetch("assets/question_index.json")
|
||||
])
|
||||
.then(function (responses) {
|
||||
var names = ["Fragenkatalog", "Metadaten", "Fragenindex"];
|
||||
responses.forEach(function (response, index) {
|
||||
if (!response.ok) throw new Error(names[index] + " nicht gefunden (HTTP " + response.status + ").");
|
||||
});
|
||||
return Promise.all(responses.map(function (response) { return response.json(); }));
|
||||
})
|
||||
.then(function (data) {
|
||||
questionIndex.value = data[2] || {};
|
||||
catalog.value = window.FiftyOhmQuestions.normalizeCatalog(data[0], data[1] || {}, questionIndex.value);
|
||||
buildSectionIndex();
|
||||
loading.value = false;
|
||||
})
|
||||
.catch(function (error) {
|
||||
loadError.value = error.message || "Unbekannter Ladefehler";
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
return {
|
||||
loading: loading,
|
||||
loadError: loadError,
|
||||
step: step,
|
||||
stepLabels: stepLabels,
|
||||
courseChoices: COURSE_CHOICES,
|
||||
catalog: catalog,
|
||||
selectedCourse: selectedCourse,
|
||||
toc: toc,
|
||||
selectedChapterIds: selectedChapterIds,
|
||||
selectedSectionIds: selectedSectionIds,
|
||||
questionSelection: questionSelection,
|
||||
selectedChapters: selectedChapters,
|
||||
selectedSectionGroups: selectedSectionGroups,
|
||||
orderedQuestions: orderedQuestions,
|
||||
selectedQuestionIds: selectedQuestionIds,
|
||||
generatedUrl: generatedUrl,
|
||||
copyMessage: copyMessage,
|
||||
sectionQuestionIds: sectionQuestionIds,
|
||||
chapterQuestionCount: chapterQuestionCount,
|
||||
goToStep: goToStep,
|
||||
selectCourse: selectCourse,
|
||||
openSectionSelection: openSectionSelection,
|
||||
openQuestionSelection: openQuestionSelection,
|
||||
setAllQuestions: setAllQuestions,
|
||||
generateLink: generateLink,
|
||||
copyLink: copyLink
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
app.mount("#homework-generator");
|
||||
})();
|
||||
Reference in New Issue
Block a user