window.Utils = { $(selector, parent) { return (parent || document).querySelector(selector); }, $$(selector, parent) { return (parent || document).querySelectorAll(selector); }, formatConfidence(value) { return (value * 100).toFixed(0) + '%'; }, confidenceLevel(value) { if (value >= 0.8) return 'high'; if (value >= 0.5) return 'medium'; return 'low'; }, formatTimestamp(ts) { var d = new Date(ts); return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }); }, formatDate(ts) { var d = new Date(ts); return d.toLocaleDateString() + ' ' + d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); }, generateId() { return Date.now().toString(36) + Math.random().toString(36).slice(2); }, formatFileSize(bytes) { if (bytes < 1024) return bytes + ' B'; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'; return (bytes / (1024 * 1024)).toFixed(1) + ' MB'; }, validateFile(file) { var ALLOWED = ['image/png', 'image/jpeg', 'image/bmp', 'image/webp']; var MAX_SIZE = 10 * 1024 * 1024; if (!ALLOWED.includes(file.type)) { return { valid: false, error: 'Invalid file type: ' + file.name }; } if (file.size > MAX_SIZE) { return { valid: false, error: 'File too large: ' + file.name + ' (' + this.formatFileSize(file.size) + ')' }; } return { valid: true }; }, showToast(message, type, duration) { type = type || 'info'; duration = duration || 4000; var container = this.$('#toast-container'); var toast = document.createElement('div'); toast.className = 'toast ' + type; toast.textContent = message; container.appendChild(toast); setTimeout(function () { toast.style.opacity = '0'; toast.style.transition = 'opacity 0.3s'; setTimeout(function () { toast.remove(); }, 300); }, duration); }, fieldLabel(key) { var labels = { id_number: 'ID Number', surname: 'Surname', names: 'Names', given_names: 'Given Names', date_of_birth: 'Date of Birth', sex: 'Sex', nationality: 'Nationality', country_of_birth: 'Country of Birth', citizenship_status: 'Citizenship', passport_number: 'Passport Number', expiry_date: 'Expiry Date', issuing_country: 'Issuing Country', }; return labels[key] || key.replace(/_/g, ' ').replace(/\b\w/g, function (c) { return c.toUpperCase(); }); }, checkLabel(key) { var labels = { image_quality: 'Image Quality', id_number_valid: 'ID Number Valid', mrz_valid: 'MRZ Valid', barcode_valid: 'Barcode Valid', data_crosscheck: 'Data Cross-check', dob_crosscheck: 'DOB Cross-check', gender_crosscheck: 'Gender Cross-check', }; return labels[key] || key.replace(/_/g, ' ').replace(/\b\w/g, function (c) { return c.toUpperCase(); }); }, docTypeLabel(docType) { var labels = { sa_id_card: 'SA Smart Card', sa_id_book: 'SA Green Book', passport: 'Passport', product_serial: 'Product Serial', }; return labels[docType] || docType; }, fieldsForDocType(docType) { if (docType === 'passport') { return ['passport_number', 'surname', 'given_names', 'date_of_birth', 'sex', 'nationality', 'expiry_date', 'issuing_country', 'id_number']; } return ['id_number', 'surname', 'names', 'date_of_birth', 'sex', 'nationality', 'country_of_birth', 'citizenship_status']; }, };