const application_state = { websocket_connection: null, pending_administrator_form: null, stopwatch_interval_identifier: null }; /* --- Deterministische Funktionen ("ehrliche" Funktionen) --- */ function calculate_time_difference(start_time_milliseconds, current_time_milliseconds) { return current_time_milliseconds - start_time_milliseconds; } function format_stopwatch_string(difference_in_milliseconds) { if (difference_in_milliseconds < 0) { return '00:00 Minuten'; } const total_seconds = Math.floor(difference_in_milliseconds / 1000); const days_passed = Math.floor(total_seconds / 86400); const hours_passed = Math.floor((total_seconds % 86400) / 3600); const minutes_passed = Math.floor((total_seconds % 3600) / 60); const seconds_passed = total_seconds % 60; let output_string = ''; if (days_passed === 1) { output_string += '1 Tag und '; } else if (days_passed > 1) { output_string += days_passed + ' Tage und '; } const padded_minutes = String(minutes_passed).padStart(2, '0'); const padded_seconds = String(seconds_passed).padStart(2, '0'); if (hours_passed === 1) { output_string += '1:' + padded_minutes + ':' + padded_seconds + ' Stunde'; } else if (hours_passed > 1) { output_string += hours_passed + ':' + padded_minutes + ':' + padded_seconds + ' Stunden'; } else { output_string += padded_minutes + ':' + padded_seconds + ' Minuten'; } return output_string; } function synchronize_element_nodes(current_node, updated_node) { if (current_node.nodeType !== updated_node.nodeType) { current_node.replaceWith(updated_node.cloneNode(true)); return; } if (current_node.nodeType === Node.TEXT_NODE) { if (current_node.nodeValue !== updated_node.nodeValue) { current_node.nodeValue = updated_node.nodeValue; } return; } if (current_node.nodeType !== Node.ELEMENT_NODE) { return; } if (current_node.tagName !== updated_node.tagName) { current_node.replaceWith(updated_node.cloneNode(true)); return; } if (current_node.namespaceURI !== updated_node.namespaceURI) { current_node.replaceWith(updated_node.cloneNode(true)); return; } const is_active_input = document.activeElement === current_node; const is_input_element = ['INPUT', 'TEXTAREA', 'SELECT'].includes(current_node.tagName); if (is_input_element && !is_active_input) { if (current_node.value !== updated_node.value) { current_node.value = updated_node.value; } } const current_attributes = current_node.attributes; const updated_attributes = updated_node.attributes; for (let index = 0; index < updated_attributes.length; index++) { const attribute = updated_attributes[index]; if (current_node.getAttribute(attribute.name) !== attribute.value) { if (!(is_input_element && is_active_input && attribute.name === 'value')) { current_node.setAttribute(attribute.name, attribute.value); } } } for (let index = current_attributes.length - 1; index >= 0; index--) { const attribute = current_attributes[index]; if (!updated_node.hasAttribute(attribute.name)) { current_node.removeAttribute(attribute.name); } } const current_children = Array.from(current_node.childNodes); const updated_children = Array.from(updated_node.childNodes); const max_length = Math.max(current_children.length, updated_children.length); for (let index = 0; index < max_length; index++) { const current_child = current_children[index]; const updated_child = updated_children[index]; if (!current_child && updated_child) { current_node.appendChild(updated_child.cloneNode(true)); } else if (current_child && !updated_child) { current_node.removeChild(current_child); } else if (current_child && updated_child) { synchronize_element_nodes(current_child, updated_child); } } } /* --- Architektur & State-Management ("unehrliche" Funktionen) --- */ function trigger_dom_update(markup_text) { const document_parser = new DOMParser(); const parsed_document = document_parser.parseFromString(markup_text, 'text/html'); const current_container = document.querySelector('.application-content'); const new_container = parsed_document.querySelector('.application-content'); if (!current_container || !new_container) { return; } const current_active_tab = document.querySelector('.active-tab'); const active_tab_target = current_active_tab ? current_active_tab.getAttribute('data-target') : null; const active_modals = []; document.querySelectorAll('.active-modal').forEach(function(modal) { active_modals.push(modal.id); }); synchronize_element_nodes(current_container, new_container); if (active_tab_target) { document.querySelectorAll('.tab-button').forEach(function(button) { button.classList.remove('active-tab'); }); document.querySelectorAll('.tab-content').forEach(function(content) { content.classList.remove('active-content'); }); const restored_tab = document.querySelector('.tab-button[data-target="' + active_tab_target + '"]'); const restored_content = document.getElementById(active_tab_target); if (restored_tab && restored_content) { restored_tab.classList.add('active-tab'); restored_content.classList.add('active-content'); } } active_modals.forEach(function(modal_identifier) { const restored_modal = document.getElementById(modal_identifier); if (restored_modal) { restored_modal.classList.add('active-modal'); } }); if (typeof lucide !== 'undefined') { lucide.createIcons(); } } function fetch_and_synchronize_dom() { fetch(window.location.href) .then(function(network_response) { if (!network_response.ok) { throw new Error('Network response failure'); } return network_response.text(); }) .then(function(markup_text) { trigger_dom_update(markup_text); }) .catch(function(error_object) { console.error('DOM Synchronization failed:', error_object); }); } function execute_asynchronous_form_submission(target_form) { fetch(target_form.action, { method: target_form.method, body: new FormData(target_form) }) .then(function(network_response) { if (!network_response.ok) { throw new Error('Form submission failure'); } return network_response.text(); }) .then(function(markup_text) { trigger_dom_update(markup_text); }) .catch(function(error_object) { console.error('Form execution failed:', error_object); }); } function update_game_stopwatch_ui() { const stopwatch_element = document.getElementById('game-stopwatch'); if (!stopwatch_element) { return; } const start_time_string = stopwatch_element.getAttribute('data-start-time'); if (!start_time_string) { return; } const start_date = new Date(start_time_string); if (isNaN(start_date.getTime())) { return; } const current_date = new Date(); const difference = calculate_time_difference(start_date.getTime(), current_date.getTime()); stopwatch_element.textContent = format_stopwatch_string(difference); } function modify_modal_state(modal_identifier, is_active) { const modal = document.getElementById(modal_identifier); if (modal) { if (is_active) { modal.classList.add('active-modal'); } else { modal.classList.remove('active-modal'); } } } function write_text_to_clipboard(text_payload) { if (!navigator.clipboard) { console.error('Clipboard API unvailable.'); return; } navigator.clipboard.writeText(text_payload).catch(function(error_object) { console.error('Clipboard action failed:', error_object); }); } /* --- Globale Event-Delegation --- */ function handle_global_clicks(mouse_event) { const target_element = mouse_event.target; const tab_button = target_element.closest('.tab-button'); if (tab_button) { const target_identifier = tab_button.getAttribute('data-target'); const parent_container = tab_button.closest('.application-content'); parent_container.querySelectorAll('.tab-button').forEach(function(button) { button.classList.remove('active-tab'); }); parent_container.querySelectorAll('.tab-content').forEach(function(content) { content.classList.remove('active-content'); }); tab_button.classList.add('active-tab'); const target_panel = document.getElementById(target_identifier); if (target_panel) { target_panel.classList.add('active-content'); } return; } const action_mapping = { 'trigger-elimination': function() { modify_modal_state('elimination-confirm-modal', true); }, 'cancel-elimination': function() { modify_modal_state('elimination-confirm-modal', false); }, 'proceed-elimination': function() { if (application_state.websocket_connection) { application_state.websocket_connection.emit('request_elimination'); } modify_modal_state('elimination-confirm-modal', false); }, 'trigger-revoke-elimination': function() { if (application_state.websocket_connection) { application_state.websocket_connection.emit('revoke_elimination_request'); } }, 'confirm-elimination': function() { if (application_state.websocket_connection) { application_state.websocket_connection.emit('confirm_elimination'); } modify_modal_state('verification-modal', false); }, 'reject-elimination': function() { if (application_state.websocket_connection) { application_state.websocket_connection.emit('reject_elimination'); } modify_modal_state('verification-modal', false); }, 'acknowledge-rejection': function() { modify_modal_state('rejection-modal', false); }, 'trigger-name-change-modal': function() { modify_modal_state('name-change-modal', true); }, 'cancel-name-change-modal': function() { modify_modal_state('name-change-modal', false); }, 'trigger-help-modal': function() { modify_modal_state('help-modal', true); }, 'close-help-modal': function() { modify_modal_state('help-modal', false); }, 'copy-identifier-button': function(button_element) { const identifier = button_element.getAttribute('data-identifier'); write_text_to_clipboard(identifier); const icon = button_element.querySelector('i, svg'); if (icon) { icon.setAttribute('data-lucide', 'check'); if (typeof lucide !== 'undefined') { lucide.createIcons(); } } }, 'confirm-admin-action': function() { if (application_state.pending_administrator_form) { const time_input = document.getElementById('modal_execution_time'); if (time_input && time_input.value) { let hidden_input = application_state.pending_administrator_form.querySelector('input[name="execution_time"]'); if (!hidden_input) { hidden_input = document.createElement('input'); hidden_input.type = 'hidden'; hidden_input.name = 'execution_time'; application_state.pending_administrator_form.appendChild(hidden_input); } hidden_input.value = time_input.value; } else { const hidden_input = application_state.pending_administrator_form.querySelector('input[name="execution_time"]'); if (hidden_input) { hidden_input.remove(); } } execute_asynchronous_form_submission(application_state.pending_administrator_form); application_state.pending_administrator_form = null; modify_modal_state('admin-confirmation-modal', false); } }, 'cancel-admin-action': function() { application_state.pending_administrator_form = null; modify_modal_state('admin-confirmation-modal', false); } }; for (const [identifier, action_function] of Object.entries(action_mapping)) { const button = target_element.closest('#' + identifier); if (button) { action_function(button); return; } } } function handle_global_submits(submit_event) { const target_form = submit_event.target; if (target_form.classList.contains('asynchronous-form')) { submit_event.preventDefault(); execute_asynchronous_form_submission(target_form); } else if (target_form.classList.contains('admin-asynchronous-form')) { submit_event.preventDefault(); application_state.pending_administrator_form = target_form; const time_input = document.getElementById('modal_execution_time'); if (time_input) { time_input.value = ''; } modify_modal_state('admin-confirmation-modal', true); } } /* --- Initialisierung --- */ document.addEventListener('DOMContentLoaded', function() { if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/service_worker.js').catch(function(error_object) { console.error('Service Worker Registration failed:', error_object); }); } if (typeof lucide !== 'undefined') { lucide.createIcons(); } if (application_state.stopwatch_interval_identifier) { clearInterval(application_state.stopwatch_interval_identifier); } application_state.stopwatch_interval_identifier = setInterval(update_game_stopwatch_ui, 1000); update_game_stopwatch_ui(); document.addEventListener('click', handle_global_clicks); document.addEventListener('submit', handle_global_submits); if (typeof io !== 'undefined') { application_state.websocket_connection = io(); application_state.websocket_connection.on('game_state_changed', function() { if (document.querySelector('.game-section') || document.querySelector('.administrator-section')) { fetch_and_synchronize_dom(); } }); application_state.websocket_connection.on('admin_state_changed', function() { if (document.querySelector('.administrator-section')) { fetch_and_synchronize_dom(); } }); application_state.websocket_connection.on('elimination_verification_requested', function() { fetch_and_synchronize_dom(); setTimeout(function() { modify_modal_state('verification-modal', true); }, 50); }); application_state.websocket_connection.on('elimination_rejected', function() { modify_modal_state('rejection-modal', true); }); application_state.websocket_connection.on('elimination_revoked', function() { modify_modal_state('verification-modal', false); fetch_and_synchronize_dom(); }); } });