document.addEventListener('keydown', function (e) { // Mac = Option, Windows/Linux = Alt const isOptionOrAlt = e.altKey || e.metaKey; if (isOptionOrAlt && e.shiftKey && e.key.toLowerCase() === 'f') { toggleFullscreen(); } }); function toggleFullscreen() { // If not in fullscreen → enter fullscreen if (!document.fullscreenElement) { document.documentElement.requestFullscreen() .catch(err => console.error("Fullscreen error:", err)); } // If already fullscreen → exit fullscreen else { document.exitFullscreen(); } } let messagePosArray = []; function displayMsg(type, msg) { const colors = ["#900000ff", "#00b200ff"]; if (type !== 0 && type !== 1) return; // idx is ALWAYS a valid non-negative index now const $div = $('
') .css({'border-color': colors[type]}) .text(msg); $('.msgDiv').append($div); // trigger entry animation setTimeout(() => { $div.addClass('show'); }, 200); setTimeout(() => { // First: set the transition properties $div.css({ 'transition': 'all 0.3s ease' }); // Next frame: apply the transform so the transition animates requestAnimationFrame(() => { $div.removeClass('show'); $div.css({ 'transform': 'scaleY(0) translateX(calc(100% + 40px))' }); }); }, 3000); // auto-remove setTimeout(() => { $div.remove(); }, 3500); } const text = document.getElementById('wsInfo'); const rect = document.getElementById('wsInfoRectangle'); let ws; let firstConnect = true; const RETRY_DELAY = 2000; // ms const urlAjaxNewWSToken = '/intern/scripts/ajax-create-ws-token.php'; async function fetchNewWSToken(freigabe) { try { const response = await fetch(urlAjaxNewWSToken, { method: "POST", headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ access: freigabe }) }); if (!response.ok) return null; const data = await response.json(); return data.success ? data.token : null; } catch (error) { console.error("Token fetch failed:", error); return null; } } async function startWebSocket() { console.log("Attempting WebSocket connection..."); let token; if (firstConnect) { token = window.WS_ACCESS_TOKEN; } else { token = await fetchNewWSToken('kampfrichter'); } if (!token) { console.error("No valid token available. Retrying..."); scheduleRetry(); return; } try { ws = new WebSocket(`wss://${window.location.hostname}/ws/?access=token&token=${token}`); } catch (err) { console.error("Malformed WebSocket URL", err); scheduleRetry(); return; } ws.onopen = () => { console.log("WebSocket connected!"); if (!firstConnect) { displayMsg(1, "Live Syncronisation wieder verfügbar"); } firstConnect = true; rect.innerHTML = ''; requestAnimationFrame(() => { text.style.opacity = 1; }); }; ws.onerror = (event) => { console.error("WebSocket error observed." + JSON.stringify(event)); }; ws.onclose = (event) => { displayMsg(0, "Live Syncronisation verloren"); firstConnect = false; rect.innerHTML = ''; requestAnimationFrame(() => { text.style.opacity = 1; }); scheduleRetry(); }; } function scheduleRetry() { console.log(`Retrying in ${RETRY_DELAY}ms...`); setTimeout(startWebSocket, RETRY_DELAY); } // Start the initial connection attempt safely startWebSocket(); $.fn.updateCurrentEdit = function() { return this.each(function() { const $input = $(this); const url = '/intern/scripts/kampfrichter/ajax/ajax-kampfrichter_currentedit.php'; if ($input.attr('data-person-id') === "0"){ $('
', { action: '/intern/kampfrichter', method: 'post' }) .append($('', { type: 'hidden', name: 'next_subabt', value: 1 })) .append($('', { type: 'hidden', name: 'next_subabt_submit', value: '>' })) .appendTo('body') .submit(); } fetch(url, { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ csrf_token: window.CSDR_TOKEN, editId: $input.attr('data-person-id'), geraet: $input.attr('data-geraet-id') ?? null }) }) .then(res => res.json()) .then(response => { if (response.success) { $(".current-turnerin-name").css({ 'color': '#209200ff', 'transition': 'all 0.3s ease-out' }); setTimeout(() => $(".current-turnerin-name").css({ 'color': '' }), 2000); $(".div_edit_values_user").css("display", "flex"); $(".current-turnerin-name").text(response.titel); $(".fv_nextturnerin").text(response.nturnerin?.name ?? ''); $(".fv_nextturnerin").val(response.nturnerin?.id ?? 0).attr('data-person-id', response.nturnerin?.id ?? 0); $(".submit-display-turnerin").css("opacity", "1"); $(".submit-display-start").css("opacity", "1"); $(".submit-display-result").css("opacity", "1"); const $editAllDiv = $('.div_edit_values_all_gereate'); const noten = response.noten; const programmId = response.programm_id; // First, reset all containers to a single input state and clear values $editAllDiv.find('.note-container').each(function() { const $container = $(this); const $tbody = $container.find('tbody'); const $headerRow = $container.find('thead tr'); // Reset header $headerRow.find('.run-num-header').remove(); // Remove extra inputs beyond run 1 $tbody.find('.inputs-row').each(function() { $(this).find('td:not(.input-cell-run-1)').remove(); }); // Clear value of run 1 $container.find('input[data-run="1"]').val('').attr('data-person-id', response.id); }); // Now loop through the data and populate/expand for (const [geraetId, noteGroup] of Object.entries(noten)) { for (const [noteId, runGroup] of Object.entries(noteGroup)) { const $container = $editAllDiv.find(`.note-container[data-note-id="${noteId}"]`); if ($container.length === 0) continue; const $tbody = $container.find('tbody'); const $headerRow = $container.find('thead tr'); const $inputsRow = $tbody.find('.inputs-row'); const runCount = Object.keys(runGroup).length; $headerRow.find('.note-name-header .rm').remove(); const originalText = $headerRow.find('.note-name-header').text().trim(); // If more than 1 run, add headers if not already present if (runCount > 1 && $headerRow.find('.run-num-header').length === 0) { $headerRow.find('.note-name-header').html(originalText + ' (R1)'); for (let r = 2; r <= runCount; r++) { $headerRow.append(`${originalText} (R${r})`); } } for (const [runNum, val] of Object.entries(runGroup)) { let $input = $inputsRow.find(`input[data-run="${runNum}"][data-geraet-id="${geraetId}"]`); // If input doesn't exist yet (for Run 2+), clone it if ($input.length === 0) { const $cell1 = $inputsRow.find('.input-cell-run-1'); const $newCell = $cell1.clone(); $newCell.removeClass('input-cell-run-1').addClass(`input-cell-run-${runNum}`); $input = $newCell.find('input'); $input.attr('data-run', runNum).val(val ?? ''); $inputsRow.append($newCell); // Re-bind change event to new input //bindAjaxInput($input); } else { $input.val(val ?? '').attr('data-person-id', response.id); } } } } $(".submit-display-turnerin").attr('data-person-id', response.id); $(".submit-display-start").attr('data-person-id', response.id); const submitMusikStart = $(".submit-musik-start"); const submitMusikStopp = $(".submit-musik-stopp"); if (submitMusikStart.length > 0 && submitMusikStopp.length > 0){ submitMusikStart.attr('data-id', response.id); submitMusikStopp.attr('data-id', response.id); } $(".submit-display-result").attr('data-person-id', response.id); } else { displayMsg(0, response.message); $input.css({ 'background-color': '#f8d7da', 'color': '#fff', 'transition': 'all 0.3s ease-out' }); setTimeout(() => $input.css({ 'background-color': '', 'color': '' }), 2000); } }) .catch(err => { $input.css('background-color', '#f8d7da'); console.error('AJAX fetch error:', err); }); }); }; jQuery(document).ready(function($) { $('.editTurnerin').on('click', function() { $(this).updateCurrentEdit(); }); function bindAjaxInput($el) { $el.on('change', function() { const start = performance.now(); const $input = $(this); const url = `/intern/scripts/kampfrichter/ajax/ajax-update_value_kampfrichter.php`; personId = $input.data('person-id'); fieldTypeId = $input.data('field-type-id'); gereatId = $input.data('geraet-id'); runNum = $input.attr('data-run') || 1; jahr = window.AKTUELLES_JAHR; value = $input.val(); fetch(url, { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ personId: personId, fieldTypeId: fieldTypeId, gereatId: gereatId, run: runNum, jahr: jahr, value: value }) }) .then(res => res.json()) .then(response => { const end = performance.now(); console.log(`Total AJAX time: ${(end - start).toFixed(3)} ms`); if (response.success) { let objValues = []; const rowId = $input.attr('data-id'); $input.css({"color": "#0e670d", "font-weight": "600"}); setTimeout(() => $input.css({'color': '', "font-weight": ""}), 2000); const noten = response.noten; for (const [keyN, noteGroup] of Object.entries(noten)) { for (const [key, runGroup] of Object.entries(noteGroup)) { if (key == fieldTypeId && keyN == gereatId) { continue; } for (const [run, value] of Object.entries(runGroup)) { $(`input.changebleValue[data-field-type-id="${key}"][data-geraet-id="${keyN}"][data-person-id="${personId}"][data-run="${run}"]`) .val(value ?? ''); $(`.changebleValue:not(input)[data-field-type-id="${key}"][data-geraet-id="${keyN}"][data-person-id="${personId}"][data-run="${run}"]`) .text(value ?? ''); } } } ws.send(JSON.stringify({ type: "KAMPFRICHTER_UPDATE", payload: { discipline: window.FREIGABE, gereatId: gereatId, personId: personId, jahr: jahr, noten: noten } })); } else { // Flash red on error $input.css({'color': '#ff6a76ff'}); displayMsg(0, response.message || 'Unknown error'); console.error(response.message || 'Unknown error'); } }) .catch(err => { $input.css({'color': '#670d0d'}); console.error('AJAX fetch error:', err); }); }); } $('.ajax-input').each(function() { bindAjaxInput($(this)); }); /*$('.ranglisteExport').on('click', function(e) { const $input = $(this); if ($input.data('field_type') !== 'upload_programm') {e.preventDefault();} // Build the data to send const data = new URLSearchParams(); data.append('prog', $input.data('id')); data.append('type', $input.data('field_type')); // Record start time const start = performance.now(); const url = '/intern/scripts/kampfrichter/ajax/ajax-neu_rangliste.php'; fetch(url, { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ prog: $input.data('id'), type: $input.data('field_type') }) }) .then(res => res.blob()) .then(blob => { if ($input.data('field_type') !== 'upload_programm'){ const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = "KTBB_Ergebnisse.pdf"; // optional document.body.appendChild(a); a.click(); a.remove(); window.URL.revokeObjectURL(url); } else { alert('PDF auf Webseite geladen!'); } }); }); $('.protokollExport').on('click', function() { console.log('ok'); const $input = $(this); // Build the data to send const data = new URLSearchParams(); data.append('abteilung', $input.data('abteilung')); // Record start time const start = performance.now(); const url = '/intern/scripts/kampfrichter/ajax/ajax-neu_protokoll.php'; fetch(url, { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ abteilung: $input.data('abteilung') }) }) .then(res => res.blob()) .then(blob => { const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = "KTBB_Protokoll.pdf"; // optional document.body.appendChild(a); a.click(); a.remove(); window.URL.revokeObjectURL(url); }); });*/ $('.inputnamekr').on('change', function() { const $input = $(this); const url = '/intern/scripts/kampfrichter/ajax/ajax-update_name_kampfrichter_protokoll.php'; fetch(url, { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ aufgabe: $input.data('id'), abteilung: $input.data('abt'), geraet: $input.data('user'), name: $input.val() }) }) .then(res => res.json()) .then(response => { if (response.success) { console.log(response.message); $input.css({ 'background-color': '#a4bf4a', 'color': '#fff', 'transition': 'all 0.3s ease-out' }); setTimeout(() => $input.css({ 'background-color': '', 'color': '' }), 2000); } else { console.error(response.message); $input.css({ 'background-color': '#f8d7da', 'color': '#fff', 'transition': 'all 0.3s ease-out' }); setTimeout(() => $input.css({ 'background-color': '', 'color': '' }), 2000); } }) .catch(err => { $input.css('background-color', '#f8d7da'); console.error('AJAX fetch error:', err); displayMsg(0, 'AJAX fetch error:' + err); }); }); /** * Handle namekr input updates $('.ajax-input-namekr').on('change', function() { let $input = $(this); $.post(ajax_object.ajaxurl, { action: 'save_namekr_input', id: $input.data('id'), abt: $input.data('abt'), user: $input.data('user'), value: $input.val() }, function(response) { if (response.success) { console.log(response.data.message); $input.css({ 'background-color': '#a4bf4a', 'color': '#fff', 'transition': 'all 0.3s ease-out' }); setTimeout(() => $input.css({ 'background-color': '', 'color': '' }), 2000); } else { console.error(response.data.message); $input.css({ 'background-color': '#f8d7da', 'color': '#fff', 'transition': 'all 0.3s ease-out' }); setTimeout(() => $input.css({ 'background-color': '', 'color': '' }), 2000); } }, 'json'); }); */ /** * Handle display JSON updates */ $('.submit-display-turnerin').on('click', function() { const $input = $(this); // Build the URL with GET parameters safely const url = '/intern/scripts/kampfrichter/ajax/displays/ajax-display-functions.php'; fetch(url,{ method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ personId: $input.attr('data-person-id'), geraetId: $input.attr('data-geraet-id'), jahr: window.AKTUELLES_JAHR, type: "neu" }) }) .then(res => res.json()) .then(response => { if (response.success) { ws.send(JSON.stringify({ type: "UPDATE_SCORE", payload: { geraet: response.nameGeraet, data: response.data } })); displayMsg(1, 'Neue Turnerin wird angezigt'); $input.css('opacity', 0.5); } else { displayMsg(0, response.message); } }) .catch(err => { $input.css('background-color', '#f8d7da'); displayMsg(0, 'AJAX fetch error:' + err); console.error('AJAX fetch error:', err); }); }); $('.submit-display-start').on('click', function() { const $input = $(this); const url = '/intern/scripts/kampfrichter/ajax/displays/ajax-display-functions.php'; fetch(url,{ method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ geraetId: $input.attr('data-geraet-id'), type: "start" }) }) .then(res => res.json()) .then(response => { if (response.success) { ws.send(JSON.stringify({ type: "UPDATE_SCORE", payload: { geraet: response.nameGeraet, data: response.data } })); displayMsg(1, 'Start freigegeben'); $input.css('opacity', 0.5); } else { alert('Error: ' + response.message); } }) .catch(err => { $input.css('background-color', '#f8d7da'); displayMsg(0, 'AJAX fetch error:' + err); console.error('AJAX fetch error:', err); }); }); /*$('.submit-display-start').on('click', function() { let discipline = $(this).data('discipline'); $.post(ajax_object.ajaxurl, { action: 'write_discipline_json_start', discipline: discipline }, function(response) { if (response.success) { alert('Start freigegeben'); if (response.data.disable_start_button) { $('.submit-display-start').css({ 'border': '1px solid #aaa', 'background-color': '#aaa', 'color': '#555', 'pointer-events': 'none' }); $('.submit-musik-start').css({ 'background-color': '#077', 'border': '1px solid #077', 'color': '#fff', 'cursor': 'pointer', 'pointer-events': 'auto' }); } } else { alert('Error: ' + response.data.message); } }, 'json'); });*/ $('.submit-musik-start').on('click', function() { const $input = $(this); // Build the URL with GET parameters safely const url = `/intern/scripts/kampfrichter/ajax/ajax-update_kampfrichter_start_musik.php` + `?id=${$input.attr('data-id')}` + `&discipline=${encodeURIComponent($input.data('geraet'))}`; fetch(url) .then(res => res.json()) .then(response => { const end = performance.now(); ws.send(JSON.stringify({ type: "AUDIO", payload: {} })); if (response.success) { displayMsg(1, 'Musik wird abgespielt werden'); } else { displayMsg(0, 'Error: ' + response.message); } }) .catch(err => { $input.css('background-color', '#f8d7da'); displayMsg(0, 'AJAX fetch error:' + err); console.error('AJAX fetch error:', err); }); }); $('.submit-musik-stopp').on('click', function() { const $input = $(this); // Build the URL with GET parameters safely const url = `/intern/scripts/kampfrichter/ajax/ajax-update_kampfrichter_stopp_musik.php`; fetch(url) .then(res => res.json()) .then(response => { if (response.success) { ws.send(JSON.stringify({ type: "AUDIO", payload: {} })); displayMsg(1, 'Musik wird gestoppt werden'); } else { alert('Error: ' + response.message); } }) .catch(err => { $input.css('background-color', '#f8d7da'); displayMsg(0, 'AJAX fetch error:' + err); console.error('AJAX fetch error:', err); }); }); $('.submit-display-result').on('click', function() { $input = $(this); // Build the URL with GET parameters safely const url = '/intern/scripts/kampfrichter/ajax/displays/ajax-display-functions.php'; fetch(url,{ method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ personId: $input.attr('data-person-id'), geraetId: $input.attr('data-geraet-id'), jahr: window.AKTUELLES_JAHR, type: "result" }) }) .then(res => res.json()) .then(response => { if (response.success) { ws.send(JSON.stringify({ type: "UPDATE_SCORE", payload: { geraet: response.nameGeraet, data: response.data } })); $input.css('opacity', 0.5); displayMsg(1, 'Resultat wird angezeigt'); } else { alert('Error: ' + response.message); } }) .catch(err => { $input.css('background-color', '#f8d7da'); displayMsg(0, 'AJAX fetch error:' + err); console.error('AJAX fetch error:', err); }); }); }); const selecteddiscipline = window.FREIGABE; ws.addEventListener("message", event => { // Use 'event' as it's more standard than 'blob' let msgOBJ; try { msgOBJ = JSON.parse(event.data); } catch (error) { return; } // Ensure it's an UPDATE type (matches your sendToGroup logic) if (msgOBJ?.type === "UPDATE") { const data = msgOBJ.payload; // Check access rights if (data.discipline === selecteddiscipline.toLowerCase() || selecteddiscipline.toLowerCase() === 'admin') { const noten = data.noten; for (const [keyG, noteGroup] of Object.entries(noten)) { for (const [key, runGroup] of Object.entries(noteGroup)) { for (const [run, value] of Object.entries(runGroup)) { // OPTIONAL: Skip if the current user is currently focused on this specific input if (document.activeElement.dataset.fieldTypeId === key && document.activeElement.dataset.geraetId === keyG && document.activeElement.dataset.run == run) continue; // Select all matching elements (inputs and spans) const $elements = $(`.changebleValue[data-field-type-id="${key}"][data-geraet-id="${keyG}"][data-person-id="${data.personId}"][data-run="${run}"]`); $elements.each(function() { const $el = $(this); // Update value or text if ($el.is('input')) { $el.val(value ?? ''); } else { $el.text(value ?? ''); } // Visual feedback: Flash color const nativeEl = $el[0]; nativeEl.style.transition = 'color 0.3s'; nativeEl.style.color = '#008e85'; setTimeout(() => { nativeEl.style.color = ''; }, 1000); }); } } } } } }); /*document.getElementById('freigabe-select').addEventListener('change', function() { const freigabe = this.value; const user_id = document.getElementById('user_id').value; const nonce = document.getElementById('freigabe_nonce').value; const type = document.getElementById('type_freigabe').value; const params = new URLSearchParams(); params.append('action', 'save_freigabe'); params.append('freigabe', freigabe); params.append('user_id', user_id); params.append('type', type); fetch('/intern/scripts/kampfrichter/ajax/ajax-update_selected_kampfrichter.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: params }) .then(res => res.json()) .then(data => { if (data.success) { window.location.reload(); } else { alert('Error: ' + data.data); } }) .catch(err => { console.error(err); displayMsg(0, 'AJAX fetch error:' + err); }); });*/