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 };