(function (window, document, $) { 'use strict'; if (!$) { return; } var timers = {}; var observerStarted = false; var radioCounter = 0; 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) { var value = $page.attr('data-text-' + key); return typeof value === 'string' ? value : ''; } function parseModel($page) { var $script = $page.find('#awardsPlannerModel'); if (!$script.length) { return {}; } try { return JSON.parse($script.text() || '{}'); } catch (e) { return {}; } } function overrideKey(categoryId, levelId, danceId) { return [intValue(categoryId), intValue(levelId), intValue(danceId)].join(':'); } function ageOverrideKey(categoryId, levelId, danceId, ageGroupId) { return [intValue(categoryId), intValue(levelId), intValue(danceId), intValue(ageGroupId)].join(':'); } function getState($page) { var state = $page.data('awardsPlannerState'); if (state) { return state; } var model = parseModel($page); state = { model: model, leaves: model.leaves || [], selectors: model.selectors || [], categoryLabels: model.category_labels || {}, overrides: $.extend(true, {}, model.overrides || {}), leafIndex: {}, selectorIndex: {} }; $.each(state.leaves, function (_, leaf) { state.leafIndex[overrideKey(leaf.category_id, leaf.level_id, leaf.dance_id)] = leaf; }); $.each(state.selectors, function (_, category) { state.selectorIndex[String(category.id)] = category; }); $page.data('awardsPlannerState', state); return state; } 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 emptyStats() { return { competitions:0, entries:0, teams:0, planned_dancers:0 }; } function statsForCompetitions(competitions) { var stats = emptyStats(); $.each(competitions || [], function (_, competition) { var entries = intValue(competition.entries); if (!entries) { return; } stats.competitions += 1; stats.entries += entries; if (competition.type === 'team') { var teamSize = Math.max(1, intValue(competition.team_size, 1)); stats.teams += entries; stats.planned_dancers += entries * teamSize; } }); return stats; } function cloneSettings(settings) { settings = settings || {}; return { cup_mode: settings.cup_mode || 'none', team_medal_mode: settings.team_medal_mode || 'placements', award_all: !!settings.award_all, depth_mode: settings.depth_mode === 'threshold' ? 'threshold' : 'fixed', fixed_places: Math.max(1, intValue(settings.fixed_places, 6)), threshold_entries: Math.max(1, intValue(settings.threshold_entries, 5)), threshold_low_places: Math.max(1, intValue(settings.threshold_low_places, 5)), threshold_high_places: Math.max(1, intValue(settings.threshold_high_places, 6)), override_enabled: intValue(settings.override_enabled) === 1 ? 1 : 0 }; } function readSettings($scope) { return { cup_mode: $scope.find('.awards-cup-mode').val() || 'none', team_medal_mode: $scope.find('.awards-team-medal-mode').val() || 'placements', award_all: $scope.find('.awards-award-all').is(':checked'), depth_mode: $scope.find('.awards-depth-mode:checked').val() || 'fixed', fixed_places: Math.max(1, intValue($scope.find('.awards-fixed-places').val(), 1)), threshold_entries: Math.max(1, intValue($scope.find('.awards-threshold-entries').val(), 1)), threshold_low_places: Math.max(1, intValue($scope.find('.awards-threshold-low').val(), 1)), threshold_high_places: Math.max(1, intValue($scope.find('.awards-threshold-high').val(), 1)) }; } function writeSettings($scope, settings) { settings = cloneSettings(settings); $scope.find('.awards-cup-mode').val(settings.cup_mode); $scope.find('.awards-team-medal-mode').val(settings.team_medal_mode); $scope.find('.awards-award-all').prop('checked', !!settings.award_all); $scope.find('.awards-depth-mode[value="' + settings.depth_mode + '"]').prop('checked', true); $scope.find('.awards-fixed-places').val(settings.fixed_places); $scope.find('.awards-threshold-entries').val(settings.threshold_entries); $scope.find('.awards-threshold-low').val(settings.threshold_low_places); $scope.find('.awards-threshold-high').val(settings.threshold_high_places); } function updateQuantityUI($scope, active) { var awardAll = $scope.find('.awards-award-all').is(':checked'); var mode = $scope.find('.awards-depth-mode:checked').val() || 'fixed'; var quantityDisabled = !active || awardAll; $scope.toggleClass('is-settings-disabled', !active); $scope.find('.awards-cup-mode').prop('disabled', !active); var isTeam = $scope.attr('data-is-team') === '1'; $scope.find('.awards-team-medal-mode').prop('disabled', !active || !isTeam); $scope.find('.awards-team-setting-block').toggleClass('is-not-applicable', !isTeam); $scope.find('.awards-award-all').prop('disabled', !active); $scope.find('.awards-all-toggle') .toggleClass('is-active', awardAll) .toggleClass('is-control-disabled', !active); $scope.find('.awards-depth-settings').toggleClass('is-disabled', quantityDisabled); $scope.find('.awards-depth-mode').prop('disabled', quantityDisabled); $scope.find('.awards-fixed-panel').toggleClass('is-hidden', mode !== 'fixed'); $scope.find('.awards-threshold-panel').toggleClass('is-hidden', mode !== 'threshold'); $scope.find('.awards-fixed-places').prop('disabled', quantityDisabled || mode !== 'fixed'); $scope.find('.awards-threshold-entries, .awards-threshold-low, .awards-threshold-high') .prop('disabled', quantityDisabled || mode !== 'threshold'); } function normalizeNumericInput($input) { if ($input.val() === '') { return; } $input.val(Math.max(1, intValue($input.val(), 1))); } 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 calculateCompetitions(competitions, settings) { var result = emptyResult(); settings = cloneSettings(settings); $.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 mainRowForLeaf($page, leaf) { return $page.find('.awards-main-row[data-main-key="' + String(leaf.main_key).replace(/"/g, '\\"') + '"]').first(); } function baseSettingsForLeaf($page, leaf) { var $row = mainRowForLeaf($page, leaf); if (!$row.length) { return cloneSettings({}); } return cloneSettings(readSettings($row)); } function leafSettingsForLeaf($page, state, leaf) { var key = overrideKey(leaf.category_id, leaf.level_id, leaf.dance_id); var override = state.overrides[key]; if (override && intValue(override.override_enabled) === 1) { return cloneSettings(override); } return baseSettingsForLeaf($page, leaf); } function effectiveSettingsForCompetition($page, state, leaf, competition) { var settings = leafSettingsForLeaf($page, state, leaf); var ageGroupId = intValue(competition && competition.age_group_id); if (ageGroupId > 0) { var key = ageOverrideKey(leaf.category_id, leaf.level_id, leaf.dance_id, ageGroupId); var override = state.overrides[key]; if (override && intValue(override.override_enabled) === 1) { return cloneSettings(override); } } return settings; } 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 writeStats($page, $row, stats, isTeam) { var html = '
' + intValue(stats.competitions) + ' ' + escapeHtml(pageText($page,'competitions')) + '
'; if (isTeam) { html += '
' + intValue(stats.teams) + ' ' + escapeHtml(pageText($page,'teams')) + '
'; html += '
' + intValue(stats.planned_dancers) + ' ' + escapeHtml(pageText($page,'planned-dancers')) + '
'; } else { html += '
' + intValue(stats.entries) + ' ' + escapeHtml(pageText($page,'total-entries')) + '
'; } $row.find('.awards-stats-cell').html(html); } function escapeHtml(value) { return $('
').text(value == null ? '' : value).html(); } function breakdownHtml($page, items) { if (!items.length) { return '
' + escapeHtml(pageText($page,'no-cups')) + '
'; } var html = ''; $.each(items, function (_, item) { html += '
' + escapeHtml(item.label) + '' + intValue(item.value) + '
'; }); return html; } function competitionsForOverrideRow(state, $row) { var categoryId = intValue($row.attr('data-category-id')); var levelId = intValue($row.attr('data-level-id')); var danceId = intValue($row.attr('data-dance-id')); var ageGroupId = intValue($row.attr('data-age-group-id')); var leaf = state.leafIndex[overrideKey(categoryId, levelId, danceId)]; if (!leaf) { return []; } if (!ageGroupId) { return leaf.competitions || []; } return $.grep(leaf.competitions || [], function (competition) { return intValue(competition.age_group_id) === ageGroupId; }); } function recalculateAll($page) { if (!$page || !$page.length) { return; } var state = getState($page); var mainResults = {}; var categoryResults = {}; var summaryResults = {}; var totals = emptyResult(); $page.find('.awards-main-row').each(function () { mainResults[$(this).attr('data-main-key')] = emptyResult(); }); $.each(state.leaves, function (_, leaf) { var mainKey = String(leaf.main_key); var categoryKey = String(leaf.category_id); var categoryId = intValue(leaf.category_id); var summaryKey; var summaryLabel; /* * In the final cup breakdown Championship and Other dances are * shown per dance. Every other category remains grouped only by * category. */ if (categoryId === 4 || categoryId === 7) { summaryKey = 'dance:' + categoryId + ':' + intValue(leaf.dance_id); summaryLabel = leaf.dance_label || state.categoryLabels[categoryKey] || state.categoryLabels[categoryId] || categoryKey; } else { summaryKey = 'category:' + categoryId; summaryLabel = state.categoryLabels[categoryKey] || state.categoryLabels[categoryId] || categoryKey; } if (!mainResults[mainKey]) { mainResults[mainKey] = emptyResult(); } if (!categoryResults[categoryKey]) { categoryResults[categoryKey] = emptyResult(); } if (!summaryResults[summaryKey]) { summaryResults[summaryKey] = { label: summaryLabel, result: emptyResult() }; } $.each(leaf.competitions || [], function (_, competition) { var settings = effectiveSettingsForCompetition($page, state, leaf, competition); var calc = calculateCompetitions([competition], settings); addResult(mainResults[mainKey], calc); addResult(categoryResults[categoryKey], calc); addResult(summaryResults[summaryKey].result, calc); addResult(totals, calc); }); }); $page.find('.awards-main-row').each(function () { var $row = $(this); var key = $row.attr('data-main-key'); writeResult($row, mainResults[key] || emptyResult()); }); $page.find('.awards-override-row').each(function () { var $row = $(this); var competitions = competitionsForOverrideRow(state, $row); var settings = readSettings($row); writeResult($row, calculateCompetitions(competitions, settings)); }); 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 items = []; var cupTotal = 0; $.each(summaryResults, function (_, group) { var value = intValue(group.result['cup_' + place]); if (!value) { return; } items.push({ label: group.label, value: value }); cupTotal += value; }); $page.find('#awardsCup' + place + 'Breakdown').html(breakdownHtml($page, items)); $page.find('#awardsSummaryCup' + place).text(cupTotal); }); } function setStatus($scope, state, text) { var $status = $scope.find('.awards-save-status').first(); $status.removeClass('is-saving is-saved is-error'); if (state) { $status.addClass('is-' + state); } $status.text(text || ''); } function ajaxSave($page, payload, $statusScope) { if ($statusScope && $statusScope.length) { setStatus($statusScope,'saving',pageText($page,'saving')); } return $.ajax({ url: $page.attr('data-actions-url') || 'actions.php', type: 'POST', dataType: 'json', data: $.extend({action:'save_award_scope_settings'}, payload) }).done(function (response) { if (!$statusScope || !$statusScope.length) { return; } if (response && response.ok) { setStatus($statusScope,'saved',pageText($page,'saved')); } else { setStatus($statusScope,'error',(response && response.error) || pageText($page,'save-failed')); } }).fail(function (xhr) { if (!$statusScope || !$statusScope.length) { return; } var error = pageText($page,'save-failed'); if (xhr.responseJSON && xhr.responseJSON.error) { error = xhr.responseJSON.error; } setStatus($statusScope,'error',error); }); } function payloadFromScope($scope, overrideEnabled) { var settings = readSettings($scope); return { scope_type: $scope.attr('data-scope-type') || 'category', category_id: intValue($scope.attr('data-category-id')), level_id: intValue($scope.attr('data-level-id')), dance_id: intValue($scope.attr('data-dance-id')), age_group_id: intValue($scope.attr('data-age-group-id')), override_enabled: overrideEnabled ? 1 : 0, 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 }; } function saveMainRow($page, $row) { return ajaxSave($page, payloadFromScope($row,true), $row); } function keyForOverrideRow($row) { return String($row.attr('data-override-key') || ''); } function saveOverrideRow($page, $row) { var state = getState($page); var key = keyForOverrideRow($row); if (!key) { return $.Deferred().resolve().promise(); } var settings = cloneSettings(readSettings($row)); settings.override_enabled = 1; state.overrides[key] = settings; return ajaxSave($page, payloadFromScope($row,true), $row); } function scheduleMainSave($page, $row) { var key = 'main:' + ($row.attr('data-main-key') || ''); clearTimeout(timers[key]); setStatus($row,'saving',pageText($page,'changed')); timers[key] = setTimeout(function () { saveMainRow($page,$row); },450); } function scheduleOverrideSave($page, $row) { var key = keyForOverrideRow($row); var timerKey = 'override:' + key; clearTimeout(timers[timerKey]); setStatus($row,'saving',pageText($page,'changed')); timers[timerKey] = setTimeout(function () { saveOverrideRow($page,$row); },450); } function findLevel(category, levelId) { var found = null; $.each((category && category.levels) || [], function (_, level) { if (String(level.id) === String(levelId)) { found = level; return false; } }); return found; } function findDance(level, danceId) { var found = null; $.each((level && level.dances) || [], function (_, dance) { if (String(dance.id) === String(danceId)) { found = dance; return false; } }); return found; } function findAgeGroup(leaf, ageGroupId) { var found = null; $.each((leaf && leaf.age_groups) || [], function (_, ageGroup) { if (String(ageGroup.id) === String(ageGroupId)) { found = ageGroup; return false; } }); return found; } function fillCategorySelect($page) { var state = getState($page); var $select = $page.find('#awardsOverrideCategory'); var selected = $select.val(); $select.empty().append($('