Spaces:
Running
Running
| /** | |
| * Helpers - General utility functions | |
| * Provides common helper functions used across the application. | |
| * Requirements: 1.1, 2.1, 3.1, 4.1, 5.1 | |
| */ | |
| const Helpers = (function () { | |
| // βββ Debounce βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| /** | |
| * Returns a debounced version of fn that delays invocation by `wait` ms. | |
| * @param {Function} fn | |
| * @param {number} wait - milliseconds | |
| * @returns {Function} | |
| */ | |
| function debounce(fn, wait) { | |
| let timer = null; | |
| return function (...args) { | |
| clearTimeout(timer); | |
| timer = setTimeout(() => fn.apply(this, args), wait); | |
| }; | |
| } | |
| // βββ String Utilities βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| /** | |
| * Escape HTML special characters to prevent XSS. | |
| * @param {string} str | |
| * @returns {string} | |
| */ | |
| function escapeHtml(str) { | |
| if (str == null) return ''; | |
| return String(str) | |
| .replace(/&/g, '&') | |
| .replace(/</g, '<') | |
| .replace(/>/g, '>') | |
| .replace(/"/g, '"') | |
| .replace(/'/g, '''); | |
| } | |
| /** | |
| * Truncate a string to maxLength, appending ellipsis if needed. | |
| * @param {string} str | |
| * @param {number} maxLength | |
| * @returns {string} | |
| */ | |
| function truncate(str, maxLength) { | |
| if (!str) return ''; | |
| if (str.length <= maxLength) return str; | |
| return str.slice(0, maxLength - 3) + '...'; | |
| } | |
| // βββ Object Utilities βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| /** | |
| * Deep clone a plain object / array using JSON serialisation. | |
| * @param {*} obj | |
| * @returns {*} | |
| */ | |
| function deepClone(obj) { | |
| if (obj === null || typeof obj !== 'object') return obj; | |
| try { | |
| return JSON.parse(JSON.stringify(obj)); | |
| } catch (_) { | |
| return obj; | |
| } | |
| } | |
| /** | |
| * Check whether a value is a plain object (not null, not array). | |
| * @param {*} value | |
| * @returns {boolean} | |
| */ | |
| function isPlainObject(value) { | |
| return value !== null && typeof value === 'object' && !Array.isArray(value); | |
| } | |
| // βββ ID Generation ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| /** | |
| * Generate a short unique ID string. | |
| * @returns {string} | |
| */ | |
| function generateId() { | |
| return Math.random().toString(36).slice(2, 10) + Date.now().toString(36); | |
| } | |
| // βββ File Utilities βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| /** | |
| * Extract the file extension (including dot) from a file name or path. | |
| * @param {string} filename | |
| * @returns {string} e.g. ".onnx" | |
| */ | |
| function getFileExtension(filename) { | |
| if (!filename) return ''; | |
| const idx = filename.lastIndexOf('.'); | |
| return idx >= 0 ? filename.slice(idx).toLowerCase() : ''; | |
| } | |
| /** | |
| * Extract the base file name (without directory path) from a full path. | |
| * @param {string} filePath | |
| * @returns {string} | |
| */ | |
| function getBaseName(filePath) { | |
| if (!filePath) return ''; | |
| return filePath.replace(/\\/g, '/').split('/').pop() || ''; | |
| } | |
| /** | |
| * Check whether a file name has an allowed extension. | |
| * @param {string} filename | |
| * @param {string[]} allowedExtensions - e.g. ['.onnx'] | |
| * @returns {boolean} | |
| */ | |
| function hasAllowedExtension(filename, allowedExtensions) { | |
| const ext = getFileExtension(filename); | |
| return allowedExtensions.map(e => e.toLowerCase()).includes(ext); | |
| } | |
| // βββ DOM Utilities ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| /** | |
| * Safely query a DOM element; returns null if not found. | |
| * @param {string} selector | |
| * @param {Element} [root=document] | |
| * @returns {Element|null} | |
| */ | |
| function $(selector, root) { | |
| return (root || document).querySelector(selector); | |
| } | |
| /** | |
| * Create a DOM element with optional attributes and text content. | |
| * @param {string} tag | |
| * @param {Object} [attrs] | |
| * @param {string} [text] | |
| * @returns {Element} | |
| */ | |
| function createElement(tag, attrs, text) { | |
| const el = document.createElement(tag); | |
| if (attrs) { | |
| Object.entries(attrs).forEach(([k, v]) => { | |
| if (k === 'className') { | |
| el.className = v; | |
| } else if (k === 'dataset') { | |
| Object.entries(v).forEach(([dk, dv]) => { el.dataset[dk] = dv; }); | |
| } else { | |
| el.setAttribute(k, v); | |
| } | |
| }); | |
| } | |
| if (text != null) el.textContent = text; | |
| return el; | |
| } | |
| // βββ Async Utilities ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| /** | |
| * Wait for a given number of milliseconds. | |
| * @param {number} ms | |
| * @returns {Promise<void>} | |
| */ | |
| function sleep(ms) { | |
| return new Promise(resolve => setTimeout(resolve, ms)); | |
| } | |
| /** | |
| * Wrap a promise with a timeout; rejects with an error if it exceeds `ms`. | |
| * @param {Promise} promise | |
| * @param {number} ms | |
| * @returns {Promise} | |
| */ | |
| function withTimeout(promise, ms) { | |
| const timeout = new Promise((_, reject) => | |
| setTimeout(() => reject(new Error(`Operation timed out after ${ms}ms`)), ms) | |
| ); | |
| return Promise.race([promise, timeout]); | |
| } | |
| // βββ Array Utilities ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| /** | |
| * Return unique items from an array (shallow equality). | |
| * @param {Array} arr | |
| * @returns {Array} | |
| */ | |
| function unique(arr) { | |
| return [...new Set(arr)]; | |
| } | |
| /** | |
| * Chunk an array into sub-arrays of size `n`. | |
| * @param {Array} arr | |
| * @param {number} n | |
| * @returns {Array[]} | |
| */ | |
| function chunk(arr, n) { | |
| const result = []; | |
| for (let i = 0; i < arr.length; i += n) { | |
| result.push(arr.slice(i, i + n)); | |
| } | |
| return result; | |
| } | |
| // βββ Public API βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| return { | |
| debounce, | |
| escapeHtml, | |
| truncate, | |
| deepClone, | |
| isPlainObject, | |
| generateId, | |
| getFileExtension, | |
| getBaseName, | |
| hasAllowedExtension, | |
| $, | |
| createElement, | |
| sleep, | |
| withTimeout, | |
| unique, | |
| chunk, | |
| }; | |
| })(); | |
| // Export for global access in vanilla JS context | |
| window.Helpers = Helpers; | |