/* Учебная демонстрация. Действия выполняются только в памяти страницы. */ (function (window, document) { 'use strict'; var demo = document.querySelector('#feis-home [data-home-demo]'); if (!demo || demo.getAttribute('data-initialized') === '1') { return; } demo.setAttribute('data-initialized', '1'); var panels = Array.prototype.slice.call(demo.querySelectorAll('[data-demo-panel]')); var tabs = Array.prototype.slice.call(demo.querySelectorAll('[data-demo-mode]')); var slides = Array.prototype.slice.call(demo.querySelectorAll('[data-demo-slide]')); var button = demo.querySelector('[data-demo-toggle]'); var reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; var paused = Boolean(reducedMotion); var visible = !window.IntersectionObserver; var hovered = false; var current = 0; var timer = null; var modes = tabs.map(function (tab) { return panels.filter(function (panel) { return panel.getAttribute('data-demo-group') === tab.getAttribute('data-demo-mode'); }); }); function schedule() { window.clearTimeout(timer); timer = null; if (!paused && visible && !hovered && !document.hidden) { timer = window.setTimeout(function () { show((current + 1) % panels.length, false); }, 5200); } button.textContent = demo.getAttribute(paused ? 'data-play' : 'data-pause'); button.setAttribute('aria-pressed', paused ? 'true' : 'false'); } function panelIndex(key) { for (var index = 0; index < panels.length; index++) { if (panels[index].getAttribute('data-demo-key') === key) { return index; } } return 0; } function show(index, manual) { var previous = current; current = index; var mode = Number(panels[current].getAttribute('data-demo-group')); var modePosition = modes[mode].indexOf(panels[current]); panels.forEach(function (panel, position) { panel.hidden = position !== current; panel.id = 'home-demo-panel-' + position; }); tabs.forEach(function (tab, position) { var active = position === mode; tab.setAttribute('aria-selected', active ? 'true' : 'false'); tab.tabIndex = active ? 0 : -1; tab.setAttribute('aria-controls', 'home-demo-panel-' + panels.indexOf(active ? panels[current] : modes[position][0])); }); slides.forEach(function (slide, position) { slide.hidden = position >= modes[mode].length; if (!slide.hidden) { slide.setAttribute('aria-controls', 'home-demo-panel-' + panels.indexOf(modes[mode][position])); } if (position === modePosition) { slide.setAttribute('aria-current', 'step'); } else { slide.removeAttribute('aria-current'); } }); if (panels[current].getAttribute('data-demo-key') === 'dances' && previous !== current && !resultButtons[0].hidden) { openResult(resultButtons[0]); } if (manual) { paused = true; demo.querySelector('[data-demo-announcement]').textContent = panels[current].querySelector('h3').textContent; } schedule(); } tabs.forEach(function (tab, mode) { tab.addEventListener('click', function () { show(panels.indexOf(modes[mode][0]), true); }); tab.addEventListener('keydown', function (event) { if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { event.preventDefault(); var nextMode = mode === 0 ? 1 : 0; show(panels.indexOf(modes[nextMode][0]), true); tabs[nextMode].focus(); } }); }); slides.forEach(function (slide, position) { slide.addEventListener('click', function () { var mode = Number(panels[current].getAttribute('data-demo-group')); show(panels.indexOf(modes[mode][position]), true); }); }); button.addEventListener('click', function () { paused = !paused; schedule(); }); demo.querySelector('[data-demo-next]').addEventListener('click', function () { show((current + 1) % panels.length, true); }); demo.querySelector('[data-demo-previous]').addEventListener('click', function () { show((current - 1 + panels.length) % panels.length, true); }); function pauseForInteraction(event) { if (event.target.closest('.home-demo-screen') && event.target.closest('button, input, select, summary')) { paused = true; schedule(); } } ['click', 'input', 'change', 'focusin', 'dragstart', 'pointerdown'].forEach(function (name) { demo.addEventListener(name, pauseForInteraction); }); var result = demo.querySelector('[data-demo-best-result]'); var resultButtons = Array.prototype.slice.call(demo.querySelectorAll('[data-demo-place]')); var activeResult = resultButtons[0]; var restoringResultFocus = false; function openResult(source) { activeResult = source; result.hidden = false; var cards = Array.prototype.slice.call(demo.querySelectorAll('.home-demo-dances > div:not(.home-demo-best-result)')); var cardIndex = cards.indexOf(source.parentElement); var columns = 3; var rowEnd = Math.min(cards.length - 1, Math.floor(cardIndex / columns) * columns + columns - 1); cards[rowEnd].insertAdjacentElement('afterend', result); result.style.setProperty('--demo-result-anchor', ((cardIndex % columns + 0.5) / columns * 100) + '%'); resultButtons.forEach(function (badge) { badge.setAttribute('aria-expanded', badge === source ? 'true' : 'false'); }); var place = source.getAttribute('data-place'); var medal = result.querySelector('[data-demo-result-place]'); medal.className = 'home-demo-result-medal is-place-' + place; medal.textContent = place; result.querySelector('[data-demo-result-dance]').textContent = source.getAttribute('data-dance'); result.querySelector('[data-demo-result-feis]').textContent = source.getAttribute('data-feis'); result.querySelector('[data-demo-result-count]').textContent = source.getAttribute('data-count'); } function closeResult(restoreFocus) { result.hidden = true; resultButtons.forEach(function (badge) { badge.setAttribute('aria-expanded', 'false'); }); if (restoreFocus) { restoringResultFocus = true; activeResult.focus(); restoringResultFocus = false; } } resultButtons.forEach(function (source) { ['click', 'mouseenter'].forEach(function (name) { source.addEventListener(name, function () { openResult(source); }); }); source.addEventListener('focus', function () { if (!restoringResultFocus) { openResult(source); } }); }); demo.querySelector('[data-demo-result-close]').addEventListener('click', function () { closeResult(true); }); panels[panelIndex('dances')].addEventListener('keydown', function (event) { if (event.key === 'Escape' && !result.hidden) { closeResult(true); } }); // Выбранные танцы сохраняются отдельно для каждого уровня. var registrationInputs = Array.prototype.slice.call(demo.querySelectorAll('[data-demo-register-dance]')); var registrationLevels = Array.prototype.slice.call(demo.querySelectorAll('[data-demo-registration-level]')); function updateRegistrationTotal() { var total = registrationInputs.reduce(function (sum, input) { return sum + (input.checked ? Number(input.getAttribute('data-fee')) : 0); }, 0); demo.querySelector('[data-demo-registration-total]').textContent = total; Array.prototype.forEach.call(demo.querySelectorAll('[data-demo-entry-dance="101"]'), function (checkbox) { checkbox.checked = registrationInputs.some(function (input) { return input.checked && input.getAttribute('data-level') === checkbox.getAttribute('data-level') && input.getAttribute('data-dance-index') === checkbox.getAttribute('data-dance-index'); }); }); var firstChampionship = demo.querySelector('#home-demo-entry-details-101 [data-demo-entry-championship]'); if (firstChampionship) { var lowerLevelSelected = registrationInputs.some(function (input) { return input.checked && input.getAttribute('data-level') !== 'Open'; }); if (lowerLevelSelected) { firstChampionship.querySelectorAll('input').forEach(function (input) { input.checked = false; }); firstChampionship.querySelectorAll('[data-demo-entry-champ-dances]').forEach(function (cell) { cell.textContent = '—'; }); } } if (personalSelect) { filterPersonalSchedule(); } } registrationInputs.forEach(function (input) { input.addEventListener('change', updateRegistrationTotal); }); registrationLevels.forEach(function (button) { button.addEventListener('click', function () { var level = button.getAttribute('data-demo-registration-level'); registrationLevels.forEach(function (item) { item.setAttribute('aria-pressed', item === button ? 'true' : 'false'); }); registrationInputs.forEach(function (input) { input.hidden = input.getAttribute('data-level') !== level; }); resultButtons.forEach(function (badge) { // Учебная история относится к Beginner; не показываем её в других уровнях. badge.hidden = badge.getAttribute('data-level') !== level; }); demo.querySelector('[data-demo-current-level]').textContent = level; closeResult(false); }); }); updateRegistrationTotal(); // В рабочем интерфейсе при раскрытии заявки закрывается предыдущая строка. var entryRows = Array.prototype.slice.call(demo.querySelectorAll('[data-demo-entry-row]')); function toggleEntry(row) { var shouldOpen = row.getAttribute('aria-expanded') !== 'true'; entryRows.forEach(function (item) { var opened = item === row && shouldOpen; item.setAttribute('aria-expanded', opened ? 'true' : 'false'); item.querySelector('[data-demo-entry-arrow]').textContent = opened ? '⌃' : '⌄'; document.getElementById(item.getAttribute('aria-controls')).hidden = !opened; }); } function updateEntryPayments() { var paid = 0; var unpaid = 0; entryRows.forEach(function (row) { var checked = row.querySelector('[data-demo-entry-paid]').checked; var amount = Number(row.querySelector('[data-demo-fee="total"]').textContent); var status = row.querySelector('[data-demo-entry-paid-status]'); status.textContent = status.getAttribute(checked ? 'data-paid' : 'data-unpaid'); if (checked) { paid += amount; } else { unpaid += amount; } }); demo.querySelector('[data-demo-entry-paid-total]').textContent = paid; demo.querySelector('[data-demo-entry-unpaid-total]').textContent = unpaid; } entryRows.forEach(function (row) { row.querySelector('[data-demo-entry-paid]').addEventListener('change', updateEntryPayments); }); updateEntryPayments(); entryRows.forEach(function (row) { row.addEventListener('click', function (event) { if (!event.target.closest('button, input, a, select, label')) { toggleEntry(row); } }); row.addEventListener('keydown', function (event) { if (event.target === row && (event.key === 'Enter' || event.key === ' ')) { event.preventDefault(); toggleEntry(row); } }); }); // Выбор для PDF остаётся отдельным от других действий с танцором. var selectAll = demo.querySelector('[data-demo-select-all]'); var selectedPeople = Array.prototype.slice.call(demo.querySelectorAll('[data-demo-select-person]')); function updateSelection() { var count = 0; var total = 0; selectedPeople.forEach(function (checkbox) { if (checkbox.checked) { count++; total += Number(checkbox.getAttribute('data-total')); } }); selectAll.checked = count === selectedPeople.length; selectAll.indeterminate = count > 0 && count < selectedPeople.length; demo.querySelector('[data-demo-selected-total]').textContent = total; } selectedPeople.forEach(function (checkbox) { checkbox.addEventListener('change', updateSelection); }); selectAll.addEventListener('change', function () { selectedPeople.forEach(function (checkbox) { checkbox.checked = selectAll.checked; }); updateSelection(); }); demo.querySelector('[data-demo-pdf]').addEventListener('click', function () { demo.querySelector('[data-demo-pdf-message]').textContent = this.getAttribute('data-message'); }); updateSelection(); var personalSelect = demo.querySelector('[data-demo-person]'); function filterPersonalSchedule() { Array.prototype.forEach.call(demo.querySelectorAll('[data-demo-personal-number]'), function (row) { var registered = true; if (row.hasAttribute('data-demo-registration-row')) { registered = registrationInputs.some(function (input) { return input.checked && input.getAttribute('data-level') === row.getAttribute('data-level') && input.getAttribute('data-dance-index') === row.getAttribute('data-dance-index'); }); } row.hidden = row.getAttribute('data-demo-personal-number') !== personalSelect.value || !registered; }); } personalSelect.addEventListener('change', filterPersonalSchedule); filterPersonalSchedule(); function filterResults(panel) { var category = panel.querySelector('[data-demo-result-category]').value; var highlighted = panel.getAttribute('data-demo-result-highlight'); var visibleRows = 0; Array.prototype.forEach.call(panel.querySelectorAll('[data-demo-result-row]'), function (row) { row.hidden = category !== 'all' && category !== row.getAttribute('data-category'); row.classList.toggle('is-current-dancer', row.getAttribute('data-number') === highlighted); if (!row.hidden) { visibleRows++; } }); panel.querySelector('[data-demo-no-results]').hidden = visibleRows > 0; } var resultPanels = panels.filter(function (panel) { return Boolean(panel.querySelector('[data-demo-results-table]')); }); resultPanels.forEach(function (panel) { Array.prototype.forEach.call(panel.querySelectorAll('select'), function (select) { select.addEventListener('change', function () { filterResults(panel); }); }); filterResults(panel); }); Array.prototype.forEach.call(demo.querySelectorAll('[data-demo-open-person]'), function (link) { link.addEventListener('click', function () { var number = link.getAttribute('data-demo-open-person'); var key = link.getAttribute('data-demo-open-kind') === 'schedule' ? 'personal-schedule' : 'student-results'; var index = panelIndex(key); if (key === 'personal-schedule') { personalSelect.value = number; filterPersonalSchedule(); } else { panels[index].setAttribute('data-demo-result-highlight', number); panels[index].querySelector('[data-demo-result-category]').value = 'all'; filterResults(panels[index]); } show(index, true); panels[index].querySelector('select').focus({ preventScroll: true }); }); }); // Значение шкалы удвоено: 20 означает возрастную границу 10 лет. function setupAgeSlider(level) { var ages = level.getAttribute('data-ages').split(',').map(Number); var viewport = level.querySelector('[data-demo-age-viewport]'); var track = level.querySelector('[data-demo-age-track]'); var values = track.getAttribute('data-values').split(',').filter(Boolean).map(Number); var groups = level.querySelector('.home-demo-age-groups'); var editor = level.querySelector('[data-demo-boundary-number]'); var selected = 0; var pointer = null; var segments = []; var dragStart = null; var dragSpace = 360; var step = 2; var maximum = 100; var segmentMinimumWidth = 92; var padding = 16; function limits(index) { return { min: index > 0 ? values[index - 1] + step : 0, max: index < values.length - 1 ? values[index + 1] - step : maximum }; } function edges() { return [0].concat(values, [maximum]); } function groupName(index) { if (!values.length) { return groups.getAttribute('data-all'); } if (index === 0) { return groups.getAttribute('data-before').replace('{age}', values[0] / step); } if (index === values.length) { return groups.getAttribute('data-after').replace('{age}', values[index - 1] / step); } return groups.getAttribute('data-between') .replace('{from}', values[index - 1] / step) .replace('{to}', values[index] / step - 1); } function layout() { var boundaries = edges(); var width = Math.max(viewport.clientWidth, padding * 2 + (values.length + 1) * segmentMinimumWidth + 360); var freeSpace = width - padding * 2 - (values.length + 1) * segmentMinimumWidth; dragSpace = freeSpace; var position = padding; segments = []; track.style.width = width + 'px'; // Поле диапазона всегда помещается между соседними ползунками. // При малой ширине прокручивается полотно, а не вся страница. for (var index = 0; index < boundaries.length - 1; index++) { var segmentWidth = segmentMinimumWidth + (boundaries[index + 1] - boundaries[index]) / maximum * freeSpace; segments.push({ left: position, right: position + segmentWidth, lower: boundaries[index], upper: boundaries[index + 1] }); groups.children[index].style.left = (position + segmentWidth / 2) + 'px'; position += segmentWidth; } Array.prototype.forEach.call(track.querySelectorAll('[data-demo-age-handle]'), function (handle, index) { handle.style.left = segments[index].right + 'px'; }); } function update() { var boundaries = edges(); var counts = new Array(values.length + 1).fill(0); ages.forEach(function (age) { var group = 0; while (group < values.length && age * step >= values[group]) { group++; } counts[group]++; }); Array.prototype.forEach.call(groups.children, function (field, index) { var name = groupName(index); field.querySelector('[data-demo-age-name]').textContent = name; field.querySelector('[data-demo-age-count]').textContent = counts[index]; var add = field.querySelector('[data-demo-add-boundary]'); var remove = field.querySelector('[data-demo-remove-boundary]'); add.disabled = values.length >= 8 || boundaries[index + 1] - boundaries[index] < step * 2; remove.disabled = index === 0; add.setAttribute('aria-label', groups.getAttribute('data-add') + ': ' + name); remove.setAttribute('aria-label', groups.getAttribute('data-remove') + ': ' + name); }); Array.prototype.forEach.call(track.querySelectorAll('[data-demo-age-handle]'), function (handle, index) { var bounds = limits(index); handle.textContent = values[index] / step; handle.setAttribute('aria-valuenow', values[index] / step); handle.setAttribute('aria-valuemin', bounds.min / step); handle.setAttribute('aria-valuemax', bounds.max / step); handle.classList.toggle('is-active', index === selected); }); editor.parentElement.hidden = values.length === 0; if (values.length) { var bounds = limits(selected); editor.min = bounds.min / step; editor.max = bounds.max / step; editor.value = values[selected] / step; level.querySelector('[data-demo-boundary-label]').textContent = track.getAttribute('data-boundary') + ' ' + (selected + 1); } track.setAttribute('data-values', values.join(',')); layout(); } function move(value) { var bounds = limits(selected); value = Math.round(value / step) * step; values[selected] = Math.max(bounds.min, Math.min(bounds.max, value)); update(); } function splitGroup(index) { var boundaries = edges(); var lower = boundaries[index]; var upper = boundaries[index + 1]; if (values.length >= 8 || upper - lower < step * 2) { return; } var value = Math.round((lower + upper) / (step * 2)) * step; value = Math.max(lower + step, Math.min(upper - step, value)); values.splice(index, 0, value); selected = index; render(); track.querySelectorAll('[data-demo-age-handle]')[selected].focus(); } function mergeGroup(index) { if (index === 0) { return; } values.splice(index - 1, 1); selected = Math.max(0, Math.min(index - 1, values.length - 1)); render(); if (values.length) { track.querySelectorAll('[data-demo-age-handle]')[selected].focus(); } else { groups.querySelector('[data-demo-add-boundary]').focus(); } } function render() { Array.prototype.forEach.call(track.querySelectorAll('[data-demo-age-handle]'), function (handle) { handle.remove(); }); values.forEach(function (value, index) { var handle = document.createElement('button'); handle.type = 'button'; handle.className = 'home-demo-age-handle'; handle.setAttribute('data-demo-age-handle', index); handle.setAttribute('role', 'slider'); handle.setAttribute('aria-orientation', 'horizontal'); handle.setAttribute('aria-label', track.getAttribute('data-boundary') + ' ' + (index + 1) + ': ' + track.getAttribute('aria-label')); handle.addEventListener('focus', function () { selected = index; update(); }); handle.addEventListener('keydown', function (event) { var next = values[index]; if (event.key === 'ArrowRight' || event.key === 'ArrowUp') { next += step; } else if (event.key === 'ArrowLeft' || event.key === 'ArrowDown') { next -= step; } else if (event.key === 'PageUp') { next += step * 5; } else if (event.key === 'PageDown') { next -= step * 5; } else if (event.key === 'Home') { next = limits(index).min; } else if (event.key === 'End') { next = limits(index).max; } else { return; } event.preventDefault(); selected = index; move(next); }); track.appendChild(handle); }); groups.textContent = ''; for (var index = 0; index <= values.length; index++) { (function (groupIndex) { var field = document.createElement('div'); var label = document.createElement('span'); var controls = document.createElement('div'); var count = document.createElement('b'); var add = document.createElement('button'); var remove = document.createElement('button'); field.className = 'home-demo-age-group'; field.setAttribute('data-demo-age-group', groupIndex); label.setAttribute('data-demo-age-name', groupIndex); controls.className = 'home-demo-group-controls'; count.setAttribute('data-demo-age-count', groupIndex); add.type = 'button'; add.textContent = '+'; add.setAttribute('data-demo-add-boundary', groupIndex); remove.type = 'button'; remove.textContent = '−'; remove.setAttribute('data-demo-remove-boundary', groupIndex); add.addEventListener('click', function () { splitGroup(groupIndex); }); remove.addEventListener('click', function () { mergeGroup(groupIndex); }); controls.appendChild(add); controls.appendChild(count); controls.appendChild(remove); field.appendChild(label); field.appendChild(controls); groups.appendChild(field); })(index); } update(); } function valueAt(event) { var x = event.clientX - track.getBoundingClientRect().left; for (var index = 0; index < segments.length; index++) { var segment = segments[index]; if (x <= segment.right) { var fraction = Math.max(0, (x - segment.left) / (segment.right - segment.left)); return segment.lower + fraction * (segment.upper - segment.lower); } } return maximum; } track.addEventListener('pointerdown', function (event) { if (!values.length || (event.button !== undefined && event.button !== 0) || event.target.closest('.home-demo-age-group')) { return; } var handle = event.target.closest('[data-demo-age-handle]'); if (event.pointerType === 'touch' && !handle) { return; } if (handle) { selected = Number(handle.getAttribute('data-demo-age-handle')); } else { var value = valueAt(event); selected = values.reduce(function (best, current, index) { return Math.abs(current - value) < Math.abs(values[best] - value) ? index : best; }, 0); } pointer = event.pointerId; dragStart = { x: event.clientX, value: values[selected] }; event.preventDefault(); track.querySelectorAll('[data-demo-age-handle]')[selected].focus({ preventScroll: true }); if (!handle) { move(valueAt(event)); dragStart.value = values[selected]; } if (track.setPointerCapture) { track.setPointerCapture(pointer); } }); track.addEventListener('pointermove', function (event) { if (pointer !== null && event.pointerId === pointer) { event.preventDefault(); // Движение рассчитывается от начала жеста, а не от меняющихся диапазонов. move(dragStart.value + (event.clientX - dragStart.x) / dragSpace * maximum); } }); ['pointerup', 'pointercancel', 'lostpointercapture'].forEach(function (name) { track.addEventListener(name, function (event) { if (event.pointerId === pointer) { pointer = null; dragStart = null; } }); }); editor.addEventListener('change', function () { if (editor.value !== '' && isFinite(Number(editor.value))) { move(Number(editor.value) * step); } else { update(); } }); level.addEventListener('toggle', layout); window.addEventListener('resize', layout); if (window.ResizeObserver) { new window.ResizeObserver(layout).observe(viewport); } render(); } Array.prototype.forEach.call(demo.querySelectorAll('[data-demo-age-level]'), setupAgeSlider); var timetable = demo.querySelector('[data-demo-timetable]'); var draggedBlock = null; function updateTimetable(moved) { var minutes = 9 * 60; function formatTime(value) { return String(Math.floor(value / 60)).padStart(2, '0') + ':' + String(value % 60).padStart(2, '0'); } Array.prototype.forEach.call(timetable.children, function (block) { var time = formatTime(minutes); block.querySelector('[data-demo-slot]').textContent = time; var category = block.getAttribute('data-demo-schedule-block'); Array.prototype.forEach.call(demo.querySelectorAll('[data-demo-personal-category="' + category + '"]'), function (row) { row.querySelector('[data-demo-personal-time]').textContent = time; }); minutes += Number(block.getAttribute('data-duration')); }); demo.querySelector('[data-demo-schedule-end]').textContent = formatTime(minutes); if (moved) { demo.querySelector('[data-demo-announcement]').textContent = timetable.getAttribute('data-moved') + ': ' + moved.querySelector('[data-demo-expand] b').textContent; } } function clearDrag() { draggedBlock = null; Array.prototype.forEach.call(timetable.children, function (block) { block.classList.remove('is-dragging', 'is-drop-target'); }); } Array.prototype.forEach.call(timetable.children, function (block) { var expand = block.querySelector('[data-demo-expand]'); var people = document.getElementById(expand.getAttribute('aria-controls')); var handle = block.querySelector('[data-demo-drag]'); var touchPointer = null; expand.addEventListener('click', function () { people.hidden = !people.hidden; expand.setAttribute('aria-expanded', people.hidden ? 'false' : 'true'); }); handle.addEventListener('keydown', function (event) { if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') { return; } event.preventDefault(); var sibling = event.key === 'ArrowUp' ? block.previousElementSibling : block.nextElementSibling; if (sibling) { timetable.insertBefore(event.key === 'ArrowUp' ? block : sibling, event.key === 'ArrowUp' ? sibling : block); updateTimetable(block); handle.focus(); } }); handle.addEventListener('dragstart', function (event) { draggedBlock = block; block.classList.add('is-dragging'); event.dataTransfer.effectAllowed = 'move'; event.dataTransfer.setData('text/plain', block.getAttribute('data-demo-schedule-block')); }); block.addEventListener('dragover', function (event) { if (draggedBlock && draggedBlock !== block) { event.preventDefault(); event.dataTransfer.dropEffect = 'move'; block.classList.add('is-drop-target'); } }); block.addEventListener('dragleave', function (event) { if (!block.contains(event.relatedTarget)) { block.classList.remove('is-drop-target'); } }); block.addEventListener('drop', function (event) { if (draggedBlock && draggedBlock !== block) { event.preventDefault(); var blocks = Array.prototype.slice.call(timetable.children); var movingDown = blocks.indexOf(draggedBlock) < blocks.indexOf(block); timetable.insertBefore(draggedBlock, movingDown ? block.nextElementSibling : block); updateTimetable(draggedBlock); } clearDrag(); }); handle.addEventListener('dragend', clearDrag); handle.addEventListener('pointerdown', function (event) { if (event.pointerType !== 'touch') { return; } event.preventDefault(); touchPointer = event.pointerId; draggedBlock = block; block.classList.add('is-dragging'); if (handle.setPointerCapture) { handle.setPointerCapture(touchPointer); } }); handle.addEventListener('pointermove', function (event) { if (touchPointer !== event.pointerId || touchPointer === null) { return; } event.preventDefault(); var target = document.elementFromPoint(event.clientX, event.clientY); var other = target && target.closest('[data-demo-schedule-block]'); if (other && other !== block && other.parentElement === timetable) { var rectangle = other.getBoundingClientRect(); timetable.insertBefore(block, event.clientY > rectangle.top + rectangle.height / 2 ? other.nextElementSibling : other); updateTimetable(); } }); ['pointerup', 'pointercancel', 'lostpointercapture'].forEach(function (name) { handle.addEventListener(name, function (event) { if (touchPointer === event.pointerId && touchPointer !== null) { touchPointer = null; updateTimetable(block); clearDrag(); } }); }); }); updateTimetable(); var tabulation = demo.querySelector('[data-demo-tabulation]'); var calculationStatus = demo.querySelector('[data-demo-calculation-status]'); var publish = demo.querySelector('[data-demo-publish]'); var publishStatus = demo.querySelector('[data-demo-publish-status]'); function markDirty() { calculationStatus.textContent = calculationStatus.getAttribute('data-dirty'); publish.disabled = true; publishStatus.textContent = ''; Array.prototype.forEach.call(tabulation.querySelectorAll('[data-demo-score-place]'), function (place) { place.textContent = '—'; }); } Array.prototype.forEach.call(tabulation.querySelectorAll('[data-demo-score]'), function (input) { input.addEventListener('input', markDirty); }); demo.querySelector('[data-demo-calculate]').addEventListener('click', function () { var records = Array.prototype.map.call(tabulation.querySelectorAll('[data-demo-score-number]'), function (row) { var input = row.querySelector('[data-demo-score]'); return { number: row.getAttribute('data-demo-score-number'), score: parseFloat(input.value), row: row, input: input }; }); var invalid = records.filter(function (record) { var failed = !isFinite(record.score) || record.score < 0 || record.score > 100 || !record.input.checkValidity(); record.input.setAttribute('aria-invalid', failed ? 'true' : 'false'); return failed; }); if (invalid.length) { calculationStatus.textContent = calculationStatus.getAttribute('data-invalid'); invalid[0].input.focus(); return; } records.sort(function (first, second) { return second.score - first.score || Number(first.number) - Number(second.number); }); var place = 0; var previousScore = null; var scores = {}; records.forEach(function (record) { if (record.score !== previousScore) { place++; previousScore = record.score; } record.place = place; scores[record.number] = record; record.row.querySelector('[data-demo-score-place]').textContent = place; }); resultPanels.forEach(function (panel) { var body = panel.querySelector('tbody'); var rows = Array.prototype.slice.call(body.children); rows.forEach(function (row) { if (row.getAttribute('data-category') === 'reel') { var record = scores[row.getAttribute('data-number')]; var medal = row.querySelector('[data-demo-ranking]'); medal.textContent = record.place; medal.className = 'home-demo-ranking' + (record.place <= 3 ? ' is-place-' + record.place : ''); } }); var categories = ['reel', 'jig', 'championship']; rows.sort(function (first, second) { var categoryDifference = categories.indexOf(first.getAttribute('data-category')) - categories.indexOf(second.getAttribute('data-category')); if (categoryDifference) { return categoryDifference; } return Number(first.querySelector('[data-demo-ranking]').textContent) - Number(second.querySelector('[data-demo-ranking]').textContent) || Number(first.getAttribute('data-number')) - Number(second.getAttribute('data-number')); }); rows.forEach(function (row) { body.appendChild(row); }); filterResults(panel); }); calculationStatus.textContent = calculationStatus.getAttribute('data-ready'); publish.disabled = false; publishStatus.textContent = ''; }); publish.addEventListener('click', function () { publishStatus.textContent = publish.getAttribute('data-message'); publish.disabled = true; }); Array.prototype.forEach.call(demo.querySelectorAll('.home-demo-copy, .home-demo-screen'), function (content) { content.addEventListener('mouseenter', function () { hovered = true; schedule(); }); content.addEventListener('mouseleave', function () { hovered = false; schedule(); }); }); document.addEventListener('visibilitychange', schedule); if (window.IntersectionObserver) { var observer = new window.IntersectionObserver(function (entries) { visible = entries.some(function (entry) { return entry.isIntersecting; }); schedule(); }, { threshold: 0.15 }); observer.observe(demo); } openResult(resultButtons[0]); show(0, false); })(window, document);