Files
dns-things/assets/js/theme.js
dwindown e5195ba1f1 Initial commit: DNS Things - Comprehensive DNS utility website
Features implemented:
- Modular JavaScript architecture (theme.js, dns-tools.js, whois.js, punycode.js, ip-tools.js, main.js)
- Responsive design with dark/light theme toggle
- DNS Lookup and Reverse DNS Lookup tools
- Whois Lookup functionality
- IDN Punycode Converter with full Unicode support
- Comprehensive IP Address Tools (validation, IPv4-to-IPv6 mapping, IPv6 compression/expansion)
- Dynamic tab descriptions that change based on active tool
- Mobile-responsive horizontal scrollable tabs
- Copy-to-clipboard functionality for all results
- Clean footer with dynamic year
- IPv4-mapped IPv6 address explanation with clear warnings

Technical improvements:
- Separated concerns with modular JS files
- Fixed browser compatibility issues with punycode library
- Implemented proper error handling and user feedback
- Added comprehensive input validation
- Optimized for mobile devices with touch-friendly UI
2025-08-01 23:13:52 +07:00

198 lines
7.7 KiB
JavaScript

// DNS Things - Theme and UI Utilities
// Theme toggle functionality
function initTheme() {
const themeToggleBtn = document.getElementById('theme-toggle');
const lightIcon = document.getElementById('theme-toggle-light-icon');
const darkIcon = document.getElementById('theme-toggle-dark-icon');
if (!themeToggleBtn || !lightIcon || !darkIcon) return;
// Check for saved theme preference or default to system preference
const savedTheme = localStorage.getItem('theme');
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
let currentTheme = savedTheme || (systemPrefersDark ? 'dark' : 'light');
// Apply initial theme
if (currentTheme === 'dark') {
document.documentElement.classList.add('dark');
lightIcon.classList.add('hidden');
darkIcon.classList.remove('hidden');
} else {
document.documentElement.classList.remove('dark');
lightIcon.classList.remove('hidden');
darkIcon.classList.add('hidden');
}
// Toggle theme function
function toggleTheme() {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
lightIcon.classList.remove('hidden');
darkIcon.classList.add('hidden');
localStorage.setItem('theme', 'light');
} else {
document.documentElement.classList.add('dark');
lightIcon.classList.add('hidden');
darkIcon.classList.remove('hidden');
localStorage.setItem('theme', 'dark');
}
}
themeToggleBtn.addEventListener('click', toggleTheme);
}
// Tab descriptions for dynamic updates
const tabDescriptions = {
'dns-lookup-tab': 'Perform DNS lookups to resolve domain names and check DNS records',
'reverse-dns-tab': 'Find domain names associated with IP addresses using reverse DNS',
'whois-tab': 'Get detailed registration and ownership information for domains and IPs',
'punycode-tab': 'Convert international domain names between Unicode and Punycode formats',
'ip-tools-tab': 'Comprehensive IP address utilities including validation, conversion, and analysis'
};
// Tab switching functionality
function initTabs() {
const tabButtons = document.querySelectorAll('.tab-btn');
const tabContents = document.querySelectorAll('.tab-content');
const descriptionElement = document.querySelector('p.text-lg.text-gray-500.mt-2');
tabButtons.forEach(button => {
button.addEventListener('click', () => {
const targetTab = button.getAttribute('data-tab');
// Remove active class from all buttons and contents
tabButtons.forEach(btn => {
btn.classList.remove('active', 'text-indigo-600', 'dark:text-indigo-400', 'bg-white', 'dark:bg-gray-800', 'shadow');
btn.classList.add('text-gray-600', 'dark:text-gray-300', 'hover:bg-white', 'hover:dark:bg-gray-800/50');
});
tabContents.forEach(content => {
content.classList.remove('active');
content.classList.add('hidden');
});
// Add active class to clicked button and show corresponding content
button.classList.remove('text-gray-600', 'dark:text-gray-300', 'hover:bg-white', 'hover:dark:bg-gray-800/50');
button.classList.add('active', 'text-indigo-600', 'dark:text-indigo-400', 'bg-white', 'dark:bg-gray-800', 'shadow');
const targetContent = document.getElementById(targetTab);
if (targetContent) {
targetContent.classList.remove('hidden');
targetContent.classList.add('active');
}
// Update description text
if (descriptionElement && tabDescriptions[targetTab]) {
descriptionElement.textContent = tabDescriptions[targetTab];
}
});
});
// Set initial description based on active tab
const activeTab = document.querySelector('.tab-btn.active');
if (activeTab && descriptionElement) {
const activeTabId = activeTab.getAttribute('data-tab');
if (tabDescriptions[activeTabId]) {
descriptionElement.textContent = tabDescriptions[activeTabId];
}
}
}
// Copy to clipboard functionality
function copyToClipboard(text, button) {
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text).then(() => {
showCopyFeedback(button);
}).catch(err => {
console.error('Failed to copy: ', err);
fallbackCopyTextToClipboard(text, button);
});
} else {
fallbackCopyTextToClipboard(text, button);
}
}
// Make copyToClipboard globally available
window.copyToClipboard = copyToClipboard;
function fallbackCopyTextToClipboard(text, button) {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
showCopyFeedback(button);
} catch (err) {
console.error('Fallback: Oops, unable to copy', err);
}
document.body.removeChild(textArea);
}
function showCopyFeedback(button) {
const originalHTML = button.innerHTML;
button.innerHTML = `
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
`;
button.classList.add('bg-green-500');
setTimeout(() => {
button.innerHTML = originalHTML;
button.classList.remove('bg-green-500');
}, 1000);
}
// Initialize copy buttons for the original HTML structure
function initCopyButtons() {
document.querySelectorAll('.copy-btn').forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('data-copy-target');
const targetElement = document.getElementById(targetId);
const copyIcon = this.querySelector('.copy-icon');
const copyFeedback = this.querySelector('.copy-feedback');
if (targetElement) {
const textToCopy = targetElement.textContent.trim();
// Skip if text is loading or not available
if (!textToCopy || textToCopy === 'loading...' || textToCopy === 'Not available') {
return;
}
// Try to use the modern clipboard API
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(textToCopy).then(() => {
showCopyFeedback(copyIcon, copyFeedback);
}).catch(err => {
console.error('Failed to copy: ', err);
fallbackCopyTextToClipboard(textToCopy, copyIcon, copyFeedback);
});
} else {
// Fallback for older browsers
fallbackCopyTextToClipboard(textToCopy, copyIcon, copyFeedback);
}
}
});
});
}
function showCopyFeedback(copyIcon, copyFeedback) {
if (copyIcon) copyIcon.classList.add('hidden');
if (copyFeedback) copyFeedback.classList.remove('hidden');
setTimeout(() => {
if (copyIcon) copyIcon.classList.remove('hidden');
if (copyFeedback) copyFeedback.classList.add('hidden');
}, 2000);
}