Files

1732 lines
80 KiB
PHP

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require_once $baseDir . '/../scripts/session_functions.php';
ini_wkvs_session(true);
$csrf_token = $_SESSION['csrf_token'] ?? '';
$access_granted_wkl = check_user_permission('wk_leitung', true) ?? false;
if (!$access_granted_wkl):
$logintype = 'wk_leitung';
require $baseDir . '/../scripts/login/login.php';
$logintype = '';
else:
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="png" href="/intern/img/icon.png">
<title>WK-leitung - Rank Live</title>
<link rel="stylesheet" href="/intern/css/rank-live.css">
<link rel="stylesheet" href="/intern/css/sidebar.css">
<script src="/intern/js/jquery/jquery-3.7.1.min.js"></script>
<script src="/intern/js/jquery/jquery-ui.min.js"></script>
<script src="/intern/js/jquery/jquery.ui.touch-punch.min.js"></script>
<script src="/intern/js/custom-msg-display.js"></script>
<link rel="stylesheet" href="/intern/css/custom-msg-display.css">
<link rel="stylesheet" href="/intern/css/custom-select.css">
<link rel="stylesheet" href="/files/fonts/fonts.css">
<link rel="stylesheet" href="/intern/css/user.css">
<link rel="stylesheet" href="/intern/css/coloris/coloris.min.css" />
<script src="/intern/js/coloris/coloris.min.js"></script>
</head>
<body class="einstellungen">
<?php
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/websocket/ws-create-token.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true) {
echo 'Critical DB Error.';
exit;
}
$currentPage = 'rankLive';
require $baseDir . '/../scripts/sidebar/sidebar.php';
$active_slug = $_SESSION['current_ranklive_slug'] ?? 'rankLive-overview';
$allNotenTypes = db_select($mysqli, $db_tabelle_wertungstypen, "name, id, anzahl_laeufe_json");
$indexed_noten = array_column($allNotenTypes, "name", "id");
$geraeteDB = db_select($mysqli, $db_tabelle_disziplinen, "id, name");
$geraete = array_column($geraeteDB, "name", "id");
$geraete[0] = 'Pseudogerät für Berechnung';
$total_geraete_count = count($geraete) - 1;
$programme_db = db_select($mysqli, $db_tabelle_kategorien, "id, programm");
$programme = array_column($programme_db, "programm", "id");
$total_programs_count = count($programme);
$all_program_ids = array_keys($programme);
$options_note = [];
$options_note_s_geraet = [];
foreach ($allNotenTypes as $s_note) {
$id = $s_note['id'];
$anzahl_laeufe_array = json_decode($s_note['anzahl_laeufe_json'] ?? '', true) ?? [];
$default = intval($anzahl_laeufe_array['default'] ?? 1);
$runs_per_device = [];
foreach ($geraete as $g_id => $s_geraet) {
$sub_default = $anzahl_laeufe_array[$g_id]['all'] ?? $default;
$is_g_specific = isset($anzahl_laeufe_array[$g_id]['all']);
foreach ($programme as $p_id => $s_prog) {
$is_p_specific = isset($anzahl_laeufe_array[$g_id][$p_id]) && !is_array($anzahl_laeufe_array[$g_id][$p_id]);
$k = $is_p_specific ? intval($anzahl_laeufe_array[$g_id][$p_id]) : $sub_default;
if ($k < 1) continue;
if ($g_id === 0 && !($is_p_specific || $is_g_specific)) continue;
for ($i = 1; $i <= $k; $i++) {
$options_note[$id][$g_id][$i][$p_id] = 1;
}
}
$runs_per_device[$g_id] = isset($options_note[$id][$g_id]) ? count($options_note[$id][$g_id]) : 0;
}
if (isset($options_note[$id])) {
foreach ($options_note[$id] as $g_id => $g_array) {
foreach ($g_array as $run => $run_array) {
if (count($run_array) === $total_programs_count) {
$options_note[$id][$g_id][$run] = ['A'];
}
}
}
}
$max = max($runs_per_device);
for ($i = 1; $i <= $max; $i++) {
foreach ($runs_per_device as $g_id => $run_c) {
if ($run_c < $i) continue;
$options_note_s_geraet[$id][$i][$g_id] = 1;
}
}
if (isset($options_note_s_geraet[$id])) {
foreach ($options_note_s_geraet[$id] as $run => $g_array) {
$has_zero = isset($g_array[0]);
$expected_count = $has_zero ? $total_geraete_count + 1 : $total_geraete_count;
if (count($g_array) === $total_geraete_count && !$has_zero) {
$options_note_s_geraet[$id][$run] = 'A';
}
}
}
}
$getGeraetName = function($id) use ($geraete) {
return $geraete[$id] ?? '';
};
$allOptions = [
'text' => [
'text' => 'Eigener statischer Text'
],
'personData' => [
'geburtsdatum' => 'Geburtsdatum',
'jahrgang' => 'Jahrgang',
'name' => 'Name',
'vorname' => 'Vorname',
'verein' => 'Verein',
'programm' => 'Programm'
],
'sortData' => [
'rang' => 'Rang',
'startindex' => 'Startreihenfolge'
],
'layoutEls' => [
'vertical' => 'Vertical'
],
'kampfrichterEls' => [
'edit' => 'Bearbeitungsicon'
],
'notenData' => $options_note,
'notenGeraetData' => $options_note_s_geraet
];
$allOprionsNameArray = [
'text' => 'Statischer Text',
'personData' => 'Personendaten',
'sortData' => 'Rangdaten',
'notenData' => 'Noten',
'notenGeraetData' => 'Noten',
'layoutEls' => 'Layout Elemente',
'kampfrichterEls' => 'Elemente Kampfrichter'
];
$jsonstr = db_select($mysqli, $db_tabelle_tabellen_konfiguration, 'json', 'type_slug = ?', [$active_slug], '', 1);
$allowed_types = [
'rankLive-overview' => ["name" => 'RankLive Overview', 'geraet' => 'all', 'type' => 'rankLive'],
'rankLive-geraet' => ["name" => 'RankLive einzelnes Gerät', 'geraet' => 'single', 'type' => 'rankLive'],
'kampfrichter-admin' => ["name" => 'Kampfrichter Admin (alle Geräte)', 'geraet' => 'all', 'type' => 'kampfrichter'],
'kampfrichter-geraet' => ["name" => 'Kampfrichter einzelnes Gerät', 'geraet' => 'single', 'type' => 'kampfrichter'],
'rangliste' => ["name" => 'Rangliste', 'geraet' => 'all', 'type' => 'indev']
];
// 2. Decode JSON into an associative array
$data = json_decode($jsonstr[0]['json'] ?? '', true) ?? [];
$headers = $data['header'] ?? [];
$bodyColumns = $data['body'] ?? [];
function ipn($input)
{
global $programme;
return $programme[$input] ?? '';
}
$lindex = 0;
$highest_uid = 0;
function constructSingleValueSpan($token, $type) {
global $lindex;
global $highest_uid;
$lindex++;
$text = $token['text'] ?? '';
$uid = (int) ($token['uid'] ?? 0);
if ($uid > $highest_uid) $highest_uid = $uid;
$classes = 'singleValueSpan ui-sortable-handle dragable';
$extraAttributes = '';
$boldString = '';
$minDisplayWidth = $token['minDisplayWidth'] ?? 0;
$dataAttributes = 'data-min-display-width="' . $minDisplayWidth . '" data-uid="' . $uid . '" data-type="' . htmlspecialchars($type) . '"';
$style = '';
if ($type !== 'kampfrichterEls') {
$bold = $token['bold'] ?? false;
$font_size = $token['fontSize'] ?? 0;
$linked_el_uid = $token['linkedElUid'] ?? 0;
if ($font_size > 0) {
$style = ' style="font-size: ' . (int)$font_size . 'px"';
}
$boldString = ($bold === true || $bold === 'true') ? 'true' : 'false';
$dataAttributes .= ' data-bold="' . $boldString . '" data-linked-el-uid="' . $linked_el_uid . '"';
}
switch ($type) {
case 'text':
$classes .= ' staticText';
$dataAttributes .= ' data-column="' . htmlspecialchars($token['column'] ?? 'text') . '"';
break;
case 'kampfrichterEls':
$dataAttributes .= ' data-column="' . htmlspecialchars($token['column'] ?? 'text') . '"';
break;
case 'personData':
$dataAttributes .= ' data-column="' . htmlspecialchars($token['column'] ?? '') . '"';
if (($token['column'] ?? '') === 'geburtsdatum') {
$dataAttributes .= ' data-php-date-format="' . htmlspecialchars($token['phpDateFormat'] ?? '') . '"';
}
break;
case 'notenData':
$dataAttributes .= ' data-field-type-id="' . htmlspecialchars($token['field-type-id'] ?? '') . '"';
$dataAttributes .= ' data-geraet-id="' . htmlspecialchars($token['geraet-id'] ?? '') . '"';
$dataAttributes .= ' data-run="' . htmlspecialchars($token['run'] ?? '') . '"';
$dataAttributes .= ' data-programm-ids="' . htmlspecialchars($token['programm-ids'] ?? '') . '"';
$notenNullable = $token['notenNullable'] ?? false;
$notenNullableStr = ($notenNullable === 'true' || $notenNullable === true) ? 'true' : 'false';
$dataAttributes .= ' data-noten-nullable="' . $notenNullableStr . '"';
break;
case 'notenGeraetData':
$dataAttributes .= ' data-field-type-id="' . htmlspecialchars($token['field-type-id'] ?? '') . '"';
$dataAttributes .= ' data-run="' . htmlspecialchars($token['run'] ?? '') . '"';
$notenNullable = $token['notenNullable'] ?? false;
$notenNullableStr = ($notenNullable === 'true' || $notenNullable === true) ? 'true' : 'false';
$dataAttributes .= ' data-noten-nullable="' . $notenNullableStr . '"';
break;
case 'sortData':
$dataAttributes .= ' data-column="' . htmlspecialchars($token['column'] ?? '') . '"';
break;
}
?>
<span class="<?= $classes; ?>" <?= $dataAttributes; ?><?= $extraAttributes; ?><?= $style ?>>
<span class="textSingleValueSpan"><?= htmlspecialchars($text) ?></span>
<div class="settingsDivSingleValueSpan hidden">
<?php if ($type === 'text') : ?>
<div class="settingsInputGroup">
<label for="statischerTextInput-<?= $lindex ?>">Text:</label>
<input type="text"
id="statischerTextInput-<?= $lindex ?>"
class="statischerTextInput"
placeholder="Statischer Text"
value="<?= htmlspecialchars($text) ?>">
</div>
<?php elseif ($type === 'notenData' || $type === 'notenGeraetData') : ?>
<div class="settingsInputGroup">
<label for="nullNotenCheckbox-<?= $lindex ?>">Wert 0.00 <b>nicht</b> anzeigen?</label>
<label class="checkbox">
<input type="checkbox"
id="nullNotenCheckbox-<?= $lindex ?>"
class="nullNotenCheckbox"
<?= ($notenNullable === 'true' || $notenNullable === true) ? 'checked' : '' ?>>
<span class="checkbox-ui"></span>
</label>
</div>
<?php elseif ($type === 'personData' && ($token['column'] ?? '') === 'geburtsdatum') : ?>
<div class="settingsInputGroup">
<label for="gebutsdatumFormatInput-<?= $lindex ?>"><a href="https://www.php.net/manual/de/datetime.format.php#:~:text=format,-Das" target="_blank">PHP-Datumsformat</a></label>
<div class="autocomplete-container">
<input type="text" id="gebutsdatumFormatInput-<?= $lindex ?>" class="gebutsdatumFormatInput" value="<?= htmlspecialchars($token['phpDateFormat'] ?? '') ?>" placeholder="d. MMMM YYYY">
<div id="dropdownList" class="dropdown-list hidden"></div>
</div>
</div>
<?php endif; ?>
<?php if ($type !== 'kampfrichterEls') : ?>
<div class="settingsInputGroup">
<label for="boldCheckbox-<?= $lindex ?>">Fetter Text:</label>
<label class="checkbox">
<input type="checkbox"
id="boldCheckbox-<?= $lindex ?>"
class="boldCheckbox"
<?= ($bold === 'true' || $bold === true) ? 'checked' : '' ?>>
<span class="checkbox-ui"></span>
</label>
</div>
<div class="settingsInputGroup">
<label for="inputFontSize-<?= $lindex ?>">Schriftgrösse</label>
<label for="inputFontSize-<?= $lindex ?>" class="inputLabelWrapper">
<input type="number"
min="1"
id="inputFontSize-<?= $lindex ?>"
class="inputFontSize"
placeholder="16"
value="<?= ($font_size > 0) ? (int)$font_size : ''?>">
<span>px</span>
</label>
</div>
<div class="settingsInputGroup">
<label for="inputDisplayOnDisplayWidth-<?= $lindex ?>">Minimale Bildschirmbreite</label>
<label for="inputDisplayOnDisplayWidth-<?= $lindex ?>" class="inputLabelWrapper">
<input type="number"
placeholder="Kein Limit"
class="inputDisplayOnDisplayWidth"
id="inputDisplayOnDisplayWidth-<?= $lindex ?>"
value="<?= intval($minDisplayWidth) ?>">
<span>px</span>
</label>
</div>
<div class="settingsInputGroup">
<label for="inputConditionalDisplay-<?= $lindex ?>">Conditional Display</label>
<button type="button" id="inputConditionalDisplay-<?= $lindex ?>" class="inputConditionalDisplay">
Element auswählen…
</button>
<span class="spanConditionalDisplay"></span>
</div>
<?php endif; ?>
<div class="settingsInputGroup">
<label for="deleteSingleValueSpan-<?= $lindex ?>">Löschen</label>
<button type="button"
id="deleteSingleValueSpan-<?= $lindex ?>"
class="deleteSingleValueSpan">X</button>
</div>
</div>
<span class="settingSpan">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1a8.45 8.45 0 0 0-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3-1.57-3-3.5s1.07-3.5 3-3.5 3 1.57 3 3.5-1.07 3.5-3 3.5z"></path>
</svg>
</span>
</span>
<?php }
?>
<section class="bgSection">
<?php sidebarRender('modal'); ?>
<div class="headerDivTrainer">
<div class="headingPanelDiv">
<h2 class="headingPanel">Konfiguration RankLive</h2>
</div>
<div class="menuWrapper">
<?php sidebarRender('button'); ?>
</div>
</div>
<div class="dashboardGrid">
<div class="selectSlugType">
<div class="customSelect customSelectSlugType" id="selectedOptionSlugType" data-value="">
<button type="button" id="selectTriggerSlugType" class="selectTrigger paddingNormal" aria-expanded="false">
<span class="selectLabel"><?= $allowed_types[$active_slug]['name'] ?? '' ?></span>
<svg class="selectArrow" xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'
height="14" width="14">
<path d='M6 9L12 15L18 9' stroke='currentColor' stroke-width='2' stroke-linecap='round'
stroke-linejoin='round' />
</svg>
</button>
<ul class="selectOptions">
<?php foreach ($allowed_types as $s_type => $s_type_data): ?>
<?php $selected = ($s_type === $active_slug) ? 'selected' : ''; ?>
<li data-value="<?= htmlspecialchars($s_type) ?>" data-type="<?= htmlspecialchars($s_type_data['type']) ?>" data-geraet="<?= htmlspecialchars($s_type_data['geraet']) ?>" class="<?= $selected ?>"><?= htmlspecialchars($s_type_data['name']) ?></li>
<?php endforeach; ?>
</ul>
</div>
</div>
<div class="contentDiv">
<table class="mainTable">
<thead>
<tr id="headerRowRankLiveConfig">
<?php foreach ($headers as $header_data): ?>
<?php
$minDisplayWidth = intval($header_data['minDisplayWidth'] ?? 0);
$lindex++;
?>
<th class="headerThRankLiveConfig" data-min-display-width="<?= $minDisplayWidth ?>">
<span>
<input class="columnTitleInput" placeholder="Spaltenüberschrift" value="<?= htmlspecialchars($header_data['title']) ?>">
<span class="deleteColumnBtn">X</span>
<span class="addColumnBtn addColumnBtnAfter">+</span>
<span class="addColumnBtn addColumnBtnBefore">+</span>
<div class="settingsDivSingleValueSpan hidden">
<div class="settingsInputGroup">
<label for="inputDisplayOnDisplayWidth-<?= $lindex ?>">Minimale Bildschirmbreite</label>
<label for="inputDisplayOnDisplayWidth-<?= $lindex ?>" class="inputLabelWrapper">
<input type="number"
placeholder="Kein Limit"
class="inputDisplayOnDisplayWidth"
id="inputDisplayOnDisplayWidth-<?= $lindex ?>"
value="<?= intval($minDisplayWidth) ?>">
<span>px</span>
</label>
</div>
</div>
<span class="settingSpan">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1a8.45 8.45 0 0 0-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3-1.57-3-3.5s1.07-3.5 3-3.5 3 1.57 3 3.5-1.07 3.5-3 3.5z"></path>
</svg>
</span>
</span>
</th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<tr id="bodyRowRankLiveConfig">
<?php foreach ($bodyColumns as $columnTokens): ?>
<td>
<span class="rankLiveFieldContainer dragableDropArea">
<span class="addFieldBtn">+</span>
<?php
foreach ($columnTokens as $token) {
$type = $token['type'] ?? '';
if ($type === 'layoutEls') :
$column = $token['column'];
$content = $token['content'] ?? []; ?>
<div class="<?= $column ?>LayoutEl dragable" data-type="<?= $type ?>" data-column="<?= $column ?>">
<?php foreach ($content as $row) : ?>
<span class="dragableDropArea <?= $column ?>LayoutElRow">
<?php foreach ($row as $el) :
constructSingleValueSpan($el, $el['type']);
endforeach; ?>
</span>
<?php endforeach; ?>
</div>
<?php else:
constructSingleValueSpan($token, $type);
endif;
}
?>
</span>
</td>
<?php endforeach; ?>
</tr>
</tbody>
</table>
<button id="addColumn" class="blackBtn <?= count($headers) > 0 ? 'hidden' : '' ?>">Spalte hinzufügen</button>
<label for="importConfig" class="blackBtn">Tablelle importieren</label>
<input id="importConfig" type="file" class="hidden" accept=".json, .wkvs">
<button id="exportConfig" class="blackBtn">Gespeicherte Tablelle exportieren</button>
<button id="saveConfig" class="blackBtn">Tablelle speichern</button>
</div>
<div class="inDevDiv">Dieses Feature befindet sich zurzeit noch in Entwicklung</div>
</div>
<div class="customSelect customSelectAddOption hidden" id="selectedOption" data-value="">
<button type="button" id="selectTrigger" class="selectTrigger paddingNormal" aria-expanded="false">
<span class="selectLabel placeholder">Bitte auswählen…</span>
<svg class="selectArrow" xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'
height="14" width="14">
<path d='M6 9L12 15L18 9' stroke='currentColor' stroke-width='2' stroke-linecap='round'
stroke-linejoin='round' />
</svg>
</button>
<ul class="selectOptions">
<?php foreach ($allOptions as $sNotenGroupName => $sNotenGroup): ?>
<div class="singleOptionDiv <?= $sNotenGroupName ?>">
<li class="customSelectSubGroupHeader"><?= $allOprionsNameArray[$sNotenGroupName] ?></li>
<ul class="customSelectSubGroup">
<?php foreach ($sNotenGroup as $key => $sNote): ?>
<?php if ($sNotenGroupName === 'notenData'): ?>
<?php foreach ($sNote as $g_id => $n_array_gerate): ?>
<?php foreach ($n_array_gerate as $run => $n_array_run): ?>
<?php
$has_all_progs = !(is_array($n_array_run) && !(isset($n_array_run[0]) && $n_array_run[0] === 'A'));
$displayed_progs = $has_all_progs ? '' : ' (nur ' . implode(', ', array_map('ipn', array_keys($n_array_run))) . ')';
$data_progs = $has_all_progs ? 'A' : '[' . implode(', ', array_keys($n_array_run)) . ']';
$runs = count($n_array_gerate) > 1 ? ' (Run ' . $run . ')' : '';
$geraet = (intval($g_id) !== 0) ? ', ' . ($geraete[$g_id] ?? 'INTERNAL ERROR') : '';
?>
<li data-field-type-id="<?= $key ?>" data-type="<?= $sNotenGroupName ?>"
data-geraet-id="<?= $g_id ?>" data-run="<?= $run ?>" data-programm-ids="<?= $data_progs ?>"
class=""><?= $indexed_noten[$key] ?><?= $geraet ?><?= $runs ?><?= $displayed_progs ?></li>
<?php endforeach; ?>
<?php endforeach; ?>
<?php elseif ($sNotenGroupName === 'notenGeraetData'):
$note_name = $indexed_noten[$key] ?? '';
$multiple_runs = count($sNote) > 1;
foreach ($sNote as $run => $run_geraete):
$run_text = ($multiple_runs) ? " R". $run : '';
$geraete_text = (is_array($run_geraete) && !empty($run_geraete)) ? " (nur " . implode(", ", array_map($getGeraetName, array_keys($run_geraete))) . ")" : "";
?>
<li data-field-type-id="<?= $key ?>" data-type="<?= $sNotenGroupName ?>"
data-run="<?= $run ?>"
class=""><?= $note_name ?><?= $run_text ?><?= $geraete_text ?></li>
<?php endforeach; ?>
<?php else: ?>
<li data-column="<?= $key ?>" data-type="<?= $sNotenGroupName ?>" class=""><?= $sNote ?></li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
</div>
<?php endforeach; ?>
</ul>
</div>
<div class="msgDiv"></div>
</section>
<script>
const arrayWSSend = [
''
];
let $activeEl = null;
let active_type_slug = '<?= $active_slug ?>';
let currentUid = <?= $highest_uid + 1 ?>;
const $customSelectColumns = $('.customSelectAddOption');
const $headerRow = $('#headerRowRankLiveConfig');
const $bodyRow = $('#bodyRowRankLiveConfig');
const csrf_token = "<?= $csrf_token ?>";
$(document).on('click', '.selectTrigger', function (e) {
e.stopPropagation();
$(this).closest('.customSelect').toggleClass('open');
});
function setDropdownWidth() {
const $container = $('.customSelectAddOption');
const $dropdown = $container.children('.selectOptions');
const width = $dropdown.outerWidth();
if (width === 0) width = 100;
$container.outerWidth(width);
}
const $uids = $('.singleValueSpan:not([data-uid="0"])');
const arrayUids = $uids.map(function() {
return parseInt($(this).data('uid'), 10) || 0;
}).get();
let currentCheckId = arrayUids.length > 0 ? Math.max(...arrayUids) + 1 : 1;
function getNextAvailableUid() {
let candidateId = currentCheckId;
while (arrayUids.includes(candidateId)) {
candidateId++;
}
arrayUids.push(candidateId);
currentCheckId = candidateId + 1;
return candidateId;
}
function setNullUids() {
const $targetEls = $('.singleValueSpan[data-uid="0"]');
if ($targetEls.length < 1) return;
$targetEls.each((index, el) => {
$(el).attr('data-uid', getNextAvailableUid());
});
}
setNullUids();
function setDropdownPosition(el) {
const $button = $(el);
const $td = $button.closest('td');
if ($td.length === 0) {
console.error("Could not find parent <td> element.");
return;
}
let pos = $td.offset();
const $tdHeightOffset = $td.outerHeight() + 5;
pos.top = pos.top + $tdHeightOffset;
pos.left = pos.left - 1;
$('.customSelectAddOption').css({
top: pos.top,
left: pos.left
}).data('button', el);
}
$(window).on('resize', function() {
let reference = $('.customSelectAddOption').data('button') ?? null;
if (reference === null) return;
const $reference = $(reference);
const $td = $reference.closest('td');
if ($td.length === 0) {
console.error("Could not find parent <td> element.");
return;
}
let pos = $td.offset();
const $tdHeightOffset = $td.outerHeight() + 5;
pos.top = pos.top + $tdHeightOffset;
pos.left = pos.left - 1;
$('.customSelectAddOption').css({
top: pos.top,
left: pos.left
})
})
const $table = $('mainTable');
function settingsSvg() {
return $('<span></span>').addClass('settingSpan').html('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1a8.45 8.45 0 0 0-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3-1.57-3-3.5s1.07-3.5 3-3.5 3 1.57 3 3.5-1.07 3.5-3 3.5z"/></svg>');
}
function generateTh() {
const $newTh = $("<th></th>").addClass('headerThRankLiveConfig');
const $newThSpan = $("<span></span>");
const $deleteBtn = $("<span></span>").text("X").addClass('deleteColumnBtn');
const $addSpanAfter = $("<span></span>").text("+").addClass('addColumnBtn addColumnBtnAfter').data("insert", "after");
const $addSpanBefore = $("<span></span>").text("+").addClass('addColumnBtn addColumnBtnBefore').data("insert", "before");
const $input = $('<input>').attr('placeholder', 'Spaltenüberschrift').addClass('columnTitleInput');
const $div = $('<div></div>').addClass('settingsDivSingleValueSpan hidden');
/*const $boldDiv = $('<div></div>').addClass('settingsInputGroup');
const $label1 = $('<label></label>').text('Fetter Text:').attr('for', `boldCheckbox-${lindex}`);
const $input1 = createCheckbox('boldCheckbox', `boldCheckbox-${lindex}`);
$boldDiv.append($label1, $input1);
const $fontSizeDiv = $('<div></div>').addClass('settingsInputGroup');
const $labelFontSize = $('<label></label>').text('Schriftgrösse').attr('for', `inputFontSize-${lindex}`);
const $inputFontSize = $('<input>').attr('placeholder', '16').attr("type", "number").addClass('inputFontSize').attr('id', `inputFontSize-${lindex}`).attr("min", "1");
const $pxSpanFontSize = $('<span></span>').text('px');
const $labelWraperFontSize = $('<label></label>').class("inputLabelWrapper").attr('for', `inputFontSize-${lindex}`);
$labelWraperFontSize.append($inputFontSize, $pxSpanFontSize);
$fontSizeDiv.append($labelFontSize, $labelWraperFontSize);*/
const $mobileDiv = $('<div></div>').addClass('settingsInputGroup');
const $labelM = $('<label></label>').text('Minimale Bildschirmbreite').attr('for', `inputDisplayOnDisplayWidth-${lindex}`);
const $inputM = $('<input>').attr('placeholder', 'Kein Limit').attr("type", "number").addClass('inputDisplayOnDisplayWidth').attr('id', `inputDisplayOnDisplayWidth-${lindex}`);
const $pxSpan = $('<span></span>').text('px');
const $labelWraper = $('<label></label>').addClass("inputLabelWrapper").attr('for', `inputDisplayOnDisplayWidth-${lindex}`);
$labelWraper.append($inputM, $pxSpan);
$mobileDiv.append($labelM, $labelWraper);
$div.append($mobileDiv);
$newThSpan.append($input, $deleteBtn, $addSpanAfter, $addSpanBefore, $div, settingsSvg());
$newTh.append($newThSpan);
return $newTh;
}
function generateTd() {
const $td = $('<td></td>');
const $span = $('<span></span>').addClass('rankLiveFieldContainer dragableDropArea');
const $addbtn = $('<span></span>').text("+").addClass("addFieldBtn");
$span.append($addbtn);
$td.append($span);
return $td;
}
$('body').on('click', '.addFieldBtn', function (e) {
$activeEl = $(this).parent();
const prevEl = $customSelectColumns.data("last-el") ?? $('body')[0];
if (prevEl === this) {
$customSelectColumns.toggleClass('hidden');
} else {
$customSelectColumns.removeClass('hidden');
}
$customSelectColumns.data("last-el", this);
if (!$customSelectColumns.hasClass('hidden')) {
setDropdownWidth();
setDropdownPosition(this);
}
});
$('#addColumn').on('click', function (e) {
$headerRow.append(generateTh());
$bodyRow.append(generateTd());
$(this).addClass('hidden');
safeRefreshSortable();
});
$('body').on('click', '.addColumnBtn', function (e) {
const $el = $(this);
const insert = $el.data("insert") ?? '';
const index = $el.closest('th').index();
const $newTh = generateTh();
const $existingItemAtIndexHeader = $headerRow.children().eq(index);
if ($existingItemAtIndexHeader.length > 0) {
if (insert === "before") { $existingItemAtIndexHeader.before($newTh); }
else { $existingItemAtIndexHeader.after($newTh); }
} else {
$headerRow.append($newTh);
}
const $existingItemAtIndexBody = $bodyRow.children().eq(index);
const $newTd = generateTd();
if ($existingItemAtIndexBody.length > 0) {
if (insert === "before") { $existingItemAtIndexBody.before($newTd); }
else { $existingItemAtIndexBody.after($newTd); }
} else {
$bodyRow.append($newTd);
}
safeRefreshSortable();
});
$headerRow.sortable({
connectWith: "#headerRowRankLiveConfig",
items: ".headerThRankLiveConfig",
placeholder: "dropPlaceholderColumn",
helper: "clone",
appendTo: "body",
tolerance: "pointer",
start: function(e, ui) {
const elementWidth = ui.item.outerWidth() - 4;
const elementHeight = ui.item.outerHeight() - 4;
const oldindex = ui.item.index();
ui.item.data('old-index', oldindex);
ui.placeholder.width(elementWidth);
ui.placeholder.height(elementHeight);
},
stop: function(e, ui) {
const oldIndex = ui.item.data('old-index');
const newIndex = ui.item.index();
if (oldIndex === undefined || oldIndex === newIndex) return;
const $container = $('#bodyRowRankLiveConfig');
const $targetTd = $container.children().eq(oldIndex);
if ($targetTd.length !== 1) return;
if (newIndex > oldIndex) {
$targetTd.insertAfter($container.children().eq(newIndex));
} else {
$targetTd.insertBefore($container.children().eq(newIndex));
}
}
}).disableSelection();
function safeRefreshSortable() {
$(".dragableDropArea").each(function () {
if ($(this).data("ui-sortable") || $(this).data("sortable")) {
$(this).sortable("refresh");
} else {
$(this).sortable({
connectWith: ".dragableDropArea",
items: ".dragable",
placeholder: "dropPlaceholder",
helper: "clone",
appendTo: "body",
tolerance: "intersect",
start: function(e, ui) {
const elementWidth = ui.item.outerWidth() - 4;
const elementHeight = ui.item.outerHeight() - 4;
ui.placeholder.width(elementWidth);
ui.placeholder.height(elementHeight);
}
}).disableSelection();
}
});
}
safeRefreshSortable();
let lindex = <?= $lindex ?? 0 ?>;
function createCheckbox(className, id, isChecked = false) {
const $label = $('<label>', { class: 'checkbox' });
const $input = $('<input>', {
type: 'checkbox',
id: id,
class: className
});
if (isChecked) {
$input.prop('checked', true);
}
const $span = $('<span>', { class: 'checkbox-ui' });
return $label.append($input).append($span);
}
function settingsDiv(type) {
lindex ++;
const $div = $('<div></div>').addClass('settingsDivSingleValueSpan hidden');
switch (type) {
case "text":
const $sdiv1 = $('<div></div>').addClass('settingsInputGroup');
const $slabel1 = $('<label></label>').text('Text:').attr('for', `statischerTextInput-${lindex}`);
const $sinput1 = $('<input>').attr('placeholder', 'Statischer Text').addClass('statischerTextInput').attr('id', `statischerTextInput-${lindex}`);
$sdiv1.append($slabel1, $sinput1);
$div.append($sdiv1);
break;
case "notenData":
case "notenGeraetData":
const $sdiv2 = $('<div></div>').addClass('settingsInputGroup');
const $slabel2 = $('<label></label>').html('Wert 0.00 <b>nicht</b> anzeigen?').attr('for', `nullNotenCheckbox-${lindex}`);
const $sinput2 = createCheckbox('nullNotenCheckbox', `nullNotenCheckbox-${lindex}`);
$sdiv2.append($slabel2, $sinput2);
$div.append($sdiv2);
break;
}
if (type !== 'kampfrichterEls') {
const $boldDiv = $('<div></div>').addClass('settingsInputGroup');
const $label1 = $('<label></label>').text('Fetter Text:').attr('for', `boldCheckbox-${lindex}`);
const $input1 = createCheckbox('boldCheckbox', `boldCheckbox-${lindex}`);
$boldDiv.append($label1, $input1);
const $fontSizeDiv = $('<div></div>').addClass('settingsInputGroup');
const $labelFontSize = $('<label></label>').text('Schriftgrösse').attr('for', `inputFontSize-${lindex}`);
const $inputFontSize = $('<input>').attr('placeholder', '16').attr("type", "number").addClass('inputFontSize').attr('id', `inputFontSize-${lindex}`).attr("min", "1");
const $pxSpanFontSize = $('<span></span>').text('px');
const $labelWraperFontSize = $('<label></label>').addClass("inputLabelWrapper").attr('for', `inputFontSize-${lindex}`);
$labelWraperFontSize.append($inputFontSize, $pxSpanFontSize);
$fontSizeDiv.append($labelFontSize, $labelWraperFontSize);
const $mobileDiv = $('<div></div>').addClass('settingsInputGroup');
const $labelM = $('<label></label>').text('Minimale Bildschirmbreite').attr('for', `inputDisplayOnDisplayWidth-${lindex}`);
const $inputM = $('<input>').attr('placeholder', 'Kein Limit').attr("type", "number").addClass('inputDisplayOnDisplayWidth').attr('id', `inputDisplayOnDisplayWidth-${lindex}`);
const $pxSpan = $('<span></span>').text('px');
const $labelWraper = $('<label></label>').addClass("inputLabelWrapper").attr('for', `inputDisplayOnDisplayWidth-${lindex}`);
$labelWraper.append($inputM, $pxSpan);
$mobileDiv.append($labelM, $labelWraper);
const $conditionalFormatingDiv = $('<div></div>').addClass('settingsInputGroup');
const $labelCD = $('<label></label>').text('Conditional Display').attr('for', `inputConditionalDisplay-${lindex}`);
const $inputCD = $('<button></button>').text('Element auswählen…').attr('type', 'button').addClass('inputConditionalDisplay').attr('id', `inputConditionalDisplay-${lindex}`);
const $selectedElSpan = $('<span></span>').addClass('spanConditionalDisplay');
$conditionalFormatingDiv.append($labelCD, $inputCD, $selectedElSpan);
$div.append($boldDiv, $fontSizeDiv, $conditionalFormatingDiv, $mobileDiv);
}
const $removeDiv = $('<div></div>').addClass('settingsInputGroup');
const $labelR = $('<label></label>').text('Löschen').attr('for', `deleteSingleValueSpan-${lindex}`);
const $inputR = $('<button></button>').text('X').addClass('deleteSingleValueSpan').attr('id', `deleteSingleValueSpan-${lindex}`);
$removeDiv.append($labelR, $inputR);
$div.append($removeDiv);
return $div;
}
$(document).on('click', '.boldCheckbox', function () {
const $el = $(this);
const $parent = $el.closest('.singleValueSpan');
if ($parent.length !== 1) return;
const oldValue = $parent.attr('data-bold') ?? false;
const oldBool = oldValue === true || oldValue === 'true';
$parent.attr('data-bold', !oldBool);
});
$(document).on('click', '.nullNotenCheckbox', function () {
const $el = $(this);
const $parent = $el.closest('.singleValueSpan');
if ($parent.length !== 1) return;
const oldValue = $parent.attr('data-noten-nullable') ?? false;
const oldBool = oldValue === true || oldValue === 'true';
$parent.attr('data-noten-nullable', !oldBool);
});
$(document).on('input', '.statischerTextInput', function () {
const $el = $(this);
const val = $el.val();
const $target = $el.closest('.singleValueSpan').children(".textSingleValueSpan");
if ($target.length !== 1) return;
$target.html(val);
});
$(document).on('input', '.inputFontSize', function () {
const $el = $(this);
const val = parseInt($el.val(), 10) || 0;
const $parent = $el.closest('.singleValueSpan');
if ($parent.length !== 1) return;
$parent.data('font-size', val);
const $textSpan = $parent.children('.textSingleValueSpan');
if ($textSpan.length !== 1) return;
if (val === 0) {
$textSpan.css('font-size', '');
} else {
$textSpan.css('font-size', val + 'px');
}
});
$(document).on('input', '.gebutsdatumFormatInput', function () {
const $el = $(this);
const val = $el.val();
const $parent = $el.closest('.singleValueSpan');
if ($parent.length !== 1) return;
$parent.data('php-date-format', val);
});
$(document).on('input', '.singleValueSpan .inputDisplayOnDisplayWidth', function () {
const $el = $(this);
const val = $el.val();
const $parent = $el.closest('.singleValueSpan');
if ($parent.length !== 1) return;
$parent.attr('data-min-display-width', val);
});
$(document).on('input', 'th .inputDisplayOnDisplayWidth', function () {
const $el = $(this);
const val = $el.val();
const $parent = $el.closest('th');
if ($parent.length !== 1) return;
$parent.attr('data-min-display-width', val);
});
$(document).on('click', '.settingSpan', function (e) {
const $el = $(this);
const $targetEl = $el.siblings('.settingsDivSingleValueSpan');
if ($targetEl.length > 0) {
$targetEl.toggleClass('hidden');
$('.settingsDivSingleValueSpan').not($targetEl).addClass('hidden');
} else {
$('.settingsDivSingleValueSpan').addClass('hidden');
}
});
function toggleSortable(bool) {
const sortableText = bool ? "disable" : "enable";
$(".dragableDropArea").each(function () {
if ($(this).data("ui-sortable") || $(this).data("sortable")) {
$(this).sortable(sortableText);
}
});
}
$(document).on('click', '.inputConditionalDisplay', function (e) {
const $btn = $(this);
const $table = $('table.mainTable');
if ($table.length !== 1) return;
const $container = $btn.closest('.singleValueSpan');
if ($container.length !== 1) return;
$container.toggleClass('condEl');
$table.toggleClass('selectCondEl');
const tableHasClass = $table.hasClass('selectCondEl');
toggleSortable(tableHasClass);
$('.selectedCondEl').removeClass('selectedCondEl');
const savedNativeElUid = $container.attr('data-linked-el-uid') || false;
if (savedNativeElUid) {
const $oldSelectedEl = $table.find(`.singleValueSpan[data-uid=${savedNativeElUid}]`);
if ($oldSelectedEl.length === 1) {
$oldSelectedEl.addClass('selectedCondEl');
}
}
if (tableHasClass) {
$table.on('click.condDisplay', '.singleValueSpan:not(.condEl)', function(e2) {
const $clickedSpan = $(e2.target).closest('.singleValueSpan');
if ($clickedSpan.length !== 1) return;
$container.attr('data-linked-el-uid', $clickedSpan.attr('data-uid'));
$table.removeClass('selectCondEl');
$container.removeClass('condEl');
toggleSortable(false);
$table.off('click.condDisplay', '.singleValueSpan:not(.condEl)');
});
} else {
$table.off('click.condDisplay', '.singleValueSpan:not(.condEl)');
}
});
$(document).on('click', '.deleteSingleValueSpan', async function (e) {
const $el = $(this);
const $targetEl = $el.closest('.singleValueSpan');
if (!await displayConfirm("Feld löschen?")) return;
if ($targetEl.length > 0) {
$targetEl.remove();
}
});
$(document).on('click', '.customSelectAddOption .selectOptions li', function (e) {
e.stopPropagation();
const $item = $(this);
const $container = $item.closest('.customSelect');
const text = $item.text();
const $input = $container.find('.selectValue');
const $textInput = $container.find('.selectText') ?? [];
const $typeInput = $container.find('.selectType') ?? [];
const $geraetInput = $container.find('.selectGeraet') ?? [];
const $label = $container.find('.selectLabel');
const $allOptions = $container.find('.selectOptions li');
$allOptions.removeClass('selected');
$input.trigger('change');
$container.removeClass('open');
const type = $item.data("type") ?? null;
if (type === 'layoutEls') {
const $newDiv = $('<div></div>');
const dataColumn = $item.data("column") ?? null;
$newDiv.attr("data-column", dataColumn).attr("data-type", type).addClass('dragable');
switch (dataColumn) {
case 'vertical':
$newDiv.addClass('verticalLayoutEl');
const $dropSpan = $('<span></span>').addClass('dragableDropArea verticalLayoutElRow');
$newDiv.append($dropSpan, $dropSpan.clone())
break;
}
$activeEl.append($newDiv);
} else {
$span = $('<span></span>').addClass('singleValueSpan dragable').attr("data-type", type).attr('data-uid', getNextAvailableUid());
currentUid++;
const $textSpan = $('<span></span>').text(text).addClass('textSingleValueSpan');
if (type === 'notenData') {
$span.attr("data-geraet-id", $item.data("geraet-id") ?? null);
$span.attr("data-field-type-id", $item.data("field-type-id") ?? null);
$span.attr("data-run", $item.data("run") ?? null);
$span.attr("data-programm-ids", $item.data("programm-ids") ?? null);
} else if (type === 'notenGeraetData') {
$span.attr("data-field-type-id", $item.data("field-type-id") ?? null);
$span.attr("data-run", $item.data("run") ?? null);
} else {
$span.attr("data-column", $item.data("column") ?? null);
}
if (type === 'text') {
$span.addClass('staticText');
}
$span.append($textSpan, settingsDiv(type), settingsSvg());
$activeEl.append($span);
}
$activeEl = null;
$customSelectColumns.addClass('hidden');
safeRefreshSortable();
});
$(document).on('click', () => $('.customSelect').removeClass('open'));
$(document).on('click', function (event) {
const clickedInsideProtected = $(event.target).closest('.settingSpan, .settingsDivSingleValueSpan, .settingsInputGroup').length > 0;
if (clickedInsideProtected) {
return;
}
$('.settingsDivSingleValueSpan').addClass('hidden');
});
$('body').on('click', '.deleteColumnBtn', async function () {
if (!await displayConfirm("Spalte löschen?")) return;
const $el = $(this);
const $th = $el.closest('th');
const index = $th.index();
const $existingItemAtIndex = $('#bodyRowRankLiveConfig').children().eq(index);
if ($existingItemAtIndex.length < 1) {
displayMsg(0, "Fehler");
return;
}
$th.remove();
$existingItemAtIndex.remove();
showAddBtnOnEmtyHeader();
});
function showAddBtnOnEmtyHeader() {
if ($('#headerRowRankLiveConfig').children('th').length < 1) $('#addColumn').removeClass('hidden');
}
function generateTargetDataForEl($target, type) {
const minDisplayWidth = parseInt($target.attr('data-min-display-width') ?? 0, 10) || 0;
const elUid = parseInt($target.attr('data-uid') ?? 0, 10) || 0;
let targetData = {
type: type,
text: $target.children('.textSingleValueSpan').text(),
minDisplayWidth,
uid: elUid
};
if (type !== 'kampfrichterEls') {
const bold = $target.attr('data-bold') ?? false;
const fontSize = parseInt($target.data('font-size') ?? 0, 10) || 0;
const condElUid = parseInt($target.attr('data-linked-el-uid') ?? 0, 10) || 0;
targetData["bold"] = bold;
targetData["fontSize"] = fontSize;
targetData["linkedElUid"] = condElUid;
}
switch (type) {
case "notenData":
targetData["field-type-id"] = $target.data('field-type-id');
targetData["geraet-id"] = $target.data("geraet-id");
targetData["run"] = $target.data('run');
targetData["programm-ids"] = $target.data("programm-ids");
targetData["notenNullable"] = $target.attr("data-noten-nullable") ?? "false";
break;
case "notenGeraetData":
targetData["field-type-id"] = $target.data('field-type-id');
targetData["run"] = $target.data('run');
targetData["notenNullable"] = $target.attr("data-noten-nullable") ?? "false";
break;
case "text":
targetData.content = $target.data('column');
break;
case "kampfrichterEls":
targetData.content = $target.data('column');
break;
case "sortData":
targetData.column = $target.data('column');
break;
case "personData":
targetData.column = $target.data('column');
if ($target.data('column') === 'geburtsdatum') {
targetData["phpDateFormat"] = $target.data('php-date-format') ?? '';
}
break;
default:
return null;
}
return targetData;
}
function saveTable() {
const $headerRow = $('#headerRowRankLiveConfig');
const $bodyRow = $('#bodyRowRankLiveConfig');
const $headerColumns = $headerRow.children('th');
const $bodyColumns = $bodyRow.children('td');
let json = {
header: [],
body: []
};
$.each($headerColumns, function (index, el) {
const $header = $(el);
const $input = $header.find(".columnTitleInput");
const val = $input.val() || '';
const minDisplayWidth = parseInt($header.attr('data-min-display-width') ?? 0, 10) || 0;
let targetData = {
title: val,
minDisplayWidth
}
json.header.push(targetData);
});
$.each($bodyColumns, function (index, el) {
const $targets = $(el).children(".rankLiveFieldContainer").children(":not(.addFieldBtn)");
json.body[index] = [];
if ($targets.length > 0) {
$.each($targets, function (tindex, tel) {
const $target = $(tel);
const type = $target.data('type');
let targetData = {};
if (type === 'layoutEls') {
targetData = {
type,
column: $target.attr("data-column"),
content: []
};
const $rows = $target.children(".verticalLayoutElRow");
$.each($rows, function (rindex, trow) {
targetData['content'][rindex] = [];
const $row_targets = $(trow).children(".singleValueSpan");
$.each($row_targets, function (trowindex, trowel) {
const $trowel = $(trowel);
const trowelType = $trowel.data('type');
const subres = generateTargetDataForEl($trowel, trowelType);
if (subres === null) return null;
targetData['content'][rindex][trowindex] = subres;
});
});
} else {
targetData = generateTargetDataForEl($target, type);
if (targetData === null) return null;
}
json.body[index].push(targetData);
});
}
});
const jsonstr = JSON.stringify(json);
return jsonstr;
}
$('#saveConfig').on('click', function () {
const json = saveTable();
if (json === null) {
displayMsg(0, "Fehler beim Konertieren der RankLive Tabelle");
return;
}
$.ajax({
url: '/intern/scripts/ranklive/ajax-save-ranklive-config.php',
method: 'POST',
data: {
csrf_token,
slug: active_type_slug,
json: json
},
success: function (response) {
if (typeof response === "string") {
response = response ? JSON.parse(response) : {};
}
if (response.success) {
displayMsg(1, "OK");
} else {
displayMsg(2, response.message || "Unbekannter Fehler");
}
},
error: function (xhr, status, errorThrown) {
displayMsg(0, "Fehler");
console.error(xhr);
}
});
});
function loadTable(jsonInput) {
let json;
try {
json = typeof jsonInput === "string" ? JSON.parse(jsonInput) : jsonInput;
} catch (e) {
console.error("Invalid JSON provided to loadTable:", e);
return false;
}
if (!json || !json.header || !json.body) {
console.error("JSON layout structure is missing header or body blocks.");
return false;
}
const $headerRow = $('#headerRowRankLiveConfig');
const $bodyRow = $('#bodyRowRankLiveConfig');
$headerRow.empty();
$bodyRow.empty();
$('#addColumn').addClass('hidden');
$.each(json.header, function (index, headerData) {
const $th = generateTh();
$th.find('.columnTitleInput').val(headerData.title || '');
if (headerData.minDisplayWidth) {
$th.attr('data-min-display-width', headerData.minDisplayWidth);
$th.find('.inputDisplayOnDisplayWidth').val(headerData.minDisplayWidth);
}
$headerRow.append($th);
});
$.each(json.body, function (index, columnItems) {
const $td = generateTd();
const $container = $td.find('.rankLiveFieldContainer');
const $addBtn = $container.find('.addFieldBtn');
if (columnItems && columnItems.length > 0) {
$.each(columnItems, function (tindex, itemData) {
let $element;
if (itemData.type === 'layoutEls') {
$element = $('<div></div>')
.attr("data-column", itemData.column)
.attr("data-type", 'layoutEls')
.addClass('dragable');
if (itemData.column === 'vertical') {
$element.addClass('verticalLayoutEl');
}
if (itemData.content && Array.isArray(itemData.content)) {
$.each(itemData.content, function (rindex, rowItems) {
const $dropSpan = $('<span></span>').addClass('dragableDropArea verticalLayoutElRow');
if (rowItems && Array.isArray(rowItems)) {
$.each(rowItems, function (trowindex, subItemData) {
const $subEl = generateSingleValueSpanFromData(subItemData);
if ($subEl) $dropSpan.append($subEl);
});
}
$element.append($dropSpan);
});
}
} else {
$element = generateSingleValueSpanFromData(itemData);
}
if ($element) {
$addBtn.before($element);
}
});
}
$bodyRow.append($td);
});
showAddBtnOnEmtyHeader();
safeRefreshSortable();
return true;
}
// Internal functional helper to systematically convert a single data object back to a DOM node
function generateSingleValueSpanFromData(data) {
if (!data || !data.type) return null;
const type = data.type;
const elUid = data.uid || 0;
const $span = $('<span></span>').addClass('singleValueSpan dragable').attr("data-type", type).attr("data-uid", elUid);
const $textSpan = $('<span></span>').text(data.text || '').addClass('textSingleValueSpan');
// Leverage your existing DOM generator to keep control tracking context unique (lindex)
const $settings = settingsDiv(type);
// Reconstruct Global Layout/Styling Properties
if (data.bold === true || data.bold === 'true') {
$span.attr('data-bold', 'true');
$settings.find('.boldCheckbox').prop('checked', true);
}
if (data.fontSize) {
const fontSizeVal = parseInt(data.fontSize, 10) || 0;
$span.data('font-size', fontSizeVal);
$textSpan.css('font-size', fontSizeVal + 'px');
$settings.find('.inputFontSize').val(fontSizeVal);
}
if (data.minDisplayWidth) {
const minWidthVal = parseInt(data.minDisplayWidth, 10) || 0;
$span.attr('data-min-display-width', minWidthVal);
$settings.find('.inputDisplayOnDisplayWidth').val(minWidthVal);
}
if (data.linkedElUid) {
const linkedElUid = parseInt(data.linkedElUid, 10) || 0;
$span.attr('data-linked-el-uid', linkedElUid);
}
// Reconstruct Context-Specific Metadata Properties
switch (type) {
case "notenData":
case "notenGeraetData":
const zeroNotenCheck = data["notenNullable"] === true || data["notenNullable"] === 'true';
$span.attr("data-noten-nullable", zeroNotenCheck ? "true" : "false");
$settings.find('.nullNotenCheckbox').prop('checked', zeroNotenCheck);
case "notenData":
$span.attr("data-geraet-id", data["geraet-id"] ?? null);
$span.attr("data-field-type-id", data["field-type-id"] ?? null);
$span.attr("data-run", data["run"] ?? null);
$span.attr("data-programm-ids", data["programm-ids"] ?? null);
break;
case "notenGeraetData":
$span.attr("data-field-type-id", data["field-type-id"] ?? null);
$span.attr("data-run", data["run"] ?? null);
break;
case "text":
$span.attr("data-column", data.content ?? null);
$span.addClass('staticText');
$settings.find('.statischerTextInput').val(data.text || '');
break;
case "kampfrichterEls":
$span.attr("data-column", data.content ?? null);
break;
case "personData":
$span.attr("data-column", data.column ?? null);
if (data["phpDateFormat"]) {
$span.data('php-date-format', data["phpDateFormat"]);
$settings.find('.gebutsdatumFormatInput').val(data["phpDateFormat"]);
}
break;
case "sortData":
$span.attr("data-column", data.column ?? null);
break;
}
$span.append($textSpan, $settings, settingsSvg());
return $span;
}
const $contentDiv = $('.contentDiv');
const $inDevDiv = $('.inDevDiv');
function configureAddOptionDropdown(type, geraet) {
if (type === 'indev' || type === 'rangliste') {
$contentDiv.addClass('hidden');
$inDevDiv.removeClass('hidden');
return;
}
$contentDiv.removeClass('hidden');
$inDevDiv.addClass('hidden');
const $selectOptions = $customSelectColumns.find(".selectOptions");
if ($selectOptions.length !== 1) return;
const $sectionNotenAllData = $selectOptions.find('div.notenData');
const $sectionNotenGeraetData = $selectOptions.find('div.notenGeraetData');
if ($sectionNotenAllData.length !== 1 || $sectionNotenGeraetData.length !== 1) return;
const geraetAll = geraet === 'all';
$sectionNotenAllData.toggleClass('hidden', !geraetAll);
$sectionNotenGeraetData.toggleClass('hidden', geraetAll);
}
configureAddOptionDropdown('<?= $allowed_types[$active_slug]['type'] ?>', '<?= $allowed_types[$active_slug]['geraet'] ?>');
$(document).on('click', '.customSelectSlugType .selectOptions li', function (e) {
e.stopPropagation();
const $item = $(this);
const $container = $item.closest('.customSelect');
const text = $item.text();
const $label = $container.find('.selectLabel');
const $allOptions = $container.find('.selectOptions li');
const val = $item.attr('data-value') ?? '';
const type = $item.attr('data-type') ?? '';
const geraet = $item.attr('data-geraet') ?? '';
$allOptions.removeClass('selected');
$item.addClass('selected');
$label.text(text).removeClass('placeholder');
$container.removeClass('open');
$.ajax({
url: '/intern/scripts/ranklive/ajax-get-ranklive-config.php',
method: 'POST',
data: {
csrf_token,
slug: val
},
success: function (response) {
if (typeof response === "string") {
response = response ? JSON.parse(response) : {};
}
if (!response.success) {
displayMsg(2, response.message || "Unbekannter Fehler");
return;
}
active_type_slug = val;
$customSelectColumns.addClass('hidden');
if (response.data === "") {
$('#headerRowRankLiveConfig').html('');
$('#bodyRowRankLiveConfig').html('');
$('#addColumn').removeClass('hidden');
} else {
loadTable(response.data);
}
configureAddOptionDropdown(type, geraet);
},
error: function (xhr, status, errorThrown) {
displayMsg(0, "Fehler");
console.error(xhr);
}
});
});
$('#importConfig').on('change', async function () {
if (!await displayConfirm("Die aktuelle Konfiguration wird durch die ausgewählte Datei vollständig überschrieben!")) return;
const input = $(this)[0];
const formData = new FormData();
formData.append('csrf_token', csrf_token);
formData.append('configFile', input.files[0]);
formData.append('slug', active_type_slug);
$.ajax({
url: '/intern/scripts/ranklive/upload-config-file.php',
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: function (response) {
response = response ? JSON.parse(response) : {};
if (response.success) {
window.location.reload();
} else {
if (response.message) {
displayMsg(0, response.message);
} else {
displayMsg(0, "Unbekannter Fehler beim Hochladen der Config-Datei.");
}
}
},
error: function (xhr, status, errorThrown) {
let errorMsg = "Kritischer Fehler beim Hochladen der Config-Datei.";
if (xhr.responseText) {
try {
const response = JSON.parse(xhr.responseText);
if (response.message) {
errorMsg = response.message;
}
} catch (e) {
console.error("Could not parse JSON error response:", e);
}
}
displayMsg(0, errorMsg);
console.error(xhr);
}
});
});
$('#exportConfig').on('click', function () {
$.ajax({
url: '/intern/scripts/ranklive/export-config-file.php',
method: 'POST',
data: {
csrf_token: csrf_token,
slug: active_type_slug
},
dataType: 'text',
success: function(response, status, xhr) {
const disposition = xhr.getResponseHeader('Content-Disposition');
let filename = `${active_type_slug}.wkvstable`;
if (disposition && disposition.indexOf('attachment') !== -1) {
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
const matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
filename = matches[1].replace(/['"]/g, '');
}
}
const blob = new Blob([response], { type: 'application/json' });
const $tempLink = $('<a>', {
href: URL.createObjectURL(blob),
download: filename
});
$('body').append($tempLink);
$tempLink[0].click();
$tempLink.remove();
URL.revokeObjectURL($tempLink.attr('href'));
displayMsg(1, "Tabelle erfolgreich exportiert.");
},
error: function(xhr, status, error) {
displayMsg(0, "Tabelle konnte nicht exportiert werden.");
}
});
});
</script>
</body>
</html>
<?php endif; ?>