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
This commit is contained in:
176
assets/js/punycode.js
Normal file
176
assets/js/punycode.js
Normal file
@@ -0,0 +1,176 @@
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user