|
| 1 | +<!DOCTYPE html> |
| 2 | +<html lang="en"> |
| 3 | +<head> |
| 4 | + <meta charset="UTF-8"> |
| 5 | + <title>Perchance Generator – Fixed with CORS proxy</title> |
| 6 | + <style> |
| 7 | + body { font-family: system-ui, Arial, sans-serif; max-width: 800px; margin: 2rem auto; padding: 0 20px; } |
| 8 | + #output { padding: 1rem; border: 1px solid #ccc; border-radius: 12px; min-height: 80px; margin: 1rem 0; background: #f9f9fc; } |
| 9 | + button, input { padding: 8px 14px; font-size: 1rem; border-radius: 40px; border: 1px solid #aaa; } |
| 10 | + button { background: #1e2f3e; color: white; cursor: pointer; transition: 0.2s; } |
| 11 | + button:hover { background: #0f1e2a; } |
| 12 | + .error { color: #c33; background: #ffe8e6; padding: 0.5rem; border-radius: 12px; } |
| 13 | + .success { background: #e0f2e9; padding: 0.5rem; border-radius: 12px; } |
| 14 | + hr { margin: 1.5rem 0; } |
| 15 | + code { background: #eee; padding: 0.2rem 0.4rem; border-radius: 8px; } |
| 16 | + small { display: block; margin-top: 1rem; color: #555; } |
| 17 | + </style> |
| 18 | +</head> |
| 19 | +<body> |
| 20 | +<h1>🎲 Perchance Generator (CORS fixed)</h1> |
| 21 | +<p>Uses <code>corsproxy.io</code> to bypass CORS restrictions.</p> |
| 22 | + |
| 23 | +<label for="generatorInput"><strong>Generator name:</strong> (e.g. <code>fantasy-character</code>, <code>tavern-names</code>)</label><br> |
| 24 | +<input type="text" id="generatorInput" value="fantasy-character" style="width: 70%; margin-top: 6px;"> |
| 25 | + |
| 26 | +<div id="output">✨ Click "Generate" to see a random result.</div> |
| 27 | +<button id="generateBtn">Generate</button> |
| 28 | + |
| 29 | +<hr> |
| 30 | +<small>🔧 Proxy: <code>https://corsproxy.io/</code> → forwards request to Perchance API. No backend required.</small> |
| 31 | + |
| 32 | +<script> |
| 33 | + const generateBtn = document.getElementById('generateBtn'); |
| 34 | + const generatorInput = document.getElementById('generatorInput'); |
| 35 | + const outputDiv = document.getElementById('output'); |
| 36 | + |
| 37 | + async function fetchFromPerchance() { |
| 38 | + const generatorName = generatorInput.value.trim(); |
| 39 | + if (!generatorName) { |
| 40 | + outputDiv.innerHTML = '<span class="error">❌ Please enter a generator name.</span>'; |
| 41 | + return; |
| 42 | + } |
| 43 | + |
| 44 | + // Direct API URL (without proxy – would cause CORS error) |
| 45 | + const directApiUrl = `https://perchance.org/api/generate?generator=${encodeURIComponent(generatorName)}&count=1`; |
| 46 | + |
| 47 | + // CORS proxy that works with perchance.org |
| 48 | + const proxyUrl = `https://corsproxy.io/?${encodeURIComponent(directApiUrl)}`; |
| 49 | + |
| 50 | + generateBtn.disabled = true; |
| 51 | + generateBtn.textContent = '⏳ Loading...'; |
| 52 | + outputDiv.innerHTML = '🌀 Fetching via proxy...'; |
| 53 | + |
| 54 | + try { |
| 55 | + const controller = new AbortController(); |
| 56 | + const timeoutId = setTimeout(() => controller.abort(), 10000); |
| 57 | + const response = await fetch(proxyUrl, { signal: controller.signal }); |
| 58 | + clearTimeout(timeoutId); |
| 59 | + |
| 60 | + if (!response.ok) { |
| 61 | + throw new Error(`HTTP ${response.status}: ${response.statusText}`); |
| 62 | + } |
| 63 | + |
| 64 | + const data = await response.json(); |
| 65 | + |
| 66 | + if (Array.isArray(data) && data.length > 0 && typeof data[0] === 'string') { |
| 67 | + outputDiv.innerHTML = `<div class="success">✨ ${escapeHtml(data[0])}</div>`; |
| 68 | + } else { |
| 69 | + outputDiv.innerHTML = '<span class="error">⚠️ Unexpected response format. Try another generator.</span>'; |
| 70 | + } |
| 71 | + } catch (err) { |
| 72 | + console.error(err); |
| 73 | + let msg = err.message; |
| 74 | + if (err.name === 'AbortError') msg = 'Timeout – proxy/server took too long.'; |
| 75 | + else if (msg.includes('Failed to fetch')) msg = 'Network error – proxy may be blocked. Try reloading or use a different proxy.'; |
| 76 | + outputDiv.innerHTML = `<span class="error">❌ ${escapeHtml(msg)}</span>`; |
| 77 | + } finally { |
| 78 | + generateBtn.disabled = false; |
| 79 | + generateBtn.textContent = 'Generate'; |
| 80 | + } |
| 81 | + } |
| 82 | + |
| 83 | + function escapeHtml(str) { |
| 84 | + return str.replace(/[&<>]/g, function(m) { |
| 85 | + if (m === '&') return '&'; |
| 86 | + if (m === '<') return '<'; |
| 87 | + if (m === '>') return '>'; |
| 88 | + return m; |
| 89 | + }); |
| 90 | + } |
| 91 | + |
| 92 | + generateBtn.addEventListener('click', fetchFromPerchance); |
| 93 | + generatorInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') fetchFromPerchance(); }); |
| 94 | +</script> |
| 95 | +</body> |
| 96 | +</html> |
0 commit comments