(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);