89 lines
2.6 KiB
JavaScript
89 lines
2.6 KiB
JavaScript
/**
|
||
* 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, '"')
|
||
.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) + '…';
|
||
}
|