(function (window, document, $) { 'use strict'; if (!$) { return; } var settingsTimers = {}; var observerStarted = false; function intValue(value, fallback) { var n = parseInt(value, 10); if (isNaN(n)) { return typeof fallback === 'number' ? fallback : 0; } return Math.max(0, n); } function pageText($page, key, fallback) { var value = $page.attr('data-text-' + key); return (typeof value === 'string' && value !== '') ? value : (fallback || ''); } function rowSelector(categoryId, levelId, type) { return '.awards-row[data-row-type="' + type + '"][data-category-id="' + categoryId + '"][data-level-id="' + levelId + '"]'; } function scopeKey($row) { return [ $row.attr('data-scope-type') || 'category', intValue($row.attr('data-category-id')), intValue($row.attr('data-level-id')), intValue($row.attr('data-dance-id')) ].join(':'); } function timerKey($page, $row) { return ($page.index('#awardsPage') + 1) + ':' + scopeKey($row); } function competitionsForRow($row) { var raw = $row.attr('data-competitions') || '[]'; try { var parsed = JSON.parse(raw); return $.isArray(parsed) ? parsed : []; } catch (e) { return []; } } function emptyResult() { return { gold:0, silver:0, bronze:0, participation:0, cup_1:0, cup_2:0, cup_3:0 }; } function addResult(target, source) { $.each(['gold','silver','bronze','participation','cup_1','cup_2','cup_3'], function (_, key) { target[key] += intValue(source[key]); }); return target; } function readSettings($row) { return { cup_mode: $row.find('.awards-cup-mode').val() || 'none', team_medal_mode: $row.find('.awards-team-medal-mode').val() || 'placements', award_all: $row.find('.awards-award-all').is(':checked'), depth_mode: $row.find('.awards-depth-mode:checked').val() || 'fixed', fixed_places: Math.max(1, intValue($row.find('.awards-fixed-places').val(), 1)), threshold_entries: Math.max(1, intValue($row.find('.awards-threshold-entries').val(), 1)), threshold_low_places: Math.max(1, intValue($row.find('.awards-threshold-low').val(), 1)), threshold_high_places: Math.max(1, intValue($row.find('.awards-threshold-high').val(), 1)) }; } function writeSettings($row, settings) { $row.find('.awards-cup-mode').val(settings.cup_mode); $row.find('.awards-team-medal-mode').val(settings.team_medal_mode); $row.find('.awards-award-all').prop('checked', !!settings.award_all); $row.find('.awards-depth-mode[value="' + settings.depth_mode + '"]').prop('checked', true); $row.find('.awards-fixed-places').val(settings.fixed_places); $row.find('.awards-threshold-entries').val(settings.threshold_entries); $row.find('.awards-threshold-low').val(settings.threshold_low_places); $row.find('.awards-threshold-high').val(settings.threshold_high_places); } function isOverrideActive($row) { if (($row.attr('data-row-type') || '') === 'category') { return true; } return $row.find('.awards-override-enabled').is(':checked'); } function updateDepthUI($row, rowActive) { var awardAll = $row.find('.awards-award-all').is(':checked'); var mode = $row.find('.awards-depth-mode:checked').val() || 'fixed'; var depthDisabled = !rowActive || awardAll; $row.find('.awards-all-toggle') .toggleClass('is-active', awardAll) .toggleClass('is-control-disabled', !rowActive); $row.find('.awards-depth-settings').toggleClass('is-disabled', depthDisabled); $row.find('.awards-depth-mode, .awards-fixed-places, .awards-threshold-entries, .awards-threshold-low, .awards-threshold-high') .prop('disabled', depthDisabled); $row.find('.awards-fixed-panel').toggleClass('is-hidden', mode !== 'fixed'); $row.find('.awards-threshold-panel').toggleClass('is-hidden', mode !== 'threshold'); } function setRowActivation($row, active) { var $page = $row.closest('#awardsPage'); var rowType = $row.attr('data-row-type'); $row.toggleClass('is-settings-disabled', !active); $row.toggleClass('is-inherited', !active); $row.toggleClass('has-override', active && rowType !== 'category'); $row.find('.awards-cup-mode').prop('disabled', !active); if ($row.attr('data-is-team') === '1') { $row.find('.awards-team-medal-mode').prop('disabled', !active); } $row.find('.awards-award-all').prop('disabled', !active); var $override = $row.find('.awards-override-toggle'); if ($override.length) { $override.toggleClass('is-active', active); } updateDepthUI($row, active); if (!active) { var inheritedText = rowType === 'dance' ? pageText($page, 'inherited-level', '') : pageText($page, 'inherited-category', ''); $row.find('.awards-save-status').removeClass('is-saving is-saved is-error').text(inheritedText); } } function syncInheritance($page) { $page.find('.awards-category-row').each(function () { var $category = $(this); var categoryId = intValue($category.attr('data-category-id')); setRowActivation($category, true); var categorySettings = readSettings($category); $page.find(rowSelector(categoryId, 0, 'level').replace('[data-level-id="0"]','')).each(function () { /* This branch is intentionally unused; level rows are selected below. */ }); $page.find('.awards-level-row[data-category-id="' + categoryId + '"]').each(function () { var $level = $(this); var levelId = intValue($level.attr('data-level-id')); var levelActive = isOverrideActive($level); if (!levelActive) { writeSettings($level, categorySettings); } setRowActivation($level, levelActive); var levelEffective = readSettings($level); $page.find('.awards-dance-row[data-category-id="' + categoryId + '"][data-level-id="' + levelId + '"]').each(function () { var $dance = $(this); var danceActive = isOverrideActive($dance); if (!danceActive) { writeSettings($dance, levelEffective); } setRowActivation($dance, danceActive); }); }); }); } function awardedPlaces(entries, settings) { entries = intValue(entries); if (!entries) { return 0; } if (settings.award_all) { return entries; } var places = settings.depth_mode === 'threshold' ? (entries <= settings.threshold_entries ? settings.threshold_low_places : settings.threshold_high_places) : settings.fixed_places; return Math.min(entries, Math.max(1, intValue(places, 1))); } function calculateLeaf($row) { var result = emptyResult(); var settings = readSettings($row); var competitions = competitionsForRow($row); $.each(competitions, function (_, comp) { var entries = intValue(comp.entries); if (!entries) { return; } var awarded = awardedPlaces(entries, settings); if (!awarded) { return; } if (comp.type === 'team') { var teamSize = Math.max(1, intValue(comp.team_size, 1)); if (settings.team_medal_mode === 'participation') { result.participation += awarded * teamSize; } else { if (awarded >= 1) { result.gold += teamSize; } if (awarded >= 2) { result.silver += teamSize; } if (awarded >= 3) { result.bronze += teamSize; } if (awarded > 3) { result.participation += (awarded - 3) * teamSize; } } if ((settings.cup_mode === 'first' || settings.cup_mode === 'top3') && awarded >= 1) { result.cup_1 += 1; } if (settings.cup_mode === 'top3' && awarded >= 2) { result.cup_2 += 1; } if (settings.cup_mode === 'top3' && awarded >= 3) { result.cup_3 += 1; } return; } if (awarded >= 1) { if (settings.cup_mode === 'first' || settings.cup_mode === 'top3') { result.cup_1 += 1; } else { result.gold += 1; } } if (awarded >= 2) { if (settings.cup_mode === 'top3') { result.cup_2 += 1; } else { result.silver += 1; } } if (awarded >= 3) { if (settings.cup_mode === 'top3') { result.cup_3 += 1; } else { result.bronze += 1; } } if (awarded > 3) { result.participation += awarded - 3; } }); return result; } function writeResult($row, result) { $row.find('.awards-gold').text(intValue(result.gold)); $row.find('.awards-silver').text(intValue(result.silver)); $row.find('.awards-bronze').text(intValue(result.bronze)); $row.find('.awards-participation').text(intValue(result.participation)); $row.find('.awards-cup-1').text(intValue(result.cup_1)); $row.find('.awards-cup-2').text(intValue(result.cup_2)); $row.find('.awards-cup-3').text(intValue(result.cup_3)); } function resultFromRow($row) { return { gold: intValue($row.find('.awards-gold').text()), silver: intValue($row.find('.awards-silver').text()), bronze: intValue($row.find('.awards-bronze').text()), participation: intValue($row.find('.awards-participation').text()), cup_1: intValue($row.find('.awards-cup-1').text()), cup_2: intValue($row.find('.awards-cup-2').text()), cup_3: intValue($row.find('.awards-cup-3').text()) }; } function escapeHtml(value) { return $('
').text(value == null ? '' : value).html(); } function breakdownHtml($page, items) { if (!items.length) { return '
' + escapeHtml(pageText($page, 'no-cups', 'No cups')) + '
'; } var html = ''; $.each(items, function (_, item) { html += '
' + escapeHtml(item.label) + '' + item.value + '
'; }); return html; } function recalculateAll($page) { if (!$page || !$page.length) { return; } syncInheritance($page); var totals = emptyResult(); var cups = {1:[],2:[],3:[]}; /* 1. Calculate every leaf. */ $page.find('.awards-row[data-is-leaf="1"]').each(function () { var $leaf = $(this); var r = calculateLeaf($leaf); writeResult($leaf, r); addResult(totals, r); var label = $leaf.attr('data-award-label') || pageText($page, 'category', 'Category'); if (r.cup_1 > 0) { cups[1].push({label:label,value:r.cup_1}); } if (r.cup_2 > 0) { cups[2].push({label:label,value:r.cup_2}); } if (r.cup_3 > 0) { cups[3].push({label:label,value:r.cup_3}); } }); /* 2. Aggregate non-leaf levels from their dance children. */ $page.find('.awards-level-row[data-is-leaf="0"]').each(function () { var $level = $(this); var categoryId = intValue($level.attr('data-category-id')); var levelId = intValue($level.attr('data-level-id')); var sum = emptyResult(); $page.find('.awards-dance-row[data-category-id="' + categoryId + '"][data-level-id="' + levelId + '"]').each(function () { addResult(sum, resultFromRow($(this))); }); writeResult($level, sum); }); /* 3. Aggregate categories from their levels. */ $page.find('.awards-category-row').each(function () { var $category = $(this); var categoryId = intValue($category.attr('data-category-id')); var sum = emptyResult(); $page.find('.awards-level-row[data-category-id="' + categoryId + '"]').each(function () { addResult(sum, resultFromRow($(this))); }); writeResult($category, sum); }); var totalMedals = totals.gold + totals.silver + totals.bronze + totals.participation; $page.find('#awardsSummaryGold').text(totals.gold); $page.find('#awardsSummarySilver').text(totals.silver); $page.find('#awardsSummaryBronze').text(totals.bronze); $page.find('#awardsSummaryParticipation').text(totals.participation); $page.find('#awardsSummaryMedals').text(totalMedals); $.each([1,2,3], function (_, place) { var cupTotal = 0; $.each(cups[place], function (_, item) { cupTotal += item.value; }); $page.find('#awardsSummaryCup' + place).text(cupTotal); $page.find('#awardsCup' + place + 'Breakdown').html(breakdownHtml($page, cups[place])); }); } function setRowStatus($row, state, text) { var $status = $row.find('.awards-save-status'); $status.removeClass('is-saving is-saved is-error'); if (state) { $status.addClass('is-' + state); } $status.text(text || ''); } function saveSettings($page, $row) { var rowType = $row.attr('data-row-type') || 'category'; var overrideEnabled = rowType === 'category' ? 1 : ($row.find('.awards-override-enabled').is(':checked') ? 1 : 0); var settings = readSettings($row); setRowStatus($row, 'saving', pageText($page, 'saving', 'Saving...')); return $.ajax({ url: $page.attr('data-actions-url') || 'actions.php', type: 'POST', dataType: 'json', data: { action: 'save_award_scope_settings', scope_type: $row.attr('data-scope-type') || rowType, category_id: intValue($row.attr('data-category-id')), level_id: intValue($row.attr('data-level-id')), dance_id: intValue($row.attr('data-dance-id')), override_enabled: overrideEnabled, cup_mode: settings.cup_mode, team_medal_mode: settings.team_medal_mode, award_all: settings.award_all ? 1 : 0, depth_mode: settings.depth_mode, fixed_places: settings.fixed_places, threshold_entries: settings.threshold_entries, threshold_low_places: settings.threshold_low_places, threshold_high_places: settings.threshold_high_places } }).done(function (response) { if (response && response.ok) { setRowStatus($row, 'saved', pageText($page, 'saved', 'Saved')); } else { setRowStatus($row, 'error', (response && response.error) || pageText($page, 'save-failed', 'Save failed')); } }).fail(function (xhr) { var error = pageText($page, 'save-failed', 'Save failed'); if (xhr.responseJSON && xhr.responseJSON.error) { error = xhr.responseJSON.error; } setRowStatus($row, 'error', error); }); } function scheduleSave($page, $row) { var key = timerKey($page, $row); clearTimeout(settingsTimers[key]); setRowStatus($row, 'saving', pageText($page, 'changed', 'Changed...')); settingsTimers[key] = setTimeout(function () { saveSettings($page, $row); }, 450); } function normalizeNumericInput($input) { if ($input.val() === '') { return; } $input.val(Math.max(1, intValue($input.val(), 1))); } function toggleCategory($page, $row) { var categoryId = intValue($row.attr('data-category-id')); var $button = $row.find('.awards-category-toggle'); var expanded = $button.attr('aria-expanded') === 'true'; expanded = !expanded; $button.attr('aria-expanded', expanded ? 'true' : 'false'); $row.toggleClass('is-expanded', expanded); $row.find('.awards-category-chevron').html(expanded ? '▾' : '▸'); $row.find('.awards-toggle-caption').text(pageText($page, expanded ? 'hide-levels' : 'show-levels', '')); $page.find('.awards-level-row[data-category-id="' + categoryId + '"]').toggleClass('is-hidden-row', !expanded); if (!expanded) { $page.find('.awards-level-row[data-category-id="' + categoryId + '"]').removeClass('is-expanded'); $page.find('.awards-level-row[data-category-id="' + categoryId + '"] .awards-level-toggle').attr('aria-expanded','false'); $page.find('.awards-level-row[data-category-id="' + categoryId + '"] .awards-level-chevron').html('▸'); $page.find('.awards-level-row[data-category-id="' + categoryId + '"] .awards-toggle-caption').text(pageText($page,'show-dances','')); $page.find('.awards-dance-row[data-category-id="' + categoryId + '"]').addClass('is-hidden-row'); } } function toggleLevel($page, $row) { var categoryId = intValue($row.attr('data-category-id')); var levelId = intValue($row.attr('data-level-id')); var $button = $row.find('.awards-level-toggle'); var expanded = $button.attr('aria-expanded') === 'true'; expanded = !expanded; $button.attr('aria-expanded', expanded ? 'true' : 'false'); $row.toggleClass('is-expanded', expanded); $row.find('.awards-level-chevron').html(expanded ? '▾' : '▸'); $row.find('.awards-toggle-caption').text(pageText($page, expanded ? 'hide-dances' : 'show-dances', '')); $page.find('.awards-dance-row[data-category-id="' + categoryId + '"][data-level-id="' + levelId + '"]').toggleClass('is-hidden-row', !expanded); } function bindDelegatedEvents() { $(document) .off('.awardsPlanner') .on('click.awardsPlanner', '#awardsPage .awards-category-toggle', function (e) { e.preventDefault(); var $row = $(this).closest('.awards-category-row'); toggleCategory($row.closest('#awardsPage'), $row); }) .on('click.awardsPlanner', '#awardsPage .awards-level-toggle', function (e) { e.preventDefault(); var $row = $(this).closest('.awards-level-row'); toggleLevel($row.closest('#awardsPage'), $row); }) .on('change.awardsPlanner', '#awardsPage .awards-override-enabled', function () { var $row = $(this).closest('.awards-row'); var $page = $row.closest('#awardsPage'); recalculateAll($page); scheduleSave($page, $row); }) .on('input.awardsPlanner change.awardsPlanner', '#awardsPage .awards-setting-input', function () { var $input = $(this); var $row = $input.closest('.awards-row'); var $page = $row.closest('#awardsPage'); if ($input.is('[type="number"]')) { normalizeNumericInput($input); } recalculateAll($page); scheduleSave($page, $row); }) .on('click.awardsPlanner', '#awardsPage #awardsSaveAll', function () { var $button = $(this); var $page = $button.closest('#awardsPage'); var originalText = $button.text(); var requests = []; recalculateAll($page); $button.prop('disabled', true).text(pageText($page, 'saving', 'Saving...')); $page.find('.awards-row').each(function () { requests.push(saveSettings($page, $(this))); }); $.when.apply($, requests).always(function () { $button.prop('disabled', false).text(originalText); }); }); } function initPage($page) { if (!$page || !$page.length) { return; } recalculateAll($page); } function initAll() { $('#awardsPage').each(function () { initPage($(this)); }); } function startObserver() { if (observerStarted || !window.MutationObserver || !document.body) { return; } observerStarted = true; new MutationObserver(function (mutations) { var found = false; $.each(mutations, function (_, mutation) { $.each(mutation.addedNodes || [], function (_, node) { if (node.nodeType !== 1) { return; } if ($(node).is('#awardsPage') || $(node).find('#awardsPage').length) { found = true; } }); }); if (found) { initAll(); } }).observe(document.body, {childList:true, subtree:true}); } bindDelegatedEvents(); $(function () { initAll(); startObserver(); }); window.AwardsPlanner = { init: initAll, recalculateAll: function () { $('#awardsPage').each(function () { recalculateAll($(this)); }); } }; })(window, document, window.jQuery);