refactor: split serve.py and index.html into single-responsibility modules
Backend:
- path_validator.py: PathValidator-Klasse für Pfad-Validierung
- file_ops.py: read_file, write_file, directory_exists, file_exists
- content_types.py: get_content_type mit EXTENSION_MAP
- handler.py: Handler-Klasse mit do_GET/do_PUT, nutzt above modules
- serve.py: Entry-Point (main, find_free_port), setzt Handler.validator/directory
Frontend:
- css/variables.css: CSS-Variablen (--bg-*, --text-*, --accent, etc.)
- css/styles.css: Alle CSS-Regeln (modal, card, template-grid, etc.)
- js/utils.js: esc, showToast, copyContentToClipboard
- js/modal.js: showModal, closeModal, closeEditModal, wasViewModalOpen
- js/editor.js: editModalContent, createJsonEditUI, extractJsonFromForm
- js/api.js: viewTemplate, copyContent, loadTemplates, saveEditedContent
- js/templates.js: renderTemplates, applyFilters, parseTypeFromHash
- js/main.js: Event-Listener, Hash-Filter, Initialisierung
- index.html: Inline-CSS/JS entfernt, <link>/<script src>-Tags hinzugefügt
Smoke test: SO_REUSEADDR für schnelle Port-Wiederverwendung
2026-05-03 14:40:44 +02:00
|
|
|
/**
|
|
|
|
|
* Modal-Management für View und Edit Modals.
|
|
|
|
|
*/
|
|
|
|
|
|
2026-05-03 20:09:36 +02:00
|
|
|
var _lastFocusedElement = null;
|
|
|
|
|
|
|
|
|
|
function _getFocusableElements(container) {
|
|
|
|
|
var selectors = 'button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
|
|
|
return Array.from(container.querySelectorAll(selectors)).filter(function(el) {
|
|
|
|
|
return !el.closest('.hidden');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _trapFocus(e, container) {
|
|
|
|
|
if (e.key !== 'Tab') return;
|
|
|
|
|
var focusable = _getFocusableElements(container);
|
|
|
|
|
if (focusable.length === 0) return;
|
|
|
|
|
var first = focusable[0];
|
|
|
|
|
var last = focusable[focusable.length - 1];
|
|
|
|
|
if (e.shiftKey) {
|
|
|
|
|
if (document.activeElement === first) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
last.focus();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (document.activeElement === last) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
first.focus();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function _setAriaHidden(id, hidden) {
|
|
|
|
|
var el = document.getElementById(id);
|
|
|
|
|
if (el) {
|
|
|
|
|
if (hidden) {
|
|
|
|
|
el.setAttribute('aria-hidden', 'true');
|
|
|
|
|
} else {
|
|
|
|
|
el.removeAttribute('aria-hidden');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
refactor: split serve.py and index.html into single-responsibility modules
Backend:
- path_validator.py: PathValidator-Klasse für Pfad-Validierung
- file_ops.py: read_file, write_file, directory_exists, file_exists
- content_types.py: get_content_type mit EXTENSION_MAP
- handler.py: Handler-Klasse mit do_GET/do_PUT, nutzt above modules
- serve.py: Entry-Point (main, find_free_port), setzt Handler.validator/directory
Frontend:
- css/variables.css: CSS-Variablen (--bg-*, --text-*, --accent, etc.)
- css/styles.css: Alle CSS-Regeln (modal, card, template-grid, etc.)
- js/utils.js: esc, showToast, copyContentToClipboard
- js/modal.js: showModal, closeModal, closeEditModal, wasViewModalOpen
- js/editor.js: editModalContent, createJsonEditUI, extractJsonFromForm
- js/api.js: viewTemplate, copyContent, loadTemplates, saveEditedContent
- js/templates.js: renderTemplates, applyFilters, parseTypeFromHash
- js/main.js: Event-Listener, Hash-Filter, Initialisierung
- index.html: Inline-CSS/JS entfernt, <link>/<script src>-Tags hinzugefügt
Smoke test: SO_REUSEADDR für schnelle Port-Wiederverwendung
2026-05-03 14:40:44 +02:00
|
|
|
/**
|
|
|
|
|
* Zeige View-Modal mit Titel und Inhalt
|
|
|
|
|
* @param {string} title - Titel des Modals
|
|
|
|
|
* @param {string} content - Inhalt als Text
|
|
|
|
|
*/
|
|
|
|
|
function showModal(title, content) {
|
2026-05-03 20:09:36 +02:00
|
|
|
_lastFocusedElement = document.activeElement;
|
refactor: split serve.py and index.html into single-responsibility modules
Backend:
- path_validator.py: PathValidator-Klasse für Pfad-Validierung
- file_ops.py: read_file, write_file, directory_exists, file_exists
- content_types.py: get_content_type mit EXTENSION_MAP
- handler.py: Handler-Klasse mit do_GET/do_PUT, nutzt above modules
- serve.py: Entry-Point (main, find_free_port), setzt Handler.validator/directory
Frontend:
- css/variables.css: CSS-Variablen (--bg-*, --text-*, --accent, etc.)
- css/styles.css: Alle CSS-Regeln (modal, card, template-grid, etc.)
- js/utils.js: esc, showToast, copyContentToClipboard
- js/modal.js: showModal, closeModal, closeEditModal, wasViewModalOpen
- js/editor.js: editModalContent, createJsonEditUI, extractJsonFromForm
- js/api.js: viewTemplate, copyContent, loadTemplates, saveEditedContent
- js/templates.js: renderTemplates, applyFilters, parseTypeFromHash
- js/main.js: Event-Listener, Hash-Filter, Initialisierung
- index.html: Inline-CSS/JS entfernt, <link>/<script src>-Tags hinzugefügt
Smoke test: SO_REUSEADDR für schnelle Port-Wiederverwendung
2026-05-03 14:40:44 +02:00
|
|
|
document.getElementById('modal-title').textContent = title;
|
|
|
|
|
document.getElementById('modal-content').textContent = content;
|
2026-05-03 20:09:36 +02:00
|
|
|
var modal = document.getElementById('modal');
|
|
|
|
|
modal.classList.add('active');
|
|
|
|
|
_setAriaHidden('modal', false);
|
|
|
|
|
var focusable = _getFocusableElements(modal);
|
|
|
|
|
if (focusable.length > 0) {
|
|
|
|
|
focusable[0].focus();
|
|
|
|
|
}
|
|
|
|
|
modal.addEventListener('keydown', function(e) { _trapFocus(e, modal); });
|
refactor: split serve.py and index.html into single-responsibility modules
Backend:
- path_validator.py: PathValidator-Klasse für Pfad-Validierung
- file_ops.py: read_file, write_file, directory_exists, file_exists
- content_types.py: get_content_type mit EXTENSION_MAP
- handler.py: Handler-Klasse mit do_GET/do_PUT, nutzt above modules
- serve.py: Entry-Point (main, find_free_port), setzt Handler.validator/directory
Frontend:
- css/variables.css: CSS-Variablen (--bg-*, --text-*, --accent, etc.)
- css/styles.css: Alle CSS-Regeln (modal, card, template-grid, etc.)
- js/utils.js: esc, showToast, copyContentToClipboard
- js/modal.js: showModal, closeModal, closeEditModal, wasViewModalOpen
- js/editor.js: editModalContent, createJsonEditUI, extractJsonFromForm
- js/api.js: viewTemplate, copyContent, loadTemplates, saveEditedContent
- js/templates.js: renderTemplates, applyFilters, parseTypeFromHash
- js/main.js: Event-Listener, Hash-Filter, Initialisierung
- index.html: Inline-CSS/JS entfernt, <link>/<script src>-Tags hinzugefügt
Smoke test: SO_REUSEADDR für schnelle Port-Wiederverwendung
2026-05-03 14:40:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Schließe View-Modal
|
|
|
|
|
*/
|
|
|
|
|
function closeModal() {
|
2026-05-03 20:09:36 +02:00
|
|
|
var modal = document.getElementById('modal');
|
|
|
|
|
modal.classList.remove('active');
|
|
|
|
|
_setAriaHidden('modal', true);
|
|
|
|
|
if (_lastFocusedElement && _lastFocusedElement.focus) {
|
|
|
|
|
_lastFocusedElement.focus();
|
|
|
|
|
}
|
refactor: split serve.py and index.html into single-responsibility modules
Backend:
- path_validator.py: PathValidator-Klasse für Pfad-Validierung
- file_ops.py: read_file, write_file, directory_exists, file_exists
- content_types.py: get_content_type mit EXTENSION_MAP
- handler.py: Handler-Klasse mit do_GET/do_PUT, nutzt above modules
- serve.py: Entry-Point (main, find_free_port), setzt Handler.validator/directory
Frontend:
- css/variables.css: CSS-Variablen (--bg-*, --text-*, --accent, etc.)
- css/styles.css: Alle CSS-Regeln (modal, card, template-grid, etc.)
- js/utils.js: esc, showToast, copyContentToClipboard
- js/modal.js: showModal, closeModal, closeEditModal, wasViewModalOpen
- js/editor.js: editModalContent, createJsonEditUI, extractJsonFromForm
- js/api.js: viewTemplate, copyContent, loadTemplates, saveEditedContent
- js/templates.js: renderTemplates, applyFilters, parseTypeFromHash
- js/main.js: Event-Listener, Hash-Filter, Initialisierung
- index.html: Inline-CSS/JS entfernt, <link>/<script src>-Tags hinzugefügt
Smoke test: SO_REUSEADDR für schnelle Port-Wiederverwendung
2026-05-03 14:40:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Schließe Edit-Modal
|
|
|
|
|
*/
|
|
|
|
|
function closeEditModal() {
|
2026-05-03 20:09:36 +02:00
|
|
|
_lastFocusedElement = document.activeElement;
|
|
|
|
|
var modal = document.getElementById('edit-modal');
|
|
|
|
|
modal.classList.remove('active');
|
|
|
|
|
_setAriaHidden('edit-modal', true);
|
|
|
|
|
if (_lastFocusedElement && _lastFocusedElement.focus) {
|
|
|
|
|
_lastFocusedElement.focus();
|
|
|
|
|
}
|
refactor: split serve.py and index.html into single-responsibility modules
Backend:
- path_validator.py: PathValidator-Klasse für Pfad-Validierung
- file_ops.py: read_file, write_file, directory_exists, file_exists
- content_types.py: get_content_type mit EXTENSION_MAP
- handler.py: Handler-Klasse mit do_GET/do_PUT, nutzt above modules
- serve.py: Entry-Point (main, find_free_port), setzt Handler.validator/directory
Frontend:
- css/variables.css: CSS-Variablen (--bg-*, --text-*, --accent, etc.)
- css/styles.css: Alle CSS-Regeln (modal, card, template-grid, etc.)
- js/utils.js: esc, showToast, copyContentToClipboard
- js/modal.js: showModal, closeModal, closeEditModal, wasViewModalOpen
- js/editor.js: editModalContent, createJsonEditUI, extractJsonFromForm
- js/api.js: viewTemplate, copyContent, loadTemplates, saveEditedContent
- js/templates.js: renderTemplates, applyFilters, parseTypeFromHash
- js/main.js: Event-Listener, Hash-Filter, Initialisierung
- index.html: Inline-CSS/JS entfernt, <link>/<script src>-Tags hinzugefügt
Smoke test: SO_REUSEADDR für schnelle Port-Wiederverwendung
2026-05-03 14:40:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Prüfe ob View-Modal offen war (für Copy-After-View-Logic)
|
|
|
|
|
* @returns {boolean}
|
|
|
|
|
*/
|
|
|
|
|
function wasViewModalOpen() {
|
|
|
|
|
return window._wasViewModalOpen || false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-03 15:06:03 +02:00
|
|
|
// exported functions are global (loaded as <script> tags)
|