Utility Tools
Reminder / To-Do List
Day Counter / Event Countdown
Stopwatch & Timer
Alarm Clock
Age Calculator
Date Difference Calculator
World Clock / Time Zone Converter
Hijri ↔ Gregorian Calendar Converter
Unit Converter
Currency Converter
File & Document Tools
Image → PDF Converter
PDF → Image Converter
Merge PDF Files
Split PDF Pages
Compress PDF
Word → PDF Converter
Excel → PDF Converter
Text → PDF Generator
QR Code Generator/Reader
Barcode Generator
Image & Media Tools
Image Resizer / Cropper
Image Compressor
Background Remover
Image Format Converter
Video → MP3 Converter
Audio Compressor
Video Compressor
GIF Maker
Meme Generator
Watermark Adder / Remover
Text & Writing Tools
Text Case Converter
Word & Character Counter
Random Password Generator
Notes / Notepad Online
Text Encrypt / Decrypt
Remove Extra Spaces / Line Breaks
Typing Speed Test
Language Translator
Grammar / Spell Checker
Email Validator
Coding & Web Tools
HTML / CSS / JS Minifier
JSON Formatter / Validator
Base64 Encode / Decode
Color Picker / Gradient Generator
CSS Box Shadow Generator
Responsive Design Tester
Domain WHOIS Lookup
IP Address Finder
URL Shortener
Website Screenshot Generator
Tool Name
${r.text}${timeStr}
`;
listContainer.appendChild(li);
});
};
const addReminder = () => {
const text = textInput.value.trim();
const time = timeInput.value;
if (!text) return;
const newReminder = { text, time, done: false, id: Date.now() };
reminders.push(newReminder);
saveReminders();
renderReminders();
textInput.value = '';
timeInput.value = '';
// Set notification if time is specified and in the future
if (time && new Date(time) > new Date()) {
scheduleNotification(newReminder);
}
};
const scheduleNotification = (reminder) => {
if (!("Notification" in window)) return console.warn("Notifications not supported.");
Notification.requestPermission().then(permission => {
if (permission !== "granted") return console.warn("Notification permission denied.");
const delay = new Date(reminder.time).getTime() - Date.now();
if (delay < 0) return;
setTimeout(() => {
new Notification("Smart Utility Reminder", {
body: reminder.text,
icon: '๐ ️'
});
// Mark as done/delete after notification if desired, or let user handle it.
}, delay);
});
};
listContainer.addEventListener('click', (e) => {
const button = e.target.closest('button');
if (!button) return;
const index = parseInt(button.dataset.index);
const action = button.dataset.action;
if (action === 'toggle') {
reminders[index].done = !reminders[index].done;
} else if (action === 'delete') {
reminders.splice(index, 1);
}
saveReminders();
renderReminders();
});
addButton.onclick = addReminder;
renderReminders();
}
// --- Tool 5: Age Calculator ---
function initializeAgeCalculator() {
const dobInput = document.getElementById('dob-input');
const resultDiv = document.getElementById('age-result');
const calcButton = document.getElementById('calculate-age');
const calculateAge = () => {
const dob = new Date(dobInput.value);
if (isNaN(dob.getTime())) {
return resultDiv.textContent = 'Please enter a valid date of birth.';
}
const today = new Date();
const diff = today.getTime() - dob.getTime();
if (diff < 0) {
return resultDiv.textContent = 'Date of Birth cannot be in the future.';
}
// Calculate years, months, days accurately
let years = today.getFullYear() - dob.getFullYear();
let months = today.getMonth() - dob.getMonth();
let days = today.getDate() - dob.getDate();
if (days < 0) {
months--;
// Get number of days in the *previous* month
days += new Date(today.getFullYear(), today.getMonth(), 0).getDate();
}
if (months < 0) {
years--;
months += 12;
}
resultDiv.innerHTML = `
Age: ${years} years, ${months} months, ${days} days
Total Days: ${Math.floor(diff / (1000 * 60 * 60 * 24))}
`; }; calcButton.onclick = calculateAge; // Set default date to a common example for test panel ease dobInput.valueAsDate = new Date(1990, 0, 1); } // --- Tool 9: Unit Converter --- function initializeUnitConverter() { const valueInput = document.getElementById('unit-value'); const fromSelect = document.getElementById('unit-from'); const toSelect = document.getElementById('unit-to'); const resultDiv = document.getElementById('unit-result'); const convertButton = document.getElementById('convert-unit'); // Conversion factors (to base unit, then from base unit) const FACTORS = { // Length (Base: Meter) m: { toBase: 1, fromBase: 1, symbol: 'm' }, km: { toBase: 1000, fromBase: 0.001, symbol: 'km' }, mi: { toBase: 1609.34, fromBase: 1 / 1609.34, symbol: 'mi' }, // Weight (Base: Kilogram) kg: { toBase: 1, fromBase: 1, symbol: 'kg' }, lb: { toBase: 0.453592, fromBase: 1 / 0.453592, symbol: 'lb' }, // Temperature (Special handling) celsius: { toBase: 1, fromBase: 1, symbol: '°C' }, // Base is C for Temp fahrenheit: { toBase: null, fromBase: null, symbol: '°F' } }; const convertUnit = () => { const value = parseFloat(valueInput.value); const fromUnit = fromSelect.value; const toUnit = toSelect.value; if (isNaN(value)) { return resultDiv.textContent = 'Please enter a valid number.'; } // Check for temperature conversion - must be handled separately if (fromUnit === 'celsius' && toUnit === 'fahrenheit') { const result = (value * 9/5) + 32; return resultDiv.textContent = `${value} ${FACTORS[fromUnit].symbol} = ${result.toFixed(2)} ${FACTORS[toUnit].symbol}`; } if (fromUnit === 'fahrenheit' && toUnit === 'celsius') { const result = (value - 32) * 5/9; return resultDiv.textContent = `${value} ${FACTORS[fromUnit].symbol} = ${result.toFixed(2)} ${FACTORS[toUnit].symbol}`; } if (fromUnit === 'celsius' && toUnit === 'celsius' || fromUnit === 'fahrenheit' && toUnit === 'fahrenheit') { return resultDiv.textContent = `${value} ${FACTORS[fromUnit].symbol} = ${value.toFixed(2)} ${FACTORS[toUnit].symbol}`; } // Standard linear conversion (Length/Weight) if (!FACTORS[fromUnit] || !FACTORS[toUnit]) { return resultDiv.textContent = 'Unsupported unit combination.'; } // Convert to Base Unit const baseValue = value * FACTORS[fromUnit].toBase; // Convert from Base Unit to Target Unit const finalValue = baseValue * FACTORS[toUnit].fromBase; resultDiv.textContent = `${value} ${FACTORS[fromUnit].symbol} = ${finalValue.toFixed(4)} ${FACTORS[toUnit].symbol}`; }; convertButton.onclick = convertUnit; } // --- Tool 10: Currency Converter --- function initializeCurrencyConverter() { const amountInput = document.getElementById('currency-amount'); const fromSelect = document.getElementById('currency-from'); const toSelect = document.getElementById('currency-to'); const resultDiv = document.getElementById('currency-result'); const statusDiv = document.getElementById('currency-status'); const convertButton = document.getElementById('convert-currency'); const manualRateDiv = document.getElementById('currency-manual-rate'); const manualRateInput = document.getElementById('manual-rate-input'); let rates = JSON.parse(localStorage.getItem('currencyRatesCache')) || { base: 'USD', date: new Date(0), rates: {} }; // Fetch live rates from exchangerate.host const fetchRates = async () => { statusDiv.textContent = 'Fetching live rates...'; const apiUrl = 'https://api.exchangerate.host/latest?base=USD'; // Check cache validity (e.g., 1 day) const cacheDate = new Date(rates.date); const today = new Date(); if ((today.getTime() - cacheDate.getTime()) < (24 * 60 * 60 * 1000)) { statusDiv.textContent = `Using cached rates from ${cacheDate.toLocaleDateString()}.`; manualRateDiv.style.display = 'none'; return true; } try { const response = await fetch(apiUrl); if (!response.ok) throw new Error('API request failed'); const data = await response.json(); if (data.rates) { rates = { base: data.base, date: new Date().toISOString(), rates: data.rates }; localStorage.setItem('currencyRatesCache', JSON.stringify(rates)); statusDiv.textContent = `Live rates updated as of ${new Date(data.date).toLocaleDateString()}.`; manualRateDiv.style.display = 'none'; return true; } throw new Error('No rates in response'); } catch (error) { console.error('Currency API Error:', error); statusDiv.textContent = '⚠️ API failed/CORS error. Using cached/manual rates.'; manualRateDiv.style.display = 'block'; // Populate some basic manual fallback rates if cache is empty if (Object.keys(rates.rates).length === 0) { rates.rates = { USD: 1, EUR: 0.9, INR: 82, JPY: 150 }; // Hardcoded fallbacks statusDiv.textContent += ' (Using hardcoded fallbacks)'; } return false; } }; const convertCurrency = () => { const amount = parseFloat(amountInput.value); const fromCode = fromSelect.value; const toCode = toSelect.value; const manualRate = parseFloat(manualRateInput.value); if (isNaN(amount) || amount <= 0) { return resultDiv.textContent = 'Please enter a valid amount.'; } // If using manual rate if (manualRateDiv.style.display === 'block' && !isNaN(manualRate) && fromCode !== toCode) { const finalAmount = amount * manualRate; return resultDiv.textContent = `${amount} ${fromCode} = ${finalAmount.toFixed(2)} ${toCode} (Manual Rate)`; } // If using API/cached rates const rateFrom = rates.rates[fromCode]; const rateTo = rates.rates[toCode]; if (!rateFrom || !rateTo) { return resultDiv.textContent = `Conversion rate for ${fromCode} or ${toCode} not found in cache. Please try manual input.`; } // Convert to Base (USD) const amountInBase = amount / rateFrom; // Convert to Target const finalAmount = amountInBase * rateTo; resultDiv.textContent = `${amount} ${fromCode} = ${finalAmount.toFixed(2)} ${toCode}`; }; fetchRates(); convertButton.onclick = convertCurrency; amountInput.oninput = () => resultDiv.textContent = ''; // Clear result on input change } // ==================================================================== // TOOL LOGIC: FILE & DOCUMENT // ==================================================================== // --- Tool 13: Merge PDF Files --- async function initializeMergePDF() { const fileInput = document.getElementById('pdf-merge-input'); const fileListDiv = document.getElementById('pdf-merge-file-list'); const mergeButton = document.getElementById('merge-pdfs'); const progressDiv = document.getElementById('pdf-merge-progress'); const progressBar = document.getElementById('merge-progress-bar'); const statusText = document.getElementById('merge-status'); let filesToMerge = []; const updateFileList = () => { filesToMerge = Array.from(fileInput.files); if (filesToMerge.length === 0) { fileListDiv.textContent = 'No files selected.'; mergeButton.disabled = true; } else { fileListDiv.innerHTML = 'Files to Merge:' + filesToMerge.map(f => f.name).join('
'); mergeButton.disabled = filesToMerge.length < 2; } }; const mergePdfs = async () => { if (filesToMerge.length < 2) return; mergeButton.disabled = true; progressDiv.style.display = 'block'; statusText.textContent = 'Starting merge...'; try { // pdf-lib is available globally via CDN const { PDFDocument } = window.pdfLib; const mergedPdf = await PDFDocument.create(); for (let i = 0; i < filesToMerge.length; i++) { const file = filesToMerge[i]; statusText.textContent = `Processing file ${i + 1} of ${filesToMerge.length}: ${file.name}`; // Read the file as an ArrayBuffer const arrayBuffer = await file.arrayBuffer(); // Load the PDF const pdfDoc = await PDFDocument.load(arrayBuffer); // Copy pages to the merged document const copiedPages = await mergedPdf.copyPages(pdfDoc, pdfDoc.getPageIndices()); copiedPages.forEach(page => mergedPdf.addPage(page)); progressBar.value = Math.round(((i + 1) / filesToMerge.length) * 100); } statusText.textContent = 'Finalizing and serializing PDF...'; // Serialize the merged document to a Uint8Array const pdfBytes = await mergedPdf.save(); // Create a Blob and initiate download const blob = new Blob([pdfBytes], { type: 'application/pdf' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = 'merged.pdf'; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); statusText.textContent = 'Merge successful! merged.pdf downloaded.'; } catch (error) { statusText.textContent = `⚠️ Error during merge: ${error.message}`; console.error(error); } finally { mergeButton.disabled = false; progressDiv.style.display = 'none'; progressBar.value = 0; } }; fileInput.onchange = updateFileList; mergeButton.onclick = mergePdfs; } // --- Tool 19: QR Code Generator/Reader --- function initializeQRCodeGenerator() { const inputText = document.getElementById('qr-input-text'); const outputDiv = document.getElementById('qrcode-output'); const generateButton = document.getElementById('generate-qr'); // Note: QR Code Reader logic (using a library like jsQR for camera/file) is omitted for brevity but is feasible client-side. const generateQrCode = () => { outputDiv.innerHTML = ''; const text = inputText.value.trim(); if (!text) { outputDiv.innerHTML = ' '; return; } // qrcode.js is available globally via CDN // The library automatically handles creating a canvas or table element inside the target div try { new window.QRCode(outputDiv, { text: text, width: 200, height: 200, colorDark : DOM.html.classList.contains('dark-mode') ? '#ffffff' : '#000000', colorLight : DOM.html.classList.contains('dark-mode') ? '#2c2c2c' : '#ffffff', correctLevel : window.QRCode.CorrectLevel.H }); } catch (e) { outputDiv.innerHTML = ' '; console.error(e); } }; generateButton.onclick = generateQrCode; } // ==================================================================== // TOOL LOGIC: TEXT & WRITING // ==================================================================== // --- Tool 31: Text Case Converter --- function initializeTextCaseConverter() { window.textCaseConverter = (caseType) => { const input = document.getElementById('case-input').value; const outputDiv = document.getElementById('case-output'); let output = ''; switch (caseType) { case 'upper': output = input.toUpperCase(); break; case 'lower': output = input.toLowerCase(); break; case 'title': // Simple Title Case: capitalize the first letter of every word output = input.toLowerCase().split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1) ).join(' '); break; case 'sentence': // Sentence Case: capitalize the first letter of the first word output = input.toLowerCase().charAt(0).toUpperCase() + input.toLowerCase().slice(1); break; default: output = input; } outputDiv.textContent = output; }; } // --- Tool 34: Notes / Notepad Online --- function initializeNotes() { const notesTextarea = document.getElementById('notes-content'); const statusDiv = document.getElementById('notes-status'); const importInput = document.getElementById('import-notes-file'); // Load notes from localStorage notesTextarea.value = localStorage.getItem('notes-content') || ''; // Auto-save on input notesTextarea.oninput = () => { localStorage.setItem('notes-content', notesTextarea.value); statusDiv.textContent = 'Notes saved automatically!'; setTimeout(() => statusDiv.textContent = '', 2000); }; // Export Notes window.exportNotes = () => { const content = notesTextarea.value; const blob = new Blob([content], { type: 'text/plain' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'notes.txt'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); }; // Import Notes importInput.onchange = (e) => { const file = e.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (event) => { notesTextarea.value = event.target.result; localStorage.setItem('notes-content', notesTextarea.value); statusDiv.textContent = 'Notes imported successfully!'; setTimeout(() => statusDiv.textContent = '', 2000); }; reader.onerror = () => { statusDiv.textContent = 'Error importing file.'; }; reader.readAsText(file); }; } // ==================================================================== // TOOL LOGIC: CODING & WEB // ==================================================================== // --- Tool 42: JSON Formatter / Validator --- function initializeJsonFormatter() { const input = document.getElementById('json-input'); const output = document.getElementById('json-output'); const button = document.getElementById('format-json'); button.onclick = () => { const jsonString = input.value.trim(); try { const jsonObject = JSON.parse(jsonString); const formattedJson = JSON.stringify(jsonObject, null, 2); output.textContent = formattedJson; output.style.color = 'var(--color-success)'; } catch (e) { // Attempt to extract the error position for better user feedback const errorMatch = e.message.match(/at position (\d+)/); let positionInfo = ''; if (errorMatch) { const position = parseInt(errorMatch[1]); positionInfo = ` (Error near character position: ${position})`; } output.textContent = `JSON Validation Error: ${e.message}${positionInfo}`; output.style.color = 'var(--color-error)'; } }; } // --- Tool 43: Base64 Encode / Decode --- function initializeBase64Converter() { const textInput = document.getElementById('base64-input'); const outputDiv = document.getElementById('base64-output'); const fileInput = document.getElementById('base64-file-input'); const fileEncodeButton = document.getElementById('base64-file-encode'); const downloadButton = document.getElementById('base64-download-decoded'); const setTextOutput = (text, isError = false) => { outputDiv.textContent = text; outputDiv.style.color = isError ? 'var(--color-error)' : 'var(--color-text)'; downloadButton.style.display = 'none'; } // Text/Base64 String Processor window.base64Process = (action) => { const content = textInput.value.trim(); if (!content) return setTextOutput('Please enter content.', true); if (action === 'encode') { setTextOutput(btoa(content)); } else if (action === 'decode') { try { const decoded = atob(content); // Check if the decoded string looks like plain text const isPlainText = /^[ -~]*$/.test(decoded); if (isPlainText) { setTextOutput(decoded); } else { // Non-printable characters suggests binary data setTextOutput(`Decoded successful. Detected non-text/binary data (${decoded.length} bytes). Use the download button.`); prepareDownload(decoded, 'decoded_file.bin'); } } catch (e) { setTextOutput('Invalid Base64 string for decoding.', true); } } }; // File Encoder fileInput.onchange = () => { fileEncodeButton.disabled = fileInput.files.length === 0; }; fileEncodeButton.onclick = () => { const file = fileInput.files[0]; if (!file) return setTextOutput('Please select a file.', true); const reader = new FileReader(); reader.onload = (e) => { // Get the raw data buffer const binaryString = e.target.result; // Encode to Base64 const base64 = btoa(binaryString); textInput.value = base64; // Put result back in textarea setTextOutput('File successfully encoded to Base64. Result in input box.'); }; reader.onerror = () => setTextOutput('Error reading file.', true); // Read the file as a binary string (necessary for btoa on file data) reader.readAsBinaryString(file); }; // Helper for downloading decoded binary/text const prepareDownload = (data, filename) => { let blob; if (typeof data === 'string') { // For decoded binary string, need to convert to Uint8Array/Blob const charCodeArray = data.split('').map(c => c.charCodeAt(0)); blob = new Blob([new Uint8Array(charCodeArray)], { type: 'application/octet-stream' }); } else { blob = new Blob([data], { type: 'application/octet-stream' }); } const url = URL.createObjectURL(blob); downloadButton.href = url; downloadButton.download = filename; downloadButton.style.display = 'block'; }; downloadButton.onclick = (e) => { // The URL is already set, just clean up after click setTimeout(() => URL.revokeObjectURL(downloadButton.href), 100); } } // --- Tool 48: IP Address Finder --- function initializeIPFinder() { const resultDiv = document.getElementById('ip-result'); const findButton = document.getElementById('find-ip'); const findIP = async () => { resultDiv.innerHTML = 'Looking up public IP...'; findButton.disabled = true; const ipApiUrl = 'https://api.ipify.org?format=json'; const geoApiUrl = (ip) => `https://ipapi.co/${ip}/json/`; // Free, CORS-friendly Geo API try { // 1. Get Public IP const ipResponse = await fetch(ipApiUrl); if (!ipResponse.ok) throw new Error('Failed to get IP address.'); const ipData = await ipResponse.json(); const ipAddress = ipData.ip; resultDiv.innerHTML = `Found IP: ${ipAddress}. Fetching geolocation...`; // 2. Get Geolocation const geoResponse = await fetch(geoApiUrl(ipAddress)); const geoData = await geoResponse.json(); if (geoData.error) { resultDiv.innerHTML = `IP Address: ${ipAddress}
Geolocation: ${geoData.reason || 'Failed to fetch geolocation data.'}`; return; } resultDiv.innerHTML = ` Public IP: ${geoData.ip}
Country: ${geoData.country_name} (${geoData.country_code})
City: ${geoData.city}, ${geoData.region}
ISP: ${geoData.org}
Data from ipify.org & ipapi.co `; } catch (error) { console.error(error); resultDiv.innerHTML = `⚠️ Error fetching IP: ${error.message}. The service might be temporarily unavailable or blocked by CORS.`; } finally { findButton.disabled = false; } }; findButton.onclick = findIP; } // --- APPLICATION STARTUP --- function initApp() { loadPreferences(); // Optional: Auto-focus search on desktop if (window.innerWidth >= 600) { DOM.toolSearch.focus(); } } document.addEventListener('DOMContentLoaded', initApp);