199 lines
6.3 KiB
JavaScript
199 lines
6.3 KiB
JavaScript
/**
|
|
* Feedmill Recounter — Upload page interactive features
|
|
* Progressive enhancement: form works without JS (basic file input).
|
|
*/
|
|
|
|
(function () {
|
|
'use strict';
|
|
|
|
const form = document.getElementById('upload-form');
|
|
const zone = document.getElementById('upload-zone');
|
|
const fileInput = document.getElementById('video-input');
|
|
const previewWrap = document.getElementById('video-preview');
|
|
const previewVideo = document.getElementById('preview-video');
|
|
const fileInfo = document.getElementById('file-info');
|
|
const fileName = document.getElementById('file-name');
|
|
const fileSize = document.getElementById('file-size');
|
|
const fileDuration = document.getElementById('file-duration');
|
|
const submitBtn = document.getElementById('submit-btn');
|
|
const submitHint = document.getElementById('submit-hint');
|
|
const selectAllBtn = document.getElementById('select-all-btn');
|
|
const deselectAllBtn = document.getElementById('deselect-all-btn');
|
|
const modelGrid = document.getElementById('model-grid');
|
|
|
|
if (!form || !zone || !fileInput) return;
|
|
|
|
/* ------------------------------------------------
|
|
Upload Zone — click, drag-drop, keyboard
|
|
------------------------------------------------ */
|
|
function initUploadZone() {
|
|
// Click anywhere in zone (including the hidden input) triggers browse
|
|
zone.addEventListener('keydown', function (e) {
|
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
e.preventDefault();
|
|
fileInput.click();
|
|
}
|
|
});
|
|
|
|
// Drag events
|
|
['dragenter', 'dragover'].forEach(function (evt) {
|
|
zone.addEventListener(evt, function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
zone.classList.add('dragover');
|
|
});
|
|
});
|
|
|
|
['dragleave', 'drop'].forEach(function (evt) {
|
|
zone.addEventListener(evt, function (e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
zone.classList.remove('dragover');
|
|
});
|
|
});
|
|
|
|
zone.addEventListener('drop', function (e) {
|
|
var files = e.dataTransfer.files;
|
|
if (files.length > 0 && files[0].type.startsWith('video/')) {
|
|
fileInput.files = files;
|
|
handleFile(files[0]);
|
|
}
|
|
});
|
|
|
|
// File input change
|
|
fileInput.addEventListener('change', function () {
|
|
if (fileInput.files.length > 0) {
|
|
handleFile(fileInput.files[0]);
|
|
}
|
|
});
|
|
}
|
|
|
|
/* ------------------------------------------------
|
|
Video Preview & File Info
|
|
------------------------------------------------ */
|
|
function initVideoPreview() {
|
|
// Handled inside handleFile
|
|
}
|
|
|
|
function handleFile(file) {
|
|
if (!file || !file.type.startsWith('video/')) return;
|
|
|
|
// Show file info
|
|
fileName.textContent = file.name;
|
|
fileSize.textContent = formatBytes(file.size);
|
|
fileDuration.textContent = 'probing...';
|
|
fileInfo.classList.add('visible');
|
|
|
|
// Video preview via FileReader
|
|
var reader = new FileReader();
|
|
reader.onload = function (e) {
|
|
previewVideo.src = e.target.result;
|
|
previewVideo.load();
|
|
previewWrap.classList.add('visible');
|
|
|
|
previewVideo.onloadedmetadata = function () {
|
|
fileDuration.textContent = formatDuration(previewVideo.duration);
|
|
};
|
|
previewVideo.onerror = function () {
|
|
fileDuration.textContent = 'unknown';
|
|
};
|
|
};
|
|
reader.readAsDataURL(file);
|
|
|
|
updateSubmitState();
|
|
}
|
|
|
|
function formatBytes(bytes) {
|
|
if (bytes === 0) return '0 B';
|
|
var k = 1024;
|
|
var sizes = ['B', 'KB', 'MB', 'GB'];
|
|
var i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
}
|
|
|
|
function formatDuration(seconds) {
|
|
if (!isFinite(seconds)) return 'unknown';
|
|
var m = Math.floor(seconds / 60);
|
|
var s = Math.floor(seconds % 60);
|
|
return m + ':' + (s < 10 ? '0' : '') + s;
|
|
}
|
|
|
|
/* ------------------------------------------------
|
|
Model Cards — select/deselect, highlight
|
|
------------------------------------------------ */
|
|
function initModelCards() {
|
|
if (!modelGrid) return;
|
|
|
|
var cards = modelGrid.querySelectorAll('.model-card');
|
|
cards.forEach(function (card) {
|
|
var checkbox = card.querySelector('.model-card-check');
|
|
|
|
// Click card body to toggle (avoid double-toggle when clicking checkbox directly)
|
|
card.addEventListener('click', function (e) {
|
|
if (e.target === checkbox) return;
|
|
if (e.target.tagName === 'SELECT') return;
|
|
checkbox.checked = !checkbox.checked;
|
|
card.classList.toggle('selected', checkbox.checked);
|
|
updateSubmitState();
|
|
});
|
|
|
|
checkbox.addEventListener('change', function () {
|
|
card.classList.toggle('selected', checkbox.checked);
|
|
updateSubmitState();
|
|
});
|
|
});
|
|
|
|
// Select All / Deselect All
|
|
if (selectAllBtn) {
|
|
selectAllBtn.addEventListener('click', function () {
|
|
cards.forEach(function (card) {
|
|
var cb = card.querySelector('.model-card-check');
|
|
cb.checked = true;
|
|
card.classList.add('selected');
|
|
});
|
|
updateSubmitState();
|
|
});
|
|
}
|
|
|
|
if (deselectAllBtn) {
|
|
deselectAllBtn.addEventListener('click', function () {
|
|
cards.forEach(function (card) {
|
|
var cb = card.querySelector('.model-card-check');
|
|
cb.checked = false;
|
|
card.classList.remove('selected');
|
|
});
|
|
updateSubmitState();
|
|
});
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------------
|
|
Submit — enable/disable, loading state
|
|
------------------------------------------------ */
|
|
function initFormSubmit() {
|
|
submitBtn.addEventListener('click', function () {
|
|
submitBtn.disabled = true;
|
|
submitBtn.textContent = 'Starting...';
|
|
submitHint.textContent = 'Uploading and starting analysis...';
|
|
});
|
|
}
|
|
|
|
function updateSubmitState() {
|
|
var hasVideo = fileInput.files && fileInput.files.length > 0;
|
|
var hasModel = modelGrid && modelGrid.querySelector('.model-card-check:checked');
|
|
var enabled = hasVideo && !!hasModel;
|
|
submitBtn.disabled = !enabled;
|
|
submitHint.textContent = enabled
|
|
? 'Ready to start analysis.'
|
|
: 'Select a video and at least one model to begin.';
|
|
}
|
|
|
|
/* ------------------------------------------------
|
|
Init
|
|
------------------------------------------------ */
|
|
initUploadZone();
|
|
initVideoPreview();
|
|
initModelCards();
|
|
initFormSubmit();
|
|
})();
|