`).join('');
}
function logEntry(data) {
const timestamp = new Date().toLocaleString('en-US');
const logItem = {
timestamp,
ip: data.query,
location: `${data.city || 'N/A'}, ${data.country || 'N/A'}`,
isp: data.isp || 'N/A',
fullData: data // Store full data for PDF log
};
lookupLog.unshift(logItem);
if (lookupLog.length > 50) lookupLog.pop(); // Limit log size
saveState();
renderLog();
}
function renderResults(data) {
currentResult = data;
ui.resIp.textContent = escapeHTML(data.query);
ui.resLocation.textContent = `${escapeHTML(data.city)}, ${escapeHTML(data.regionName)}, ${escapeHTML(data.country)}`;
ui.resIsp.textContent = escapeHTML(data.isp);
ui.resOrg.textContent = escapeHTML(data.org);
const mapLink = `https://maps.google.com/?q=${data.lat},${data.lon}`;
ui.resMap.innerHTML = `
${mapLink}`;
ui.loader.style.display = 'none';
ui.resultsContainer.style.display = 'block';
}
function showMessage(message, isError = true) {
ui.errorMsg.textContent = message;
ui.errorMsg.style.display = 'block';
}
// --- API LOGIC ---
async function fetchIpData(ip = '') {
ui.loader.style.display = 'block';
ui.resultsContainer.style.display = 'none';
ui.errorMsg.style.display = 'none';
try {
// Using http://ip-api.com, which is free and has CORS enabled.
// Note: The free endpoint does not support HTTPS.
const response = await fetch(`http://ip-api.com/json/${encodeURIComponent(ip)}`);
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}`);
}
const data = await response.json();
if (data.status === 'fail') {
throw new Error(data.message);
}
renderResults(data);
logEntry(data);
} catch (error) {
console.error("IP Geolocation Fetch Error:", error);
showMessage(error.message);
} finally {
ui.loader.style.display = 'none';
}
}
// --- PDF LOGIC ---
function downloadPDF(type) {
const { jsPDF } = window.jspdf;
if (!jsPDF || !window.jspdf.autoTable) { alert("PDF library not loaded!"); return; }
const doc = new jsPDF();
if (type === 'current') {
if (!currentResult.query) {
alert("Please run a lookup before downloading.");
return;
}
doc.setFontSize(18);
doc.text(`IP Geolocation Report: ${currentResult.query}`, 14, 22);
doc.autoTable({
startY: 30,
theme: 'grid',
body: [
['IP Address', currentResult.query],
['Location', `${currentResult.city}, ${currentResult.regionName}, ${currentResult.country}`],
['Coordinates', `${currentResult.lat}, ${currentResult.lon}`],
['ISP', currentResult.isp],
['Organization', currentResult.org],
]
});
doc.save(`IP_Report_${currentResult.query}.pdf`);
} else if (type === 'log') {
if (lookupLog.length === 0) {
alert("Log is empty. Please perform a lookup first.");
return;
}
doc.setFontSize(18);
doc.text("IP Geolocation Lookup Log", 14, 22);
doc.autoTable({
startY: 30,
head: [['Timestamp', 'IP Address', 'Location', 'ISP']],
body: lookupLog.map(log => [log.timestamp, log.ip, log.location, log.isp]),
theme: 'striped',
headStyles: { fillColor: [8, 145, 178] } // var(--primary-color)
});
doc.save("IP_Geolocation_Log.pdf");
}
}
// --- EVENT LISTENERS ---
ui.form.addEventListener('submit', e => {
e.preventDefault();
const ip = ui.input.value.trim();
fetchIpData(ip); // API handles empty string as "my IP"
});
ui.findMeBtn.addEventListener('click', () => {
ui.input.value = '';
fetchIpData('');
});
ui.downloadReportBtn.addEventListener('click', () => downloadPDF('current'));
ui.downloadLogBtn.addEventListener('click', () => downloadPDF('log'));
ui.clearLogBtn.addEventListener('click', () => {
lookupLog = [];
saveState();
renderLog();
});
// --- TABBING LOGIC ---
function switchTab(targetTabId) {
ui.tabContents.forEach(c => c.classList.remove('active'));
ui.tabButtons.forEach(b => b.classList.remove('active'));
document.getElementById(`ipg-tab-${targetTabId}`).classList.add('active');
document.querySelector(`.ipg-tab-button[data-tab="${targetTabId}"]`).classList.add('active');
}
function navigateTabs(dir) {
const tabs = Array.from(ui.tabButtons);
const active = tabs.find(t => t.classList.contains('active'));
let index = tabs.indexOf(active) + dir;
if (index < 0) index = 0; if (index >= tabs.length) index = tabs.length - 1;
switchTab(tabs[index].dataset.tab);
}
ui.tabButtons.forEach(button => button.addEventListener('click', () => switchTab(button.dataset.tab)));
ui.nextTabBtn.addEventListener('click', () => navigateTabs(1));
ui.prevTabBtn.addEventListener('click', () => navigateTabs(-1));
// --- UTILITY ---
function escapeHTML(str) {
if (typeof str !== 'string') return '';
const p = document.createElement('p');
p.textContent = str;
return p.innerHTML;
}
// --- INITIALIZATION ---
loadState();
renderLog();
});