/**
* Escape HTML special characters to prevent XSS
* Use for any user-generated content rendered via innerHTML
* @param {string} str - Untrusted string
* @returns {string} - Escaped string safe for innerHTML
*/
export function escapeHTML(str) {
if (str === null || str === undefined) return '';
return String(str)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
/**
* Format price with currency symbol
* @param {number} price - Price value
* @param {string} currency - Currency code (EUR, USD, CHF, XMR)
* @returns {string} - Formatted price string
*/
export function formatPrice(price, currency = 'EUR') {
if (price === null || price === undefined) return '–';
const symbols = {
EUR: '€',
USD: '$',
CHF: 'CHF',
XMR: 'ɱ'
};
const symbol = symbols[currency] || currency;
if (currency === 'XMR') {
return `${price.toFixed(4)} ${symbol}`;
}
return `${symbol} ${price.toFixed(2)}`;
}
/**
* Format relative time (e.g., "vor 2 Stunden")
* @param {Date|string} date - Date to format
* @param {string} locale - Locale code
* @returns {string} - Relative time string
*/
export function formatRelativeTime(date, locale = 'de') {
const now = new Date();
const then = new Date(date);
const diffMs = now - then;
const diffSec = Math.floor(diffMs / 1000);
const diffMin = Math.floor(diffSec / 60);
const diffHour = Math.floor(diffMin / 60);
const diffDay = Math.floor(diffHour / 24);
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
if (diffDay > 0) return rtf.format(-diffDay, 'day');
if (diffHour > 0) return rtf.format(-diffHour, 'hour');
if (diffMin > 0) return rtf.format(-diffMin, 'minute');
return rtf.format(-diffSec, 'second');
}
/**
* Debounce function calls
* @param {Function} fn - Function to debounce
* @param {number} delay - Delay in ms
* @returns {Function} - Debounced function
*/
export function debounce(fn, delay = 300) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), delay);
};
}
/**
* Truncate string with ellipsis
* @param {string} str - String to truncate
* @param {number} maxLength - Maximum length
* @returns {string} - Truncated string
*/
export function truncate(str, maxLength = 100) {
if (!str || str.length <= maxLength) return str;
return str.slice(0, maxLength - 1) + '…';
}