client / static /js /utils.js
P01yH3dr0n's picture
launch
774fe36
Raw
History Blame Contribute Delete
3.55 kB
/* ===== DOM & Image Utilities ===== */
window.$ = function(sel, parent) { return (parent || document).querySelector(sel); };
window.$$ = function(sel, parent) { return [...(parent || document).querySelectorAll(sel)]; };
window.NAIUtils = {
fileToDataUrl(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
},
dataUrlToBase64(dataUrl) {
const idx = dataUrl.indexOf(',');
return idx >= 0 ? dataUrl.substring(idx + 1) : dataUrl;
},
loadImageFromDataUrl(dataUrl) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = reject;
img.src = dataUrl;
});
},
async imageToBase64Jpeg(imgElement, maxSize) {
const canvas = document.createElement('canvas');
let w = imgElement.naturalWidth || imgElement.width;
let h = imgElement.naturalHeight || imgElement.height;
if (maxSize) {
const scale = maxSize / Math.max(w, h);
if (scale < 1) { w = Math.floor(w * scale); h = Math.floor(h * scale); }
}
canvas.width = w; canvas.height = h;
canvas.getContext('2d').drawImage(imgElement, 0, 0, w, h);
return this.dataUrlToBase64(canvas.toDataURL('image/jpeg', 0.95));
},
async resizeImageToBase64(dataUrl, maxSize) {
const img = await this.loadImageFromDataUrl(dataUrl);
return this.imageToBase64Jpeg(img, maxSize);
},
async sha256(str) {
const buf = new TextEncoder().encode(str);
const hash = await crypto.subtle.digest('SHA-256', buf);
return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');
},
autoResize(textarea) {
textarea.style.height = 'auto';
textarea.style.height = Math.min(textarea.scrollHeight, 200) + 'px';
},
setupAutoResize(textarea) {
textarea.addEventListener('input', () => this.autoResize(textarea));
setTimeout(() => this.autoResize(textarea), 0);
},
createDynSlider(label, id, min, max, step, value) {
const decimals = step < 0.1 ? 2 : 1;
const group = document.createElement('div');
group.className = 'form-group dyn-slider-group';
group.innerHTML = `
<label for="${id}">${label}</label>
<div class="slider-group">
<input type="range" id="${id}" min="${min}" max="${max}" step="${step}" value="${value}">
<span id="${id}-val" class="slider-value">${parseFloat(value).toFixed(decimals)}</span>
</div>`;
setTimeout(() => {
const sl = $(`#${id}`);
const valEl = $(`#${id}-val`);
if (sl && valEl) {
sl.addEventListener('input', () => {
valEl.textContent = parseFloat(sl.value).toFixed(decimals);
});
}
}, 0);
return group;
},
bindSlider(sliderId, valueId, transform) {
const slider = $(`#${sliderId}`);
const valueEl = $(`#${valueId}`);
if (!slider || !valueEl) return;
const update = () => {
valueEl.textContent = transform ? transform(parseFloat(slider.value)) : slider.value;
};
slider.addEventListener('input', update);
update();
},
};