fOERbico/docs/Editor_4.html

1125 lines
28 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>AMB Metadaten Editor</title>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<style>
body {
font-family: sans-serif;
margin: 2rem;
max-width: 1100px;
}
h1, h2 {
border-bottom: 1px solid #ccc;
padding-bottom: 0.25rem;
}
fieldset {
border: 1px solid #ccc;
padding: 1rem;
margin-bottom: 1.2rem;
}
legend {
font-weight: bold;
}
label {
display: block;
margin-top: 0.35rem;
}
input[type="text"],
input[type="url"],
textarea,
select {
width: 100%;
padding: 0.4rem;
}
button {
padding: 0.6rem 1.2rem;
font-size: 1rem;
margin-right: 1rem;
}
pre {
background: #f5f5f5;
padding: 1rem;
white-space: pre-wrap;
}
.help {
font-size: 0.9rem;
color: #555;
}
/* -------- Keywords UI -------- */
.keyword-group-header {
display: flex;
align-items: center;
cursor: pointer;
margin-top: 0.5rem;
}
.keyword-toggle {
font-weight: bold;
font-size: 1.1rem;
margin-right: 0.5rem;
}
.kw-parent-label {
font-weight: bold;
font-size: 1.05rem;
}
.keyword-children {
margin-left: 1.5rem;
margin-top: 0.4rem;
display: none;
}
.keyword-group.open .keyword-children {
display: block;
}
/* -------- Learning Resource Type UI -------- */
.lrt-group-header {
display: flex;
align-items: center;
cursor: pointer;
margin-top: 0.5rem;
padding: 0.5rem;
background-color: #f0f0f0;
border-radius: 4px;
position: relative;
}
.lrt-toggle {
font-weight: bold;
font-size: 1.1rem;
margin-right: 0.5rem;
transition: transform 0.2s;
}
.lrt-parent-label {
font-weight: bold;
font-size: 1.05rem;
flex-grow: 1;
}
/* Pfeil anzeigen, wenn Unterpunkte vorhanden */
.lrt-group.has-children .lrt-toggle::after {
content: "▼";
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
transition: transform 0.2s;
}
/* Pfeil rotieren bei offenem Menü */
.lrt-group.open.has-children .lrt-toggle::after {
transform: translateY(-50%) rotate(180deg);
}
/* Kein Pfeil bei Menüs ohne Unterpunkte */
.lrt-group.no-children .lrt-toggle::after {
display: none;
}
/* Markierung für Menüs mit Unterpunkten */
.lrt-group.has-children .lrt-parent-label::after {
content: " ▼";
font-size: 0.8em;
opacity: 0.7;
}
/* Keine Markierung bei Menüs ohne Unterpunkte */
.lrt-group.no-children .lrt-parent-label::after {
display: none;
}
.lrt-children {
margin-left: 1.5rem;
margin-top: 0.4rem;
display: none;
}
.lrt-group.open .lrt-children {
display: block;
}
/* -------- Audience UI -------- */
.audience-item {
display: flex;
align-items: center;
margin-bottom: 0.5rem;
padding: 0.5rem;
background-color: #f8f9fa;
border-radius: 4px;
}
.audience-item input[type="checkbox"] {
margin-right: 0.5rem;
}
.audience-item label {
margin: 0;
flex-grow: 1;
}
/* -------- Relations UI -------- */
.relations-field {
margin-bottom: 1rem;
}
.relations-field textarea {
min-height: 100px;
}
/* -------- Competencies UI -------- */
.competency-group-header {
display: flex;
align-items: center;
cursor: pointer;
margin-top: 0.5rem;
padding: 0.5rem;
background-color: #e8f4f8;
border-radius: 4px;
position: relative;
}
.competency-toggle {
font-weight: bold;
font-size: 1.1rem;
margin-right: 0.5rem;
transition: transform 0.2s;
}
.competency-parent-label {
font-weight: bold;
font-size: 1.05rem;
flex-grow: 1;
}
.competency-children {
margin-left: 1.5rem;
margin-top: 0.4rem;
display: none;
}
.competency-group.open .competency-children {
display: block;
}
.competency-note {
background-color: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 4px;
padding: 0.5rem;
margin-top: 0.5rem;
font-size: 0.9rem;
color: #856404;
}
</style>
</head>
<body>
<h1>AMB Metadaten Editor</h1>
<div id="loading">Lade Konfiguration...</div>
<form onsubmit="return false;" style="display:none;">
<h2>Allgemein</h2>
<fieldset>
<legend>@context *</legend>
<label>
Sprache
<select id="context-language">
<option value="de" selected>Deutsch</option>
<option value="en">Englisch</option>
</select>
</label>
<label>
<input type="checkbox" id="context-schemaorg">
schema.org ergänzen
</label>
</fieldset>
<fieldset>
<legend>Ressourcen-ID *</legend>
<input type="url" id="amb-id">
</fieldset>
<fieldset>
<legend>Art der Bildungsressource *</legend>
<label><input type="checkbox" checked disabled> LearningResource</label>
</fieldset>
<fieldset>
<legend>Titel *</legend>
<input type="text" id="amb-name">
</fieldset>
<fieldset>
<legend>Beschreibung</legend>
<textarea id="amb-description" rows="4"></textarea>
</fieldset>
<h2>Datum</h2>
<fieldset>
<legend>Erstellungsdatum (dateCreated)</legend>
<input type="date" id="date-created">
</fieldset>
<fieldset>
<legend>Veröffentlichungsdatum (datePublished)</legend>
<input type="date" id="date-published">
</fieldset>
<fieldset>
<legend>Änderungsdatum (dateModified)</legend>
<input type="date" id="date-modified">
</fieldset>
<h2>Veröffentlicher</h2>
<fieldset>
<legend>Veröffentlicher (publisher)</legend>
<label>
Typ
<select id="publisher-type">
<option value="Organization">Organisation</option>
<option value="Person">Person</option>
</select>
</label>
<label>
Name *
<input type="text" id="publisher-name">
</label>
<label>
Identifier (ORCID, GND, ROR, Wikidata …)
<input type="url" id="publisher-id">
</label>
</fieldset>
<h2>Zielgruppe</h2>
<fieldset>
<legend>Zielgruppe (audience)</legend>
<p class="help">
Zielgruppe der Bildungsressource gemäß LRMI Educational Audience Roles.
</p>
<div id="audience-container"></div>
</fieldset>
<h2>Sprache der Bildungsressource</h2>
<fieldset>
<legend>inLanguage</legend>
<p class="help">
Sprache(n), in der die Bildungsressource vorliegt
(gemäß BCP47, z. B. <code>de</code>, <code>en</code>).
</p>
<label>
<input type="checkbox" class="in-language" value="de">
Deutsch (de)
</label>
<label>
<input type="checkbox" class="in-language" value="en">
Englisch (en)
</label>
<label>
<input type="checkbox" class="in-language" value="fr">
Französisch (fr)
</label>
<label>
<input type="checkbox" class="in-language" value="es">
Spanisch (es)
</label>
<label>
Weitere Sprachcodes (kommagetrennt)
<input type="text" id="inLanguage-free"
placeholder="z. B. it, tr, ar">
</label>
</fieldset>
<fieldset>
<legend>Vorschaubild</legend>
<label>
Thumbnail / Vorschaubild (image)
<input type="url" id="amb-image"
placeholder="https://example.org/thumbnail.jpg">
</label>
</fieldset>
<h2>Fach</h2>
<fieldset id="religion-container">
<legend>Religion</legend>
<!-- Wird dynamisch geladen -->
</fieldset>
<h2>Schlagworte</h2>
<div id="keywords-container"></div>
<h2>Entstehung</h2>
<fieldset>
<legend>Urheber:innen (creator)</legend>
<p class="help">
Personen oder Organisationen, die die Ressource erstellt haben.
</p>
<div id="creator-list"></div>
<button type="button" onclick="addCreator()">
+ Urheber:in hinzufügen
</button>
</fieldset>
<h2>Kompetenzen (teaches)</h2>
<fieldset>
<legend>Kompetenzen, die mit der Bildungsressource vermittelt werden</legend>
<p class="competency-note">
Dieser Editor arbeitet nur mit dem Auswahlfeld 'teaches' aus dem AMB.
Es wird also nicht zwischen Kompetenzen, die vermittelt werden,
und Kompetenzen, die überprüft werden, unterschieden.
</p>
<p class="help">
Wählen Sie die relevanten Kompetenzen aus:
</p>
<div id="competency-container"></div>
</fieldset>
<fieldset>
<legend>Mitwirkende (contributor)</legend>
<p class="help">
Sonstige Personen oder Organisationen, die an der Ressource mitgewirkt haben
(z. B. Redaktion, Illustration, technische Umsetzung).
</p>
<div id="contributor-list"></div>
<button type="button" onclick="addContributor()">
+ Mitwirkende:n hinzufügen
</button>
</fieldset>
<h2>Pädagogisch</h2>
<fieldset>
<legend>Lernressourcentyp (learningResourceType)</legend>
<p class="help">
Art des Lernmittels, eigene Tabelle aufbauend auf HCRT und OEHRT Taxonomie.
</p>
<div id="lrt-container"></div>
</fieldset>
<h2>Relationen</h2>
<fieldset>
<legend>isBasedOn</legend>
<p class="help">
Verweis auf eine andere Ressource, von der die beschriebene Bildungsressource ein Derivat ist.
</p>
<div class="relations-field">
<textarea id="is-based-on" placeholder="JSON-Objekt für isBasedOn"></textarea>
</div>
</fieldset>
<fieldset>
<legend>isPartOf</legend>
<p class="help">
Verweis auf eine übergeordnete Ressource, zu der die beschriebene Bildungsressource gehört.
</p>
<div class="relations-field">
<textarea id="is-part-of" placeholder="JSON-Objekt für isPartOf"></textarea>
</div>
</fieldset>
<fieldset>
<legend>hasPart</legend>
<p class="help">
Verweis auf eine untergeordnete Ressource, die Teil der beschriebenen Bildungsressource ist.
</p>
<div class="relations-field">
<textarea id="has-part" placeholder="JSON-Objekt für hasPart"></textarea>
</div>
</fieldset>
<button onclick="generate()">JSON erzeugen</button>
<button onclick="downloadJSON()">JSON herunterladen</button>
</form>
<h2>Erzeugtes JSON</h2>
<pre id="output"></pre>
<script>
/* ---------- Konfiguration ---------- */
const CONFIG_URL = 'Editor_4.md';
/* ---------- Globale Daten ---------- */
let keywordGroups = {};
let religionOptions = [];
let competencyGroups = {};
let learningResourceTypes = {};
let audienceOptions = [];
async function loadConfig() {
try {
const response = await fetch(CONFIG_URL);
if (!response.ok) throw new Error('Konnte Konfiguration nicht laden');
const markdown = await response.text();
parseMarkdown(markdown);
renderReligionOptions();
renderKeywords();
renderCompetencies();
renderLearningResourceTypes();
renderAudienceOptions();
document.getElementById('loading').style.display = 'none';
document.querySelector('form').style.display = 'block';
} catch (error) {
document.getElementById('loading').innerHTML =
`<p style="color:red">Fehler beim Laden: ${error.message}</p>
<p>Bitte stellen Sie sicher, dass die Datei "${CONFIG_URL}" erreichbar ist.</p>`;
}
}
function parseMarkdown(md) {
const lines = md.split('\n');
let currentSection = null;
let currentParent = null;
for (let line of lines) {
const trimmed = line.trim();
if (trimmed.startsWith('## Religion')) {
currentSection = 'religion';
continue;
}
if (trimmed.startsWith('## Schlagworte')) {
currentSection = 'keywords';
currentParent = null;
continue;
}
if (trimmed.startsWith('## Kompetenzen')) {
currentSection = 'competencies';
currentParent = null;
continue;
}
if (trimmed.startsWith('## Lernressourcentypen')) {
currentSection = 'learningresourcetypes';
currentParent = null;
continue;
}
if (trimmed.startsWith('## Audience')) {
currentSection = 'audience';
continue;
}
if (currentSection === 'religion' && trimmed.startsWith('- [')) {
const match = trimmed.match(/^- \[([^\]]+)\]\(([^\)]+)\): (.+)$/);
if (match) {
const [, label, id] = match;
religionOptions.push({ id, label: label.trim() });
}
}
if (currentSection === 'keywords') {
if (trimmed.startsWith('### ')) {
currentParent = trimmed.substring(4).trim();
keywordGroups[currentParent] = [];
} else if (trimmed.startsWith('- ') && currentParent) {
const keyword = trimmed.substring(2).trim();
if (keyword) keywordGroups[currentParent].push(keyword);
}
}
if (currentSection === 'competencies') {
if (trimmed.startsWith('### ')) {
currentParent = trimmed.substring(4).trim();
competencyGroups[currentParent] = [];
} else if (trimmed.startsWith('- ') && currentParent) {
const competency = trimmed.substring(2).trim();
if (competency) competencyGroups[currentParent].push(competency);
}
}
if (currentSection === 'learningresourcetypes') {
if (trimmed.startsWith('### ')) {
currentParent = trimmed.substring(4).trim();
learningResourceTypes[currentParent] = [];
} else if (trimmed.startsWith('- ') && currentParent) {
const lrt = trimmed.substring(2).trim();
if (lrt) learningResourceTypes[currentParent].push(lrt);
}
}
if (currentSection === 'audience' && trimmed.startsWith('- ')) {
const option = trimmed.substring(2).trim();
if (option) audienceOptions.push(option);
}
}
}
function renderReligionOptions() {
const container = document.getElementById('religion-container');
let html = `
<p class="help">
Das Fach <strong>Religion</strong> ist gesetzt.
Optional können Zielrichtungen des Religionsunterrichts ergänzt werden.
</p>
<label><input type="checkbox" checked disabled> Religion</label>
`;
religionOptions.forEach(option => {
html += `
<label><input type="checkbox" class="about-detail"
data-id="${option.id}"
data-label="${option.label}"> ${option.label}</label>
`;
});
container.innerHTML = html;
}
function renderKeywords() {
const kc = document.getElementById("keywords-container");
kc.innerHTML = '';
for (const [parent, children] of Object.entries(keywordGroups)) {
const group = document.createElement("div");
group.className = "keyword-group";
group.innerHTML = `
<div class="keyword-group-header">
<span class="keyword-toggle"></span>
<label class="kw-parent-label">
<input type="checkbox" class="kw-parent" value="${parent}">
${parent}
</label>
</div>
<div class="keyword-children"></div>
`;
const childContainer = group.querySelector(".keyword-children");
children.forEach(c => {
childContainer.innerHTML += `
<label>
<input type="checkbox" class="kw-child"
data-parent="${parent}" value="${c}">
${c}
</label>`;
});
childContainer.innerHTML += `
<label>
Freitext
<input type="text" class="kw-free"
data-parent="${parent}" placeholder="kommagetrennt">
</label>`;
kc.appendChild(group);
}
}
function renderCompetencies() {
const competencyContainer = document.getElementById("competency-container");
competencyContainer.innerHTML = '';
for (const [parent, children] of Object.entries(competencyGroups)) {
const group = document.createElement("div");
group.className = "competency-group";
group.innerHTML = `
<div class="competency-group-header">
<span class="competency-toggle"></span>
<label class="competency-parent-label">
<input type="checkbox" class="competency-parent" value="${parent}">
${parent}
</label>
</div>
<div class="competency-children"></div>
`;
const childContainer = group.querySelector(".competency-children");
children.forEach(c => {
childContainer.innerHTML += `
<label>
<input type="checkbox" class="competency-child"
data-parent="${parent}" value="${c}">
${c}
</label>`;
});
competencyContainer.appendChild(group);
}
}
function renderLearningResourceTypes() {
const lrtContainer = document.getElementById("lrt-container");
lrtContainer.innerHTML = '';
for (const [parent, children] of Object.entries(learningResourceTypes)) {
const group = document.createElement("div");
group.className = "lrt-group";
const hasChildren = children.length > 0;
if (hasChildren) {
group.classList.add("has-children");
} else {
group.classList.add("no-children");
}
group.innerHTML = `
<div class="lrt-group-header">
<span class="lrt-toggle"></span>
<label class="lrt-parent-label">
<input type="checkbox" class="lrt-parent" value="${parent}">
${parent}
</label>
</div>
<div class="lrt-children"></div>
`;
const childContainer = group.querySelector(".lrt-children");
children.forEach(c => {
childContainer.innerHTML += `
<label>
<input type="checkbox" class="lrt-child"
data-parent="${parent}" value="${c}">
${c}
</label>`;
});
lrtContainer.appendChild(group);
}
}
function renderAudienceOptions() {
const audienceContainer = document.getElementById("audience-container");
audienceContainer.innerHTML = '';
audienceOptions.forEach(option => {
const div = document.createElement("div");
div.className = "audience-item";
div.innerHTML = `
<label>
<input type="checkbox" class="audience-checkbox" value="${option}">
${option}
</label>
`;
audienceContainer.appendChild(div);
});
}
document.addEventListener("click", e => {
if (e.target.closest(".keyword-group-header")) {
const group = e.target.closest(".keyword-group");
group.classList.toggle("open");
group.querySelector(".keyword-toggle").textContent =
group.classList.contains("open") ? "▼" : "▶";
}
if (e.target.closest(".competency-group-header")) {
const group = e.target.closest(".competency-group");
group.classList.toggle("open");
group.querySelector(".competency-toggle").textContent =
group.classList.contains("open") ? "▼" : "▶";
}
if (e.target.closest(".lrt-group-header")) {
const group = e.target.closest(".lrt-group");
group.classList.toggle("open");
group.querySelector(".lrt-toggle").textContent =
group.classList.contains("open") ? "▼" : "▶";
}
});
document.addEventListener("change", e => {
if (e.target.classList.contains("kw-child") && e.target.checked) {
const parent = e.target.dataset.parent;
document.querySelector(`.kw-parent[value="${parent}"]`).checked = true;
}
if (e.target.classList.contains("competency-child") && e.target.checked) {
const parent = e.target.dataset.parent;
document.querySelector(`.competency-parent[value="${parent}"]`).checked = true;
}
if (e.target.classList.contains("lrt-child") && e.target.checked) {
const parent = e.target.dataset.parent;
document.querySelector(`.lrt-parent[value="${parent}"]`).checked = true;
}
});
function buildContext() {
const ctx = ["https://w3id.org/kim/amb/context.jsonld"];
if (document.getElementById('context-schemaorg').checked) ctx.push("https://schema.org");
ctx.push({ "@language": document.getElementById('context-language').value });
return ctx;
}
function buildAbout() {
const res = [{
id: "https://example.org/vocab/school-subject/religion",
type: "Concept",
prefLabel: { de: "Religion" }
}];
document.querySelectorAll(".about-detail:checked").forEach(cb => {
res.push({
id: cb.dataset.id,
type: "Concept",
prefLabel: { de: cb.dataset.label }
});
});
return res;
}
function buildKeywords() {
const set = new Set();
document.querySelectorAll(".kw-parent:checked").forEach(cb => set.add(cb.value));
document.querySelectorAll(".kw-child:checked").forEach(cb => set.add(cb.value));
document.querySelectorAll(".kw-free").forEach(input => {
input.value.split(",").map(v => v.trim()).filter(Boolean).forEach(v => {
set.add(v);
set.add(input.dataset.parent);
});
});
return set.size ? Array.from(set) : undefined;
}
function buildCreator() {
const entries = document.querySelectorAll('.creator-entry');
if (entries.length === 0) return undefined;
const creators = [];
entries.forEach(entry => {
const type = entry.querySelector('.creator-type').value;
const name = entry.querySelector('.creator-name').value;
const id = entry.querySelector('.creator-id').value;
const affiliationName = entry.querySelector('.creator-affiliation-name').value;
const affiliationId = entry.querySelector('.creator-affiliation-id').value;
if (name) {
const creator = { type, name };
if (id) creator.id = id;
if (affiliationName) {
creator.affiliation = { name };
if (affiliationId) creator.affiliation.id = affiliationId;
}
creators.push(creator);
}
});
return creators.length > 0 ? creators : undefined;
}
function buildContributor() {
const entries = document.querySelectorAll('.contributor-entry');
if (entries.length === 0) return undefined;
const contributors = [];
entries.forEach(entry => {
const type = entry.querySelector('.creator-type').value;
const name = entry.querySelector('.creator-name').value;
const id = entry.querySelector('.creator-id').value;
const affiliationName = entry.querySelector('.creator-affiliation-name').value;
const affiliationId = entry.querySelector('.creator-affiliation-id').value;
if (name) {
const contributor = { type, name };
if (id) contributor.id = id;
if (affiliationName) {
contributor.affiliation = { name };
if (affiliationId) contributor.affiliation.id = affiliationId;
}
contributors.push(contributor);
}
});
return contributors.length > 0 ? contributors : undefined;
}
function buildAudience() {
const checked = document.querySelectorAll(".audience-checkbox:checked");
if (checked.length === 0) return undefined;
return Array.from(checked).map(cb => ({ prefLabel: { de: cb.value } }));
}
function buildCompetencies() {
const set = new Set();
document.querySelectorAll(".competency-parent:checked").forEach(cb => set.add(cb.value));
document.querySelectorAll(".competency-child:checked").forEach(cb => set.add(cb.value));
if (set.size === 0) return undefined;
return Array.from(set).map(item => ({ prefLabel: { de: item } }));
}
function buildLearningResourceType() {
const set = new Set();
document.querySelectorAll(".lrt-parent:checked").forEach(cb => set.add(cb.value));
document.querySelectorAll(".lrt-child:checked").forEach(cb => set.add(cb.value));
if (set.size === 0) return undefined;
return Array.from(set).map(item => ({ prefLabel: { de: item } }));
}
function buildPublisher() {
const type = document.getElementById('publisher-type').value;
const name = document.getElementById('publisher-name').value;
const id = document.getElementById('publisher-id').value;
if (!name) return undefined;
const publisher = { type, name };
if (id) publisher.id = id;
return publisher;
}
function buildImage() {
const image = document.getElementById('amb-image').value.trim();
return image || undefined;
}
function buildDateCreated() {
const date = document.getElementById('date-created').value;
return date || undefined;
}
function buildDatePublished() {
const date = document.getElementById('date-published').value;
return date || undefined;
}
function buildDateModified() {
const date = document.getElementById('date-modified').value;
return date || undefined;
}
function buildInLanguage() {
const selected = [];
document.querySelectorAll(".in-language:checked").forEach(cb => {
selected.push(cb.value);
});
const freeInput = document.getElementById('inLanguage-free').value.trim();
if (freeInput) {
freeInput.split(",").forEach(lang => {
const trimmed = lang.trim();
if (trimmed && !selected.includes(trimmed)) {
selected.push(trimmed);
}
});
}
return selected.length > 0 ? selected : undefined;
}
function buildIsBasedOn() {
const value = document.getElementById('is-based-on').value.trim();
if (!value) return undefined;
try {
return JSON.parse(value);
} catch (e) {
return undefined;
}
}
function buildIsPartOf() {
const value = document.getElementById('is-part-of').value.trim();
if (!value) return undefined;
try {
return JSON.parse(value);
} catch (e) {
return undefined;
}
}
function buildHasPart() {
const value = document.getElementById('has-part').value.trim();
if (!value) return undefined;
try {
return JSON.parse(value);
} catch (e) {
return undefined;
}
}
function addCreator() {
const creatorList = document.getElementById('creator-list');
const entry = document.createElement('div');
entry.className = 'creator-entry';
entry.innerHTML = `
<fieldset>
<legend>Urheber:in</legend>
<label>
Typ
<select class="creator-type">
<option value="Person">Person</option>
<option value="Organization">Organisation</option>
</select>
</label>
<label>
Name *
<input type="text" class="creator-name">
</label>
<label>
Identifier (ORCID, GND, ROR, Wikidata …)
<input type="url" class="creator-id">
</label>
<label>
Affiliation (Name)
<input type="text" class="creator-affiliation-name">
</label>
<label>
Affiliation (ID)
<input type="url" class="creator-affiliation-id">
</label>
<button type="button" onclick="this.parentElement.parentElement.remove();">- Löschen</button>
</fieldset>
`;
creatorList.appendChild(entry);
}
function addContributor() {
const contributorList = document.getElementById('contributor-list');
const entry = document.createElement('div');
entry.className = 'contributor-entry';
entry.innerHTML = `
<fieldset>
<legend>Mitwirkende:r</legend>
<label>
Typ
<select class="creator-type">
<option value="Person">Person</option>
<option value="Organization">Organisation</option>
</select>
</label>
<label>
Name *
<input type="text" class="creator-name">
</label>
<label>
Identifier (ORCID, GND, ROR, Wikidata …)
<input type="url" class="creator-id">
</label>
<label>
Affiliation (Name)
<input type="text" class="creator-affiliation-name">
</label>
<label>
Affiliation (ID)
<input type="url" class="creator-affiliation-id">
</label>
<button type="button" onclick="this.parentElement.parentElement.remove();">- Löschen</button>
</fieldset>
`;
contributorList.appendChild(entry);
}
function generate() {
const ambId = document.getElementById('amb-id');
const ambName = document.getElementById('amb-name');
const ambDescription = document.getElementById('amb-description');
const output = document.getElementById('output');
if (!ambId.value || !ambName.value)
return alert("Pflichtfelder fehlen.");
const json = {
"@context": buildContext(),
id: ambId.value.trim(),
type: ["LearningResource"],
name: ambName.value.trim(),
about: buildAbout()
};
if (ambDescription.value.trim())
json.description = ambDescription.value.trim();
const dateCreated = buildDateCreated();
if (dateCreated) json.dateCreated = dateCreated;
const datePublished = buildDatePublished();
if (datePublished) json.datePublished = datePublished;
const dateModified = buildDateModified();
if (dateModified) json.dateModified = dateModified;
const publisher = buildPublisher();
if (publisher) json.publisher = publisher;
const audience = buildAudience();
if (audience) json.audience = audience;
const competencies = buildCompetencies();
if (competencies) json.teaches = competencies;
const lrt = buildLearningResourceType();
if (lrt) json.learningResourceType = lrt;
const isBasedOn = buildIsBasedOn();
if (isBasedOn) json.isBasedOn = isBasedOn;
const isPartOf = buildIsPartOf();
if (isPartOf) json.isPartOf = isPartOf;
const hasPart = buildHasPart();
if (hasPart) json.hasPart = hasPart;
const img = buildImage();
if (img) json.image = img;
const inLang = buildInLanguage();
if (inLang) json.inLanguage = inLang;
const kw = buildKeywords();
if (kw) json.keywords = kw;
const cr = buildCreator();
if (cr) json.creator = cr;
const co = buildContributor();
if (co) json.contributor = co;
output.textContent = JSON.stringify(json, null, 2);
}
function downloadJSON() {
const output = document.getElementById('output');
if (!output.textContent) return;
const blob = new Blob([output.textContent], { type: "application/json" });
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "amb-metadata.json";
a.click();
}
loadConfig();
</script>
</body>
</html>