Qwen3 / public /utils.js
Semnykcz's picture
Upload 17 files
fe77b2f verified
/**
* Utility functions for the AI Chat Application
*
* This module provides various utility functions for:
* - Text processing and formatting
* - Date/time operations
* - Local storage management
* - Event handling
* - Performance monitoring
* - Debouncing and throttling
*/
// Text Processing Utilities
class TextUtils {
// Truncate text to specified length
static truncate(text, maxLength, suffix = '...') {
if (!text || text.length <= maxLength) return text;
return text.substring(0, maxLength - suffix.length) + suffix;
}
// Count words in text
static wordCount(text) {
if (!text) return 0;
return text.trim().split(/\s+/).filter(word => word.length > 0).length;
}
// Estimate reading time (assuming 200 words per minute)
static estimateReadingTime(text) {
const words = this.wordCount(text);
const minutes = Math.ceil(words / 200);
return minutes;
}
// Clean and normalize text
static normalize(text) {
if (!text) return '';
return text
.trim()
.replace(/\s+/g, ' ')
.replace(/[^\w\s.,!?;:-]/g, '');
}
// Extract hashtags from text
static extractHashtags(text) {
const hashtagRegex = /#[\w]+/g;
return text.match(hashtagRegex) || [];
}
// Extract mentions from text
static extractMentions(text) {
const mentionRegex = /@[\w]+/g;
return text.match(mentionRegex) || [];
}
// Convert text to slug format
static slugify(text) {
return text
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, '')
.replace(/[\s_-]+/g, '-')
.replace(/^-+|-+$/g, '');
}
// Highlight search terms in text
static highlight(text, searchTerm, className = 'highlight') {
if (!searchTerm) return text;
const regex = new RegExp(`(${searchTerm})`, 'gi');
return text.replace(regex, `<span class="${className}">$1</span>`);
}
// Strip HTML tags from text
static stripHtml(html) {
const div = document.createElement('div');
div.innerHTML = html;
return div.textContent || div.innerText || '';
}
// Convert markdown-like syntax to HTML
static simpleMarkdown(text) {
return text
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.*?)\*/g, '<em>$1</em>')
.replace(/`(.*?)`/g, '<code>$1</code>')
.replace(/\n/g, '<br>');
}
}
// Date and Time Utilities
class DateUtils {
// Format date relative to now (e.g., "2 minutes ago")
static formatRelative(date) {
const now = new Date();
const diffInSeconds = Math.floor((now - date) / 1000);
if (diffInSeconds < 60) return 'just now';
if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)} minutes ago`;
if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)} hours ago`;
if (diffInSeconds < 604800) return `${Math.floor(diffInSeconds / 86400)} days ago`;
return date.toLocaleDateString();
}
// Format date for display
static formatDisplay(date, options = {}) {
const defaultOptions = {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
};
return date.toLocaleDateString(undefined, { ...defaultOptions, ...options });
}
// Get start of day
static startOfDay(date = new Date()) {
const start = new Date(date);
start.setHours(0, 0, 0, 0);
return start;
}
// Get end of day
static endOfDay(date = new Date()) {
const end = new Date(date);
end.setHours(23, 59, 59, 999);
return end;
}
// Check if date is today
static isToday(date) {
const today = new Date();
return this.startOfDay(date).getTime() === this.startOfDay(today).getTime();
}
// Check if date is yesterday
static isYesterday(date) {
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
return this.startOfDay(date).getTime() === this.startOfDay(yesterday).getTime();
}
// Get time zone offset
static getTimezoneOffset() {
return new Date().getTimezoneOffset();
}
// Parse various date formats
static parseDate(dateString) {
// Try different date formats
const formats = [
/^\d{4}-\d{2}-\d{2}$/, // YYYY-MM-DD
/^\d{2}\/\d{2}\/\d{4}$/, // MM/DD/YYYY
/^\d{2}-\d{2}-\d{4}$/, // MM-DD-YYYY
];
for (const format of formats) {
if (format.test(dateString)) {
const date = new Date(dateString);
if (!isNaN(date.getTime())) {
return date;
}
}
}
return null;
}
}
// Local Storage Utilities
class StorageUtils {
// Safe JSON parse
static safeJsonParse(str, fallback = null) {
try {
return JSON.parse(str);
} catch (error) {
console.warn('Failed to parse JSON:', error);
return fallback;
}
}
// Safe JSON stringify
static safeJsonStringify(obj, fallback = '{}') {
try {
return JSON.stringify(obj);
} catch (error) {
console.warn('Failed to stringify JSON:', error);
return fallback;
}
}
// Get item from localStorage with fallback
static getItem(key, fallback = null) {
try {
const item = localStorage.getItem(key);
return item !== null ? this.safeJsonParse(item, fallback) : fallback;
} catch (error) {
console.warn('Failed to get localStorage item:', error);
return fallback;
}
}
// Set item in localStorage
static setItem(key, value) {
try {
localStorage.setItem(key, this.safeJsonStringify(value));
return true;
} catch (error) {
console.warn('Failed to set localStorage item:', error);
return false;
}
}
// Remove item from localStorage
static removeItem(key) {
try {
localStorage.removeItem(key);
return true;
} catch (error) {
console.warn('Failed to remove localStorage item:', error);
return false;
}
}
// Clear all localStorage data
static clear() {
try {
localStorage.clear();
return true;
} catch (error) {
console.warn('Failed to clear localStorage:', error);
return false;
}
}
// Get storage usage
static getStorageUsage() {
let total = 0;
for (let key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
total += localStorage[key].length + key.length;
}
}
return total;
}
// Check if storage is available
static isAvailable() {
try {
const test = '__storage_test__';
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch (error) {
return false;
}
}
}
// Event Handling Utilities
class EventUtils {
// Debounce function calls
static debounce(func, wait, immediate = false) {
let timeout;
return function executedFunction(...args) {
const later = () => {
timeout = null;
if (!immediate) func.apply(this, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(this, args);
};
}
// Throttle function calls
static throttle(func, limit) {
let inThrottle;
return function executedFunction(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// Add event listener with cleanup
static addListener(element, event, handler, options = {}) {
element.addEventListener(event, handler, options);
return () => element.removeEventListener(event, handler, options);
}
// Create event emitter
static createEmitter() {
const listeners = new Map();
return {
on(event, callback) {
if (!listeners.has(event)) {
listeners.set(event, []);
}
listeners.get(event).push(callback);
},
off(event, callback) {
const eventListeners = listeners.get(event);
if (eventListeners) {
const index = eventListeners.indexOf(callback);
if (index > -1) {
eventListeners.splice(index, 1);
}
}
},
emit(event, data) {
const eventListeners = listeners.get(event);
if (eventListeners) {
eventListeners.forEach(callback => callback(data));
}
},
once(event, callback) {
const onceWrapper = (data) => {
callback(data);
this.off(event, onceWrapper);
};
this.on(event, onceWrapper);
}
};
}
// Check if element is in viewport
static isInViewport(element) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
// Smooth scroll to element
static scrollToElement(element, options = {}) {
const defaultOptions = {
behavior: 'smooth',
block: 'start',
inline: 'nearest'
};
element.scrollIntoView({ ...defaultOptions, ...options });
}
}
// Performance Utilities
class PerformanceUtils {
// Measure function execution time
static measureTime(func, ...args) {
const start = performance.now();
const result = func.apply(this, args);
const end = performance.now();
console.log(`Function executed in ${end - start} milliseconds`);
return result;
}
// Measure async function execution time
static async measureAsyncTime(func, ...args) {
const start = performance.now();
const result = await func.apply(this, args);
const end = performance.now();
console.log(`Async function executed in ${end - start} milliseconds`);
return result;
}
// Create performance observer
static observePerformance(callback) {
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver(callback);
observer.observe({ entryTypes: ['measure', 'navigation', 'resource'] });
return observer;
}
return null;
}
// Memory usage information
static getMemoryInfo() {
if (performance.memory) {
return {
used: Math.round(performance.memory.usedJSHeapSize / 1048576),
total: Math.round(performance.memory.totalJSHeapSize / 1048576),
limit: Math.round(performance.memory.jsHeapSizeLimit / 1048576)
};
}
return null;
}
// Request idle callback wrapper
static onIdle(callback, options = {}) {
if ('requestIdleCallback' in window) {
return requestIdleCallback(callback, options);
} else {
return setTimeout(callback, 0);
}
}
// Cancel idle callback
static cancelIdle(id) {
if ('cancelIdleCallback' in window) {
cancelIdleCallback(id);
} else {
clearTimeout(id);
}
}
}
// Validation Utilities
class ValidationUtils {
// Validate email format
static isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// Validate URL format
static isValidUrl(url) {
try {
new URL(url);
return true;
} catch {
return false;
}
}
// Check if string is empty or whitespace
static isEmpty(str) {
return !str || str.trim().length === 0;
}
// Validate message content
static isValidMessage(content) {
if (typeof content !== 'string') return false;
if (this.isEmpty(content)) return false;
if (content.length > 10000) return false; // Max length check
return true;
}
// Check for profanity (basic implementation)
static containsProfanity(text) {
const profanityList = ['spam', 'scam']; // Add more as needed
const lowerText = text.toLowerCase();
return profanityList.some(word => lowerText.includes(word));
}
// Validate JSON structure
static isValidJson(str) {
try {
JSON.parse(str);
return true;
} catch {
return false;
}
}
}
// Random Utilities
class RandomUtils {
// Generate random ID
static generateId(length = 8) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
// Generate UUID v4
static generateUuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// Get random element from array
static randomElement(array) {
return array[Math.floor(Math.random() * array.length)];
}
// Shuffle array
static shuffle(array) {
const shuffled = [...array];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
return shuffled;
}
// Generate random color
static randomColor() {
return `#${Math.floor(Math.random()*16777215).toString(16)}`;
}
}
// Export utilities
const Utils = {
Text: TextUtils,
Date: DateUtils,
Storage: StorageUtils,
Event: EventUtils,
Performance: PerformanceUtils,
Validation: ValidationUtils,
Random: RandomUtils
};
// Make available globally
window.Utils = Utils;
// Export for module usage
if (typeof module !== 'undefined' && module.exports) {
module.exports = Utils;
}