(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($('