model-explorer / js /utils /helpers.js
mr4's picture
Upload 71 files
9bd422a verified
/**
* 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, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
/**
* 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;