espial-0.0.37: frontend/src/util.ts
import type { TFunction } from 'i18next';
export type QueryStringArray = Array<[string, string | null]>;
function unsafeDecode(str: string): string {
return decodeURIComponent(str);
}
export function parseQueryString(searchOrHash: string): QueryStringArray {
const first = searchOrHash.slice(0, 1);
const qs = first === '#' || first === '?' ? searchOrHash.slice(1) : searchOrHash;
const parts = qs.split('&').filter((x) => x !== '');
const decode = (s: string) => unsafeDecode(s.replace(/\+/g, ' '));
const out: QueryStringArray = [];
for (const kv of parts) {
const [k, ...rest] = kv.split('=');
if (!k) continue;
if (rest.length === 0) out.push([decode(k), null]);
else if (rest.length === 1) out.push([decode(k), decode(rest[0] ?? '')]);
}
return out;
}
export function curQuerystring(): QueryStringArray {
return parseQueryString(window.location.search);
}
export function lookupQueryStringValue(qs: QueryStringArray, k: string): string | null {
for (const [key, val] of qs) {
if (key === k) return val;
}
return null;
}
export function encodeTag(tag: string): string {
return encodeURIComponent(tag.replace(/\+/g, '%2B'));
}
export function fromNullableStr(s: string | null | undefined): string {
return s ?? '';
}
export function normalizeTags(tags: string | null): string {
return tags?.replace(/,/g, ' ').replace(/\s+/g, ' ').trim() ?? '';
}
export function apiErrorMsg(t: TFunction, status: number, bodyText: string): string {
const body = bodyText.trim();
if (!body.includes('\n') && !body.includes('doctype html') && body.length > 0) {
return t('error.errorWithMsg', { status, msg: body });
}
return t('error.error', { status });
}
/**
* Generates a short, non-cryptographic hash from a string.
* @param str - The input string to hash.
* @returns A short alphanumeric string hash.
*/
export function getShortHash(str: string): string {
let hash = 5381;
let i = str.length;
while (i) {
hash = (hash * 33) ^ str.charCodeAt(--i);
}
// Convert to an unsigned 32-bit integer, then to a base36 string
return (hash >>> 0).toString(36);
}