Items 10-15: ES modules, inline style cleanup, template modal, code modernization

- Item 10: Convert to ES modules with import/export, single module entry point
- Item 11: Replace inline styles with CSS classes (background overlay, card
  animations, highlight effect, config modal form elements)
- Item 12: Move ConfigManager modal HTML from JS template literal to
  <template> element in index.html
- Item 13: Replace deprecated url.parse() with new URL() in server.js
  and update route handlers to use searchParams
- Item 14: Replace JSON.parse/stringify deep clone with structuredClone()
- Item 15: Remove dead JSON-fixing regex code from departures.js route

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 14:30:03 +01:00
parent 392a50b535
commit 1fdb3e48c7
12 changed files with 1883 additions and 1780 deletions

View File

@@ -424,7 +424,7 @@ class DeparturesManager {
this.updateExistingCards(departures);
}
this.currentDepartures = JSON.parse(JSON.stringify(departures));
this.currentDepartures = structuredClone(departures);
}
/**
@@ -444,8 +444,8 @@ class DeparturesManager {
this.updateCardContent(existingCard, departure);
} else {
const newCard = this.createDepartureCard(departure);
newCard.style.opacity = '0';
newCard.classList.add('card-entering');
if (index === 0) {
this.container.prepend(newCard);
} else if (index >= this.container.children.length) {
@@ -453,24 +453,22 @@ class DeparturesManager {
} else {
this.container.insertBefore(newCard, this.container.children[index]);
}
setTimeout(() => {
newCard.style.transition = 'opacity 0.5s ease-in';
newCard.style.opacity = '1';
}, 10);
requestAnimationFrame(() => {
newCard.classList.add('card-visible');
});
}
});
const newDepartureIds = newDepartures.map(d => d.journey.id.toString());
currentCards.forEach(card => {
if (!newDepartureIds.includes(card.dataset.journeyId)) {
card.style.transition = 'opacity 0.5s ease-out';
card.style.opacity = '0';
setTimeout(() => {
if (card.parentNode) {
card.parentNode.removeChild(card);
}
}, 500);
card.classList.add('card-leaving');
card.addEventListener('transitionend', () => {
card.remove();
}, { once: true });
// Fallback removal if transitionend doesn't fire
setTimeout(() => card.remove(), 600);
}
});
}
@@ -505,13 +503,10 @@ class DeparturesManager {
* @param {HTMLElement} element - Element to highlight
*/
highlightElement(element) {
element.style.transition = 'none';
element.style.backgroundColor = 'rgba(255, 255, 0, 0.3)';
setTimeout(() => {
element.style.transition = 'background-color 1.5s ease-out';
element.style.backgroundColor = 'transparent';
}, 10);
element.classList.remove('highlight-flash');
// Force reflow to restart animation
void element.offsetWidth;
element.classList.add('highlight-flash');
}
/**
@@ -632,5 +627,8 @@ class DeparturesManager {
}
}
// Export the class
// ES module export
export { DeparturesManager };
// Keep window reference for backward compatibility
window.DeparturesManager = DeparturesManager;