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
177 lines
7.4 KiB
JavaScript
177 lines
7.4 KiB
JavaScript
// DNS Things - Punycode Module
|
||
|
||
// Real Punycode conversion functions using punycode.js library
|
||
function toPunycode(domain) {
|
||
try {
|
||
// Check if punycode library is available
|
||
if (typeof punycode === 'undefined') {
|
||
throw new Error('Punycode library not loaded');
|
||
}
|
||
|
||
// Split domain into parts and convert each part
|
||
const parts = domain.split('.');
|
||
const convertedParts = parts.map(part => {
|
||
// Check if part contains non-ASCII characters
|
||
if (/[^\x00-\x7F]/.test(part)) {
|
||
return punycode.toASCII(part);
|
||
}
|
||
return part;
|
||
});
|
||
|
||
return convertedParts.join('.');
|
||
} catch (error) {
|
||
// Fallback to URL API if punycode library fails
|
||
try {
|
||
const url = new URL(`http://${domain}`);
|
||
return url.hostname;
|
||
} catch {
|
||
throw new Error('Invalid domain format');
|
||
}
|
||
}
|
||
}
|
||
|
||
function fromPunycode(domain) {
|
||
try {
|
||
// Check if domain contains punycode
|
||
if (!domain.includes('xn--')) {
|
||
return domain; // No punycode to convert
|
||
}
|
||
|
||
// Check if punycode library is available
|
||
if (typeof punycode === 'undefined') {
|
||
throw new Error('Punycode library not loaded');
|
||
}
|
||
|
||
// Split domain into parts and convert each part
|
||
const parts = domain.split('.');
|
||
const convertedParts = parts.map(part => {
|
||
if (part.startsWith('xn--')) {
|
||
try {
|
||
return punycode.toUnicode(part);
|
||
} catch {
|
||
return part; // Return original if conversion fails
|
||
}
|
||
}
|
||
return part;
|
||
});
|
||
|
||
return convertedParts.join('.');
|
||
} catch (error) {
|
||
throw new Error('Invalid punycode format');
|
||
}
|
||
}
|
||
|
||
// Initialize IDN Punycode Converter
|
||
function initPunycodeConverter() {
|
||
const unicodeInput = document.getElementById('unicode-input');
|
||
const punycodeInput = document.getElementById('punycode-input');
|
||
const unicodeToPunycodeBtn = document.getElementById('unicode-to-punycode-btn');
|
||
const punycodeToUnicodeBtn = document.getElementById('punycode-to-unicode-btn');
|
||
const punycodeResult = document.getElementById('punycode-result');
|
||
const unicodeResult = document.getElementById('unicode-result');
|
||
|
||
if (!unicodeInput || !punycodeInput) return;
|
||
|
||
// Unicode to Punycode conversion
|
||
const convertUnicodeToPunycode = () => {
|
||
const input = unicodeInput.value.trim();
|
||
if (!input) {
|
||
punycodeResult.innerHTML = '';
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const converted = toPunycode(input);
|
||
const isConverted = converted !== input;
|
||
|
||
punycodeResult.innerHTML = `
|
||
<div class="p-4 bg-green-50 dark:bg-green-900/30 rounded-lg">
|
||
<div class="flex justify-between items-start">
|
||
<div class="flex-1">
|
||
<h5 class="font-medium text-green-800 dark:text-green-200 mb-2">Punycode Result:</h5>
|
||
<div class="font-mono text-sm break-all p-2 bg-white dark:bg-gray-800 rounded border">${converted}</div>
|
||
${isConverted ? '<p class="text-xs text-green-600 dark:text-green-400 mt-2">✅ Conversion applied</p>' : '<p class="text-xs text-gray-500 dark:text-gray-400 mt-2">ℹ️ No conversion needed (ASCII only)</p>'}
|
||
</div>
|
||
<button onclick="copyToClipboard('${converted}', this)" class="ml-2 p-2 text-green-600 dark:text-green-400 hover:bg-green-100 dark:hover:bg-green-800 rounded" title="Copy to clipboard">
|
||
<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="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
} catch (error) {
|
||
punycodeResult.innerHTML = `
|
||
<div class="p-4 bg-red-50 dark:bg-red-900/30 text-red-700 dark:text-red-300 rounded-lg">
|
||
<h5 class="font-medium mb-1">Error:</h5>
|
||
<p class="text-sm">${error.message}</p>
|
||
</div>
|
||
`;
|
||
}
|
||
};
|
||
|
||
// Punycode to Unicode conversion
|
||
const convertPunycodeToUnicode = () => {
|
||
const input = punycodeInput.value.trim();
|
||
if (!input) {
|
||
unicodeResult.innerHTML = '';
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const converted = fromPunycode(input);
|
||
const isConverted = converted !== input;
|
||
|
||
unicodeResult.innerHTML = `
|
||
<div class="p-4 bg-green-50 dark:bg-green-900/30 rounded-lg">
|
||
<div class="flex justify-between items-start">
|
||
<div class="flex-1">
|
||
<h5 class="font-medium text-green-800 dark:text-green-200 mb-2">Unicode Result:</h5>
|
||
<div class="font-mono text-sm break-all p-2 bg-white dark:bg-gray-800 rounded border">${converted}</div>
|
||
${isConverted ? '<p class="text-xs text-green-600 dark:text-green-400 mt-2">✅ Conversion applied</p>' : '<p class="text-xs text-gray-500 dark:text-gray-400 mt-2">ℹ️ No conversion needed (no punycode found)</p>'}
|
||
</div>
|
||
<button onclick="copyToClipboard('${converted}', this)" class="ml-2 p-2 text-green-600 dark:text-green-400 hover:bg-green-100 dark:hover:bg-green-800 rounded" title="Copy to clipboard">
|
||
<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="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2 2v8a2 2 0 002 2z"></path>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
`;
|
||
} catch (error) {
|
||
unicodeResult.innerHTML = `
|
||
<div class="p-4 bg-red-50 dark:bg-red-900/30 text-red-700 dark:text-red-300 rounded-lg">
|
||
<h5 class="font-medium mb-1">Error:</h5>
|
||
<p class="text-sm">${error.message}</p>
|
||
</div>
|
||
`;
|
||
}
|
||
};
|
||
|
||
// Event listeners
|
||
unicodeToPunycodeBtn.addEventListener('click', convertUnicodeToPunycode);
|
||
punycodeToUnicodeBtn.addEventListener('click', convertPunycodeToUnicode);
|
||
|
||
// Auto-convert on input (with debounce)
|
||
let unicodeTimeout, punycodeTimeout;
|
||
|
||
unicodeInput.addEventListener('input', () => {
|
||
clearTimeout(unicodeTimeout);
|
||
unicodeTimeout = setTimeout(convertUnicodeToPunycode, 500);
|
||
});
|
||
|
||
punycodeInput.addEventListener('input', () => {
|
||
clearTimeout(punycodeTimeout);
|
||
punycodeTimeout = setTimeout(convertPunycodeToUnicode, 500);
|
||
});
|
||
|
||
// Paste support
|
||
unicodeInput.addEventListener('paste', () => {
|
||
setTimeout(convertUnicodeToPunycode, 100);
|
||
});
|
||
|
||
punycodeInput.addEventListener('paste', () => {
|
||
setTimeout(convertPunycodeToUnicode, 100);
|
||
});
|
||
}
|