diff --git a/js/common.js b/js/common.js index ccc9c3d..b8ac4ac 100644 --- a/js/common.js +++ b/js/common.js @@ -1,14 +1,16 @@ -// Common JavaScript utilities for DevDunia tools +// =========================================================== +// DevDunia Common JavaScript Utilities +// Author: Aditya Rana +// Version: 2.1 +// Description: Utility + Theme Management + Export Ready +// =========================================================== -// Utility functions const DevDuniaUtils = { - // Copy text to clipboard async copyToClipboard(text) { try { await navigator.clipboard.writeText(text); return true; } catch (err) { - // Fallback for older browsers const textArea = document.createElement('textarea'); textArea.value = text; document.body.appendChild(textArea); @@ -17,33 +19,29 @@ const DevDuniaUtils = { document.execCommand('copy'); document.body.removeChild(textArea); return true; - } catch (fallbackErr) { + } catch { document.body.removeChild(textArea); return false; } } }, - // Show visual feedback for copy operations showCopyFeedback(button, success = true) { const originalText = button.innerHTML; const originalClasses = button.className; - if (success) { - button.innerHTML = 'Copied!'; + button.innerHTML = '✅ Copied!'; button.classList.add('bg-green-600'); } else { - button.innerHTML = 'Failed!'; + button.innerHTML = '❌ Failed!'; button.classList.add('bg-red-600'); } - setTimeout(() => { button.innerHTML = originalText; button.className = originalClasses; }, 2000); }, - // Format bytes to human readable format formatBytes(bytes) { if (bytes === 0) return '0 B'; const k = 1024; @@ -52,235 +50,127 @@ const DevDuniaUtils = { return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }, - // Show status message showStatus(message, type = 'info', container) { if (!container) return; - - const statusDiv = document.createElement('div'); - statusDiv.className = `p-3 rounded-lg mb-4 ${ - type === 'error' ? 'bg-red-100 text-red-800 border border-red-200' : - type === 'success' ? 'bg-green-100 text-green-800 border border-green-200' : - type === 'warning' ? 'bg-yellow-100 text-yellow-800 border border-yellow-200' : - 'bg-blue-100 text-blue-800 border border-blue-200' + const div = document.createElement('div'); + div.className = `p-3 rounded-lg mb-4 ${ + type === 'error' + ? 'bg-red-100 text-red-800 border border-red-200' + : type === 'success' + ? 'bg-green-100 text-green-800 border border-green-200' + : 'bg-blue-100 text-blue-800 border border-blue-200' }`; - statusDiv.textContent = message; - + div.textContent = message; container.innerHTML = ''; - container.appendChild(statusDiv); + container.appendChild(div); container.classList.remove('hidden'); - - // Auto-hide after 5 seconds for non-error messages - if (type !== 'error') { - setTimeout(() => { - container.classList.add('hidden'); - }, 5000); - } - }, - - // Hide status message - hideStatus(container) { - if (container) { - container.classList.add('hidden'); - } + if (type !== 'error') setTimeout(() => container.classList.add('hidden'), 4000); }, - // Auto-resize textarea autoResizeTextarea(textarea) { textarea.style.height = 'auto'; textarea.style.height = textarea.scrollHeight + 'px'; }, - // Validate JSON - isValidJSON(str) { - try { - JSON.parse(str); - return true; - } catch (e) { - return false; - } - }, - - // Validate Base64 - isValidBase64(str) { - try { - return btoa(atob(str)) === str; - } catch (err) { - return false; - } - }, + isValidJSON(str) { try { JSON.parse(str); return true; } catch { return false; } }, + isValidURL(str) { try { new URL(str); return true; } catch { return false; } }, - // Validate URL - isValidURL(str) { - try { - new URL(str); - return true; - } catch (e) { - return false; - } - }, - - // Validate IP address - isValidIP(ip) { - const ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; - return ipRegex.test(ip); - }, - - // Generate random string generateRandomString(length, charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') { let result = ''; - for (let i = 0; i < length; i++) { - result += charset.charAt(Math.floor(Math.random() * charset.length)); - } + for (let i = 0; i < length; i++) result += charset.charAt(Math.floor(Math.random() * charset.length)); return result; }, - // Debounce function debounce(func, wait) { let timeout; - return function executedFunction(...args) { - const later = () => { - clearTimeout(timeout); - func(...args); - }; + return function (...args) { clearTimeout(timeout); - timeout = setTimeout(later, wait); + timeout = setTimeout(() => func.apply(this, args), wait); }; }, - // Throttle function throttle(func, limit) { let inThrottle; - return function() { - const args = arguments; - const context = this; + return function (...args) { if (!inThrottle) { - func.apply(context, args); + func.apply(this, args); inThrottle = true; - setTimeout(() => inThrottle = false, limit); + setTimeout(() => (inThrottle = false), limit); } }; }, - // Format number with commas - formatNumber(num) { - return num.toLocaleString(); - }, + formatNumber(num) { return num.toLocaleString(); }, - // Calculate text statistics getTextStats(text) { const chars = text.length; const words = text.trim() ? text.trim().split(/\s+/).length : 0; const lines = text.split('\n').length; const bytes = new Blob([text]).size; - - return { - characters: chars, - words: words, - lines: lines, - bytes: bytes, - size: this.formatBytes(bytes) - }; + return { characters: chars, words, lines, bytes, size: this.formatBytes(bytes) }; }, - // Escape HTML - escapeHtml(text) { - const div = document.createElement('div'); - div.textContent = text; - return div.innerHTML; - }, - - // Unescape HTML - unescapeHtml(html) { - const div = document.createElement('div'); - div.innerHTML = html; - return div.textContent || div.innerText || ''; - }, - - // Generate UUID/GUID - 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); - }); + // --- New Utility Additions --- + timeAgo(date) { + const seconds = Math.floor((new Date() - new Date(date)) / 1000); + const intervals = { year: 31536000, month: 2592000, day: 86400, hour: 3600, minute: 60 }; + for (const [key, value] of Object.entries(intervals)) { + const count = Math.floor(seconds / value); + if (count >= 1) return `${count} ${key}${count > 1 ? 's' : ''} ago`; + } + return 'just now'; }, - // Hash functions (using Web Crypto API) - async hashText(text, algorithm = 'SHA-256') { - const encoder = new TextEncoder(); - const data = encoder.encode(text); - const hashBuffer = await crypto.subtle.digest(algorithm, data); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + getDeviceInfo() { + return { + browser: navigator.userAgent, + platform: navigator.platform, + language: navigator.language, + online: navigator.onLine + }; }, - // Color utilities - hexToRgb(hex) { - const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? { - r: parseInt(result[1], 16), - g: parseInt(result[2], 16), - b: parseInt(result[3], 16) - } : null; + downloadFile(filename, content, type = 'text/plain') { + const blob = new Blob([content], { type }); + const link = document.createElement('a'); + link.href = URL.createObjectURL(blob); + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); }, - rgbToHex(r, g, b) { - return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); + storage: { + set(key, value) { localStorage.setItem(key, JSON.stringify(value)); }, + get(key) { try { return JSON.parse(localStorage.getItem(key)); } catch { return null; } }, + remove(key) { localStorage.removeItem(key); }, + clear() { localStorage.clear(); } }, - // Date utilities - formatDate(date, format = 'YYYY-MM-DD') { - const d = new Date(date); - const year = d.getFullYear(); - const month = String(d.getMonth() + 1).padStart(2, '0'); - const day = String(d.getDate()).padStart(2, '0'); - const hours = String(d.getHours()).padStart(2, '0'); - const minutes = String(d.getMinutes()).padStart(2, '0'); - const seconds = String(d.getSeconds()).padStart(2, '0'); - - return format - .replace('YYYY', year) - .replace('MM', month) - .replace('DD', day) - .replace('HH', hours) - .replace('mm', minutes) - .replace('ss', seconds); + showToast(message, type = 'info') { + const toast = document.createElement('div'); + toast.className = `fixed bottom-6 right-6 z-50 p-3 rounded-lg shadow-md text-white + ${type === 'success' ? 'bg-green-600' : + type === 'error' ? 'bg-red-600' : + type === 'warning' ? 'bg-yellow-500' : 'bg-blue-600'}`; + toast.textContent = message; + document.body.appendChild(toast); + setTimeout(() => { + toast.style.opacity = '0'; + setTimeout(() => toast.remove(), 500); + }, 3000); }, - // String transformation utilities - transformString(text, transform) { - switch (transform) { - case 'uppercase': - return text.toUpperCase(); - case 'lowercase': - return text.toLowerCase(); - case 'capitalize': - return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase(); - case 'title-case': - return text.replace(/\w\S*/g, txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()); - case 'camel-case': - return text.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => { - return index === 0 ? word.toLowerCase() : word.toUpperCase(); - }).replace(/\s+/g, ''); - case 'pascal-case': - return text.replace(/(?:^\w|[A-Z]|\b\w)/g, word => word.toUpperCase()).replace(/\s+/g, ''); - case 'kebab-case': - return text.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/[\s_]+/g, '-').toLowerCase(); - case 'snake-case': - return text.replace(/([a-z])([A-Z])/g, '$1_$2').replace(/[\s-]+/g, '_').toLowerCase(); - case 'constant-case': - return text.replace(/([a-z])([A-Z])/g, '$1_$2').replace(/[\s-]+/g, '_').toUpperCase(); - case 'reverse': - return text.split('').reverse().join(''); - case 'slug': - return text.toLowerCase() - .replace(/[^\w\s-]/g, '') - .replace(/[\s_-]+/g, '-') - .replace(/^-+|-+$/g, ''); - case 'remove-spaces': - return text.replace(/\s+/g, ''); - default: - return text; + autoThemeByTime() { + const hour = new Date().getHours(); + if (hour >= 19 || hour <= 6) { + document.documentElement.classList.add('dark'); + document.documentElement.classList.remove('light'); + localStorage.setItem('theme', 'dark'); + } else { + document.documentElement.classList.add('light'); + document.documentElement.classList.remove('dark'); + localStorage.setItem('theme', 'light'); } } }; @@ -293,46 +183,27 @@ const ThemeManager = { if (savedTheme) { document.documentElement.classList.toggle('dark', savedTheme === 'dark'); document.documentElement.classList.toggle('light', savedTheme === 'light'); - } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) { - document.documentElement.classList.add('dark'); - document.documentElement.classList.remove('light'); - localStorage.setItem('theme', 'dark'); } else { - document.documentElement.classList.remove('dark'); - document.documentElement.classList.add('light'); - localStorage.setItem('theme', 'light'); - } - - this.updateThemeToggle(); - const themeToggle = document.getElementById('theme-toggle'); - if (themeToggle) { - themeToggle.addEventListener('click', () => this.toggleTheme()); - } - const floatToggle = document.getElementById('theme-toggle-float'); - if (floatToggle) { - floatToggle.addEventListener('click', () => this.toggleTheme()); + this.autoDetectTheme(); } + this.updateToggle(); + const toggle = document.getElementById('theme-toggle'); + if (toggle) toggle.addEventListener('click', () => this.toggleTheme()); }, - injectLightStyles() { if (document.getElementById('light-theme-overrides')) return; const css = ` -html.light body { background: #ffffff !important; color: #0f172a !important; } -html.light nav, html.light [class*="bg-slate-900"], html.light [class*="bg-slate-800"], html.light .hero-bg { background: #ffffff !important; color: #0f172a !important; } -html.light [class*="text-white"] { color: #0f172a !important; } -html.light [class*="text-gray-300"], html.light [class*="text-gray-400"], html.light [class*="text-gray-500"] { color: #475569 !important; } -html.light [class*="bg-slate-800"] { background-color: #f8fafc !important; } -html.light [class*="border-slate-700"], html.light [class*="border-slate-600"], html.light [class*="border-slate-700\\/50"] { border-color: rgba(15,23,42,0.08) !important; } -html.light .bg-slate-900\\/95 { background-color: #ffffff !important; } - `; +html.light body { background: #fff !important; color: #0f172a !important; } +html.light [class*="bg-slate-900"], html.light [class*="bg-slate-800"] { background: #ffffff !important; color: #0f172a !important; } +html.light [class*="text-white"] { color: #0f172a !important; }`; const style = document.createElement('style'); style.id = 'light-theme-overrides'; style.appendChild(document.createTextNode(css)); document.head.appendChild(style); }, - updateThemeToggle() { + updateToggle() { const isDark = document.documentElement.classList.contains('dark'); document.querySelectorAll('.theme-toggle-dark').forEach(el => el.classList.toggle('hidden', !isDark)); document.querySelectorAll('.theme-toggle-light').forEach(el => el.classList.toggle('hidden', isDark)); @@ -342,96 +213,36 @@ html.light .bg-slate-900\\/95 { background-color: #ffffff !important; } const isDark = document.documentElement.classList.toggle('dark'); document.documentElement.classList.toggle('light', !isDark); localStorage.setItem('theme', isDark ? 'dark' : 'light'); - this.updateThemeToggle(); + this.updateToggle(); + }, + + autoDetectTheme() { + if (window.matchMedia('(prefers-color-scheme: dark)').matches) { + document.documentElement.classList.add('dark'); + localStorage.setItem('theme', 'dark'); + } else { + document.documentElement.classList.add('light'); + localStorage.setItem('theme', 'light'); + } } }; -// Common event listeners setup -document.addEventListener('DOMContentLoaded', function() { +// Initialize when DOM ready +document.addEventListener('DOMContentLoaded', () => { ThemeManager.init(); - - // Auto-resize all textareas - document.querySelectorAll('textarea').forEach(textarea => { - textarea.addEventListener('input', function() { - DevDuniaUtils.autoResizeTextarea(this); - }); - }); - - // Common copy button functionality - document.addEventListener('click', function(e) { - if (e.target.classList.contains('copy-btn') || e.target.closest('.copy-btn')) { - const button = e.target.classList.contains('copy-btn') ? e.target : e.target.closest('.copy-btn'); - const targetId = button.getAttribute('data-target'); - const targetElement = document.getElementById(targetId); - - if (targetElement) { - const text = targetElement.value || targetElement.textContent; - DevDuniaUtils.copyToClipboard(text).then(success => { - DevDuniaUtils.showCopyFeedback(button, success); - }); - } - } - }); - - // Common clear button functionality - document.addEventListener('click', function(e) { - if (e.target.classList.contains('clear-btn') || e.target.closest('.clear-btn')) { - const button = e.target.classList.contains('clear-btn') ? e.target : e.target.closest('.clear-btn'); - const container = button.closest('.tool-container') || document; - - // Clear all inputs - container.querySelectorAll('input[type="text"], input[type="number"], textarea').forEach(input => { - input.value = ''; - }); - - // Clear all outputs - container.querySelectorAll('.output-section').forEach(section => { - section.classList.add('hidden'); - }); - - // Hide status messages - container.querySelectorAll('.status-display').forEach(status => { - status.classList.add('hidden'); - }); - } - }); + document.querySelectorAll('textarea').forEach(t => + t.addEventListener('input', () => DevDuniaUtils.autoResizeTextarea(t)) + ); }); -// --- Back to Top Button --- -(function() { - function createBackToTopBtn() { - if (document.getElementById('back-to-top-btn')) return; - const btn = document.createElement('button'); - btn.id = 'back-to-top-btn'; - btn.type = 'button'; - btn.setAttribute('aria-label', 'Back to top'); - btn.className = 'fixed bottom-6 right-6 z-50 hidden p-3 rounded-full bg-white/80 dark:bg-slate-900 text-slate-700 dark:text-white shadow-lg border border-slate-200 dark:border-slate-700 hover:bg-blue-100 dark:hover:bg-slate-700 transition-all duration-300'; - btn.innerHTML = ``; - document.body.appendChild(btn); - - // Scroll to top - btn.addEventListener('click', function() { - window.scrollTo({ top: 0, behavior: 'smooth' }); - }); - } - - function toggleBackToTop() { - const btn = document.getElementById('back-to-top-btn'); - if (!btn) return; - if (window.scrollY > 200) { - btn.classList.remove('hidden'); - btn.classList.add('animate-fade-in'); - } else { - btn.classList.add('hidden'); - btn.classList.remove('animate-fade-in'); - } - } - - document.addEventListener('DOMContentLoaded', createBackToTopBtn); - window.addEventListener('scroll', DevDuniaUtils.throttle(toggleBackToTop, 100)); -})(); - -// Export for use in other scripts -window.DevDuniaUtils = DevDuniaUtils; -// Expose ThemeManager so other scripts (floating toggle) can call it -window.ThemeManager = ThemeManager; +// =========================================================== +// Exports (for module usage) +// =========================================================== +if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { + module.exports = { DevDuniaUtils, ThemeManager }; +} else { + window.DevDuniaUtils = DevDuniaUtils; + window.ThemeManager = ThemeManager; +} + +export { DevDuniaUtils, ThemeManager };