'use strict'; const els = { statusDot: document.getElementById('statusDot'), statusText: document.getElementById('statusText'), ipValue: document.getElementById('ipValue'), fetchedAt: document.getElementById('fetchedAt'), refreshBtn: document.getElementById('refreshBtn'), originCard: document.getElementById('originHeadersCard'), originBody: document.getElementById('originHeadersBody'), originNote: document.getElementById('originNote'), echoCard: document.getElementById('echoHeadersCard'), echoBody: document.getElementById('echoHeadersBody'), }; const ECHO_ENDPOINT = 'https://httpbingo.org/get'; const TIMEOUT_MS = 8000; function withTimeout(ms) { const controller = new AbortController(); const id = setTimeout(() => controller.abort(), ms); return { signal: controller.signal, cancel: () => clearTimeout(id) }; } function setStatus(state, text) { els.statusDot.className = 'dot ' + state; els.statusText.textContent = text; } function fillTable(tbody, entries) { tbody.innerHTML = ''; entries .slice() .sort((a, b) => a[0].localeCompare(b[0])) .forEach(([key, value]) => { const tr = document.createElement('tr'); const tdKey = document.createElement('td'); tdKey.className = 'h-key'; tdKey.textContent = key; const tdVal = document.createElement('td'); tdVal.className = 'h-val'; tdVal.textContent = value; tr.append(tdKey, tdVal); tbody.appendChild(tr); }); } async function loadOriginHeaders() { const t = withTimeout(TIMEOUT_MS); try { const res = await fetch(window.location.href, { method: 'HEAD', cache: 'no-store', signal: t.signal, headers: { 'Pragma': 'akamai-x-cache-on, akamai-x-cache-remote-on, akamai-x-check-cacheable, akamai-x-get-cache-key, akamai-x-get-true-cache-key' } }); fillTable(els.originBody, Array.from(res.headers.entries())); els.originNote.textContent = 'HTTP ' + res.status + ' — Actual response headers obtained via a same-origin HEAD request to this page itself.'; els.originCard.hidden = false; } catch (err) { els.originNote.textContent = 'Fetch failed (' + err.message + ')'; els.originCard.hidden = false; console.error('origin headers fetch failed:', err); } finally { t.cancel(); } } async function loadEchoInfo() { const t = withTimeout(TIMEOUT_MS); try { const res = await fetch(ECHO_ENDPOINT, { cache: 'no-store', signal: t.signal }); if (!res.ok) throw new Error('HTTP ' + res.status); const data = await res.json(); els.ipValue.textContent = data.origin || 'Could not fetch'; fillTable(els.echoBody, Object.entries(data.headers || {})); els.echoCard.hidden = false; setStatus('ok', 'Fetch complete'); } catch (err) { els.ipValue.textContent = 'Could not fetch'; setStatus('err', 'Failed to reach the external echo API (httpbingo.org)'); console.error('echo fetch failed:', err); } finally { t.cancel(); // 日付の書式も英語(米国)基準に変更 els.fetchedAt.textContent = new Date().toLocaleString('en-US'); } } async function loadFileList() { const listContainer = document.getElementById('fileList'); try { const res = await fetch('files/files.json?t=' + Date.now()); if (!res.ok) throw new Error('HTTP ' + res.status); const files = await res.json(); if (files.length === 0) { listContainer.innerHTML = '
  • No files found
  • '; return; } listContainer.innerHTML = files.map(fileName => { const fileUrl = `files/${fileName}`; return `
  • ${fileName}
  • `; }).join(''); } catch (err) { console.error('Manifest fetch failed:', err); listContainer.innerHTML = '
  • Failed to fetch the file list
  • '; } } function renderBrowserInfo() { document.getElementById('uaValue').textContent = navigator.userAgent; document.getElementById('langValue').textContent = (navigator.languages && navigator.languages.join(', ')) || navigator.language; document.getElementById('screenValue').textContent = screen.width + '×' + screen.height + ' (DPR ' + (window.devicePixelRatio || 1) + ')'; document.getElementById('tzValue').textContent = Intl.DateTimeFormat().resolvedOptions().timeZone; } function loadAll() { setStatus('loading', 'Fetching...'); els.echoCard.hidden = true; loadOriginHeaders(); loadEchoInfo(); loadFileList(); } els.refreshBtn.addEventListener('click', loadAll); renderBrowserInfo(); loadAll();