robots.txt parser
HTTP /api/v1/http/robotsFetch and parse robots.txt — User-agent groups, Disallow/Allow, Sitemap, Crawl-delay.
https://crypt.tools/robots.txt
200
207558 bytes
0 User-agent groups
Raw robots.txt
<!DOCTYPE html><html lang="en" dir="ltr" class="h-full">
<head> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Crypt.Tools - Advanced Cryptography Toolkit</title>
<!-- Dark mode initialization - Must be as early as possible -->
<script> // Check for saved dark mode preference or system preference if (localStorage.getItem('theme') === 'dark' || (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) { document.documentElement.classList.add('dark'); } </script> <!-- SEO Meta Tags --> <meta name="description" content="Perform cryptographic operations with Crypt.Tools. Hash with MD5, encrypt/decrypt with Base64, and use other cryptographic algorithms easily."> <meta name="keywords" content="cryptography, md5, base64, encryption, decryption, hash, sha1, sha256, crypto tools"> <!-- Open Graph Meta Tags --> <meta property="og:title" content="Crypt.Tools - Advanced Cryptography Toolkit"> <meta property="og:description" content="Perform cryptographic operations with Crypt.Tools. Hash with MD5, encrypt/decrypt with Base64, and use other cryptographic algorithms easily.">
<meta property="og:type" content="website"> <meta property="og:url" content="https://crypt.tools/">
<!-- Generated with HostFavIcons.com -->
<link rel="icon" type="image/svg+xml" href="https://hostfavicons.com/emoji.php?t=CT&shape=rounded&size=256&bg=0170f5&fg=ffffff&padding=0.18">
<link rel="icon" type="image/png" sizes="32x32" href="https://hostfavicons.com/emoji.php?t=CT&shape=rounded&size=32&bg=0170f5&fg=ffffff&padding=0.18">
<link rel="icon" type="image/png" sizes="16x16" href="https://hostfavicons.com/emoji.php?t=CT&shape=rounded&size=16&bg=0170f5&fg=ffffff&padding=0.18">
<link rel="apple-touch-icon" href="https://hostfavicons.com/emoji.php?t=CT&shape=rounded&size=180&bg=0170f5&fg=ffffff&padding=0.18">
<!-- Tailwind CSS --> <script src="https://cdn.tailwindcss.com"></script> <!-- Tailwind dark mode configuration --> <script> tailwind.config = { darkMode: 'class', theme: { extend: { animation: { 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite', } }, }, } </script> <!-- Font Awesome --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <!-- CryptoJS library for cryptographic functions --> <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/aes.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/tripledes.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/rabbit.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/rc4.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/hmac.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/pbkdf2.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/js-blake2/dist/blake2b.js"></script> <script src="https://cdn.jsdelivr.net/npm/js-blake2/dist/blake2s.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/base32.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bs58.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/ascii85.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/punycode.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/scrypt.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bcrypt.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/qrcode.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jwt.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.min.js"></script>
<!-- Custom CSS -->
<style>
/* General styling */
.notification {
transition: opacity 0.3s, transform 0.3s;
}
/* Tool selector styling */
.tool-selector {
width: 100%;
padding: 0.75rem; /* p-3 */
border: 1px solid #D1D5DB; /* gray-300 */
border-radius: 0.5rem; /* rounded-lg */
background-color: #FFFFFF;
color: #111827;
}
.tool-selector:focus {
outline: none;
box-shadow: 0 0 0 2px #2563EB; /* ring-2 ring-blue-600 */
border-color: #2563EB;
}
.dark .tool-selector {
background-color: #374151; /* gray-700 */
border-color: #4B5563; /* gray-600 */
color: #FFFFFF;
}
/* Sidebar transition and positioning */
#sidebar {
position: fixed;
left: 0;
right: auto;
transition: transform 0.3s ease-in-out;
}
body.sidebar-right #sidebar {
left: auto;
right: 0;
}
/* File drop zone styles */
#file-drop-zone {
transition: all 0.2s ease-in-out;
}
#file-drop-zone:hover {
border-color: #60A5FA; /* blue-400 */
background-color: rgba(239, 246, 255, 0.5); /* blue-50/50 */
}
.dark #file-drop-zone:hover {
background-color: rgba(30, 58, 138, 0.1); /* blue-900/10 */
}
/* Responsive adjustments */
@media (max-width: 768px) {
#sidebar {
transform: translateX(-100%);
}
#sidebar.translate-x-0 {
transform: translateX(0);
}
#main-content {
margin-left: 0 !important;
}
}
@media (min-width: 769px) {
/* Apply content offset only when the sidebar is open */
body.sidebar-open:not(.sidebar-right) #main-content {
margin-left: 16rem; /* w-64 = 16rem */
}
body.sidebar-open.sidebar-right #main-content {
margin-right: 16rem; /* w-64 = 16rem */
}
/* When closed, no extra margins */
body:not(.sidebar-open) #main-content {
margin-left: 0;
margin-right: 0;
}
}
/* Dark mode adjustments for syntax highlighting */
.dark pre {
background-color: #1F2937; /* gray-800 */
}
/* Loading indicator */
.loading-spinner {
border: 3px solid rgba(0, 0, 0, 0.1);
border-radius: 50%;
border-top: 3px solid #3498db;
width: 20px;
height: 20px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.dark .loading-spinner {
border-color: rgba(255, 255, 255, 0.1);
border-top-color: #3498db;
}
/* QR Code section */
.qr-code-container {
display: flex;
justify-content: center;
align-items: center;
padding: 1rem; /* p-4 */
background: #FFFFFF; /* bg-white */
border-radius: 0.5rem; /* rounded-lg */
box-shadow: inset 0 2px 4px rgba(0,0,0,0.06); /* shadow-inner */
max-width: 20rem; /* max-w-xs */
margin-left: auto;
margin-right: auto; /* mx-auto */
}
/* Tool tag badges */
.tool-tag {
display: inline-block;
font-size: 0.75rem; /* text-xs */
padding: 0.25rem 0.5rem; /* px-2 py-1 */
border-radius: 9999px; /* rounded-full */
background: #DBEAFE; /* blue-100 */
color: #1E40AF; /* blue-800 */
margin-right: 0.5rem; /* mr-2 */
margin-bottom: 0.5rem; /* mb-2 */
}
.dark .tool-tag {
background: #1E3A8A; /* blue-900 */
color: #BFDBFE; /* blue-200 */
}
/* Collapse and expand animations */
.collapse-enter-active, .collapse-leave-active {
transition: max-height 0.3s ease-out;
overflow: hidden;
max-height: 1000px;
}
.collapse-enter, .collapse-leave-to {
max-height: 0;
overflow: hidden;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: #cbd5e0;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #a0aec0;
}
.dark ::-webkit-scrollbar-thumb {
background: #4a5568;
}
.dark ::-webkit-scrollbar-thumb:hover {
background: #718096;
}
/* Highlight effect for active tool */
.tool-link.active {
color: #2563EB; /* blue-600 */
font-weight: 600; /* semibold */
}
.dark .tool-link.active {
color: #60A5FA; /* blue-400 */
}
/* Tooltip styles */
.tooltip {
visibility: hidden;
position: absolute;
z-index: 10;
padding: 0.5rem 0.75rem; /* px-3 py-2 */
font-size: 0.875rem; /* text-sm */
font-weight: 500; /* font-medium */
color: #FFFFFF; /* text-white */
transition: opacity 0.3s ease;
background: #111827; /* gray-900 */
border-radius: 0.5rem; /* rounded-lg */
box-shadow: 0 1px 2px rgba(0,0,0,0.05); /* shadow-sm */
opacity: 0;
}
.dark .tooltip {
background: #374151; /* gray-700 */
}
.has-tooltip:hover .tooltip {
visibility: visible;
opacity: 1;
}
/* Function feature badges */
.feature-badge {
font-size: 0.75rem; /* text-xs */
padding: 0.125rem 0.5rem; /* py-0.5 px-2 */
border-radius: 9999px; /* rounded-full */
}
.feature-badge.new {
background: #D1FAE5; /* green-100 */
color: #065F46; /* green-800 */
}
.dark .feature-badge.new {
background: #064E3B; /* green-900 */
color: #A7F3D0; /* green-200 */
}
.feature-badge.popular {
background: #FFEDD5; /* orange-100 */
color: #9A3412; /* orange-800 */
}
.dark .feature-badge.popular {
background: #7C2D12; /* orange-900 */
color: #FED7AA; /* orange-200 */
}
/* Zoom effect on hovering cards */
.zoom-on-hover {
transition: transform 0.2s ease-in-out;
}
.zoom-on-hover:hover {
transform: scale(1.02);
}
/* Pulse animation for important elements */
.pulse-animation {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
/* Custom checkbox styles */
.custom-checkbox {
width: 1rem; /* w-4 */
height: 1rem; /* h-4 */
background: #F3F4F6; /* gray-100 */
border: 1px solid #D1D5DB; /* gray-300 */
border-radius: 0.25rem; /* rounded */
accent-color: #2563EB; /* text-blue-600 equivalent for inputs */
}
.custom-checkbox:focus {
outline: none;
box-shadow: 0 0 0 2px #3B82F6; /* ring-2 ring-blue-500 */
}
.dark .custom-checkbox {
background: #374151; /* gray-700 */
border-color: #4B5563; /* gray-600 */
}
</style>
</head> <body class="bg-gray-50 dark:bg-gray-900 text-gray-800 dark:text-gray-200 transition-colors duration-300 min-h-screen flex flex-col"> <!-- Header --> <header class="bg-white dark:bg-gray-800 shadow fixed top-0 left-0 w-full z-50 h-16"> <div class="flex justify-between items-center h-full px-4"> <!-- Left section with hamburger and logo --> <div class="flex items-center"> <!-- Hamburger menu for sidebar --> <button id="sidebar-toggle" class="text-gray-600 hover:text-blue-600 mr-4 focus:outline-none transition-colors duration-300 dark:text-gray-300 dark:hover:text-blue-400"> <i class="fas fa-bars fa-lg"></i> </button> <!-- Logo --> <a href="#" class="text-blue-600 text-2xl font-bold flex items-center"> <i class="fas fa-lock mr-3"></i> <span class="hidden sm:inline">Crypt.Tools</span> </a> </div>
<!-- Center - Primary Navigation (Desktop) -->
<nav class="hidden lg:flex items-center space-x-6">
<a href="index.html" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Home</a>
<a href="#faq" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">FAQ</a>
<a href="#contact" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Contact</a>
<a href="#api" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">API</a>
<a href="partners.html" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Partners</a>
</nav>
<!-- Right Side (Settings, Dark Mode) -->
<div class="flex items-center space-x-2 md:space-x-4">
<!-- Settings Button -->
<button id="settings-btn" class="text-gray-600 hover:text-blue-600 focus:outline-none transition-colors duration-300 dark:text-gray-300 dark:hover:text-blue-400 hidden md:block" title="Settings">
<i class="fas fa-cog fa-lg"></i>
</button>
<!-- Dark Mode Toggle -->
<button id="dark-mode-toggle" class="text-gray-600 hover:text-blue-600 focus:outline-none transition-colors duration-300 dark:text-gray-300 dark:hover:text-blue-400" title="Toggle Dark Mode">
<i class="fas fa-moon fa-lg"></i>
<i class="fas fa-sun fa-lg hidden"></i>
</button>
<!-- Mobile Menu Button -->
<button id="menu-button" class="text-gray-600 hover:text-blue-600 md:hidden focus:outline-none transition-colors duration-300 dark:text-gray-300 dark:hover:text-blue-400">
<i class="fas fa-ellipsis-v fa-lg"></i>
</button>
</div>
</div>
</header>
<!-- Mobile Navigation Menu -->
<div id="mobile-menu" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden">
<div class="fixed top-0 right-0 h-full w-64 bg-white dark:bg-gray-800 shadow-lg transform transition-transform duration-300 translate-x-full">
<div class="p-6">
<button id="close-menu" class="absolute top-4 right-4 text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">
<i class="fas fa-times fa-lg"></i>
</button>
<nav class="mt-8 space-y-4">
<a href="index.html" class="block text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Home</a>
<a href="#faq" class="block text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">FAQ</a>
<a href="#contact" class="block text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Contact</a>
<a href="#api" class="block text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">API</a>
<a href="partners.html" class="block text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Partners</a>
<hr class="my-4 border-gray-200 dark:border-gray-700">
<!-- Dark Mode Toggle for Mobile -->
<div class="pt-2">
<button id="mobile-dark-mode-toggle" data-toggle="dark-mode" class="flex items-center justify-between w-full text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">
<span>Dark Mode</span>
<span>
<i class="fas fa-moon fa-lg"></i>
<i class="fas fa-sun fa-lg hidden"></i>
</span>
</button>
</div>
</nav>
</div>
</div>
</div>
<!-- Keyboard Shortcuts Modal -->
<div id="shortcuts-modal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden flex items-center justify-center">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-2xl w-full mx-4 max-h-[90vh] overflow-y-auto">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-semibold dark:text-white">Keyboard Shortcuts</h3>
<button id="close-shortcuts" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">
<i class="fas fa-times fa-lg"></i>
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<h4 class="font-medium mb-2 text-gray-800 dark:text-gray-200">General</h4>
<ul class="space-y-2">
<li class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Process current function</span>
<kbd class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">Ctrl+Enter</kbd>
</li>
<li class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Search tools</span>
<kbd class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">Ctrl+/</kbd>
</li>
<li class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Clear input</span>
<kbd class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">Esc</kbd>
</li>
<li class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Toggle dark mode</span>
<kbd class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">Shift+D</kbd>
</li>
<li class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Toggle sidebar</span>
<kbd class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">Shift+S</kbd>
</li>
</ul>
</div>
<div>
<h4 class="font-medium mb-2 text-gray-800 dark:text-gray-200">Quick Access</h4>
<ul class="space-y-2">
<li class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">MD5 Hash</span>
<kbd class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">Alt+1</kbd>
</li>
<li class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Base64</span>
<kbd class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">Alt+2</kbd>
</li>
<li class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">AES Encryption</span>
<kbd class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">Alt+3</kbd>
</li>
<li class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">URL Encode/Decode</span>
<kbd class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">Alt+4</kbd>
</li>
<li class="flex justify-between">
<span class="text-gray-600 dark:text-gray-400">Password Generator</span>
<kbd class="px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded text-sm">Alt+5</kbd>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- Settings Modal -->
<div id="settings-modal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden flex items-center justify-center">
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full mx-4">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-semibold dark:text-white">Settings</h3>
<button id="close-settings" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">
<i class="fas fa-times fa-lg"></i>
</button>
</div>
<div class="space-y-4">
<!-- Theme Settings -->
<div>
<h4 class="font-medium mb-2 text-gray-800 dark:text-gray-200">Theme</h4>
<div class="flex items-center justify-between">
<span class="text-gray-600 dark:text-gray-400">Dark Mode</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="settings-dark-mode" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</label>
</div>
</div>
<!-- Interface Settings -->
<div>
<h4 class="font-medium mb-2 text-gray-800 dark:text-gray-200">Interface</h4>
<div class="space-y-2">
<div class="flex items-center justify-between">
<span class="text-gray-600 dark:text-gray-400">Sidebar Position</span>
<select id="sidebar-position" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="left">Left</option>
<option value="right">Right</option>
</select>
</div>
<div class="flex items-center justify-between">
<span class="text-gray-600 dark:text-gray-400">Font Size</span>
<select id="font-size" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="small">Small</option>
<option value="medium" selected>Medium</option>
<option value="large">Large</option>
</select>
</div>
<div class="flex items-center justify-between">
<span class="text-gray-600 dark:text-gray-400">Autosave</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="autosave-setting" class="sr-only peer" checked>
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</label>
</div>
</div>
</div>
<!-- Behavior Settings -->
<div>
<h4 class="font-medium mb-2 text-gray-800 dark:text-gray-200">Behavior</h4>
<div class="space-y-2">
<div class="flex items-center justify-between">
<span class="text-gray-600 dark:text-gray-400">Live Preview</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="live-preview-setting" class="sr-only peer" checked>
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</label>
</div>
<div class="flex items-center justify-between">
<span class="text-gray-600 dark:text-gray-400">Keep History</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="keep-history-setting" class="sr-only peer" checked>
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</label>
</div>
</div>
</div>
<div class="pt-4 flex justify-end">
<button id="reset-settings" class="mr-2 px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-md dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600">
Reset to Default
</button>
<button id="save-settings" class="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md">
Save Changes
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Main Content Container with Sidebar -->
<div class="flex pt-16">
<!-- Sidebar -->
<aside id="sidebar" class="bg-white dark:bg-gray-800 shadow-lg w-64 fixed h-full overflow-y-auto transition-all duration-300 z-40 transform -translate-x-full">
<!-- Search Box -->
<div class="p-4 border-b border-gray-200 dark:border-gray-700">
<div class="relative">
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
<i class="fas fa-search text-gray-400"></i>
</div>
<input type="search" id="tool-search" class="block w-full p-2 pl-10 text-sm border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white" placeholder="Search tools...">
</div>
</div>
<div class="p-4">
<!-- Favorites Section -->
<div class="mb-6" id="favorites-section">
<div class="flex items-center justify-between mb-3">
<h3 class="text-lg font-semibold dark:text-white">Favorites</h3>
<button id="manage-favorites" class="text-xs text-blue-600 dark:text-blue-400 hover:underline">
Manage
</button>
</div>
<ul id="favorites-list" class="space-y-1 text-gray-600 dark:text-gray-300">
<li class="italic text-gray-500 dark:text-gray-400 text-sm">No favorites yet</li>
</ul>
</div>
<!-- Recent Functions -->
<div class="mb-6" id="recent-section">
<div class="flex items-center justify-between mb-3">
<h3 class="text-lg font-semibold dark:text-white">Recent</h3>
<button id="clear-recent" class="text-xs text-blue-600 dark:text-blue-400 hover:underline">
Clear
</button>
</div>
<ul id="recentFunctions" class="space-y-1 text-gray-600 dark:text-gray-300">
<li class="italic text-gray-500 dark:text-gray-400 text-sm">No recent functions</li>
</ul>
</div>
<!-- Available Tools -->
<div class="mb-6">
<h3 class="text-lg font-semibold mb-3 dark:text-white" id="hash-section">Hash Functions</h3>
<ul class="space-y-1 text-gray-600 dark:text-gray-300">
<li class="flex items-center">
<a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300 flex-grow" data-function="md5">MD5</a>
<span class="feature-badge popular text-xs">Popular</span>
</li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="sha1">SHA-1</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="sha256">SHA-256</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="sha512">SHA-512</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="sha3">SHA-3</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="ripemd160">RIPEMD-160</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="blake2b">BLAKE2b</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="blake2s">BLAKE2s</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="keccak">Keccak</a></li>
</ul>
<h3 class="text-lg font-semibold mb-3 mt-6 dark:text-white" id="encoding-section">Encoding/Decoding</h3>
<ul class="space-y-1 text-gray-600 dark:text-gray-300">
<li class="flex items-center">
<a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300 flex-grow" data-function="base64" data-has-mode="true">Base64</a>
<span class="feature-badge popular text-xs">Popular</span>
</li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="base32" data-has-mode="true">Base32</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="base58" data-has-mode="true">Base58</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="ascii85" data-has-mode="true">ASCII85</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="url" data-has-mode="true">URL</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="html" data-has-mode="true">HTML</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="hex" data-has-mode="true">Hex</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="binary" data-has-mode="true">Binary</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="morse" data-has-mode="true">Morse Code</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="punycode" data-has-mode="true">Punycode</a></li>
<li class="flex items-center">
<a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300 flex-grow" data-function="json" data-has-mode="true">JSON</a>
<span class="feature-badge new text-xs">New</span>
</li>
</ul>
<h3 class="text-lg font-semibold mb-3 mt-6 dark:text-white" id="encryption-section">Encryption</h3>
<ul class="space-y-1 text-gray-600 dark:text-gray-300">
<li class="flex items-center">
<a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300 flex-grow" data-function="aes" data-has-mode="true">AES</a>
<span class="feature-badge popular text-xs">Popular</span>
</li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="des" data-has-mode="true">DES</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="tripledes" data-has-mode="true">Triple DES</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="rabbit" data-has-mode="true">Rabbit</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="rc4" data-has-mode="true">RC4</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="blowfish" data-has-mode="true">Blowfish</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="twofish" data-has-mode="true">Twofish</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="camellia" data-has-mode="true">Camellia</a></li>
</ul>
<h3 class="text-lg font-semibold mb-3 mt-6 dark:text-white">Key Derivation</h3>
<ul class="space-y-1 text-gray-600 dark:text-gray-300">
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="pbkdf2">PBKDF2</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="scrypt">Scrypt</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="bcrypt">Bcrypt</a></li>
</ul>
<h3 class="text-lg font-semibold mb-3 mt-6 dark:text-white" id="other-section">Other Tools</h3>
<ul class="space-y-1 text-gray-600 dark:text-gray-300">
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="hmac">HMAC</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="jwt" data-has-mode="true">JWT</a></li>
<li class="flex items-center">
<a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300 flex-grow" data-function="password-generator">Password Generator</a>
<span class="feature-badge popular text-xs">Popular</span>
</li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="uuid">UUID Generator</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="hash-compare">Hash Comparison</a></li>
<li><a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300" data-function="file-checksum">File Checksum</a></li>
<li class="flex items-center">
<a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300 flex-grow" data-function="qr-code" data-has-mode="true">QR Code</a>
<span class="feature-badge new text-xs">New</span>
</li>
<li class="flex items-center">
<a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300 flex-grow" data-function="diff-checker">Text Diff Checker</a>
<span class="feature-badge new text-xs">New</span>
</li>
<li class="flex items-center">
<a href="#" class="tool-link hover:text-blue-600 transition-colors duration-300 flex-grow" data-function="regex-tester">Regex Tester</a>
<span class="feature-badge new text-xs">New</span>
</li>
</ul>
</div>
</div>
<!-- Footer Information -->
<div class="mt-auto p-4 text-xs text-gray-500 dark:text-gray-400 border-t border-gray-200 dark:border-gray-700">
<p>All operations are performed locally in your browser. Your data never leaves your device.</p>
<p class="mt-2"> 2025 Crypt.Tools | <a href="#" class="text-blue-600 dark:text-blue-400 hover:underline">Privacy Policy</a></p>
</div>
</aside>
<!-- Sidebar Overlay -->
<div id="sidebar-overlay" class="fixed inset-0 bg-black bg-opacity-50 z-30 hidden md:hidden"></div>
<!-- Main Content -->
<main class="flex-1 p-4 md:p-6 transition-all duration-300" id="main-content">
<div class="container mx-auto px-4 py-6">
<!-- Breadcrumb -->
<nav class="flex mb-5" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<a href="#" class="inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400">
<i class="fas fa-home mr-2"></i>
Home
</a>
</li>
<li aria-current="page">
<div class="flex items-center">
<i class="fas fa-chevron-right text-gray-400 mx-2 text-xs"></i>
<span id="breadcrumb-current" class="text-sm font-medium text-gray-500 dark:text-gray-400">Select a Tool</span>
</div>
</li>
</ol>
</nav>
<!-- Tool Section -->
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-xl p-6 mb-8">
<div class="flex flex-col md:flex-row justify-between items-center mb-6 gap-4">
<div class="w-full md:w-auto">
<select id="tool-selector" class="tool-selector">
<optgroup label="Hash Functions">
<option value="md5">MD5</option>
<option value="sha1">SHA-1</option>
<option value="sha256">SHA-256</option>
<option value="sha512">SHA-512</option>
<option value="sha3">SHA-3</option>
<option value="ripemd160">RIPEMD-160</option>
<option value="blake2b">BLAKE2b</option>
<option value="blake2s">BLAKE2s</option>
<option value="keccak">Keccak</option>
</optgroup>
<optgroup label="Encoding/Decoding">
<option value="base64" data-has-mode="true">Base64</option>
<option value="base32" data-has-mode="true">Base32</option>
<option value="base58" data-has-mode="true">Base58</option>
<option value="ascii85" data-has-mode="true">ASCII85</option>
<option value="url" data-has-mode="true">URL</option>
<option value="html" data-has-mode="true">HTML</option>
<option value="hex" data-has-mode="true">Hex</option>
<option value="binary" data-has-mode="true">Binary</option>
<option value="morse" data-has-mode="true">Morse Code</option>
<option value="punycode" data-has-mode="true">Punycode</option>
<option value="json" data-has-mode="true">JSON</option>
</optgroup>
<optgroup label="Encryption">
<option value="aes" data-has-mode="true">AES</option>
<option value="des" data-has-mode="true">DES</option>
<option value="tripledes" data-has-mode="true">Triple DES</option>
<option value="rabbit" data-has-mode="true">Rabbit</option>
<option value="rc4" data-has-mode="true">RC4</option>
<option value="blowfish" data-has-mode="true">Blowfish</option>
<option value="twofish" data-has-mode="true">Twofish</option>
<option value="camellia" data-has-mode="true">Camellia</option>
</optgroup>
<optgroup label="Key Derivation">
<option value="pbkdf2">PBKDF2</option>
<option value="scrypt">Scrypt</option>
<option value="bcrypt">Bcrypt</option>
</optgroup>
<optgroup label="Other Tools">
<option value="hmac">HMAC</option>
<option value="jwt" data-has-mode="true">JWT</option>
<option value="password-generator">Password Generator</option>
<option value="uuid">UUID Generator</option>
<option value="hash-compare">Hash Comparison</option>
<option value="file-checksum">File Checksum</option>
<option value="qr-code" data-has-mode="true">QR Code</option>
<option value="diff-checker">Text Diff Checker</option>
<option value="regex-tester">Regex Tester</option>
</optgroup>
</select>
</div>
<!-- Tool Actions -->
<div class="flex gap-2">
<!-- Mode Switch (Encode/Decode) - Hidden by default -->
<div id="mode-switch-container" class="hidden">
<label class="inline-flex items-center cursor-pointer">
<span class="mr-3 text-sm font-medium text-gray-600 dark:text-gray-300">Encode</span>
<div class="relative">
<input type="checkbox" id="mode-switch" class="sr-only peer">
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
</div>
<span class="ml-3 text-sm font-medium text-gray-600 dark:text-gray-300">Decode</span>
</label>
</div>
<!-- Favorite Button -->
<button id="favorite-btn" class="p-2.5 text-gray-500 hover:text-yellow-500 dark:text-gray-400 dark:hover:text-yellow-400 transition-colors duration-200 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700" title="Add to Favorites">
<i class="far fa-star"></i>
</button>
<!-- Share Button -->
<button id="share-btn" class="p-2.5 text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400 transition-colors duration-200 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700" title="Share Tool">
<i class="fas fa-share-alt"></i>
</button>
</div>
</div>
<!-- File Drop Zone (Hidden by default) -->
<div id="file-drop-zone" class="border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-lg p-8 text-center mb-6 hidden">
<div class="flex flex-col items-center">
<i class="fas fa-file-upload text-4xl text-gray-400 dark:text-gray-500 mb-3"></i>
<p class="text-gray-600 dark:text-gray-400 mb-2">Drag & drop a file here</p>
<p class="text-gray-500 dark:text-gray-500 text-sm">or</p>
<label class="mt-3 cursor-pointer">
<span class="bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg transition duration-300">Browse Files</span>
<input type="file" id="file-input" class="hidden">
</label>
</div>
</div>
<!-- Diff Checker UI (Hidden by default) -->
<div id="diff-checker-container" class="hidden mb-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Original Text -->
<div>
<h4 class="text-lg font-medium mb-3 dark:text-white">Original Text</h4>
<div class="relative mb-3">
<div class="textbox-icons absolute top-2 right-2 flex gap-2 z-10">
<button type="button" class="text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400">
<i class="fas fa-eraser"></i>
</button>
</div>
<textarea id="diffOriginalText" rows="10" class="w-full p-4 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white resize-y" placeholder="Enter original text..."></textarea>
</div>
</div>
<!-- Modified Text -->
<div>
<h4 class="text-lg font-medium mb-3 dark:text-white">Modified Text</h4>
<div class="relative mb-3">
<div class="textbox-icons absolute top-2 right-2 flex gap-2 z-10">
<button type="button" class="text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400">
<i class="fas fa-eraser"></i>
</button>
</div>
<textarea id="diffModifiedText" rows="10" class="w-full p-4 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white resize-y" placeholder="Enter modified text..."></textarea>
</div>
</div>
</div>
<div class="flex justify-center mt-4">
<div class="inline-flex rounded-md shadow-sm" role="group">
<button id="diffInlineBtn" class="px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-l-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-blue-500 dark:focus:text-white">
Inline
</button>
<button id="diffSideBySideBtn" class="px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-r-lg hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-2 focus:ring-blue-700 focus:text-blue-700 dark:bg-gray-700 dark:border-gray-600 dark:text-white dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-blue-500 dark:focus:text-white">
Side by Side
</button>
</div>
</div>
</div>
<!-- Regex Tester UI (Hidden by default) -->
<div id="regex-tester-container" class="hidden mb-6">
<div class="mb-4">
<label class="block text-gray-700 dark:text-gray-300 mb-2" for="regexPattern">Regular Expression:</label>
<div class="flex">
<span class="inline-flex items-center px-3 text-sm text-gray-900 bg-gray-200 border border-r-0 border-gray-300 rounded-l-md dark:bg-gray-600 dark:text-gray-400 dark:border-gray-600">
/
</span>
<input type="text" id="regexPattern" class="rounded-none rounded-r-lg bg-gray-50 border border-gray-300 text-gray-900 focus:ring-blue-500 focus:border-blue-500 block flex-1 min-w-0 w-full text-sm p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Enter regex pattern...">
<span class="inline-flex items-center px-3 text-sm text-gray-900 bg-gray-200 border border-l-0 border-gray-300 rounded-r-md dark:bg-gray-600 dark:text-gray-400 dark:border-gray-600">
/
</span>
<input type="text" id="regexFlags" class="rounded-lg bg-gray-50 border border-gray-300 text-gray-900 focus:ring-blue-500 focus:border-blue-500 block w-20 text-sm p-2.5 ml-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="flags" value="g">
</div>
</div>
<div class="mb-4">
<label class="block text-gray-700 dark:text-gray-300 mb-2" for="regexTestString">Test String:</label>
<textarea id="regexTestString" rows="6" class="w-full p-4 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white resize-y" placeholder="Enter text to test against the regex..."></textarea>
</div>
</div>
<!-- QR Code Generator UI (Hidden by default) -->
<div id="qr-code-container" class="hidden mb-6">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h4 class="text-lg font-medium mb-3 dark:text-white">QR Code Input</h4>
<div class="mb-4">
<textarea id="qrCodeInput" rows="6" class="w-full p-4 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white resize-y" placeholder="Enter text or URL to generate QR code..."></textarea>
</div>
<div class="mb-4">
<label class="block text-gray-700 dark:text-gray-300 mb-2">QR Code Options</label>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm text-gray-600 dark:text-gray-400 mb-1">Size:</label>
<select id="qrCodeSize" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="128">Small (128px)</option>
<option value="200" selected>Medium (200px)</option>
<option value="300">Large (300px)</option>
<option value="400">Extra Large (400px)</option>
</select>
</div>
<div>
<label class="block text-sm text-gray-600 dark:text-gray-400 mb-1">Error Correction:</label>
<select id="qrCodeErrorCorrection" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="L">Low (7%)</option>
<option value="M" selected>Medium (15%)</option>
<option value="Q">Quartile (25%)</option>
<option value="H">High (30%)</option>
</select>
</div>
</div>
</div>
</div>
<div>
<h4 class="text-lg font-medium mb-3 dark:text-white">QR Code Output</h4>
<div class="bg-white dark:bg-gray-700 p-4 rounded-lg shadow-inner flex items-center justify-center">
<div id="qrCodeOutput" class="qr-code-container">
<div class="text-gray-400 dark:text-gray-500 text-center">
<i class="fas fa-qrcode text-4xl mb-2"></i>
<p>QR code will appear here</p>
</div>
</div>
</div>
<div class="mt-4 flex space-x-2 justify-center">
<button id="downloadQrCodeBtn" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-300 flex items-center disabled:opacity-50 disabled:cursor-not-allowed" disabled>
<i class="fas fa-download mr-2"></i>
Download QR Code
</button>
</div>
</div>
</div>
</div>
<!-- Two-column layout for input/output -->
<div id="standard-input-output" class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Input Column -->
<div>
<div class="flex justify-between items-center mb-3">
<h4 class="text-lg font-medium dark:text-white">Input</h4>
<div class="flex items-center space-x-2">
<div class="text-sm text-gray-500 dark:text-gray-400">
<span id="inputCharCount">0</span> chars
</div>
<select id="input-format" class="text-sm bg-gray-50 border border-gray-300 text-gray-900 rounded-lg focus:ring-blue-500 focus:border-blue-500 p-1 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="text">Text</option>
<option value="json">JSON</option>
<option value="xml">XML</option>
<option value="html">HTML</option>
</select>
</div>
</div>
<div class="relative mb-6">
<!-- Icons in the top right of the textbox -->
<div class="textbox-icons absolute top-2 right-2 flex gap-2 z-10">
<button type="button" id="pasteBtn" title="Paste from Clipboard" class="text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400">
<i class="fas fa-paste"></i>
</button>
<button type="button" id="clearBtn" title="Clear Text" class="text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400">
<i class="fas fa-eraser"></i>
</button>
<button type="button" id="copyBtn" title="Copy to Clipboard" class="text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400">
<i class="fas fa-copy"></i>
</button>
</div>
<textarea id="inputText" rows="12" class="w-full p-4 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white resize-y font-mono" placeholder="Type or paste your text here..."></textarea>
</div>
</div>
<!-- Output Column -->
<div>
<div class="flex justify-between items-center mb-3">
<h4 class="text-lg font-medium dark:text-white">Output</h4>
<div class="flex items-center space-x-2">
<div class="text-sm text-gray-500 dark:text-gray-400">
<span id="outputCharCount">0</span> chars
</div>
<select id="output-format" class="text-sm bg-gray-50 border border-gray-300 text-gray-900 rounded-lg focus:ring-blue-500 focus:border-blue-500 p-1 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="text">Text</option>
<option value="json">JSON</option>
<option value="xml">XML</option>
<option value="html">HTML</option>
</select>
</div>
</div>
<div class="relative mb-6">
<div class="textbox-icons absolute top-2 right-2 flex gap-2 z-10">
<button type="button" id="copyResultBtn" title="Copy to Clipboard" class="text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400">
<i class="fas fa-copy"></i>
</button>
<button type="button" id="downloadResultBtn" title="Download Result" class="text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400">
<i class="fas fa-download"></i>
</button>
</div>
<!-- HTML Output Container (for tools that output HTML markup) -->
<div id="outputHtml" class="w-full p-4 border rounded-lg dark:bg-gray-700 dark:border-gray-600 text-sm whitespace-pre-wrap hidden"></div>
<textarea id="outputText" rows="12" class="w-full p-4 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white resize-y font-mono" readonly placeholder="Results will appear here..."></textarea>
</div>
</div>
</div>
<!-- Additional input fields that appear based on selected function -->
<div id="additionalInputs" class="mb-6 hidden">
<!-- HMAC Key Input (shown only when HMAC selected) -->
<div id="hmacKeyField" class="hidden">
<label class="block text-gray-600 dark:text-gray-400 mb-2">Secret Key:</label>
<input type="text" id="hmacKey" class="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white" placeholder="Enter your secret key">
</div>
<!-- Encryption Key Input -->
<div id="encryptionKeyField" class="hidden">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-gray-600 dark:text-gray-400 mb-2">Encryption Key:</label>
<div class="relative">
<input type="password" id="encryptionKey" class="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white pr-10" placeholder="Enter your encryption key">
<button id="toggleKeyVisibility" class="absolute inset-y-0 right-0 px-3 flex items-center text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400">
<i class="fas fa-eye"></i>
</button>
</div>
</div>
<div>
<label class="block text-gray-600 dark:text-gray-400 mb-2">Encryption Options:</label>
<select id="encryptionMode" class="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option value="cbc">CBC Mode</option>
<option value="ecb">ECB Mode</option>
<option value="cfb">CFB Mode</option>
<option value="ofb">OFB Mode</option>
</select>
</div>
</div>
<div class="mt-2">
<div class="flex items-center">
<input id="rememberKey" type="checkbox" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<label for="rememberKey" class="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Remember this key in browser storage</label>
</div>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">The key will be stored securely in your browser's local storage</p>
</div>
</div>
<!-- Random String Generator Options -->
<div id="randomStringOptions" class="hidden">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label class="block text-gray-600 dark:text-gray-400 mb-2">Length:</label>
<div class="flex items-center">
<input type="range" id="randomLengthSlider" min="1" max="1024" value="16" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700">
<input type="number" id="randomLength" class="ml-4 w-20 p-2 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white" value="16" min="1" max="1024">
</div>
</div>
<div>
<label class="block text-gray-600 dark:text-gray-400 mb-2">Character Set:</label>
<select id="randomCharset" class="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option value="alphanumeric">Alphanumeric</option>
<option value="alphabetic">Alphabetic only</option>
<option value="numeric">Numeric only</option>
<option value="hex">Hexadecimal</option>
<option value="custom">Custom character set</option>
</select>
</div>
</div>
<div id="customCharset" class="hidden mt-4">
<label class="block text-gray-600 dark:text-gray-400 mb-2">Custom Character Set:</label>
<input type="text" id="customCharsetInput" class="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white" placeholder="Enter characters to use">
</div>
<div class="mt-4">
<div class="flex items-center">
<input id="excludeSimilar" type="checkbox" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<label for="excludeSimilar" class="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Exclude similar characters (i, l, 1, L, o, 0, O)</label>
</div>
<div class="flex items-center mt-2">
<input id="excludeAmbiguous" type="checkbox" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<label for="excludeAmbiguous" class="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Exclude ambiguous characters ({ } [ ] ( ) / \ ' " ` ~ , ; : . < >)</label>
</div>
</div>
</div>
<!-- Password Generator Options -->
<div id="passwordOptions" class="hidden">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-gray-600 dark:text-gray-400 mb-2">Length: <span id="passwordLengthValue">16</span> characters</label>
<div class="flex items-center">
<input type="range" id="passwordLengthSlider" min="4" max="64" value="16" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer dark:bg-gray-700">
<input type="number" id="passwordLength" class="ml-4 w-20 p-2 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white" value="16" min="4" max="64">
</div>
<div class="mt-4">
<label class="block text-gray-600 dark:text-gray-400 mb-2">Password Strength</label>
<div class="w-full bg-gray-200 rounded-full h-2.5 dark:bg-gray-700 mb-1">
<div id="passwordStrength" class="bg-green-600 h-2.5 rounded-full" style="width: 100%"></div>
</div>
<p id="passwordStrengthText" class="text-sm text-green-700 dark:text-green-500">Very Strong</p>
</div>
<div class="mt-4">
<label class="block text-gray-600 dark:text-gray-400 mb-2">Memorable Password</label>
<div class="flex items-center">
<input id="memorablePassword" type="checkbox" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<label for="memorablePassword" class="ml-2 text-sm font-medium text-gray-900 dark:text-gray-300">Generate memorable password</label>
</div>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">Creates a password using words instead of random characters</p>
</div>
</div>
<div>
<label class="block text-gray-600 dark:text-gray-400 mb-2">Character Types:</label>
<div class="space-y-2">
<label class="flex items-center">
<input type="checkbox" id="includeUppercase" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" checked>
<span class="ml-2 text-gray-600 dark:text-gray-400">Uppercase Letters (A-Z)</span>
</label>
<label class="flex items-center">
<input type="checkbox" id="includeLowercase" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" checked>
<span class="ml-2 text-gray-600 dark:text-gray-400">Lowercase Letters (a-z)</span>
</label>
<label class="flex items-center">
<input type="checkbox" id="includeNumbers" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" checked>
<span class="ml-2 text-gray-600 dark:text-gray-400">Numbers (0-9)</span>
</label>
<label class="flex items-center">
<input type="checkbox" id="includeSymbols" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" checked>
<span class="ml-2 text-gray-600 dark:text-gray-400">Special Characters (!@#$%^&*)</span>
</label>
</div>
<div class="mt-4">
<label class="block text-gray-600 dark:text-gray-400 mb-2">Additional Options:</label>
<div class="space-y-2">
<label class="flex items-center">
<input type="checkbox" id="excludeSimilarChars" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
<span class="ml-2 text-gray-600 dark:text-gray-400">Exclude similar characters (i, l, 1, I, o, 0, O)</span>
</label>
<label class="flex items-center">
<input id="requireAllTypes" type="checkbox" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" checked>
<span class="ml-2 text-gray-600 dark:text-gray-400">Require at least one of each selected type</span>
</label>
</div>
</div>
<button id="generatePasswordBtn" class="mt-4 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-300 flex items-center">
<i class="fas fa-sync-alt mr-2"></i>
Generate New Password
</button>
</div>
</div>
</div>
<!-- Hash Comparison -->
<div id="hashCompareField" class="hidden">
<label class="block text-gray-600 dark:text-gray-400 mb-2">Hash to Compare:</label>
<input type="text" id="hashToCompare" class="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white" placeholder="Enter hash to compare">
<div class="mt-2">
<select id="hashAlgorithm" class="w-full p-3 border rounded-lg focus:ring-2 focus:ring-blue-600 dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option value="auto">Auto-detect algorithm</option>
<option value="md5">MD5</option>
<option value="sha1">SHA-1</option>
<option value="sha256">SHA-256</option>
<option value="sha512">SHA-512</option>
<option value="sha3">SHA-3</option>
</select>
</div>
</div>
</div>
<div class="flex justify-center">
<button id="processBtn" class="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-3 px-8 rounded-lg transition duration-300 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:ring-opacity-50 flex items-center">
<i class="fas fa-play mr-2"></i>
Process
</button>
</div>
</section>
<!-- All Results Section -->
<section id="allResultsSection" class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6 mb-8 hidden">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-semibold dark:text-white">All Results</h3>
<button id="clearAllResults" class="text-sm text-gray-600 hover:text-red-600 dark:text-gray-400 dark:hover:text-red-600 transition-colors duration-200">
<i class="fas fa-trash mr-1"></i>
Clear All
</button>
</div>
<div id="allResultsContainer" class="space-y-4">
<!-- Results will be added here dynamically -->
</div>
</section>
<!-- Tool Information Section -->
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6 mb-8">
<div class="flex items-center justify-between mb-4">
<h3 class="text-xl font-semibold dark:text-white">Tool Information</h3>
<div class="flex items-center">
<button id="expandToolInfo" class="text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400 transition-colors duration-200">
<i class="fas fa-chevron-down"></i>
</button>
</div>
</div>
<div id="toolInfo" class="prose dark:prose-invert">
<p class="text-gray-600 dark:text-gray-300">Select a cryptographic function from the dropdown or sidebar to get started. Input your text and click "Process" to see the results.</p>
</div>
</section>
<!-- Related Tools Section -->
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6 mb-8">
<h3 class="text-xl font-semibold mb-4 dark:text-white">Related Tools</h3>
<div id="relatedTools" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Related tools will be added here dynamically -->
<div class="p-4 border rounded-lg shadow-sm bg-gray-50 dark:bg-gray-700 hover:shadow-md transition-shadow duration-300 zoom-on-hover">
<div class="flex items-center mb-2">
<i class="fas fa-hashtag text-blue-600 dark:text-blue-400 mr-3"></i>
<h4 class="font-medium text-gray-800 dark:text-white">MD5 Hash</h4>
</div>
<p class="text-sm text-gray-600 dark:text-gray-300">Generate MD5 hash from text input</p>
</div>
<div class="p-4 border rounded-lg shadow-sm bg-gray-50 dark:bg-gray-700 hover:shadow-md transition-shadow duration-300 zoom-on-hover">
<div class="flex items-center mb-2">
<i class="fas fa-exchange-alt text-blue-600 dark:text-blue-400 mr-3"></i>
<h4 class="font-medium text-gray-800 dark:text-white">Base64 Encode/Decode</h4>
</div>
<p class="text-sm text-gray-600 dark:text-gray-300">Convert text to/from Base64 encoding</p>
</div>
<div class="p-4 border rounded-lg shadow-sm bg-gray-50 dark:bg-gray-700 hover:shadow-md transition-shadow duration-300 zoom-on-hover">
<div class="flex items-center mb-2">
<i class="fas fa-key text-blue-600 dark:text-blue-400 mr-3"></i>
<h4 class="font-medium text-gray-800 dark:text-white">SHA-256 Hash</h4>
</div>
<p class="text-sm text-gray-600 dark:text-gray-300">Generate SHA-256 hash from text input</p>
</div>
</div>
</section>
<!-- Usage Stats Section -->
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6 mb-8">
<div class="flex justify-between items-center mb-4">
<h3 class="text-xl font-semibold dark:text-white">Usage Statistics</h3>
<div>
<button id="toggleStatsView" class="text-sm text-blue-600 dark:text-blue-400 hover:underline">
Switch to Table View
</button>
</div>
</div>
<div id="statsChartView">
<canvas id="usageStatsChart" height="200"></canvas>
</div>
<div id="statsTableView" class="hidden">
<div class="overflow-x-auto">
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" class="px-6 py-3">Tool</th>
<th scope="col" class="px-6 py-3">Usage Count</th>
<th scope="col" class="px-6 py-3">Last Used</th>
</tr>
</thead>
<tbody id="statsTableBody">
<!-- Will be populated dynamically -->
</tbody>
</table>
</div>
</div>
</section>
</div>
</main>
</div>
<!-- Footer -->
<footer class="bg-white dark:bg-gray-800 shadow py-8 transition-all duration-300 mt-auto" id="main-footer">
<div class="container mx-auto px-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- Quick Links -->
<div>
<h3 class="text-lg font-semibold mb-4 dark:text-white">Quick Links</h3>
<ul class="space-y-2">
<li><a href="#hash-section" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Hash Functions</a></li>
<li><a href="#encoding-section" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Encoding Tools</a></li>
<li><a href="#encryption-section" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Encryption</a></li>
<li><a href="#other-section" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Other Tools</a></li>
</ul>
</div>
<!-- Popular Tools -->
<div>
<h3 class="text-lg font-semibold mb-4 dark:text-white">Popular Tools</h3>
<ul class="space-y-2">
<li><a href="#" class="tool-link text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400" data-function="md5">MD5 Hash</a></li>
<li><a href="#" class="tool-link text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400" data-function="base64" data-has-mode="true">Base64 Encode/Decode</a></li>
<li><a href="#" class="tool-link text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400" data-function="aes" data-has-mode="true">AES Encryption</a></li>
<li><a href="#" class="tool-link text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400" data-function="password-generator">Password Generator</a></li>
</ul>
</div>
<!-- Connect -->
<div>
<h3 class="text-lg font-semibold mb-4 dark:text-white">Connect</h3>
<ul class="space-y-2">
<li><a href="https://twitter.com/crypttools" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400"><i class="fab fa-twitter mr-2"></i>Twitter</a></li>
<li><a href="https://github.com/crypttools" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400"><i class="fab fa-github mr-2"></i>GitHub</a></li>
<li><a href="https://discord.gg/crypttools" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400"><i class="fab fa-discord mr-2"></i>Discord</a></li>
<li><a href="/cdn-cgi/l/email-protection#13707c7d677270675370616a63673d677c7c7f60" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400"><i class="fas fa-envelope mr-2"></i>Email</a></li>
</ul>
</div>
</div>
<div class="border-t border-gray-200 dark:border-gray-700 mt-8 pt-8 text-center">
<p class="text-gray-500 dark:text-gray-400 text-sm">All cryptographic operations are performed in your browser. No data is sent to our servers.</p>
<div class="flex justify-center space-x-4 mt-4">
<a href="/privacy" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Privacy Policy</a>
<a href="/terms" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Terms of Service</a>
<a href="/security" class="text-gray-600 hover:text-blue-600 dark:text-gray-300 dark:hover:text-blue-400">Security</a>
</div>
</div>
</div>
</footer>
<!-- Toast Notifications Container -->
<div id="toast-container" class="fixed bottom-4 right-4 z-50 flex flex-col gap-2"></div>
<!-- App Scripts -->
<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script>
// DOM Elements
const sidebar = document.getElementById('sidebar');
const sidebarToggle = document.getElementById('sidebar-toggle');
const sidebarOverlay = document.getElementById('sidebar-overlay');
const mainContent = document.getElementById('main-content');
const toolSelector = document.getElementById('tool-selector');
const modeSwitch = document.getElementById('mode-switch');
const modeSwitchContainer = document.getElementById('mode-switch-container');
const inputText = document.getElementById('inputText');
const outputText = document.getElementById('outputText');
const outputHtml = document.getElementById('outputHtml');
const processBtn = document.getElementById('processBtn');
const clearBtn = document.getElementById('clearBtn');
const pasteBtn = document.getElementById('pasteBtn');
const copyBtn = document.getElementById('copyBtn');
const copyResultBtn = document.getElementById('copyResultBtn');
const downloadResultBtn = document.getElementById('downloadResultBtn');
const recentFunctionsList = document.getElementById('recentFunctions');
const toolInfo = document.getElementById('toolInfo');
const darkModeToggle = document.getElementById('dark-mode-toggle');
const mobileDarkModeToggle = document.getElementById('mobile-dark-mode-toggle');
const additionalInputs = document.getElementById('additionalInputs');
const allResultsSection = document.getElementById('allResultsSection');
const allResultsContainer = document.getElementById('allResultsContainer');
const clearAllResults = document.getElementById('clearAllResults');
const toolSearch = document.getElementById('tool-search');
const favoritesList = document.getElementById('favorites-list');
const favoriteBtn = document.getElementById('favorite-btn');
const inputCharCount = document.getElementById('inputCharCount');
const outputCharCount = document.getElementById('outputCharCount');
const standardInputOutput = document.getElementById('standard-input-output');
const diffCheckerContainer = document.getElementById('diff-checker-container');
const regexTesterContainer = document.getElementById('regex-tester-container');
const qrCodeContainer = document.getElementById('qr-code-container');
const fileDropZone = document.getElementById('file-drop-zone');
const toggleKeyVisibility = document.getElementById('toggleKeyVisibility');
const breadcrumbCurrent = document.getElementById('breadcrumb-current');
const relatedTools = document.getElementById('relatedTools');
const settingsBtn = document.getElementById('settings-btn');
const settingsModal = document.getElementById('settings-modal');
const closeSettings = document.getElementById('close-settings');
const saveSettings = document.getElementById('save-settings');
const resetSettings = document.getElementById('reset-settings');
const settingsDarkMode = document.getElementById('settings-dark-mode');
const shortcutsBtn = document.getElementById('shortcuts-btn');
const shortcutsModal = document.getElementById('shortcuts-modal');
const closeShortcuts = document.getElementById('close-shortcuts');
const passwordLengthSlider = document.getElementById('passwordLengthSlider');
const passwordLength = document.getElementById('passwordLength');
const passwordLengthValue = document.getElementById('passwordLengthValue');
const passwordStrength = document.getElementById('passwordStrength');
const passwordStrengthText = document.getElementById('passwordStrengthText');
const generatePasswordBtn = document.getElementById('generatePasswordBtn');
const usageStatsChart = document.getElementById('usageStatsChart');
const toggleStatsView = document.getElementById('toggleStatsView');
const statsChartView = document.getElementById('statsChartView');
const statsTableView = document.getElementById('statsTableView');
const statsTableBody = document.getElementById('statsTableBody');
const manageFavorites = document.getElementById('manage-favorites');
const clearRecent = document.getElementById('clear-recent');
const shareBtn = document.getElementById('share-btn');
// Global state
let currentFunction = '';
let chartInstance = null;
// Initialize the app
document.addEventListener('DOMContentLoaded', () => {
initializeSidebar();
initializeDarkMode();
initializeToolSelector();
initializeButtons();
initializeRecentFunctions();
initializeFavorites();
initializeFileDropZone();
initializeLivePreview();
initializeSearch();
initializeCharCount();
initializeModals();
initializeRangeSliders();
initializeStats();
initializeQRCode();
initializeRegexTester();
initializeDiffChecker();
initializeKeyboardShortcuts();
// Check URL params for direct tool access
checkUrlParameters();
});
// Sidebar functionality
function initializeSidebar() {
// Apply saved sidebar position
applySavedSidebarPosition();
// Toggle sidebar on button click
sidebarToggle.addEventListener('click', () => {
toggleSidebar();
});
// Close sidebar when clicking on overlay
sidebarOverlay.addEventListener('click', () => {
closeSidebar();
});
// Close sidebar on window resize if in mobile view
window.addEventListener('resize', () => {
if (window.innerWidth >= 768) {
// On desktop, default to showing the sidebar
showSidebar();
sidebarOverlay.classList.add('hidden');
} else {
// On mobile, keep it closed by default
closeSidebar();
}
});
// Show sidebar by default on desktop at load time
if (window.innerWidth >= 768) {
showSidebar();
} else {
closeSidebar();
}
// Wire settings select to update sidebar position
const sidebarPositionSelect = document.getElementById('sidebar-position');
if (sidebarPositionSelect) {
// Set initial value from storage
const savedPos = getSavedSidebarPosition();
if (savedPos) {
sidebarPositionSelect.value = savedPos;
}
sidebarPositionSelect.addEventListener('change', () => {
setSavedSidebarPosition(sidebarPositionSelect.value);
applySavedSidebarPosition();
});
}
// Handle tool link clicks
document.querySelectorAll('.tool-link').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const functionName = link.getAttribute('data-function');
const hasMode = link.getAttribute('data-has-mode') === 'true';
// Set the tool selector value
toolSelector.value = functionName;
// Highlight active tool in sidebar
document.querySelectorAll('.tool-link').forEach(l => l.classList.remove('active'));
document.querySelectorAll(`.tool-link[data-function="${functionName}"]`).forEach(l => l.classList.add('active'));
// Handle mode switch visibility
toggleModeSwitch(hasMode);
// Update tool information
updateToolInfo(functionName);
// Update breadcrumb
updateBreadcrumb(functionName);
// Add to recent functions
addToRecentFunctions(functionName);
// Handle additional inputs
showRelevantInputs(functionName);
// Show appropriate UI based on tool
toggleUIBasedOnTool(functionName);
// Update related tools
updateRelatedTools(functionName);
// Update favorite button state
updateFavoriteButtonState(functionName);
// On mobile, close the sidebar after selecting a tool
if (window.innerWidth < 768) {
closeSidebar();
}
// Update URL with selected tool
updateUrlWithTool(functionName);
// Store current function
currentFunction = functionName;
});
});
}
function toggleSidebar() {
if (sidebar.classList.contains('translate-x-0')) {
closeSidebar();
} else {
showSidebar();
}
}
function showSidebar() {
if (isSidebarRight()) {
sidebar.classList.remove('translate-x-full');
} else {
sidebar.classList.remove('-translate-x-full');
}
sidebar.classList.add('translate-x-0');
document.body.classList.add('sidebar-open');
if (window.innerWidth < 768) {
sidebarOverlay.classList.remove('hidden');
}
}
function closeSidebar() {
sidebar.classList.remove('translate-x-0');
if (isSidebarRight()) {
sidebar.classList.add('translate-x-full');
sidebar.classList.remove('-translate-x-full');
} else {
sidebar.classList.add('-translate-x-full');
sidebar.classList.remove('translate-x-full');
}
sidebarOverlay.classList.add('hidden');
document.body.classList.remove('sidebar-open');
}
function isSidebarRight() {
return document.body.classList.contains('sidebar-right');
}
function getSavedSidebarPosition() {
return localStorage.getItem('sidebarPosition');
}
function setSavedSidebarPosition(pos) {
localStorage.setItem('sidebarPosition', pos);
}
function applySavedSidebarPosition() {
const pos = getSavedSidebarPosition() || 'left';
if (pos === 'right') {
document.body.classList.add('sidebar-right');
// Ensure hidden state uses translate-x-full when right-sided
sidebar.classList.remove('-translate-x-full');
if (!sidebar.classList.contains('translate-x-0')) {
sidebar.classList.add('translate-x-full');
}
} else {
document.body.classList.remove('sidebar-right');
sidebar.classList.remove('translate-x-full');
if (!sidebar.classList.contains('translate-x-0')) {
sidebar.classList.add('-translate-x-full');
}
}
}
// Dark mode functionality
function initializeDarkMode() {
// Function to toggle dark mode
function toggleDarkMode() {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
document.querySelectorAll('.fa-moon').forEach(icon => icon.classList.remove('hidden'));
document.querySelectorAll('.fa-sun').forEach(icon => icon.classList.add('hidden'));
if (settingsDarkMode) settingsDarkMode.checked = false;
} else {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
document.querySelectorAll('.fa-moon').forEach(icon => icon.classList.add('hidden'));
document.querySelectorAll('.fa-sun').forEach(icon => icon.classList.remove('hidden'));
if (settingsDarkMode) settingsDarkMode.checked = true;
}
// Update chart colors if chart exists
updateChartTheme();
}
// Initialize dark mode icons based on current state
if (document.documentElement.classList.contains('dark')) {
document.querySelectorAll('.fa-moon').forEach(icon => icon.classList.add('hidden'));
document.querySelectorAll('.fa-sun').forEach(icon => icon.classList.remove('hidden'));
if (settingsDarkMode) settingsDarkMode.checked = true;
} else {
document.querySelectorAll('.fa-moon').forEach(icon => icon.classList.remove('hidden'));
document.querySelectorAll('.fa-sun').forEach(icon => icon.classList.add('hidden'));
if (settingsDarkMode) settingsDarkMode.checked = false;
}
// Add event listeners
darkModeToggle.addEventListener('click', toggleDarkMode);
mobileDarkModeToggle.addEventListener('click', toggleDarkMode);
if (settingsDarkMode) {
settingsDarkMode.addEventListener('change', toggleDarkMode);
}
}
// Update chart theme based on dark mode
function updateChartTheme() {
if (chartInstance) {
const isDark = document.documentElement.classList.contains('dark');
chartInstance.options.plugins.legend.labels.color = isDark ? '#e2e8f0' : '#4a5568';
chartInstance.options.scales.x.ticks.color = isDark ? '#e2e8f0' : '#4a5568';
chartInstance.options.scales.y.ticks.color = isDark ? '#e2e8f0' : '#4a5568';
chartInstance.options.scales.x.grid.color = isDark ? '#374151' : '#e2e8f0';
chartInstance.options.scales.y.grid.color = isDark ? '#374151' : '#e2e8f0';
chartInstance.update();
}
}
// Tool selector functionality
function initializeToolSelector() {
toolSelector.addEventListener('change', () => {
const selectedFunction = toolSelector.value;
const selectedOption = toolSelector.options[toolSelector.selectedIndex];
const hasMode = selectedOption.getAttribute('data-has-mode') === 'true';
// Highlight active tool in sidebar
document.querySelectorAll('.tool-link').forEach(l => l.classList.remove('active'));
document.querySelectorAll(`.tool-link[data-function="${selectedFunction}"]`).forEach(l => l.classList.add('active'));
// Handle mode switch visibility
toggleModeSwitch(hasMode);
// Update tool information
updateToolInfo(selectedFunction);
// Update breadcrumb
updateBreadcrumb(selectedFunction);
// Add to recent functions
addToRecentFunctions(selectedFunction);
// Handle additional inputs
showRelevantInputs(selectedFunction);
// Show appropriate UI based on tool
toggleUIBasedOnTool(selectedFunction);
// Update related tools
updateRelatedTools(selectedFunction);
// Update favorite button state
updateFavoriteButtonState(selectedFunction);
// Update URL with selected tool
updateUrlWithTool(selectedFunction);
// Store current function
currentFunction = selectedFunction;
});
// Mode switch for encode/decode functions
modeSwitch.addEventListener('change', () => {
const mode = modeSwitch.checked ? 'decode' : 'encode';
updateToolInfo(toolSelector.value, mode);
});
// Quick tool buttons
document.querySelectorAll('.quick-tool-btn').forEach(btn => {
btn.addEventListener('click', () => {
const functionName = btn.getAttribute('data-function');
toolSelector.value = functionName;
const event = new Event('change');
toolSelector.dispatchEvent(event);
});
});
}
// Show/hide mode switch based on function type
function toggleModeSwitch(hasMode) {
if (hasMode) {
modeSwitchContainer.classList.remove('hidden');
} else {
modeSwitchContainer.classList.add('hidden');
}
}
// Toggle UI based on selected tool
function toggleUIBasedOnTool(functionName) {
// Hide all special containers first
standardInputOutput.classList.add('hidden');
diffCheckerContainer.classList.add('hidden');
regexTesterContainer.classList.add('hidden');
qrCodeContainer.classList.add('hidden');
fileDropZone.classList.add('hidden');
// Show appropriate container based on function
switch (functionName) {
case 'diff-checker':
diffCheckerContainer.classList.remove('hidden');
standardInputOutput.classList.remove('hidden');
break;
case 'regex-tester':
regexTesterContainer.classList.remove('hidden');
standardInputOutput.classList.remove('hidden');
break;
case 'qr-code':
qrCodeContainer.classList.remove('hidden');
break;
case 'file-checksum':
standardInputOutput.classList.remove('hidden');
fileDropZone.classList.remove('hidden');
break;
default:
standardInputOutput.classList.remove('hidden');
break;
}
}
// Show relevant additional input fields based on selected function
function showRelevantInputs(functionName) {
// Hide all input fields first
document.querySelectorAll('#additionalInputs > div').forEach(div => {
div.classList.add('hidden');
});
// Show the additional inputs container
additionalInputs.classList.remove('hidden');
// Show relevant fields based on function
switch (functionName) {
case 'hmac':
document.getElementById('hmacKeyField').classList.remove('hidden');
break;
case 'aes':
case 'des':
case 'tripledes':
case 'rabbit':
case 'rc4':
case 'blowfish':
case 'twofish':
case 'camellia':
document.getElementById('encryptionKeyField').classList.remove('hidden');
break;
case 'random-string':
document.getElementById('randomStringOptions').classList.remove('hidden');
break;
case 'password-generator':
document.getElementById('passwordOptions').classList.remove('hidden');
break;
case 'hash-compare':
document.getElementById('hashCompareField').classList.remove('hidden');
break;
default:
additionalInputs.classList.add('hidden');
break;
}
}
// Update breadcrumb
function updateBreadcrumb(functionName) {
if (breadcrumbCurrent) {
breadcrumbCurrent.textContent = formatFunctionName(functionName);
}
}
// Initialize buttons
function initializeButtons() {
// Process button
processBtn.addEventListener('click', () => {
processCurrentFunction();
});
// Clear input button
clearBtn.addEventListener('click', () => {
inputText.value = '';
updateCharCount();
// Clear any additional inputs that might be visible
document.querySelectorAll('#additionalInputs input, #additionalInputs select').forEach(input => {
if (input.type === 'checkbox') {
input.checked = true; // Reset checkboxes to checked
} else if (input.type === 'number') {
// Reset number inputs to default values
if (input.id === 'passwordLength' || input.id === 'randomLength') {
input.value = '16';
}
} else if (input.type !== 'range') {
input.value = '';
}
});
showToast('Input cleared!');
});
// Paste button
if (pasteBtn) {
pasteBtn.addEventListener('click', async () => {
try {
const text = await navigator.clipboard.readText();
inputText.value = text;
updateCharCount();
showToast('Text pasted from clipboard!');
} catch (err) {
showToast('Failed to read clipboard contents!', 'error');
}
});
}
// Copy input button
copyBtn.addEventListener('click', () => {
copyToClipboard(inputText.value);
showToast('Input copied to clipboard!');
});
// Copy result button (supports HTML and Text outputs)
copyResultBtn.addEventListener('click', () => {
const isHtmlVisible = outputHtml && !outputHtml.classList.contains('hidden');
const text = isHtmlVisible ? outputHtml.innerText : outputText.value;
copyToClipboard(text);
showToast('Result copied to clipboard!');
});
// Download result button
downloadResultBtn.addEventListener('click', () => {
downloadResult();
});
// Clear all results button
if (clearAllResults) {
clearAllResults.addEventListener('click', () => {
allResultsContainer.innerHTML = '';
allResultsSection.classList.add('hidden');
showToast('All results cleared!');
});
}
// Custom charset toggle
const randomCharset = document.getElementById('randomCharset');
if (randomCharset) {
randomCharset.addEventListener('change', () => {
const customCharsetDiv = document.getElementById('customCharset');
if (randomCharset.value === 'custom') {
customCharsetDiv.classList.remove('hidden');
} else {
customCharsetDiv.classList.add('hidden');
}
});
}
// Toggle password visibility
if (toggleKeyVisibility) {
toggleKeyVisibility.addEventListener('click', () => {
const encryptionKey = document.getElementById('encryptionKey');
const icon = toggleKeyVisibility.querySelector('i');
if (encryptionKey.type === 'password') {
encryptionKey.type = 'text';
icon.classList.remove('fa-eye');
icon.classList.add('fa-eye-slash');
} else {
encryptionKey.type = 'password';
icon.classList.remove('fa-eye-slash');
icon.classList.add('fa-eye');
}
});
}
// Favorite button
if (favoriteBtn) {
favoriteBtn.addEventListener('click', toggleFavorite);
}
// Generate password button
if (generatePasswordBtn) {
generatePasswordBtn.addEventListener('click', () => {
generatePassword();
});
}
// Toggle stats view
if (toggleStatsView) {
toggleStatsView.addEventListener('click', () => {
const isTableView = statsTableView.classList.contains('hidden');
if (isTableView) {
statsChartView.classList.add('hidden');
statsTableView.classList.remove('hidden');
toggleStatsView.textContent = 'Switch to Chart View';
} else {
statsChartView.classList.remove('hidden');
statsTableView.classList.add('hidden');
toggleStatsView.textContent = 'Switch to Table View';
}
});
}
// Manage favorites
if (manageFavorites) {
manageFavorites.addEventListener('click', () => {
// Logic for managing favorites would go here
showToast('Favorites management coming soon!', 'info');
});
}
// Clear recent
if (clearRecent) {
clearRecent.addEventListener('click', () => {
localStorage.removeItem('recentFunctions');
updateRecentFunctionsDisplay([]);
showToast('Recent functions cleared!');
});
}
// Share button
if (shareBtn) {
shareBtn.addEventListener('click', shareTool);
}
}
// Function to share the current tool
function shareTool() {
if (navigator.share && currentFunction) {
const url = new URL(window.location);
url.searchParams.set('tool', currentFunction);
navigator.share({
title: `Crypt.Tools - ${formatFunctionName(currentFunction)}`,
text: `Check out this useful cryptography tool: ${formatFunctionName(currentFunction)}`,
url: url.toString()
})
.then(() => showToast('Shared successfully!'))
.catch((error) => {
if (error.name !== 'AbortError') {
showToast('Error sharing tool', 'error');
}
});
} else {
// Fallback to copy link
const url = new URL(window.location);
url.searchParams.set('tool', currentFunction);
copyToClipboard(url.toString());
showToast('Link copied to clipboard!');
}
}
// Initialize modals
function initializeModals() {
// Settings modal
if (settingsBtn && settingsModal && closeSettings) {
settingsBtn.addEventListener('click', () => {
settingsModal.classList.remove('hidden');
});
closeSettings.addEventListener('click', () => {
settingsModal.classList.add('hidden');
});
// Close when clicking outside
settingsModal.addEventListener('click', (e) => {
if (e.target === settingsModal) {
settingsModal.classList.add('hidden');
}
});
// Save settings
if (saveSettings) {
saveSettings.addEventListener('click', () => {
// Logic to save settings would go here
showToast('Settings saved!');
settingsModal.classList.add('hidden');
});
}
// Reset settings
if (resetSettings) {
resetSettings.addEventListener('click', () => {
// Logic to reset settings would go here
showToast('Settings reset to default!');
});
}
}
// Shortcuts modal
if (shortcutsBtn && shortcutsModal && closeShortcuts) {
shortcutsBtn.addEventListener('click', () => {
shortcutsModal.classList.remove('hidden');
});
closeShortcuts.addEventListener('click', () => {
shortcutsModal.classList.add('hidden');
});
// Close when clicking outside
shortcutsModal.addEventListener('click', (e) => {
if (e.target === shortcutsModal) {
shortcutsModal.classList.add('hidden');
}
});
}
// Mobile menu
const menuButton = document.getElementById('menu-button');
const mobileMenu = document.getElementById('mobile-menu');
const closeMenu = document.getElementById('close-menu');
if (menuButton && mobileMenu && closeMenu) {
menuButton.addEventListener('click', () => {
mobileMenu.classList.remove('hidden');
setTimeout(() => {
const mobileMenuContent = mobileMenu.querySelector('div');
mobileMenuContent.classList.remove('translate-x-full');
}, 10);
});
closeMenu.addEventListener('click', () => {
const mobileMenuContent = mobileMenu.querySelector('div');
mobileMenuContent.classList.add('translate-x-full');
setTimeout(() => {
mobileMenu.classList.add('hidden');
}, 300);
});
// Close when clicking outside
mobileMenu.addEventListener('click', (e) => {
if (e.target === mobileMenu) {
const mobileMenuContent = mobileMenu.querySelector('div');
mobileMenuContent.classList.add('translate-x-full');
setTimeout(() => {
mobileMenu.classList.add('hidden');
}, 300);
}
});
}
// User menu
const userMenuBtn = document.getElementById('user-menu-btn');
const userMenu = document.getElementById('user-menu');
if (userMenuBtn && userMenu) {
userMenuBtn.addEventListener('click', () => {
userMenu.classList.toggle('hidden');
});
// Close when clicking elsewhere
document.addEventListener('click', (e) => {
if (!userMenuBtn.contains(e.target) && !userMenu.contains(e.target)) {
userMenu.classList.add('hidden');
}
});
}
}
// Initialize range sliders
function initializeRangeSliders() {
// Password length slider
if (passwordLengthSlider && passwordLength && passwordLengthValue) {
passwordLengthSlider.addEventListener('input', () => {
passwordLength.value = passwordLengthSlider.value;
passwordLengthValue.textContent = passwordLengthSlider.value;
updatePasswordStrength();
});
passwordLength.addEventListener('input', () => {
// Ensure value is within min and max
const value = Math.max(4, Math.min(64, parseInt(passwordLength.value) || 16));
passwordLength.value = value;
passwordLengthSlider.value = value;
passwordLengthValue.textContent = value;
updatePasswordStrength();
});
}
// Random string length slider
const randomLengthSlider = document.getElementById('randomLengthSlider');
const randomLength = document.getElementById('randomLength');
if (randomLengthSlider && randomLength) {
randomLengthSlider.addEventListener('input', () => {
randomLength.value = randomLengthSlider.value;
});
randomLength.addEventListener('input', () => {
// Ensure value is within min and max
const value = Math.max(1, Math.min(1024, parseInt(randomLength.value) || 16));
randomLength.value = value;
randomLengthSlider.value = value;
});
}
}
// Update password strength indicator
function updatePasswordStrength() {
if (!passwordStrength || !passwordStrengthText) return;
const length = parseInt(passwordLength.value) || 16;
const useUpper = document.getElementById('includeUppercase').checked;
const useLower = document.getElementById('includeLowercase').checked;
const useNumbers = document.getElementById('includeNumbers').checked;
const useSymbols = document.getElementById('includeSymbols').checked;
// Calculate strength score (0-100)
let score = 0;
// Length contribution (up to 50 points)
score += Math.min(50, length * 1.5);
// Character set contribution (up to 50 points)
let charsetScore = 0;
if (useUpper) charsetScore += 10;
if (useLower) charsetScore += 10;
if (useNumbers) charsetScore += 15;
if (useSymbols) charsetScore += 15;
score += charsetScore;
// Cap at 100
score = Math.min(100, score);
// Update visual indicators
passwordStrength.style.width = `${score}%`;
// Update color and text based on score
if (score < 40) {
passwordStrength.className = 'bg-red-600 h-2.5 rounded-full';
passwordStrengthText.className = 'text-sm text-red-700 dark:text-red-500';
passwordStrengthText.textContent = 'Weak';
} else if (score < 70) {
passwordStrength.className = 'bg-yellow-500 h-2.5 rounded-full';
passwordStrengthText.className = 'text-sm text-yellow-700 dark:text-yellow-500';
passwordStrengthText.textContent = 'Moderate';
} else if (score < 90) {
passwordStrength.className = 'bg-blue-600 h-2.5 rounded-full';
passwordStrengthText.className = 'text-sm text-blue-700 dark:text-blue-500';
passwordStrengthText.textContent = 'Strong';
} else {
passwordStrength.className = 'bg-green-600 h-2.5 rounded-full';
passwordStrengthText.className = 'text-sm text-green-700 dark:text-green-500';
passwordStrengthText.textContent = 'Very Strong';
}
}
// Copy text to clipboard
function copyToClipboard(text) {
if (!text) return;
navigator.clipboard.writeText(text).catch(err => {
// Fallback method for browsers that don't support clipboard API
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
} catch (err) {
console.error('Failed to copy: ', err);
}
document.body.removeChild(textArea);
});
}
// Download result as a text file
function downloadResult() {
const isHtmlVisible = outputHtml && !outputHtml.classList.contains('hidden');
const text = isHtmlVisible ? outputHtml.innerText : outputText.value;
if (!text) {
showToast('No result to download!', 'error');
return;
}
const blob = new Blob([text], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
const functionName = toolSelector.value;
a.href = url;
a.download = `${functionName}-result-${new Date().toISOString().slice(0, 10)}.txt`;
document.body.appendChild(a);
a.click();
// Cleanup
document.body.removeChild(a);
URL.revokeObjectURL(url);
showToast('Result downloaded!');
}
// Process the current function
function processCurrentFunction() {
const functionName = toolSelector.value;
const input = inputText.value;
// Check if input is empty for functions that require input
if (!input && !['random-string', 'password-generator', 'uuid'].includes(functionName)) {
showToast('Please enter some input text!', 'error');
return;
}
let result;
const isDecoding = modeSwitch.checked;
try {
// Add loading indicator to process button
processBtn.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Processing...';
processBtn.disabled = true;
// Process based on function type
switch (functionName) {
// Hash functions
case 'md5':
result = CryptoJS.MD5(input).toString();
break;
case 'sha1':
result = CryptoJS.SHA1(input).toString();
break;
case 'sha256':
result = CryptoJS.SHA256(input).toString();
break;
case 'sha512':
result = CryptoJS.SHA512(input).toString();
break;
case 'sha3':
result = CryptoJS.SHA3(input).toString();
break;
case 'ripemd160':
result = CryptoJS.RIPEMD160(input).toString();
break;
case 'keccak':
result = CryptoJS.SHA3(input, { outputLength: 256 }).toString();
break;
// Encoding/Decoding functions
case 'base64':
if (isDecoding) {
try {
result = atob(input.trim());
} catch (e) {
throw new Error('Invalid Base64 string');
}
} else {
result = btoa(input);
}
break;
case 'url':
if (isDecoding) {
result = decodeURIComponent(input);
} else {
result = encodeURIComponent(input);
}
break;
case 'html':
if (isDecoding) {
const textarea = document.createElement('textarea');
textarea.innerHTML = input;
result = textarea.value;
} else {
const div = document.createElement('div');
div.textContent = input;
result = div.innerHTML;
}
break;
case 'hex':
if (isDecoding) {
result = hexToString(input);
} else {
result = stringToHex(input);
}
break;
case 'binary':
if (isDecoding) {
result = binaryToString(input);
} else {
result = stringToBinary(input);
}
break;
case 'json':
if (isDecoding) {
try {
const obj = JSON.parse(input);
result = JSON.stringify(obj, null, 2);
} catch (e) {
throw new Error('Invalid JSON string');
}
} else {
try {
const obj = JSON.parse(input);
result = JSON.stringify(obj);
} catch (e) {
throw new Error('Invalid JSON object');
}
}
break;
// Encryption functions
case 'aes':
const aesKey = document.getElementById('encryptionKey').value;
if (!aesKey) {
showToast('Please enter an encryption key!', 'error');
return;
}
if (isDecoding) {
try {
const decrypted = CryptoJS.AES.decrypt(input, aesKey);
result = decrypted.toString(CryptoJS.enc.Utf8);
if (!result) throw new Error('Decryption failed');
} catch (e) {
throw new Error('Decryption failed. Check your key and input!');
}
} else {
result = CryptoJS.AES.encrypt(input, aesKey).toString();
}
break;
case 'des':
case 'tripledes':
const desKey = document.getElementById('encryptionKey').value;
if (!desKey) {
showToast('Please enter an encryption key!', 'error');
return;
}
if (isDecoding) {
try {
const decrypted = functionName === 'des'
? CryptoJS.DES.decrypt(input, desKey)
: CryptoJS.TripleDES.decrypt(input, desKey);
result = decrypted.toString(CryptoJS.enc.Utf8);
if (!result) throw new Error('Decryption failed');
} catch (e) {
throw new Error('Decryption failed. Check your key and input!');
}
} else {
result = functionName === 'des'
? CryptoJS.DES.encrypt(input, desKey).toString()
: CryptoJS.TripleDES.encrypt(input, desKey).toString();
}
break;
case 'rabbit':
const rabbitKey = document.getElementById('encryptionKey').value;
if (!rabbitKey) {
showToast('Please enter an encryption key!', 'error');
return;
}
if (isDecoding) {
try {
const decrypted = CryptoJS.Rabbit.decrypt(input, rabbitKey);
result = decrypted.toString(CryptoJS.enc.Utf8);
if (!result) throw new Error('Decryption failed');
} catch (e) {
throw new Error('Decryption failed. Check your key and input!');
}
} else {
result = CryptoJS.Rabbit.encrypt(input, rabbitKey).toString();
}
break;
case 'rc4':
const rc4Key = document.getElementById('encryptionKey').value;
if (!rc4Key) {
showToast('Please enter an encryption key!', 'error');
return;
}
if (isDecoding) {
try {
const decrypted = CryptoJS.RC4.decrypt(input, rc4Key);
result = decrypted.toString(CryptoJS.enc.Utf8);
if (!result) throw new Error('Decryption failed');
} catch (e) {
throw new Error('Decryption failed. Check your key and input!');
}
} else {
result = CryptoJS.RC4.encrypt(input, rc4Key).toString();
}
break;
// Other functions
case 'hmac':
const hmacKey = document.getElementById('hmacKey').value;
if (!hmacKey) {
showToast('Please enter a secret key!', 'error');
return;
}
result = CryptoJS.HmacSHA256(input, hmacKey).toString();
break;
case 'random-string':
const length = parseInt(document.getElementById('randomLength').value) || 16;
const charsetType = document.getElementById('randomCharset').value;
const excludeSimilar = document.getElementById('excludeSimilar').checked;
const excludeAmbiguous = document.getElementById('excludeAmbiguous').checked;
result = generateRandomString(length, charsetType, excludeSimilar, excludeAmbiguous);
break;
case 'password-generator':
result = generatePassword();
break;
case 'uuid':
result = generateUUID();
break;
case 'hash-compare':
const hashToCompare = document.getElementById('hashToCompare').value;
const hashAlgorithm = document.getElementById('hashAlgorithm').value;
if (!hashToCompare) {
showToast('Please enter a hash to compare!', 'error');
return;
}
result = compareHash(input, hashToCompare, hashAlgorithm);
break;
case 'diff-checker':
const originalText = document.getElementById('diffOriginalText').value;
const modifiedText = document.getElementById('diffModifiedText').value;
result = calculateDiff(originalText, modifiedText, 'inline');
// Render HTML result in outputHtml for diff checker
outputHtml.innerHTML = result;
outputHtml.classList.remove('hidden');
outputText.classList.add('hidden');
// Add to usage stats
addToUsageStats(functionName);
// Reset process button
processBtn.innerHTML = '<i class="fas fa-play mr-2"></i>Process';
processBtn.disabled = false;
return;
case 'regex-tester':
const regexPattern = document.getElementById('regexPattern').value;
const regexFlags = document.getElementById('regexFlags').value;
const testString = document.getElementById('regexTestString').value;
if (!regexPattern) {
showToast('Please enter a regex pattern!', 'error');
return;
}
result = testRegex(regexPattern, regexFlags, testString);
// Render HTML result in outputHtml for regex tester
outputHtml.innerHTML = result;
outputHtml.classList.remove('hidden');
outputText.classList.add('hidden');
// Add to usage stats
addToUsageStats(functionName);
// Reset process button
processBtn.innerHTML = '<i class="fas fa-play mr-2"></i>Process';
processBtn.disabled = false;
return;
case 'qr-code':
const qrInput = document.getElementById('qrCodeInput').value;
const qrSize = parseInt(document.getElementById('qrCodeSize').value) || 200;
const qrErrorCorrection = document.getElementById('qrCodeErrorCorrection').value || 'M';
if (!qrInput) {
showToast('Please enter text to generate QR code!', 'error');
return;
}
generateQRCode(qrInput, qrSize, qrErrorCorrection);
// QR code is handled differently
// Add to usage stats
addToUsageStats(functionName);
// Reset process button
processBtn.innerHTML = '<i class="fas fa-play mr-2"></i>Process';
processBtn.disabled = false;
return;
case 'file-checksum':
if (!fileDropZone.files || !fileDropZone.files[0]) {
showToast('Please select a file first!', 'error');
return;
}
calculateFileChecksum(fileDropZone.files[0]);
// File checksum is handled asynchronously
// Reset process button is handled in the callback
return;
default:
result = 'Function not implemented yet.';
break;
}
// Update output (default: plain text in textarea)
if (typeof result === 'string') {
outputText.value = result;
} else {
outputText.value = String(result ?? '');
}
updateCharCount();
outputText.classList.remove('html-content');
outputText.classList.remove('hidden');
if (outputHtml) outputHtml.classList.add('hidden');
// Add to recent functions
addToRecentFunctions(functionName);
// Add to all results if it's not already there
addToAllResults(functionName, input, result);
// Add to usage stats
addToUsageStats(functionName);
showToast('Processing complete!');
} catch (error) {
console.error('Processing error:', error);
showToast(error.message || 'Error processing your request!', 'error');
} finally {
// Reset process button
processBtn.innerHTML = '<i class="fas fa-play mr-2"></i>Process';
processBtn.disabled = false;
}
}
// Helper functions for processing
function stringToHex(str) {
let hex = '';
for (let i = 0; i < str.length; i++) {
const charCode = str.charCodeAt(i);
hex += charCode.toString(16).padStart(2, '0');
}
return hex;
}
function hexToString(hex) {
hex = hex.replace(/\s+/g, ''); // Remove whitespace
const hexArray = hex.match(/.{1,2}/g) || [];
let str = '';
for (let i = 0; i < hexArray.length; i++) {
str += String.fromCharCode(parseInt(hexArray[i], 16));
}
return str;
}
function stringToBinary(str) {
return str.split('').map(char => {
return char.charCodeAt(0).toString(2).padStart(8, '0');
}).join(' ');
}
function binaryToString(binary) {
binary = binary.replace(/\s+/g, ''); // Remove whitespace
const binaryArray = binary.match(/.{1,8}/g) || [];
let str = '';
for (let i = 0; i < binaryArray.length; i++) {
str += String.fromCharCode(parseInt(binaryArray[i], 2));
}
return str;
}
function generateRandomString(length, charsetType, excludeSimilar = false, excludeAmbiguous = false) {
let charset = '';
// Define character sets
const upperChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const lowerChars = 'abcdefghijklmnopqrstuvwxyz';
const numberChars = '0123456789';
const hexChars = '0123456789abcdef';
const similarChars = 'iIlL1oO0';
const ambiguousChars = ['{','}','[',']','(',')','/','"','\'','`',',',';',':','.', '>', '<'];
switch (charsetType) {
case 'alphanumeric':
charset = upperChars + lowerChars + numberChars;
break;
case 'alphabetic':
charset = upperChars + lowerChars;
break;
case 'numeric':
charset = numberChars;
break;
case 'hex':
charset = hexChars;
break;
case 'custom':
charset = document.getElementById('customCharsetInput').value;
if (!charset) {
showToast('Please enter a custom character set!', 'error');
return '';
}
break;
}
// Remove similar/ambiguous characters if requested
if (excludeSimilar) {
for (const char of similarChars) {
charset = charset.replace(new RegExp(char, 'g'), '');
}
}
if (excludeAmbiguous) {
for (const ch of ambiguousChars) {
// Remove each ambiguous character without using regex to avoid escaping pitfalls
charset = charset.split(ch).join('');
}
}
if (charset.length === 0) {
showToast('Character set is empty after applying filters!', 'error');
return '';
}
let result = '';
const charsetLength = charset.length;
// Use crypto.getRandomValues for better randomness if available
if (window.crypto && window.crypto.getRandomValues) {
const values = new Uint32Array(length);
window.crypto.getRandomValues(values);
for (let i = 0; i < length; i++) {
result += charset.charAt(values[i] % charsetLength);
}
} else {
for (let i = 0; i < length; i++) {
result += charset.charAt(Math.floor(Math.random() * charsetLength));
}
}
return result;
}
function generatePassword() {
const length = parseInt(document.getElementById('passwordLength').value) || 16;
const options = {
uppercase: document.getElementById('includeUppercase').checked,
lowercase: document.getElementById('includeLowercase').checked,
numbers: document.getElementById('includeNumbers').checked,
symbols: document.getElementById('includeSymbols').checked,
excludeSimilar: document.getElementById('excludeSimilarChars').checked,
requireAllTypes: document.getElementById('requireAllTypes').checked,
memorable: document.getElementById('memorablePassword')?.checked || false
};
// Check if at least one character type is selected
if (!options.uppercase && !options.lowercase && !options.numbers && !options.symbols) {
showToast('Please select at least one character type!', 'error');
return '';
}
// Generate memorable password
if (options.memorable) {
return generateMemorablePassword(length);
}
// Define character sets
let charset = '';
const upperChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const lowerChars = 'abcdefghijklmnopqrstuvwxyz';
const numberChars = '0123456789';
const symbolChars = '!@#$%^&*()-_=+[]{}|;:,./<>?';
const similarChars = 'iIlL1oO0';
// Build charset based on options
if (options.uppercase) charset += upperChars;
if (options.lowercase) charset += lowerChars;
if (options.numbers) charset += numberChars;
if (options.symbols) charset += symbolChars;
// Remove similar characters if requested
if (options.excludeSimilar) {
for (const char of similarChars) {
charset = charset.replace(new RegExp(char, 'g'), '');
}
}
// Generate password
let password = '';
// Ensure at least one of each required type if specified
if (options.requireAllTypes) {
if (options.uppercase) {
password += upperChars.charAt(Math.floor(Math.random() * upperChars.length));
}
if (options.lowercase) {
password += lowerChars.charAt(Math.floor(Math.random() * lowerChars.length));
}
if (options.numbers) {
password += numberChars.charAt(Math.floor(Math.random() * numberChars.length));
}
if (options.symbols) {
password += symbolChars.charAt(Math.floor(Math.random() * symbolChars.length));
}
}
// Fill remaining length with random characters
const remainingLength = length - password.length;
// Use crypto.getRandomValues for better randomness if available
if (window.crypto && window.crypto.getRandomValues) {
const values = new Uint32Array(remainingLength);
window.crypto.getRandomValues(values);
for (let i = 0; i < remainingLength; i++) {
password += charset.charAt(values[i] % charset.length);
}
} else {
for (let i = 0; i < remainingLength; i++) {
password += charset.charAt(Math.floor(Math.random() * charset.length));
}
}
// Shuffle the password to mix the required characters randomly
if (options.requireAllTypes && password.length > 1) {
password = shuffleString(password);
}
return password;
}
function generateMemorablePassword(targetLength) {
// List of common words (this is a small sample, you'd want a larger dictionary in production)
const words = [
'apple', 'banana', 'orange', 'grape', 'lemon', 'peach', 'cherry', 'pear', 'plum', 'melon',
'house', 'car', 'road', 'tree', 'river', 'mountain', 'ocean', 'forest', 'desert', 'sky',
'book', 'pen', 'paper', 'desk', 'chair', 'table', 'phone', 'computer', 'keyboard', 'mouse',
'dog', 'cat', 'bird', 'fish', 'horse', 'tiger', 'lion', 'elephant', 'monkey', 'bear',
'happy', 'sad', 'angry', 'calm', 'quiet', 'loud', 'fast', 'slow', 'hot', 'cold'
];
// Generate a password by concatenating words
let password = '';
let capitalizeNext = true;
while (password.length < targetLength) {
// Get a random word
const word = words[Math.floor(Math.random() * words.length)];
// Capitalize first letter or after a delimiter
if (capitalizeNext) {
password += word.charAt(0).toUpperCase() + word.slice(1);
capitalizeNext = false;
} else {
password += word;
}
// Add a delimiter if we need more words
if (password.length < targetLength - 1) {
// Use various delimiters
const delimiters = '.-_!@#$%&*';
const delimiter = delimiters.charAt(Math.floor(Math.random() * delimiters.length));
password += delimiter;
capitalizeNext = true;
}
}
// Truncate to exact length if needed
if (password.length > targetLength) {
password = password.slice(0, targetLength);
}
// Add a random number if password is too short
if (password.length < targetLength) {
const numberToAdd = Math.floor(Math.random() * 100);
password += numberToAdd;
}
return password;
}
function shuffleString(str) {
const array = str.split('');
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array.join('');
}
function generateUUID() {
// Use crypto.getRandomValues for better randomness if available
if (window.crypto && window.crypto.getRandomValues) {
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
}
// Fallback to Math.random()
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);
});
}
function compareHash(input, hashToCompare, algorithm) {
// Normalize the hashes (remove whitespace, convert to lowercase)
hashToCompare = hashToCompare.replace(/\s+/g, '').toLowerCase();
let calculatedHash;
if (algorithm === 'auto') {
// Auto-detect based on length
switch (hashToCompare.length) {
case 32: // MD5
calculatedHash = CryptoJS.MD5(input).toString();
algorithm = 'MD5';
break;
case 40: // SHA-1
calculatedHash = CryptoJS.SHA1(input).toString();
algorithm = 'SHA-1';
break;
case 64: // SHA-256
calculatedHash = CryptoJS.SHA256(input).toString();
algorithm = 'SHA-256';
break;
case 128: // SHA-512
calculatedHash = CryptoJS.SHA512(input).toString();
algorithm = 'SHA-512';
break;
default:
// Try multiple algorithms
if (CryptoJS.MD5(input).toString() === hashToCompare) {
return 'Match found: MD5 ?';
}
if (CryptoJS.SHA1(input).toString() === hashToCompare) {
return 'Match found: SHA-1 ?';
}
if (CryptoJS.SHA256(input).toString() === hashToCompare) {
return 'Match found: SHA-256 ?';
}
if (CryptoJS.SHA512(input).toString() === hashToCompare) {
return 'Match found: SHA-512 ?';
}
if (CryptoJS.SHA3(input).toString() === hashToCompare) {
return 'Match found: SHA-3 ?';
}
if (CryptoJS.RIPEMD160(input).toString() === hashToCompare) {
return 'Match found: RIPEMD-160 ?';
}
return 'No match found for any common hash algorithm ?';
}
} else {
// Use specified algorithm
switch (algorithm) {
case 'md5':
calculatedHash = CryptoJS.MD5(input).toString();
algorithm = 'MD5';
break;
case 'sha1':
calculatedHash = CryptoJS.SHA1(input).toString();
algorithm = 'SHA-1';
break;
case 'sha256':
calculatedHash = CryptoJS.SHA256(input).toString();
algorithm = 'SHA-256';
break;
case 'sha512':
calculatedHash = CryptoJS.SHA512(input).toString();
algorithm = 'SHA-512';
break;
case 'sha3':
calculatedHash = CryptoJS.SHA3(input).toString();
algorithm = 'SHA-3';
break;
default:
calculatedHash = CryptoJS.SHA256(input).toString();
algorithm = 'SHA-256';
break;
}
}
const matches = calculatedHash.toLowerCase() === hashToCompare;
if (matches) {
return `Hash matches! Algorithm: ${algorithm} ?`;
} else {
return `Hash does not match! Algorithm: ${algorithm} ?\n\nCalculated: ${calculatedHash}\nProvided: ${hashToCompare}`;
}
}
function calculateFileChecksum(file) {
const reader = new FileReader();
// Show loading state
outputText.value = 'Calculating checksums...';
reader.onload = function(e) {
const wordArray = CryptoJS.lib.WordArray.create(e.target.result);
// Calculate multiple hash types
const md5 = CryptoJS.MD5(wordArray).toString();
const sha1 = CryptoJS.SHA1(wordArray).toString();
const sha256 = CryptoJS.SHA256(wordArray).toString();
const sha512 = CryptoJS.SHA512(wordArray).toString();
// Format file size
const fileSize = formatFileSize(file.size);
// Display results
outputText.value = `File: ${file.name} (${fileSize})\n\n` +
`MD5: ${md5}\n` +
`SHA-1: ${sha1}\n` +
`SHA-256: ${sha256}\n` +
`SHA-512: ${sha512}`;
// Update character count
updateCharCount();
// Add to all results
addToAllResults('file-checksum', `File: ${file.name}`, outputText.value);
// Add to usage stats
addToUsageStats('file-checksum');
showToast('File checksum calculated!');
// Reset process button
processBtn.innerHTML = '<i class="fas fa-play mr-2"></i>Process';
processBtn.disabled = false;
};
reader.onerror = function() {
outputText.value = 'Error reading file';
showToast('Error reading file!', 'error');
// Reset process button
processBtn.innerHTML = '<i class="fas fa-play mr-2"></i>Process';
processBtn.disabled = false;
};
reader.readAsArrayBuffer(file);
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Initialize QR Code functionality
function initializeQRCode() {
const qrCodeInput = document.getElementById('qrCodeInput');
const qrCodeOutput = document.getElementById('qrCodeOutput');
const qrCodeSize = document.getElementById('qrCodeSize');
const qrCodeErrorCorrection = document.getElementById('qrCodeErrorCorrection');
const downloadQrCodeBtn = document.getElementById('downloadQrCodeBtn');
if (!qrCodeInput || !qrCodeOutput) return;
// Generate QR code on input change
qrCodeInput.addEventListener('input', () => {
if (qrCodeInput.value.trim()) {
const size = parseInt(qrCodeSize.value) || 200;
const errorCorrection = qrCodeErrorCorrection.value || 'M';
generateQRCode(qrCodeInput.value, size, errorCorrection);
downloadQrCodeBtn.disabled = false;
} else {
qrCodeOutput.innerHTML = `
<div class="text-gray-400 dark:text-gray-500 text-center">
<i class="fas fa-qrcode text-4xl mb-2"></i>
<p>QR code will appear here</p>
</div>
`;
downloadQrCodeBtn.disabled = true;
}
});
// Update QR code when size changes
qrCodeSize.addEventListener('change', () => {
if (qrCodeInput.value.trim()) {
const size = parseInt(qrCodeSize.value) || 200;
const errorCorrection = qrCodeErrorCorrection.value || 'M';
generateQRCode(qrCodeInput.value, size, errorCorrection);
}
});
// Update QR code when error correction changes
qrCodeErrorCorrection.addEventListener('change', () => {
if (qrCodeInput.value.trim()) {
const size = parseInt(qrCodeSize.value) || 200;
const errorCorrection = qrCodeErrorCorrection.value || 'M';
generateQRCode(qrCodeInput.value, size, errorCorrection);
}
});
// Download QR code
downloadQrCodeBtn.addEventListener('click', () => {
const canvas = document.querySelector('#qrCodeOutput canvas');
if (!canvas) {
showToast('No QR code to download!', 'error');
return;
}
const link = document.createElement('a');
link.download = 'qrcode.png';
link.href = canvas.toDataURL('image/png');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showToast('QR code downloaded!');
});
}
function generateQRCode(text, size = 200, errorCorrectionLevel = 'M') {
const qrCodeOutput = document.getElementById('qrCodeOutput');
if (!qrCodeOutput) return;
// Clear previous QR code
qrCodeOutput.innerHTML = '';
// Generate new QR code
try {
QRCode.toCanvas(qrCodeOutput, text, {
width: size,
margin: 1,
color: {
dark: document.documentElement.classList.contains('dark') ? '#FFFFFF' : '#000000',
light: document.documentElement.classList.contains('dark') ? '#111827' : '#FFFFFF'
},
errorCorrectionLevel: errorCorrectionLevel
}, function(error) {
if (error) {
console.error(error);
qrCodeOutput.innerHTML = `
<div class="text-red-500 text-center">
<i class="fas fa-exclamation-circle text-4xl mb-2"></i>
<p>Error generating QR code</p>
</div>
`;
showToast('Error generating QR code!', 'error');
}
});
} catch (error) {
console.error(error);
qrCodeOutput.innerHTML = `
<div class="text-red-500 text-center">
<i class="fas fa-exclamation-circle text-4xl mb-2"></i>
<p>Error generating QR code</p>
</div>
`;
showToast('Error generating QR code!', 'error');
}
}
// Initialize Regex Tester functionality
function initializeRegexTester() {
const regexPattern = document.getElementById('regexPattern');
const regexFlags = document.getElementById('regexFlags');
const regexTestString = document.getElementById('regexTestString');
if (!regexPattern || !regexFlags || !regexTestString) return;
// Live preview for regex tester
const updateRegexPreview = () => {
if (regexPattern.value && regexTestString.value) {
const result = testRegex(regexPattern.value, regexFlags.value, regexTestString.value);
outputHtml.innerHTML = result;
outputHtml.classList.remove('hidden');
outputText.classList.add('hidden');
}
};
regexPattern.addEventListener('input', updateRegexPreview);
regexFlags.addEventListener('input', updateRegexPreview);
regexTestString.addEventListener('input', updateRegexPreview);
}
function testRegex(pattern, flags, testString) {
try {
const regex = new RegExp(pattern, flags);
// Find all matches
const matches = [];
let match;
while ((match = regex.exec(testString)) !== null) {
// Avoid infinite loops
if (match.index === regex.lastIndex) {
regex.lastIndex++;
}
matches.push({
match: match[0],
index: match.index,
groups: match.slice(1)
});
// Break if global flag is not set
if (!flags.includes('g')) break;
}
// Prepare result HTML
let resultHtml = `<div class="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg mb-4">
<h3 class="font-medium text-blue-800 dark:text-blue-300 mb-2">Regex Information</h3>
<p class="text-gray-700 dark:text-gray-300">Pattern: <code class="bg-gray-100 dark:bg-gray-700 p-1 rounded">${escapeHtml(pattern)}</code></p>
<p class="text-gray-700 dark:text-gray-300">Flags: <code class="bg-gray-100 dark:bg-gray-700 p-1 rounded">${escapeHtml(flags)}</code></p>
<p class="text-gray-700 dark:text-gray-300">Matches: <span class="font-medium">${matches.length}</span></p>
</div>`;
if (matches.length > 0) {
resultHtml += `<div class="overflow-x-auto">
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" class="px-6 py-3">#</th>
<th scope="col" class="px-6 py-3">Match</th>
<th scope="col" class="px-6 py-3">Index</th>
<th scope="col" class="px-6 py-3">Groups</th>
</tr>
</thead>
<tbody>`;
matches.forEach((match, index) => {
resultHtml += `<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
<td class="px-6 py-4">${index + 1}</td>
<td class="px-6 py-4 font-medium text-gray-900 dark:text-white">${escapeHtml(match.match)}</td>
<td class="px-6 py-4">${match.index}</td>
<td class="px-6 py-4">${match.groups.length > 0 ? match.groups.map(g => `<span class="inline-block bg-gray-100 dark:bg-gray-700 px-2 py-1 rounded mr-1">${escapeHtml(g)}</span>`).join('') : '<span class="text-gray-400">No groups</span>'}</td>
</tr>`;
});
resultHtml += `</tbody></table></div>`;
// Highlighted text
resultHtml += `<div class="mt-4 p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<h3 class="font-medium text-gray-800 dark:text-gray-200 mb-2">Highlighted Matches</h3>
<div class="bg-white dark:bg-gray-700 p-3 rounded border dark:border-gray-600 whitespace-pre-wrap">`;
// Highlight matches in the text
let lastIndex = 0;
let highlightedText = '';
// Sort matches by index
const sortedMatches = [...matches].sort((a, b) => a.index - b.index);
sortedMatches.forEach(match => {
// Add text before match
highlightedText += escapeHtml(testString.slice(lastIndex, match.index));
// Add highlighted match
highlightedText += `<span class="bg-yellow-200 dark:bg-yellow-700">${escapeHtml(match.match)}</span>`;
// Update last index
lastIndex = match.index + match.match.length;
});
// Add remaining text
highlightedText += escapeHtml(testString.slice(lastIndex));
resultHtml += highlightedText + '</div></div>';
} else {
resultHtml += `<div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<p class="text-gray-700 dark:text-gray-300">No matches found.</p>
</div>`;
}
return resultHtml;
} catch (error) {
return `<div class="p-4 bg-red-50 dark:bg-red-900/20 rounded-lg">
<h3 class="font-medium text-red-800 dark:text-red-300 mb-2">Error</h3>
<p class="text-gray-700 dark:text-gray-300">${error.message}</p>
</div>`;
}
}
// Initialize Diff Checker functionality
function initializeDiffChecker() {
const diffOriginalText = document.getElementById('diffOriginalText');
const diffModifiedText = document.getElementById('diffModifiedText');
const diffInlineBtn = document.getElementById('diffInlineBtn');
const diffSideBySideBtn = document.getElementById('diffSideBySideBtn');
if (!diffOriginalText || !diffModifiedText || !diffInlineBtn || !diffSideBySideBtn) return;
// Calculate diff on button click
diffInlineBtn.addEventListener('click', () => {
const result = calculateDiff(diffOriginalText.value, diffModifiedText.value, 'inline');
outputHtml.innerHTML = result;
outputHtml.classList.remove('hidden');
outputText.classList.add('hidden');
});
diffSideBySideBtn.addEventListener('click', () => {
const result = calculateDiff(diffOriginalText.value, diffModifiedText.value, 'sideBySide');
outputHtml.innerHTML = result;
outputHtml.classList.remove('hidden');
outputText.classList.add('hidden');
});
}
function calculateDiff(originalText, modifiedText, viewType = 'inline') {
// Simple diff algorithm (character by character)
function simpleDiff(oldStr, newStr) {
const result = [];
let i = 0, j = 0;
while (i < oldStr.length || j < newStr.length) {
if (i < oldStr.length && j < newStr.length && oldStr[i] === newStr[j]) {
result.push({ type: 'common', value: oldStr[i] });
i++;
j++;
} else {
const oldRemaining = oldStr.substr(i);
const newRemaining = newStr.substr(j);
// Look for the next common character
let nextCommonIndex = -1;
for (let k = 0; k < oldRemaining.length; k++) {
const index = newRemaining.indexOf(oldRemaining[k]);
if (index !== -1) {
nextCommonIndex = index;
break;
}
}
if (nextCommonIndex === -1) {
// No more common characters, remove all remaining old and add all remaining new
while (i < oldStr.length) {
result.push({ type: 'removed', value: oldStr[i] });
i++;
}
while (j < newStr.length) {
result.push({ type: 'added', value: newStr[j] });
j++;
}
} else {
// Remove characters until the next common one
while (i < oldStr.length && oldStr[i] !== newRemaining[nextCommonIndex]) {
result.push({ type: 'removed', value: oldStr[i] });
i++;
}
// Add characters until the next common one
for (let k = 0; k < nextCommonIndex; k++) {
result.push({ type: 'added', value: newRemaining[k] });
j++;
}
}
}
}
// Group consecutive same-type characters
const grouped = [];
let current = { type: result[0]?.type, value: '' };
for (const char of result) {
if (char.type === current.type) {
current.value += char.value;
} else {
if (current.value) grouped.push({ ...current });
current = { type: char.type, value: char.value };
}
}
if (current.value) grouped.push(current);
return grouped;
}
// Process line by line
const oldLines = originalText.split('\n');
const newLines = modifiedText.split('\n');
// Generate HTML based on view type
if (viewType === 'inline') {
let html = `<div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<h3 class="font-medium text-gray-800 dark:text-gray-200 mb-2">Inline Diff</h3>
<div class="bg-white dark:bg-gray-700 p-3 rounded border dark:border-gray-600">`;
for (let i = 0; i < Math.max(oldLines.length, newLines.length); i++) {
const oldLine = i < oldLines.length ? oldLines[i] : '';
const newLine = i < newLines.length ? newLines[i] : '';
if (oldLine === newLine) {
html += `<div class="diff-line">${escapeHtml(oldLine)}</div>`;
} else {
const diff = simpleDiff(oldLine, newLine);
html += '<div class="diff-line">';
for (const part of diff) {
if (part.type === 'common') {
html += escapeHtml(part.value);
} else if (part.type === 'removed') {
html += `<span class="bg-red-100 dark:bg-red-900/50 line-through">${escapeHtml(part.value)}</span>`;
} else if (part.type === 'added') {
html += `<span class="bg-green-100 dark:bg-green-900/50">${escapeHtml(part.value)}</span>`;
}
}
html += '</div>';
}
}
html += '</div></div>';
return html;
} else {
// Side by side view
let html = `<div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
<h3 class="font-medium text-gray-800 dark:text-gray-200 mb-2">Side by Side Diff</h3>
<div class="grid grid-cols-2 gap-4">
<div class="bg-white dark:bg-gray-700 p-3 rounded border dark:border-gray-600">
<h4 class="text-sm font-medium mb-2 text-gray-500 dark:text-gray-400">Original</h4>`;
// Original text
for (let i = 0; i < oldLines.length; i++) {
const line = oldLines[i];
if (i < newLines.length && line === newLines[i]) {
html += `<div class="diff-line">${escapeHtml(line)}</div>`;
} else {
html += `<div class="diff-line bg-red-100 dark:bg-red-900/30">${escapeHtml(line)}</div>`;
}
}
html += '</div><div class="bg-white dark:bg-gray-700 p-3 rounded border dark:border-gray-600">';
html += '<h4 class="text-sm font-medium mb-2 text-gray-500 dark:text-gray-400">Modified</h4>';
// Modified text
for (let i = 0; i < newLines.length; i++) {
const line = newLines[i];
if (i < oldLines.length && line === oldLines[i]) {
html += `<div class="diff-line">${escapeHtml(line)}</div>`;
} else {
html += `<div class="diff-line bg-green-100 dark:bg-green-900/30">${escapeHtml(line)}</div>`;
}
}
html += '</div></div></div>';
return html;
}
}
// Helper function to escape HTML
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// Recent functions functionality
function initializeRecentFunctions() {
// Load from local storage
loadRecentFunctions();
}
function addToRecentFunctions(functionName) {
// Get existing recent functions
const recentFunctions = JSON.parse(localStorage.getItem('recentFunctions') || '[]');
// Remove if already exists
const index = recentFunctions.indexOf(functionName);
if (index > -1) {
recentFunctions.splice(index, 1);
}
// Add to beginning
recentFunctions.unshift(functionName);
// Limit to 5 recent functions
if (recentFunctions.length > 5) {
recentFunctions.pop();
}
// Save to local storage
localStorage.setItem('recentFunctions', JSON.stringify(recentFunctions));
// Update display
updateRecentFunctionsDisplay(recentFunctions);
}
function loadRecentFunctions() {
const recentFunctions = JSON.parse(localStorage.getItem('recentFunctions') || '[]');
updateRecentFunctionsDisplay(recentFunctions);
}
function updateRecentFunctionsDisplay(functions) {
if (!recentFunctionsList) return;
if (functions.length === 0) {
recentFunctionsList.innerHTML = '<li class="italic text-gray-500 dark:text-gray-400 text-sm">No recent functions</li>';
return;
}
recentFunctionsList.innerHTML = '';
functions.forEach(func => {
const li = document.createElement('li');
li.innerHTML = `<a href="#" class="recent-function hover:text-blue-600 transition-colors duration-300" data-function="${func}">${formatFunctionName(func)}</a>`;
recentFunctionsList.appendChild(li);
});
// Add event listeners
document.querySelectorAll('.recent-function').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const functionName = link.getAttribute('data-function');
// Set the tool selector value
toolSelector.value = functionName;
// Trigger change event to update UI
const event = new Event('change');
toolSelector.dispatchEvent(event);
});
});
}
// Favorites functionality
function initializeFavorites() {
// Load from local storage
loadFavorites();
// Update favorite button state for current tool
if (currentFunction) {
updateFavoriteButtonState(currentFunction);
}
}
function loadFavorites() {
const favorites = JSON.parse(localStorage.getItem('favorites') || '[]');
updateFavoritesDisplay(favorites);
}
function updateFavoritesDisplay(favorites) {
if (!favoritesList) return;
if (favorites.length === 0) {
favoritesList.innerHTML = '<li class="italic text-gray-500 dark:text-gray-400 text-sm">No favorites yet</li>';
return;
}
favoritesList.innerHTML = '';
favorites.forEach(func => {
const li = document.createElement('li');
li.className = 'flex items-center justify-between';
li.innerHTML = `
<a href="#" class="favorite-function hover:text-blue-600 transition-colors duration-300" data-function="${func}">${formatFunctionName(func)}</a>
<button class="remove-favorite text-gray-400 hover:text-red-500" data-function="${func}">
<i class="fas fa-times"></i>
</button>
`;
favoritesList.appendChild(li);
});
// Add event listeners
document.querySelectorAll('.favorite-function').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const functionName = link.getAttribute('data-function');
// Set the tool selector value
toolSelector.value = functionName;
// Trigger change event to update UI
const event = new Event('change');
toolSelector.dispatchEvent(event);
});
});
document.querySelectorAll('.remove-favorite').forEach(btn => {
btn.addEventListener('click', (e) => {
e.preventDefault();
const functionName = btn.getAttribute('data-function');
toggleFavorite(functionName);
});
});
}
function updateFavoriteButtonState(functionName) {
if (!favoriteBtn) return;
const favorites = JSON.parse(localStorage.getItem('favorites') || '[]');
const isFavorite = favorites.includes(functionName);
if (isFavorite) {
favoriteBtn.innerHTML = '<i class="fas fa-star text-yellow-500"></i>';
favoriteBtn.title = 'Remove from Favorites';
} else {
favoriteBtn.innerHTML = '<i class="far fa-star"></i>';
favoriteBtn.title = 'Add to Favorites';
}
// Update data attribute
favoriteBtn.setAttribute('data-function', functionName);
}
function toggleFavorite(functionNameParam) {
// Get function name from parameter or button
const functionName = typeof functionNameParam === 'string'
? functionNameParam
: (favoriteBtn ? favoriteBtn.getAttribute('data-function') : currentFunction);
if (!functionName) return;
const favorites = JSON.parse(localStorage.getItem('favorites') || '[]');
const index = favorites.indexOf(functionName);
if (index > -1) {
// Remove from favorites
favorites.splice(index, 1);
showToast(`Removed ${formatFunctionName(functionName)} from favorites`);
} else {
// Add to favorites
favorites.push(functionName);
showToast(`Added ${formatFunctionName(functionName)} to favorites`);
}
// Save to local storage
localStorage.setItem('favorites', JSON.stringify(favorites));
// Update displays
updateFavoritesDisplay(favorites);
updateFavoriteButtonState(functionName);
}
function formatFunctionName(name) {
return name
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
// Initialize character counter
function initializeCharCount() {
if (inputText) {
inputText.addEventListener('input', updateCharCount);
}
if (outputText) {
outputText.addEventListener('input', updateCharCount);
}
// Initial update
updateCharCount();
}
function updateCharCount() {
if (inputCharCount && inputText) {
inputCharCount.textContent = inputText.value.length;
}
if (outputCharCount && outputText) {
outputCharCount.textContent = outputText.value.length;
}
}
// Tool information update
function updateToolInfo(functionName, mode = null) {
if (!toolInfo) return;
const isDecoding = mode === 'decode' || (modeSwitch && modeSwitch.checked);
// Clear previous content
toolInfo.innerHTML = '';
// Create info content based on function
switch (functionName) {
case 'md5':
toolInfo.innerHTML = `
<h4 class="font-semibold mb-2">MD5 (Message Digest Algorithm 5)</h4>
<p class="mb-3">MD5 is a widely used cryptographic hash function that produces a 128-bit (16-byte) hash value. However, it is no longer considered secure for cryptographic purposes due to vulnerabilities.</p>
<div class="bg-yellow-50 dark:bg-yellow-900/20 p-3 rounded-lg">
<p class="text-yellow-800 dark:text-yellow-200 font-medium">?? Warning: MD5 is not collision-resistant and should not be used for security-critical applications.</p>
</div>
`;
break;
case 'sha256':
toolInfo.innerHTML = `
<h4 class="font-semibold mb-2">SHA-256 (Secure Hash Algorithm 256-bit)</h4>
<p class="mb-3">SHA-256 is a cryptographic hash function that generates a 256-bit (32-byte) hash value. It's part of the SHA-2 family designed by the NSA and is widely used in security applications and protocols.</p>
<p>Use cases include:</p>
<ul class="list-disc ml-5 mb-3">
<li>Digital signatures</li>
<li>SSL certificate validation</li>
<li>Password storage (with proper salting)</li>
<li>Data integrity verification</li>
</ul>
`;
break;
case 'base64':
const actionText = isDecoding ? 'decode' : 'encode';
toolInfo.innerHTML = `
<h4 class="font-semibold mb-2">Base64 ${isDecoding ? 'Decoding' : 'Encoding'}</h4>
<p class="mb-3">Base64 is a binary-to-text encoding scheme that represents binary data in an ASCII string format by translating it into a radix-64 representation.</p>
<p>Toggle the switch to ${isDecoding ? 'encode' : 'decode'} instead.</p>
<div class="bg-blue-50 dark:bg-blue-900/20 p-3 rounded-lg mt-3">
<p class="text-blue-800 dark:text-blue-200 font-medium">?? Note: Base64 is not encryption. It's an encoding format that can be easily reversed.</p>
</div>
`;
break;
case 'aes':
toolInfo.innerHTML = `
<h4 class="font-semibold mb-2">AES (Advanced Encryption Standard) ${isDecoding ? 'Decryption' : 'Encryption'}</h4>
<p class="mb-3">AES is a symmetric encryption algorithm widely adopted as a standard by governments and organizations around the world. It encrypts data in blocks of 128 bits using cryptographic keys of 128, 192, or 256 bits.</p>
<p class="mb-3">This implementation uses AES-256 with the key you provide to ${isDecoding ? 'decrypt' : 'encrypt'} your data.</p>
<div class="bg-blue-50 dark:bg-blue-900/20 p-3 rounded-lg">
<p class="text-blue-800 dark:text-blue-200 font-medium">?? Your encryption key should be strong and kept secure. Anyone with the key can decrypt your data.</p>
</div>
`;
break;
case 'hmac':
toolInfo.innerHTML = `
<h4 class="font-semibold mb-2">HMAC (Hash-based Message Authentication Code)</h4>
<p class="mb-3">HMAC is a specific type of message authentication code (MAC) involving a cryptographic hash function and a secret cryptographic key. It provides a way to verify both the data integrity and authenticity of a message.</p>
<p class="mb-3">This implementation uses HMAC-SHA256 by default, providing a 256-bit output.</p>
`;
break;
case 'random-string':
toolInfo.innerHTML = `
<h4 class="font-semibold mb-2">Random String Generator</h4>
<p class="mb-3">Generate random strings with customizable length and character sets. Useful for creating random identifiers, test data, or other random string needs.</p>
<p>Choose the length and character set type, then click "Process" to generate.</p>
`;
break;
case 'password-generator':
toolInfo.innerHTML = `
<h4 class="font-semibold mb-2">Secure Password Generator</h4>
<p class="mb-3">Generate strong, secure passwords with customizable options. The generated passwords use cryptographically secure random values.</p>
<p class="mb-3">Select the desired length and character types to include, then click "Process" to generate a password.</p>
<div class="bg-green-50 dark:bg-green-900/20 p-3 rounded-lg">
<p class="text-green-800 dark:text-green-200 font-medium">?? Strong passwords should be at least 12 characters long and include a mix of uppercase, lowercase, numbers, and special characters.</p>
</div>
`;
break;
case 'qr-code':
toolInfo.innerHTML = `
<h4 class="font-semibold mb-2">QR Code Generator</h4>
<p class="mb-3">Create QR codes for text, URLs, contact information, or any other data. QR codes can be scanned by smartphones and other devices with cameras.</p>
<p class="mb-3">Enter the data you want to encode, adjust the size and error correction level, then generate your QR code.</p>
<div class="bg-blue-50 dark:bg-blue-900/20 p-3 rounded-lg">
<p class="text-blue-800 dark:text-blue-200 font-medium">?? Higher error correction levels make the QR code more resistant to damage but require more space.</p>
</div>
`;
break;
case 'diff-checker':
toolInfo.innerHTML = `
<h4 class="font-semibold mb-2">Text Diff Checker</h4>
<p class="mb-3">Compare two texts and highlight the differences between them. Useful for comparing code versions, documents, or any text that has changed.</p>
<p class="mb-3">Enter the original and modified text, then choose between inline or side-by-side view.</p>
`;
break;
case 'regex-tester':
toolInfo.innerHTML = `
<h4 class="font-semibold mb-2">Regular Expression Tester</h4>
<p class="mb-3">Test and debug regular expressions with real-time highlighting of matches. Includes detailed information about each match, including groups and positions.</p>
<p class="mb-3">Enter your regex pattern, flags, and test string to see the results.</p>
<div class="bg-blue-50 dark:bg-blue-900/20 p-3 rounded-lg">
<p class="text-blue-800 dark:text-blue-200 font-medium">?? Common flags: g (global), i (case-insensitive), m (multiline), s (dotall), u (unicode)</p>
</div>
`;
break;
default:
toolInfo.innerHTML = `
<p class="text-gray-600 dark:text-gray-300">Select a cryptographic function from the dropdown or sidebar to get started. Input your text and click "Process" to see the results.</p>
`;
}
}
// Update related tools based on current function
function updateRelatedTools(functionName) {
if (!relatedTools) return;
// Define related tools mapping
const relatedToolsMap = {
'md5': ['sha1', 'sha256', 'hash-compare'],
'sha1': ['md5', 'sha256', 'hash-compare'],
'sha256': ['md5', 'sha512', 'hash-compare'],
'sha512': ['sha256', 'sha3', 'hash-compare'],
'base64': ['url', 'hex', 'binary'],
'url': ['base64', 'html', 'hex'],
'hex': ['base64', 'binary', 'url'],
'binary': ['hex', 'base64', 'url'],
'aes': ['des', 'tripledes', 'rabbit'],
'des': ['aes', 'tripledes', 'rabbit'],
'tripledes': ['aes', 'des', 'rabbit'],
'password-generator': ['random-string', 'uuid', 'hmac'],
'random-string': ['password-generator', 'uuid', 'base64'],
'qr-code': ['base64', 'url', 'json'],
'diff-checker': ['regex-tester', 'json', 'file-checksum'],
'regex-tester': ['diff-checker', 'url', 'html']
};
// Default related tools if not in map
const defaultRelated = ['md5', 'base64', 'sha256'];
// Get related tools for current function
const related = relatedToolsMap[functionName] || defaultRelated;
// Generate HTML for related tools
relatedTools.innerHTML = '';
related.forEach(tool => {
const div = document.createElement('div');
div.className = 'p-4 border rounded-lg shadow-sm bg-gray-50 dark:bg-gray-700 hover:shadow-md transition-shadow duration-300 zoom-on-hover cursor-pointer';
div.setAttribute('data-function', tool);
// Get icon for tool
let icon = 'fas fa-tools';
if (tool.includes('hash')) icon = 'fas fa-hashtag';
else if (tool.includes('base64') || tool.includes('url') || tool.includes('hex') || tool.includes('binary')) icon = 'fas fa-exchange-alt';
else if (tool.includes('aes') || tool.includes('des') || tool.includes('crypt')) icon = 'fas fa-key';
else if (tool.includes('password')) icon = 'fas fa-shield-alt';
else if (tool.includes('random') || tool.includes('uuid')) icon = 'fas fa-dice';
else if (tool.includes('qr')) icon = 'fas fa-qrcode';
else if (tool.includes('diff')) icon = 'fas fa-code-branch';
else if (tool.includes('regex')) icon = 'fas fa-search';
else if (tool.includes('file')) icon = 'fas fa-file';
div.innerHTML = `
<div class="flex items-center mb-2">
<i class="${icon} text-blue-600 dark:text-blue-400 mr-3"></i>
<h4 class="font-medium text-gray-800 dark:text-white">${formatFunctionName(tool)}</h4>
</div>
<p class="text-sm text-gray-600 dark:text-gray-300">${getToolDescription(tool)}</p>
`;
relatedTools.appendChild(div);
// Add click event
div.addEventListener('click', () => {
toolSelector.value = tool;
const event = new Event('change');
toolSelector.dispatchEvent(event);
});
});
}
function getToolDescription(tool) {
switch (tool) {
case 'md5': return 'Generate MD5 hash from text input';
case 'sha1': return 'Generate SHA-1 hash from text input';
case 'sha256': return 'Generate SHA-256 hash from text input';
case 'sha512': return 'Generate SHA-512 hash from text input';
case 'base64': return 'Convert text to/from Base64 encoding';
case 'url': return 'Encode/decode URL components';
case 'hex': return 'Convert text to/from hexadecimal';
case 'binary': return 'Convert text to/from binary';
case 'aes': return 'Encrypt/decrypt data using AES algorithm';
case 'des': return 'Encrypt/decrypt data using DES algorithm';
case 'tripledes': return 'Encrypt/decrypt data using Triple DES';
case 'rabbit': return 'Encrypt/decrypt data using Rabbit cipher';
case 'password-generator': return 'Generate secure random passwords';
case 'random-string': return 'Generate random strings with custom options';
case 'uuid': return 'Generate random UUIDs (v4)';
case 'hash-compare': return 'Compare hash values of different inputs';
case 'qr-code': return 'Generate QR codes from text or URLs';
case 'diff-checker': return 'Compare and highlight differences between texts';
case 'regex-tester': return 'Test and debug regular expressions';
case 'file-checksum': return 'Calculate file hash checksums (MD5, SHA-256, etc.)';
case 'json': return 'Format, validate, and transform JSON data';
case 'html': return 'Encode/decode HTML entities';
default: return 'Process text with this cryptographic tool';
}
}
// Usage statistics functionality
function initializeStats() {
// Load stats from local storage
const stats = JSON.parse(localStorage.getItem('usageStats') || '{}');
// Display stats if we have data
if (Object.keys(stats).length > 0) {
displayStats(stats);
}
}
function addToUsageStats(functionName) {
// Get existing stats
const stats = JSON.parse(localStorage.getItem('usageStats') || '{}');
// Update stats for this function
if (!stats[functionName]) {
stats[functionName] = {
count: 0,
lastUsed: null
};
}
stats[functionName].count++;
stats[functionName].lastUsed = new Date().toISOString();
// Save to local storage
localStorage.setItem('usageStats', JSON.stringify(stats));
// Update display
displayStats(stats);
}
function displayStats(stats) {
// Update chart if it exists
if (usageStatsChart) {
// Convert stats to arrays for chart
const labels = [];
const data = [];
// Sort by usage count (descending)
const sortedFunctions = Object.entries(stats)
.sort((a, b) => b[1].count - a[1].count)
.slice(0, 10); // Only show top 10
sortedFunctions.forEach(([func, stat]) => {
labels.push(formatFunctionName(func));
data.push(stat.count);
});
// Determine colors based on dark mode
const isDark = document.documentElement.classList.contains('dark');
const textColor = isDark ? '#e2e8f0' : '#4a5568';
const gridColor = isDark ? '#374151' : '#e2e8f0';
const barColor = isDark ? 'rgba(59, 130, 246, 0.8)' : 'rgba(59, 130, 246, 0.6)';
const hoverColor = isDark ? 'rgba(96, 165, 250, 0.9)' : 'rgba(59, 130, 246, 0.8)';
// Create or update chart
if (chartInstance) {
// Update existing chart
chartInstance.data.labels = labels;
chartInstance.data.datasets[0].data = data;
chartInstance.update();
} else {
// Create new chart
chartInstance = new Chart(usageStatsChart, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Usage Count',
data: data,
backgroundColor: barColor,
hoverBackgroundColor: hoverColor,
borderWidth: 0,
borderRadius: 4,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
label: function(context) {
return `Used ${context.raw} times`;
}
}
}
},
scales: {
x: {
grid: {
display: false,
color: gridColor
},
ticks: {
color: textColor
}
},
y: {
beginAtZero: true,
grid: {
color: gridColor
},
ticks: {
precision: 0,
color: textColor
}
}
}
}
});
}
// Update table view
if (statsTableBody) {
statsTableBody.innerHTML = '';
sortedFunctions.forEach(([func, stat]) => {
const tr = document.createElement('tr');
tr.className = 'bg-white border-b dark:bg-gray-800 dark:border-gray-700';
const lastUsedDate = stat.lastUsed
? new Date(stat.lastUsed).toLocaleString()
: 'Never';
tr.innerHTML = `
<td class="px-6 py-4 font-medium text-gray-900 dark:text-white">${formatFunctionName(func)}</td>
<td class="px-6 py-4">${stat.count}</td>
<td class="px-6 py-4">${lastUsedDate}</td>
`;
statsTableBody.appendChild(tr);
});
}
}
}
// File drop zone functionality
function initializeFileDropZone() {
const fileInput = document.getElementById('file-input');
if (!fileDropZone || !fileInput) return;
// File input change
fileInput.addEventListener('change', (e) => {
handleFileSelect(e);
});
// Drag and drop handlers
fileDropZone.addEventListener('dragover', (e) => {
e.preventDefault();
e.stopPropagation();
fileDropZone.classList.add('border-blue-500', 'bg-blue-50', 'dark:bg-blue-900/20');
});
fileDropZone.addEventListener('dragleave', (e) => {
e.preventDefault();
e.stopPropagation();
fileDropZone.classList.remove('border-blue-500', 'bg-blue-50', 'dark:bg-blue-900/20');
});
fileDropZone.addEventListener('drop', (e) => {
e.preventDefault();
e.stopPropagation();
fileDropZone.classList.remove('border-blue-500', 'bg-blue-50', 'dark:bg-blue-900/20');
if (e.dataTransfer.files.length) {
handleFileSelect({ target: { files: e.dataTransfer.files } });
}
});
}
function handleFileSelect(e) {
const file = e.target.files[0];
if (!file) return;
// Store file in drop zone
fileDropZone.files = e.target.files;
// Show file info
inputText.value = `File: ${file.name} (${formatFileSize(file.size)})`;
updateCharCount();
// Process file based on current function
if (toolSelector.value === 'file-checksum') {
calculateFileChecksum(file);
}
}
// Live preview functionality
function initializeLivePreview() {
let debounceTimeout;
inputText.addEventListener('input', () => {
// Clear any existing timeout
clearTimeout(debounceTimeout);
// Set a new timeout to process after user stops typing
debounceTimeout = setTimeout(() => {
const selectedFunction = toolSelector.value;
// Only process automatically for simple functions
const autoProcessFunctions = [
'md5', 'sha1', 'sha256', 'sha512', 'base64', 'url', 'hex', 'binary'
];
if (autoProcessFunctions.includes(selectedFunction)) {
processCurrentFunction();
}
}, 500);
});
}
// Search functionality
function initializeSearch() {
if (!toolSearch) return;
toolSearch.addEventListener('input', () => {
const searchTerm = toolSearch.value.toLowerCase();
// Filter sidebar links
document.querySelectorAll('#sidebar .tool-link').forEach(link => {
const toolName = link.textContent.toLowerCase();
const li = link.closest('li');
if (toolName.includes(searchTerm)) {
li.classList.remove('hidden');
} else {
li.classList.add('hidden');
}
});
// Show/hide section headers based on visible items
document.querySelectorAll('#sidebar h3').forEach(header => {
const nextUl = header.nextElementSibling;
if (nextUl && nextUl.tagName === 'UL') {
const hasVisibleItems = Array.from(nextUl.querySelectorAll('li')).some(li => !li.classList.contains('hidden'));
if (hasVisibleItems || searchTerm === '') {
header.classList.remove('hidden');
} else {
header.classList.add('hidden');
}
}
});
});
}
// URL parameters for direct tool access
function updateUrlWithTool(functionName) {
const url = new URL(window.location);
url.searchParams.set('tool', functionName);
window.history.replaceState({}, '', url);
}
function checkUrlParameters() {
const urlParams = new URLSearchParams(window.location.search);
const tool = urlParams.get('tool');
if (tool) {
// Check if tool exists in options
const optionExists = Array.from(toolSelector.options).some(option => option.value === tool);
if (optionExists) {
// Set the tool selector
toolSelector.value = tool;
// Trigger change event
const event = new Event('change');
toolSelector.dispatchEvent(event);
}
}
}
// All results functionality
function addToAllResults(functionName, input, result) {
// Show the section if hidden
allResultsSection.classList.remove('hidden');
// Create a unique ID for this result
const resultId = `result-${Date.now()}`;
// Create new result item
const resultItem = document.createElement('div');
resultItem.className = 'result-item bg-gray-50 dark:bg-gray-700 p-4 rounded-lg';
resultItem.setAttribute('data-function', functionName);
resultItem.setAttribute('id', resultId);
// Truncate input for display
const displayInput = input.length > 50 ? input.substring(0, 47) + '...' : input;
// Truncate result for display
const displayResult = result.length > 100 ? result.substring(0, 97) + '...' : result;
resultItem.innerHTML = `
<div class="flex justify-between items-center mb-2">
<h4 class="font-semibold dark:text-white flex items-center">
${formatFunctionName(functionName)}
<span class="ml-2 text-xs text-gray-500 dark:text-gray-400">${new Date().toLocaleTimeString()}</span>
</h4>
<div class="flex gap-2">
<button class="expand-result-btn text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400" title="Expand/Collapse" data-expanded="false">
<i class="fas fa-chevron-down"></i>
</button>
<button class="copy-result-btn text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400" title="Copy Result">
<i class="fas fa-copy"></i>
</button>
<button class="remove-result-btn text-gray-500 hover:text-red-600 dark:text-gray-400 dark:hover:text-red-400" title="Remove Result">
<i class="fas fa-times"></i>
</button>
</div>
</div>
<div class="result-summary">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2 text-sm">
<div class="text-gray-600 dark:text-gray-400 break-all">
<span class="font-medium">Input:</span> ${escapeHtml(displayInput)}
</div>
<div class="text-gray-700 dark:text-gray-300 break-all">
<span class="font-medium">Result:</span> ${escapeHtml(displayResult)}
</div>
</div>
</div>
<div class="result-details mt-3 hidden">
<div class="grid grid-cols-1 gap-3">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Input:</label>
<div class="bg-white dark:bg-gray-800 p-2 rounded border dark:border-gray-600 break-all">
${escapeHtml(input)}
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Result:</label>
<div class="bg-white dark:bg-gray-800 p-2 rounded border dark:border-gray-600 break-all">
${escapeHtml(result)}
</div>
</div>
</div>
</div>
`;
// Add to container (at the top)
allResultsContainer.insertBefore(resultItem, allResultsContainer.firstChild);
// Add event listeners
resultItem.querySelector('.copy-result-btn').addEventListener('click', () => {
copyToClipboard(result);
showToast(`${formatFunctionName(functionName)} result copied to clipboard!`);
});
resultItem.querySelector('.remove-result-btn').addEventListener('click', () => {
resultItem.remove();
// Hide section if no results left
if (allResultsContainer.children.length === 0) {
allResultsSection.classList.add('hidden');
}
});
resultItem.querySelector('.expand-result-btn').addEventListener('click', (e) => {
const btn = e.currentTarget;
const isExpanded = btn.getAttribute('data-expanded') === 'true';
const details = resultItem.querySelector('.result-details');
if (isExpanded) {
// Collapse
details.classList.add('hidden');
btn.setAttribute('data-expanded', 'false');
btn.querySelector('i').classList.remove('fa-chevron-up');
btn.querySelector('i').classList.add('fa-chevron-down');
} else {
// Expand
details.classList.remove('hidden');
btn.setAttribute('data-expanded', 'true');
btn.querySelector('i').classList.remove('fa-chevron-down');
btn.querySelector('i').classList.add('fa-chevron-up');
}
});
// Limit to 10 results
if (allResultsContainer.children.length > 10) {
allResultsContainer.removeChild(allResultsContainer.lastChild);
}
}
// Toast notification system
function showToast(message, type = 'success') {
const toastContainer = document.getElementById('toast-container');
// Create toast element
const toast = document.createElement('div');
toast.className = `flex items-center p-4 mb-4 rounded-lg shadow-lg text-sm ${
type === 'success' ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' :
type === 'error' ? 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200' :
type === 'warning' ? 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200' :
'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200'
} opacity-0 transform translate-y-2 transition-all duration-300`;
// Set icon based on type
const icon =
type === 'success' ? 'fas fa-check-circle' :
type === 'error' ? 'fas fa-exclamation-circle' :
type === 'warning' ? 'fas fa-exclamation-triangle' :
'fas fa-info-circle';
toast.innerHTML = `
<div class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 rounded-lg ${
type === 'success' ? 'bg-green-200 text-green-600 dark:bg-green-800 dark:text-green-200' :
type === 'error' ? 'bg-red-200 text-red-600 dark:bg-red-800 dark:text-red-200' :
type === 'warning' ? 'bg-yellow-200 text-yellow-600 dark:bg-yellow-800 dark:text-yellow-200' :
'bg-blue-200 text-blue-600 dark:bg-blue-800 dark:text-blue-200'
} mr-3">
<i class="${icon}"></i>
</div>
<div class="ml-3 text-sm font-normal">${message}</div>
<button type="button" class="ml-auto -mx-1.5 -my-1.5 rounded-lg p-1.5 inline-flex h-8 w-8 hover:bg-gray-200 dark:hover:bg-gray-700 focus:outline-none" data-dismiss-target="#toast-success" aria-label="Close">
<i class="fas fa-times"></i>
</button>
`;
// Add to container
toastContainer.appendChild(toast);
// Animate in
setTimeout(() => {
toast.classList.remove('opacity-0', 'translate-y-2');
}, 10);
// Close button
const closeBtn = toast.querySelector('button');
closeBtn.addEventListener('click', () => {
closeToast(toast);
});
// Auto close after 5 seconds
setTimeout(() => {
closeToast(toast);
}, 5000);
}
function closeToast(toast) {
toast.classList.add('opacity-0', 'translate-y-2');
setTimeout(() => {
toast.remove();
}, 300);
}
// Initialize keyboard shortcuts
function initializeKeyboardShortcuts() {
document.addEventListener('keydown', (e) => {
// Ctrl+Enter to process
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
e.preventDefault();
processCurrentFunction();
}
// Ctrl+/ to focus search
if ((e.ctrlKey || e.metaKey) && e.key === '/') {
e.preventDefault();
if (toolSearch) {
toolSearch.focus();
}
}
// Esc to clear input
if (e.key === 'Escape') {
// Only clear if input is focused
if (document.activeElement === inputText) {
inputText.value = '';
updateCharCount();
}
}
// Shift+S to toggle sidebar
if (e.shiftKey && e.key === 'S') {
e.preventDefault();
toggleSidebar();
}
// Shift+D to toggle dark mode
if (e.shiftKey && e.key === 'D') {
e.preventDefault();
darkModeToggle.click();
}
// Alt+1 through Alt+5 for quick tools
if (e.altKey && e.key >= '1' && e.key <= '5') {
e.preventDefault();
const index = parseInt(e.key) - 1;
const quickButtons = document.querySelectorAll('.quick-tool-btn');
if (index < quickButtons.length) {
quickButtons[index].click();
}
}
});
}
</script>
</body>
</html>
Between content blocks · 728x90 ·
advertise here
How to use robots.txt parser
-
1
Paste your input
Enter the value at the top — domain, IP, URL, email, ASN, hash, whatever fits this tool. The smart input auto-detects type.
-
2
Click "Inspect"
host.tools issues real probes (DNS, HTTP, TCP, TLS, WHOIS where applicable) and renders the result in milliseconds.
-
3
Open the API tab
Every web tool has a sibling /api/v1/http/robots JSON endpoint with the same payload. One copy-as-curl click and you're scripting it.
Why this matters
Headers are how the modern web declares its security posture. Auditing them is the highest-ROI thing you can do this week.
API equivalent
/api/v1/http/robots?q=https%3A%2F%2Fcrypt.tools
curl -s '/api/v1/http/robots?q=https%3A%2F%2Fcrypt.tools'
Embed this tool
<iframe src="/http/robots?q={INPUT}&embed=1"
width="100%" height="600" frameborder="0"></iframe>
Drop into any HTML page. The embed=1 flag hides nav and footer.
Related tools
More in HTTP
Sidebar — half-page · 300x600 ·
advertise here
Sidebar — medium · 300x250 ·
advertise here
FAQ · robots.txt parser
Common questions
Is robots.txt parser free?
Yes — every tool is free on the web with a 200/hour rate limit per IP. The matching API endpoint /api/v1/http/robots is free up to 100 requests/hour, no key required.
Where does the data come from?
Real-time probes against authoritative sources (DNS root, RIRs, registries, the target server itself), plus partner data feeds from hostinfo.com (GeoIP/ASN) and hostcheck.com (reputation).
How fresh are the results?
Live by default. Cached for 5 minutes to make repeat queries instant; pass
?nocache=1 for a forced refresh.Can I run this from the command line?
Yes — every tool ships with a copy-as-curl. There's also an official CLI:
host.tools http robots YOUR_INPUT.Can I monitor results over time?
Pro tier lets you schedule any tool to run every 1/5/15/60 min and alert on diff. See monitors.
host.tools Pro
Run robots.txt parser on a schedule. Get pinged when it changes.
Pro gets you bulk lookups, monitors, webhook alerts, history, exports and 10,000 API calls/day. $19/mo.
- ✓Schedule any tool — every 1, 5, 15, 60 min
- ✓Diff against last run, alert on change
- ✓Webhook + email + Slack + PagerDuty + OpsGenie
- ✓Bulk CSV upload, 1,000 inputs per job
- ✓Export results as CSV / NDJSON / Excel
- ✓90-day history, comparison view