2026-07-15 15:26:23 +02:00
|
|
|
<!DOCTYPE html>
|
|
|
|
|
<html lang="da">
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
<title>Kodegenerator</title>
|
|
|
|
|
<style>
|
|
|
|
|
* { box-sizing: border-box; margin: 0; }
|
|
|
|
|
body { font-family: -apple-system, sans-serif; background: #f5f5f5; padding: 2rem; }
|
|
|
|
|
.container { max-width: 800px; margin: 0 auto; }
|
|
|
|
|
h1 { margin-bottom: 1.5rem; }
|
|
|
|
|
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
|
|
|
|
|
.card { background: #fff; border-radius: 8px; padding: 1.5rem; box-shadow: 0 1px 3px rgba(0,0,0,.1); }
|
|
|
|
|
.card h2 { font-size: 1rem; margin-bottom: .75rem; }
|
|
|
|
|
textarea { width: 100%; height: 200px; font-family: monospace; font-size: .9rem; padding: .5rem; border: 1px solid #ccc; border-radius: 4px; resize: vertical; }
|
|
|
|
|
label { display: flex; align-items: center; gap: .5rem; margin-bottom: .5rem; font-size: .9rem; }
|
|
|
|
|
input[type="number"] { width: 70px; padding: .3rem; border: 1px solid #ccc; border-radius: 4px; }
|
|
|
|
|
.controls { display: flex; gap: .75rem; flex-wrap: wrap; align-items: center; margin: 1rem 0; }
|
|
|
|
|
button { padding: .6rem 1.2rem; border: none; border-radius: 6px; cursor: pointer; font-size: .9rem; }
|
|
|
|
|
.btn-primary { background: #2563eb; color: #fff; }
|
|
|
|
|
.btn-primary:hover { background: #1d4ed8; }
|
|
|
|
|
.btn-secondary { background: #6b7280; color: #fff; }
|
|
|
|
|
.btn-secondary:hover { background: #4b5563; }
|
2026-07-15 16:34:13 +02:00
|
|
|
.btn-danger { background: #dc2626; color: #fff; }
|
|
|
|
|
.btn-danger:hover { background: #b91c1c; }
|
2026-07-15 15:26:23 +02:00
|
|
|
#output { width: 100%; height: 300px; font-family: monospace; font-size: .9rem; padding: .5rem; border: 1px solid #ccc; border-radius: 4px; resize: vertical; background: #fafafa; }
|
|
|
|
|
.info { font-size: .8rem; color: #666; margin-top: .5rem; }
|
|
|
|
|
</style>
|
|
|
|
|
</head>
|
|
|
|
|
<body>
|
|
|
|
|
<div class="container">
|
|
|
|
|
<h1>Kodegenerator</h1>
|
|
|
|
|
<div class="grid">
|
|
|
|
|
<div class="card">
|
|
|
|
|
<h2>Ordbog (kun navneord)</h2>
|
|
|
|
|
<textarea id="words" placeholder="Vent på indlæsning..." readonly></textarea>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="card">
|
|
|
|
|
<h2>Opsætning</h2>
|
|
|
|
|
<p style="font-size:.85rem;color:#666;margin-bottom:.75rem">Format: Ord1 + Ord2 + suffix</p>
|
|
|
|
|
<label>Separator mellem ord: <input type="text" id="separator" value="" style="width:30px;padding:.3rem;border:1px solid #ccc;border-radius:4px"></label>
|
2026-07-15 16:34:13 +02:00
|
|
|
<label>Suffix længde: <input type="number" id="suffixLen" value="3" min="1" max="10"></label>
|
2026-07-15 15:26:23 +02:00
|
|
|
<label><input type="checkbox" id="useSymbols" checked> Specialtegn (!@#$%&*+-=?)</label>
|
|
|
|
|
<label><input type="checkbox" id="useDigits" checked> Tal (0-9)</label>
|
|
|
|
|
<label>Antal koder: <input type="number" id="count" value="3" min="1" max="20"></label>
|
|
|
|
|
<div class="controls">
|
|
|
|
|
<button class="btn-primary" onclick="generate()">Generer</button>
|
|
|
|
|
<button class="btn-secondary" onclick="copy()">Kopiér</button>
|
|
|
|
|
<button class="btn-secondary" onclick="clearOutput()">Ryd</button>
|
2026-07-15 16:34:13 +02:00
|
|
|
<button class="btn-danger" onclick="resetAll()">Nulstil</button>
|
2026-07-15 15:26:23 +02:00
|
|
|
</div>
|
|
|
|
|
<div class="info" id="status"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<textarea id="output" placeholder="Genererede koder vises her..." readonly></textarea>
|
|
|
|
|
</div>
|
|
|
|
|
<script>
|
|
|
|
|
async function loadWords() {
|
|
|
|
|
const el = document.getElementById('words');
|
|
|
|
|
try {
|
|
|
|
|
const res = await fetch('ordliste.txt');
|
|
|
|
|
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
|
|
|
const text = await res.text();
|
|
|
|
|
el.value = text.trim();
|
|
|
|
|
el.readOnly = false;
|
|
|
|
|
el.placeholder = 'Et ord pr. linje';
|
|
|
|
|
document.getElementById('status').textContent = 'Indlæst ' + text.trim().split('\n').length + ' ord';
|
|
|
|
|
} catch (e) {
|
|
|
|
|
el.placeholder = 'Kunne ikke indlæse ordliste.txt (' + e.message + ')';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
loadWords();
|
|
|
|
|
|
|
|
|
|
const SYMBOLS = '!@#$%&*+-=?';
|
|
|
|
|
const DIGITS = '0123456789';
|
|
|
|
|
function rand(n) { return Math.floor(Math.random() * n); }
|
|
|
|
|
function getSuffixCharset() {
|
|
|
|
|
const useSym = document.getElementById('useSymbols').checked;
|
|
|
|
|
const useDig = document.getElementById('useDigits').checked;
|
|
|
|
|
let s = '';
|
|
|
|
|
if (useSym) s += SYMBOLS;
|
|
|
|
|
if (useDig) s += DIGITS;
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function randomSuffix(len, charset) {
|
|
|
|
|
let r = '';
|
|
|
|
|
for (let i = 0; i < len; i++) r += charset[rand(charset.length)];
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function generate() {
|
|
|
|
|
const words = document.getElementById('words').value.split('\n').map(w => w.trim()).filter(w => w);
|
|
|
|
|
let suffixLen = parseInt(document.getElementById('suffixLen').value, 10);
|
2026-07-15 16:34:13 +02:00
|
|
|
if (isNaN(suffixLen) || suffixLen < 1) suffixLen = 3;
|
2026-07-15 15:26:23 +02:00
|
|
|
const countEl = document.getElementById('count');
|
|
|
|
|
let count = parseInt(countEl.value, 10);
|
|
|
|
|
if (isNaN(count) || count < 1) count = 3;
|
|
|
|
|
const sep = document.getElementById('separator').value;
|
|
|
|
|
const output = document.getElementById('output');
|
|
|
|
|
const charset = getSuffixCharset();
|
|
|
|
|
const status = document.getElementById('status');
|
|
|
|
|
|
|
|
|
|
if (!words.length) { output.value = 'Tilføj mindst ét ord i ordbogen.'; status.textContent = ''; return; }
|
|
|
|
|
if (words.length < 2) { output.value = 'Brug mindst 2 ord i ordbogen.'; status.textContent = ''; return; }
|
|
|
|
|
if (!charset.length) { output.value = 'Vælg mindst én suffix-type (specialtegn eller tal).'; status.textContent = ''; return; }
|
|
|
|
|
|
|
|
|
|
const codes = [];
|
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
|
|
|
let w1, w2;
|
|
|
|
|
do { w1 = words[rand(words.length)]; w2 = words[rand(words.length)]; } while (w1 === w2);
|
|
|
|
|
codes.push(w1 + sep + w2 + randomSuffix(suffixLen, charset));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output.value = codes.join('\n');
|
|
|
|
|
status.textContent = `Genereret ${count} koder`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function copy() {
|
|
|
|
|
const el = document.getElementById('output');
|
|
|
|
|
if (!el.value) return;
|
|
|
|
|
navigator.clipboard.writeText(el.value).then(() => {
|
|
|
|
|
document.getElementById('status').textContent = 'Kopieret til udklipsholder';
|
|
|
|
|
}).catch(() => {
|
|
|
|
|
el.select();
|
|
|
|
|
document.execCommand('copy');
|
|
|
|
|
document.getElementById('status').textContent = 'Kopieret';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function clearOutput() {
|
|
|
|
|
document.getElementById('output').value = '';
|
|
|
|
|
document.getElementById('status').textContent = '';
|
|
|
|
|
}
|
2026-07-15 16:34:13 +02:00
|
|
|
|
|
|
|
|
function resetAll() {
|
|
|
|
|
document.getElementById('separator').value = '';
|
|
|
|
|
document.getElementById('suffixLen').value = 3;
|
|
|
|
|
document.getElementById('useSymbols').checked = true;
|
|
|
|
|
document.getElementById('useDigits').checked = true;
|
|
|
|
|
document.getElementById('count').value = 3;
|
|
|
|
|
document.getElementById('output').value = '';
|
|
|
|
|
document.getElementById('status').textContent = 'Nulstillet';
|
|
|
|
|
loadWords();
|
|
|
|
|
}
|
2026-07-15 15:26:23 +02:00
|
|
|
</script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|