| <!DOCTYPE html> |
| <html lang="id"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> |
| <title>GPT-5</title> |
|
|
| <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"> |
| <meta http-equiv="Pragma" content="no-cache"> |
| <meta http-equiv="Expires" content="0"> |
|
|
| <style> |
| html, body { margin: 0; padding: 0; width: 100%; height: 100%; } |
| #loading { |
| position: fixed; inset: 0; background: rgba(255,255,255,0.9); |
| display: flex; justify-content: center; align-items: center; |
| font-size: 18px; z-index: 1000; |
| } |
| iframe { width: 100%; height: 100vh; border: none; } |
| </style> |
| </head> |
| <body> |
| <div id="loading">Please wait... Memilih server optimal...</div> |
| <iframe id="streamlit-frame" referrerpolicy="no-referrer"></iframe> |
|
|
| <script> |
| const VERSION = "v1"; |
| const SERVERS = [ |
| "https://gepatest-hwak3xkal5nltlveztf5nn.streamlit.app/?embed=true" |
| ]; |
| |
| |
| const pingTimeoutMs = 5000; |
| const iframeLoadTimeoutMs = 12000; |
| const overallTryTimeoutMs = 20000; |
| |
| |
| function ping(url, timeout = pingTimeoutMs) { |
| return new Promise((resolve) => { |
| const start = performance.now(); |
| const img = new Image(); |
| let done = false; |
| |
| const timer = setTimeout(() => { |
| if (done) return; |
| done = true; |
| |
| resolve({ url, ok: false, latency: Infinity }); |
| img.src = ""; |
| }, timeout); |
| |
| const onComplete = (ok) => { |
| if (done) return; |
| done = true; |
| clearTimeout(timer); |
| const latency = performance.now() - start; |
| resolve({ url, ok, latency }); |
| }; |
| |
| img.onload = () => onComplete(true); |
| img.onerror = () => onComplete(false); |
| |
| |
| |
| |
| const u = new URL(url); |
| u.searchParams.set("cb", Date.now().toString()); |
| img.src = u.toString(); |
| }); |
| } |
| |
| async function rankServersByPing(urls) { |
| const results = await Promise.all(urls.map(u => ping(u))); |
| |
| return results |
| .sort((a,b) => a.latency - b.latency) |
| .map(r => r.url); |
| } |
| |
| function setIframeSrc(iframe, baseUrl) { |
| const u = new URL(baseUrl); |
| u.searchParams.set("cb", Date.now().toString()); |
| iframe.src = u.toString(); |
| } |
| |
| |
| function tryLoadServer(iframe, url) { |
| return new Promise((resolve) => { |
| let settled = false; |
| |
| |
| const hardTimer = setTimeout(() => { |
| if (settled) return; |
| settled = true; |
| resolve(false); |
| }, overallTryTimeoutMs); |
| |
| |
| const readyTimer = setTimeout(() => { |
| if (settled) return; |
| |
| settled = true; |
| clearTimeout(hardTimer); |
| resolve(false); |
| }, iframeLoadTimeoutMs); |
| |
| |
| iframe.onload = () => { |
| |
| |
| |
| |
| setTimeout(() => { |
| if (settled) return; |
| settled = true; |
| clearTimeout(readyTimer); |
| clearTimeout(hardTimer); |
| resolve(true); |
| }, 1500); |
| }; |
| |
| |
| iframe.onerror = () => { |
| if (settled) return; |
| settled = true; |
| clearTimeout(readyTimer); |
| clearTimeout(hardTimer); |
| resolve(false); |
| }; |
| |
| setIframeSrc(iframe, url); |
| }); |
| } |
| |
| async function loadWithFallback() { |
| const iframe = document.getElementById("streamlit-frame"); |
| const loading = document.getElementById("loading"); |
| const storageKey = `streamlitUrl_${VERSION}`; |
| |
| |
| const cached = sessionStorage.getItem(storageKey); |
| |
| |
| let prioritized = await rankServersByPing(SERVERS); |
| |
| |
| if (cached && prioritized.includes(cached)) { |
| prioritized = [cached, ...prioritized.filter(u => u !== cached)]; |
| } |
| |
| for (const url of prioritized) { |
| const ok = await tryLoadServer(iframe, url); |
| if (ok) { |
| |
| sessionStorage.setItem(storageKey, url); |
| loading.style.display = "none"; |
| |
| iframe.addEventListener('error', () => { |
| sessionStorage.removeItem(storageKey); |
| }); |
| return; |
| } |
| } |
| |
| |
| sessionStorage.removeItem(storageKey); |
| loading.textContent = "Semua server gagal dimuat. Coba refresh halaman."; |
| } |
| |
| window.addEventListener("load", loadWithFallback); |
| </script> |
| </body> |
| </html> |