Files
hyprcrd/public/js/modules/util.js
2026-04-06 20:27:19 +02:00

66 lines
2.0 KiB
JavaScript

// ── Utilities ──
export function esc(s) {
const d = document.createElement('div');
d.textContent = s;
return d.innerHTML;
}
export function escRe(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
let csrfToken = '';
export function setCsrf(token) { csrfToken = token; }
export function getCsrf() { return csrfToken; }
export async function api(path, opts = {}) {
const headers = { 'Content-Type': 'application/json' };
if (csrfToken) headers['X-CSRF-Token'] = csrfToken;
const res = await fetch(path, {
headers,
...opts,
body: opts.body ? JSON.stringify(opts.body) : undefined,
});
return res.json();
}
export function $(id) { return document.getElementById(id); }
export function fuzzyMatch(needle, haystack) {
if (needle.length < 3) return null;
let ni = 0, hi = 0, score = 0, streak = 0;
while (ni < needle.length && hi < haystack.length) {
if (needle[ni] === haystack[hi]) {
streak++; score += streak * 2;
if (hi === 0 || /[\s\-_./]/.test(haystack[hi - 1])) score += 10;
if (hi < 5) score += 5 - hi;
ni++;
} else { streak = 0; }
hi++;
}
if (ni < needle.length) return null;
return score - (haystack.length - needle.length) * 0.3;
}
export function countSubstring(str, sub) {
let c = 0, p = 0;
while ((p = str.indexOf(sub, p)) !== -1) { c++; p += sub.length; }
return c;
}
export function highlight(html, words) {
let result = html;
for (const w of words) result = result.replace(new RegExp('(' + escRe(w) + ')', 'gi'), '<mark>$1</mark>');
if (!result.includes('<mark>')) {
const full = words.join('');
let fi = 0, out = '';
for (let i = 0; i < html.length && fi < full.length; i++) {
if (html[i].toLowerCase() === full[fi]) { out += '<mark>' + html[i] + '</mark>'; fi++; } else { out += html[i]; }
}
out += html.substring(out.replace(/<\/?mark>/g, '').length);
result = out;
}
return result;
}