Skip to content

Commit 9421acc

Browse files
added presets yaml
1 parent 05b3805 commit 9421acc

6 files changed

Lines changed: 318 additions & 65 deletions

File tree

dist/index.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@
3131
"asset:xapi": "cd LiaScript && npm i && npm run build:xapi && cp -r dist ../dist/assets/xapi",
3232
"asset:capacitor": "cd LiaScript && git checkout feat/capacitor && rm -rf node_modules && npm i && npm run build:android && cp -r dist ../dist/assets/capacitor && git checkout development && rm -rf node_modules && npm i",
3333
"asset:deduplicate": "./scripts/deduplicate-assets.sh",
34-
"build": "npx parcel build --no-cache --no-source-maps src/index.ts && npm run shebang && npm run copy:public",
34+
"build": "npx parcel build --no-cache --no-source-maps src/index.ts && npm run shebang && npm run copy:public && npm run copy:presets",
3535
"build:debug": "npx parcel build --target node --no-minify --log-level 5 src/index.ts",
3636
"copy:public": "mkdir -p dist/server && cp -r src/server/public dist/server/",
37+
"copy:presets": "mkdir -p dist/server && cp src/server/presets.yaml dist/server/",
3738
"run:moodle": "cd docker/moodle && docker-compose up",
3839
"run:ilias": "cd docker/ilias && docker-compose up",
3940
"fix:file": "find dist/ -type f -name \"*.js\" -exec sed -i -r 's/\\/\\(https\\?\\|file\\|ftp\\|\\(chrome\\|moz\\|safari-web\\)-extension\\):\\\\\\/\\\\\\/\\[/\\/(https?|file|ftp|(chrome|moz|safari-web)-extension):\\\\\\/\\\\\\/\\\\\\/?[/g' {} +",

src/server/presets.yaml

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
presets:
2+
- id: moodle
3+
name: Moodle
4+
logo: 🎓
5+
format: scorm2004
6+
subtitle: SCORM 2004
7+
description: >
8+
Moodle ist das weltweit am häufigsten verwendete Open-Source-Lernmanagementsystem.
9+
Diese Konfiguration verwendet SCORM 2004 für maximale Kompatibilität mit Moodle 3.x und 4.x.
10+
<a href='https://moodle.org' target='_blank'>Mehr erfahren</a>
11+
options:
12+
# SCORM-spezifische Einstellungen
13+
responsiveVoice: false
14+
translateWithGoogle: false
15+
debugging: false
16+
removeBase: false
17+
scormOrganization: ""
18+
typicalDuration: "PT0H5M0S"
19+
scormIframe: false
20+
scormEmbed: true # Empfohlen für Moodle 4
21+
format: scorm2004
22+
23+
- id: ilias
24+
name: ILIAS
25+
logo: 📚
26+
format: scorm12
27+
subtitle: SCORM 1.2
28+
description: >
29+
ILIAS ist ein leistungsstarkes Open-Source-LMS aus Deutschland.
30+
Diese Konfiguration nutzt SCORM 1.2 für bestmögliche Kompatibilität mit ILIAS-Versionen.
31+
<a href='https://www.ilias.de' target='_blank'>Mehr erfahren</a>
32+
options:
33+
responsiveVoice: false
34+
translateWithGoogle: false
35+
debugging: false
36+
removeBase: false
37+
scormOrganization: ""
38+
typicalDuration: "PT0H5M0S"
39+
scormIframe: true # Oft besser für ILIAS
40+
scormEmbed: false
41+
format: scorm12
42+
43+
- id: opal
44+
name: OPAL
45+
logo: 🏛️
46+
format: scorm2004
47+
subtitle: SCORM 2004
48+
description: >
49+
OPAL (Online-Plattform für Akademisches Lehren und Lernen) ist das zentrale LMS
50+
für sächsische Hochschulen. Optimiert für SCORM 2004.
51+
<a href='https://bildungsportal.sachsen.de/opal' target='_blank'>Mehr erfahren</a>
52+
options:
53+
responsiveVoice: false
54+
translateWithGoogle: false
55+
debugging: false
56+
removeBase: false
57+
scormOrganization: ""
58+
typicalDuration: "PT0H5M0S"
59+
scormIframe: false
60+
scormEmbed: false
61+
format: scorm2004
62+
63+
- id: generic
64+
name: Generic LMS
65+
logo: 🌐
66+
format: scorm2004
67+
subtitle: SCORM 2004
68+
description: >
69+
Universelle SCORM 2004 Konfiguration für beliebige Lernmanagementsysteme,
70+
die den SCORM 2004 Standard unterstützen. Funktioniert mit den meisten modernen LMS-Plattformen.
71+
options:
72+
responsiveVoice: false
73+
translateWithGoogle: false
74+
debugging: false
75+
removeBase: false
76+
scormOrganization: ""
77+
typicalDuration: "PT0H5M0S"
78+
scormIframe: false
79+
scormEmbed: false
80+
format: scorm2004
81+
82+
- id: openolat
83+
name: OpenOlat
84+
logo: 🔓
85+
format: scorm2004
86+
subtitle: SCORM 2004
87+
description: >
88+
OpenOlat ist eine Open-Source E-Learning-Plattform aus der Schweiz.
89+
Diese Konfiguration nutzt SCORM 2004 für volle Funktionalität.
90+
<a href='https://www.openolat.com' target='_blank'>Mehr erfahren</a>
91+
options:
92+
responsiveVoice: false
93+
translateWithGoogle: false
94+
debugging: false
95+
removeBase: false
96+
scormOrganization: ""
97+
typicalDuration: "PT0H5M0S"
98+
scormIframe: false
99+
scormEmbed: false
100+
format: scorm2004
101+
102+
- id: openedx
103+
name: Open edX
104+
logo: 🎯
105+
format: scorm2004
106+
subtitle: SCORM 2004
107+
description: >
108+
Open edX ist die Open-Source-Plattform hinter edX.org und wird weltweit für MOOCs eingesetzt.
109+
Verwendet SCORM 2004 über das SCORM XBlock.
110+
<a href='https://openedx.org' target='_blank'>Mehr erfahren</a>
111+
options:
112+
responsiveVoice: false
113+
translateWithGoogle: false
114+
debugging: false
115+
removeBase: false
116+
scormOrganization: ""
117+
typicalDuration: "PT0H5M0S"
118+
scormIframe: true # Oft erforderlich für Open edX
119+
scormEmbed: false
120+
format: scorm2004

