// Minimal row editor for timetable groups. (function () { var form = document.getElementById('timetable-group-form'); var body = document.querySelector('[data-timetable-entries-body]'); var addButton = document.querySelector('[data-add-timetable-entry]'); var template = document.getElementById('timetable-entry-row-template'); if (!form || !body || !addButton || !template) { return; } function markDirty() { form.dataset.dirty = 'true'; } function pad(value) { return String(value).padStart(2, '0'); } function formatDateTimeLocalValue(date) { if (!(date instanceof Date) || Number.isNaN(date.getTime())) { return ''; } return [ String(date.getFullYear()).padStart(4, '0'), '-', pad(date.getMonth() + 1), '-', pad(date.getDate()), 'T', pad(date.getHours()), ':', pad(date.getMinutes()) ].join(''); } function parseUtcDateTimeLocalValue(value) { var raw = String(value || '').trim(); if (!raw) { return null; } var normalized = /(?:[zZ]|[+-]\d\d(?::?\d\d)?)$/.test(raw) ? raw : raw + 'Z'; var date = new Date(normalized); return Number.isNaN(date.getTime()) ? null : date; } function toUtcDateTimeLocalValue(value) { var localDate = new Date(String(value || '').trim()); return Number.isNaN(localDate.getTime()) ? '' : localDate.toISOString(); } function syncRowValuesToLocal(row) { if (!row) { return; } ['entry_start_datetime[]', 'entry_end_datetime[]'].forEach(function (name) { var input = row.querySelector('[name="' + name + '"]'); if (!input || input.dataset.timetableTimezoneSynced === 'true') { return; } var localValue = formatDateTimeLocalValue(parseUtcDateTimeLocalValue(input.value)); if (localValue) { input.value = localValue; } input.dataset.timetableTimezoneSynced = 'true'; }); } function syncFormDataToUtc(formData) { var rows = body.querySelectorAll('[data-timetable-entry-row]'); ['entry_id[]', 'entry_title[]', 'entry_short_description[]', 'entry_start_datetime[]', 'entry_end_datetime[]'].forEach(function (name) { formData.delete(name); }); rows.forEach(function (row) { var idInput = row.querySelector('[name="entry_id[]"]'); var titleInput = row.querySelector('[name="entry_title[]"]'); var descriptionInput = row.querySelector('[name="entry_short_description[]"]'); var startInput = row.querySelector('[name="entry_start_datetime[]"]'); var endInput = row.querySelector('[name="entry_end_datetime[]"]'); formData.append('entry_id[]', idInput ? idInput.value : ''); formData.append('entry_title[]', titleInput ? titleInput.value : ''); formData.append('entry_short_description[]', descriptionInput ? descriptionInput.value : ''); formData.append('entry_start_datetime[]', startInput ? toUtcDateTimeLocalValue(startInput.value) : ''); formData.append('entry_end_datetime[]', endInput ? toUtcDateTimeLocalValue(endInput.value) : ''); }); } function bindRemove(row) { var button = row.querySelector('[data-remove-timetable-entry]'); if (!button) { return; } button.addEventListener('click', function () { row.remove(); markDirty(); }); } function addRow() { var fragment = template.content.cloneNode(true); var row = fragment.querySelector('[data-timetable-entry-row]'); if (!row) { return; } bindRemove(row); body.appendChild(fragment); markDirty(); } body.querySelectorAll('[data-timetable-entry-row]').forEach(function (row) { bindRemove(row); syncRowValuesToLocal(row); }); form.addEventListener('formdata', function (event) { syncFormDataToUtc(event.formData); }); addButton.addEventListener('click', addRow); }());