src/server/public/app.js

Lines changed: 143 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,71 @@
22
let selectedFiles = []
33
let currentSourceType = 'upload'
44
let currentExportTab = 'presets'
5+
let presetsConfig = null
56

67
// Initialize app
7-
document.addEventListener('DOMContentLoaded', () => {
8+
document.addEventListener('DOMContentLoaded', async () => {
9+
await loadPresets()
810
initializeTabs()
911
initializeExportTabs()
1012
initializeUpload()
1113
initializeAdvancedSettings()
1214
initializeForm()
1315
initializeExportSelection()
1416
initializeFormatDescription()
17+
initializePresetDescription()
1518
})
1619

20+
// Load presets from server
21+
async function loadPresets() {
22+
try {
23+
const response = await fetch('/api/presets')
24+
const data = await response.json()
25+
presetsConfig = data.presets
26+
27+
const presetsGrid = document.getElementById('presets-grid')
28+
presetsGrid.innerHTML = ''
29+
30+
presetsConfig.forEach((preset, index) => {
31+
const label = document.createElement('label')
32+
label.className = 'preset-tile'
33+
34+
const input = document.createElement('input')
35+
input.type = 'radio'
36+
input.name = 'preset'
37+
input.value = preset.id
38+
input.dataset.description = preset.description
39+
input.dataset.presetOptions = JSON.stringify(preset.options)
40+
if (index === 0) input.checked = true
41+
42+
const content = document.createElement('div')
43+
content.className = 'preset-content'
44+
45+
const logo = document.createElement('div')
46+
logo.style.fontSize = '2rem'
47+
logo.style.marginBottom = '0.5rem'
48+
logo.textContent = preset.logo
49+
50+
const title = document.createElement('h3')
51+
title.textContent = preset.name
52+
53+
const subtitle = document.createElement('p')
54+
subtitle.textContent = preset.subtitle
55+
56+
content.appendChild(logo)
57+
content.appendChild(title)
58+
content.appendChild(subtitle)
59+
60+
label.appendChild(input)
61+
label.appendChild(content)
62+
63+
presetsGrid.appendChild(label)
64+
})
65+
} catch (error) {
66+
console.error('Failed to load presets:', error)
67+
}
68+
}
69+
1770
// Tab switching
1871
function initializeTabs() {
1972
const tabs = document.querySelectorAll('.tab-button')
@@ -168,25 +221,33 @@ function escapeHtml(text) {
168221

169222
// Export selection handling
170223
function initializeExportSelection() {
171-
const presetRadios = document.querySelectorAll('input[name="preset"]')
172-
const formatRadios = document.querySelectorAll('input[name="format"]')
224+
// Use event delegation for dynamically loaded presets
225+
document.getElementById('presets-grid').addEventListener('change', (e) => {
226+
if (e.target.name === 'preset' && e.target.checked) {
227+
// Deselect all formats
228+
const formatRadios = document.querySelectorAll('input[name="format"]')
229+
formatRadios.forEach((radio) => {
230+
radio.checked = false
231+
})
173232

174-
// When preset is selected, deselect formats and update settings
175-
presetRadios.forEach((radio) => {
176-
radio.addEventListener('change', () => {
177-
if (radio.checked) {
178-
formatRadios.forEach((formatRadio) => {
179-
formatRadio.checked = false
180-
})
181-
updateAdvancedSettings(radio.value)
182-
}
183-
})
233+
// Apply preset options to the form
234+
applyPresetOptions(e.target)
235+
236+
// Get the preset configuration to determine the format
237+
const presetOptions = JSON.parse(e.target.dataset.presetOptions || '{}')
238+
const format = presetOptions.format || e.target.value
239+
240+
updateAdvancedSettings(format)
241+
}
184242
})
185243

244+
const formatRadios = document.querySelectorAll('input[name="format"]')
245+
186246
// When format is selected, deselect presets and update settings
187247
formatRadios.forEach((radio) => {
188248
radio.addEventListener('change', () => {
189249
if (radio.checked) {
250+
const presetRadios = document.querySelectorAll('input[name="preset"]')
190251
presetRadios.forEach((presetRadio) => {
191252
presetRadio.checked = false
192253
})
@@ -195,13 +256,51 @@ function initializeExportSelection() {
195256
})
196257
})
197258

198-
// Initialize with default selection (Moodle)
199-
updateAdvancedSettings('moodle')
259+
// Initialize with default selection
260+
setTimeout(() => {
261+
const checkedPreset = document.querySelector('input[name="preset"]:checked')
262+
if (checkedPreset) {
263+
const presetOptions = JSON.parse(
264+
checkedPreset.dataset.presetOptions || '{}'
265+
)
266+
const format = presetOptions.format || checkedPreset.value
267+
applyPresetOptions(checkedPreset)
268+
updateAdvancedSettings(format)
269+
}
270+
}, 100)
200271

201272
// PDF-specific: Toggle header/footer template fields
202273
initializePdfHeaderFooter()
203274
}
204275

276+
// Apply preset options to form fields
277+
function applyPresetOptions(presetInput) {
278+
try {
279+
const options = JSON.parse(presetInput.dataset.presetOptions || '{}')
280+
281+
// Apply each option to the corresponding form field
282+
Object.keys(options).forEach((key) => {
283+
const value = options[key]
284+
const fieldName = `option_${key}`
285+
286+
// Try to find the field by name
287+
const field = document.querySelector(`[name="${fieldName}"]`)
288+
289+
if (field) {
290+
if (field.type === 'checkbox') {
291+
field.checked = Boolean(value)
292+
} else if (field.type === 'number') {
293+
field.value = value
294+
} else {
295+
field.value = value || ''
296+
}
297+
}
298+
})
299+
} catch (error) {
300+
console.error('Failed to apply preset options:', error)
301+
}
302+
}
303+
205304
// Initialize PDF header/footer toggle
206305
function initializePdfHeaderFooter() {
207306
const displayHeaderFooter = document.getElementById('pdfDisplayHeaderFooter')
@@ -412,3 +511,32 @@ function initializeFormatDescription() {
412511
})
413512
})
414513
}
514+
// Preset description display
515+
function initializePresetDescription() {
516+
// Use event delegation since presets are loaded dynamically
517+
document.getElementById('presets-grid').addEventListener('change', (e) => {
518+
if (e.target.name === 'preset') {
519+
const descriptionBox = document.getElementById('preset-description')
520+
const descriptionText = descriptionBox.querySelector('p')
521+
const description = e.target.dataset.description
522+
523+
if (description) {
524+
descriptionText.innerHTML = description
525+
descriptionBox.style.display = 'block'
526+
} else {
527+
descriptionBox.style.display = 'none'
528+
}
529+
}
530+
})
531+
532+
// Show description for initially checked preset
533+
setTimeout(() => {
534+
const checkedPreset = document.querySelector('input[name="preset"]:checked')
535+
if (checkedPreset && checkedPreset.dataset.description) {
536+
const descriptionBox = document.getElementById('preset-description')
537+
const descriptionText = descriptionBox.querySelector('p')
538+
descriptionText.innerHTML = checkedPreset.dataset.description
539+
descriptionBox.style.display = 'block'
540+
}
541+
}, 100)
542+
}

0 commit comments

Comments
 (0)