Files
wordle-cheater/templates/index.html

466 lines
29 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>WordleCheater (DE)</title>
<meta name="description" content="WordleCheater: Finde deutsche 5BuchstabenWörter anhand bekannter Buchstaben und Positionen. Quellen: OpenThesaurus und wordfreq." />
<meta name="robots" content="index,follow" />
<link rel="canonical" href="{{ request.url_root }}" />
<meta property="og:type" content="website" />
<meta property="og:title" content="WordleCheater (DE)" />
<meta property="og:description" content="Finde deutsche 5BuchstabenWörter mit Positions- und Buchstabenfiltern. Quellen: OpenThesaurus & wordfreq." />
<meta property="og:url" content="{{ request.url_root }}" />
<meta property="og:site_name" content="WordleCheater (DE)" />
<meta property="og:locale" content="de_DE" />
<meta property="og:image" content="{{ url_for('screenshot_image', _external=True) }}" />
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content="WordleCheater (DE)" />
<meta name="twitter:description" content="Finde deutsche 5BuchstabenWörter mit Positions- und Buchstabenfiltern. Quellen: OpenThesaurus & wordfreq." />
<meta name="twitter:image" content="{{ url_for('screenshot_image', _external=True) }}" />
<link rel="alternate" hreflang="de" href="{{ request.url_root }}" />
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}" />
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#0b1220" />
<meta name="color-scheme" content="light dark" />
<script>
(function() {
try {
var saved = localStorage.getItem('theme');
var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
var theme = saved || (prefersDark ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', theme);
} catch (e) {}
})();
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "WordleCheater (DE)",
"url": "{{ request.url_root }}",
"applicationCategory": "UtilitiesApplication",
"operatingSystem": "Web",
"description": "Finde deutsche 5BuchstabenWörter anhand bekannter Buchstaben und Positionen. Quellen: OpenThesaurus & wordfreq.",
"inLanguage": "de",
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "EUR" }
}
</script>
<style>
:root { --bg:#ffffff; --text:#111827; --muted:#6b7280; --badge-bg:#e5e7eb; --badge-text:#111827; --border:#e5e7eb; --skip-bg:#111827; --skip-text:#ffffff; --button-bg:#111827; --button-text:#ffffff; --input-bg:#ffffff; --input-text:#111827; --error:#b91c1c; }
[data-theme="dark"] { --bg:#0b1220; --text:#e5e7eb; --muted:#9ca3af; --badge-bg:#374151; --badge-text:#f9fafb; --border:#334155; --skip-bg:#e5e7eb; --skip-text:#111827; --button-bg:#e5e7eb; --button-text:#111827; --input-bg:#111827; --input-text:#e5e7eb; --error:#ef4444; }
html, body { background: var(--bg); color: var(--text); }
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; margin: 2rem; }
.container { max-width: 800px; margin: 0 auto; }
.page-header { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; }
.page-header h1 { margin: 0; }
@media (max-width: 480px) { .page-header h1 { font-size: 1.5rem; } }
.grid { display: grid; grid-template-columns: repeat(5, 3rem); gap: .5rem; }
.grid input { text-align: center; font-size: 1.25rem; padding: .4rem; background: var(--input-bg); color: var(--input-text); border: 1px solid var(--border); border-radius: .375rem; font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace; text-transform: uppercase; }
.grid input.filled { background: #6baa64; color: #ffffff; }
.text-input { font-size: 1.1rem; padding: .5rem; background: var(--input-bg); color: var(--input-text); border: 1px solid var(--border); border-radius: .375rem; width: 100%; box-sizing: border-box; font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace; text-transform: uppercase; }
.letter-input { width: calc(1ch + 2rem); padding: .5rem 1rem; font-size: 1rem; line-height: 1; background: var(--input-bg); color: var(--input-text); border: 1px solid var(--border); border-radius: .5rem; text-align: center; text-transform: uppercase; font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace; }
.plus-button { width: calc(1ch + 2rem); text-align: center; line-height: 1; display: inline-flex; align-items: center; justify-content: center; font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace; }
label { font-weight: 600; display: block; margin-top: 1rem; margin-bottom: .25rem; }
.results { margin-top: 1.5rem; }
.results-box { border: 1px solid var(--border); border-radius: .5rem; padding: .75rem; }
.badge { display: inline-block; padding: .25rem .5rem; background: var(--badge-bg); color: var(--badge-text); border-radius: .375rem; margin-right: .25rem; margin-bottom: .25rem; }
.badge .letter { display: inline-block; }
.badge .letter.included { background: #ffc100; color: #ffffff; border-radius: .25rem; padding: 0 .1rem; }
#includes-list .badge, #excludes-list .badge { cursor: pointer; }
#includes-list .badge { background: #ffc100; color: #ffffff; }
.source { font-size: .75rem; padding: .1rem .35rem; border-radius: .25rem; margin-left: .25rem; }
.source.ot { background: #dbeafe; color: #1e40af; }
.source.wf { background: #dcfce7; color: #065f46; }
button { margin-top: 1rem; padding: .5rem 1rem; font-size: 1rem; margin-right: 0.5rem; background: var(--button-bg); color: var(--button-text); border: 1px solid var(--border); border-radius: .5rem; cursor: pointer; }
.reset-button { background: var(--muted); }
summary { cursor: pointer; }
.footer { margin-top: 2rem; font-size: .9rem; color: var(--muted); }
.footer a { color: inherit; text-decoration: underline; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 1px, 1px); white-space: nowrap; border: 0; }
.skip-link { position: absolute; left: -9999px; top: auto; width: 1px; height: 1px; overflow: hidden; }
.skip-link:focus { position: static; width: auto; height: auto; padding: .5rem .75rem; background: var(--skip-bg); color: var(--skip-text); border-radius: .25rem; }
.hint { margin-top: .25rem; color: var(--muted); font-size: .9rem; }
.inline-controls { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.filter-box { border: 1px solid var(--border); border-radius: .5rem; padding: .5rem .75rem; margin: .5rem 0 1rem; }
.word-list { list-style: none; padding: 0; margin: 0; }
#includes-list, #excludes-list { margin-top: .75rem; }
.word-list li { display: inline-block; margin: 0 .25rem .25rem 0; }
fieldset { border: 1px solid var(--border); border-radius: .5rem; padding: .75rem; }
legend { font-weight: 700; padding: 0 .25rem; }
.theme-toggle { background: var(--button-bg); color: var(--button-text); border: 1px solid var(--border); border-radius: .5rem; padding: .4rem .6rem; font-size: .95rem; margin-top: 0; }
.theme-toggle:focus { outline: 2px solid #3b82f6; outline-offset: 2px; }
.inline-controls .plus-button { margin-top: 0; margin-right: 0; }
.drop-target { outline: 2px dashed #3b82f6; outline-offset: 2px; }
</style>
</head>
<body>
<a href="#results" class="skip-link">Zum Ergebnisbereich springen</a>
<div class="container">
<header class="page-header">
<button id="theme-toggle" class="theme-toggle" aria-pressed="false" aria-label="Theme umschalten" title="Theme umschalten">🌞/🌙</button>
<h1>WordleCheater (Deutsch)</h1>
</header>
<p>Wortliste geladen: <strong>{{ words_count }}</strong> Wörter</p>
<main id="main" role="main">
<form id="search-form" method="post" aria-describedby="form-hint">
<p id="form-hint" class="hint">Gib bekannte Buchstaben ein. Leere Felder werden ignoriert.</p>
<fieldset>
<legend>Buchstaben mit korrekter Position</legend>
<div class="grid" aria-describedby="pos-hint">
<input id="pos1" name="pos1" maxlength="1" aria-label="Position 1" inputmode="text" autocomplete="off" pattern="[A-Za-zÄÖÜäöüß]" value="{{ pos[0] }}" />
<input id="pos2" name="pos2" maxlength="1" aria-label="Position 2" inputmode="text" autocomplete="off" pattern="[A-Za-zÄÖÜäöüß]" value="{{ pos[1] }}" />
<input id="pos3" name="pos3" maxlength="1" aria-label="Position 3" inputmode="text" autocomplete="off" pattern="[A-Za-zÄÖÜäöüß]" value="{{ pos[2] }}" />
<input id="pos4" name="pos4" maxlength="1" aria-label="Position 4" inputmode="text" autocomplete="off" pattern="[A-Za-zÄÖÜäöüß]" value="{{ pos[3] }}" />
<input id="pos5" name="pos5" maxlength="1" aria-label="Position 5" inputmode="text" autocomplete="off" pattern="[A-Za-zÄÖÜäöüß]" value="{{ pos[4] }}" />
</div>
<p id="pos-hint" class="hint">Je Feld ein Buchstabe. Ziehe einen Buchstaben heraus, wenn du ihn löschen möchtest.</p>
</fieldset>
<label for="includes-input-one">Weitere enthaltene Buchstaben (beliebige Reihenfolge)</label>
<div class="inline-controls" aria-describedby="includes-hint">
<input id="includes-input-one" maxlength="1" aria-label="Buchstabe hinzufügen (enthalten)" inputmode="text" autocomplete="off" pattern="[A-Za-zÄÖÜäöüß]" class="letter-input" />
<button type="button" id="includes-add-button" class="plus-button" aria-label="Buchstabe zu 'Enthalten' hinzufügen">+</button>
<input type="hidden" id="includes" name="includes" value="{{ includes }}" />
</div>
<ul id="includes-list" class="word-list" aria-live="polite"></ul>
<p id="includes-hint" class="hint">Gib einen Buchstaben ein und füge ihn mit „+“ zur Liste hinzu. Ziehe ihn auf ein Positionsfeld, wenn er dort passen würde.</p>
<label for="excludes-input-one">Ausgeschlossene Buchstaben</label>
<div class="inline-controls" aria-describedby="excludes-hint">
<input id="excludes-input-one" maxlength="1" aria-label="Buchstabe hinzufügen (ausschließen)" inputmode="text" autocomplete="off" pattern="[A-Za-zÄÖÜäöüß]" class="letter-input" />
<button type="button" id="excludes-add-button" class="plus-button" aria-label="Buchstabe zu 'Ausschließen' hinzufügen">+</button>
<input type="hidden" id="excludes" name="excludes" value="{{ excludes }}" />
</div>
<ul id="excludes-list" class="word-list" aria-live="polite"></ul>
<p id="excludes-hint" class="hint">Gib einen Buchstaben ein und füge ihn mit „+“ zur Liste hinzu.</p>
<button type="submit">Suchen</button>
<button type="button" id="reset-button" class="reset-button">Zurücksetzen</button>
</form>
{% if results is not none %}
<div class="results" id="results" role="region" aria-labelledby="results-title">
<h2 id="results-title">Vorschläge (<span id="visible-count">{{ results|length }}</span>)</h2>
<fieldset class="filter-box" role="group">
<legend>WortquellenFilter</legend>
<div class="inline-controls">
<label><input id="filter-ot" type="checkbox" name="use_ot" form="search-form" {% if use_ot %}checked{% endif %}/> OT (OpenThesaurus)</label>
<label><input id="filter-wf" type="checkbox" name="use_wf" form="search-form" {% if use_wf %}checked{% endif %}/> WF (wordfreq)</label>
</div>
</fieldset>
<fieldset class="filter-box" role="group">
<legend>Umlaute</legend>
<div class="inline-controls">
<label><input id="filter-umlaut" type="checkbox" /> Umlaute einbeziehen (ä, ö, ü, ß)</label>
</div>
</fieldset>
<div class="results-box">
{% if results|length == 0 %}
<p>Keine Treffer. Bitte Bedingungen anpassen.</p>
{% else %}
<details open>
<summary>Liste anzeigen</summary>
<ul class="word-list">
{% for w in results %}
{% set srcs = sources_map.get(w, []) %}
{% set has_umlaut = ('ä' in w or 'ö' in w or 'ü' in w or 'ß' in w) %}
<li data-sources="{{ srcs|join(' ') }}" data-umlaut="{{ 1 if has_umlaut else 0 }}">
<span class="badge">
{% for ch in w %}
{% if ch in includes|lower %}
<span class="letter included">{{ ch }}</span>
{% else %}
<span class="letter">{{ ch }}</span>
{% endif %}
{% endfor %}
{% for s in srcs %}
{% if s == 'ot' %}
<span class="source ot">OT</span>
{% elif s == 'wf' %}
<span class="source wf">WF</span>
{% endif %}
{% endfor %}
</span>
</li>
{% endfor %}
</ul>
</details>
{% endif %}
</div>
<p>
<strong>Legende:</strong>
<span class="source wf">WF</span> = <a href="https://pypi.org/project/wordfreq/" target="_blank" rel="noopener noreferrer">Wordfreq</a>,
<span class="source ot">OT</span> = <a href="https://www.openthesaurus.de" target="_blank" rel="noopener noreferrer">OpenThesaurus</a>
</p>
</div>
{% endif %}
</main>
<footer class="footer">
Yet another <a href="https://gitea.elpatron.me/elpatron/wordle-cheater" rel="noopener noreferrer">Open Source Project</a>, vibe coded in less than 4 hours by <a href="mailto:elpatron@mailbox.org">Markus F.J. Busche</a>
</footer>
</div>
<script>
(function() {
var btn = document.getElementById('theme-toggle');
if (!btn) return;
function currentTheme() { return document.documentElement.getAttribute('data-theme') || 'light'; }
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
try { localStorage.setItem('theme', theme); } catch (e) {}
btn.setAttribute('aria-pressed', theme === 'dark');
btn.textContent = theme === 'dark' ? '🌙' : '🌞';
btn.title = 'Theme umschalten (' + (theme === 'dark' ? 'Dunkel' : 'Hell') + ')';
}
setTheme(currentTheme());
btn.addEventListener('click', function() {
var next = currentTheme() === 'dark' ? 'light' : 'dark';
setTheme(next);
});
})();
</script>
<script>
function applyFilters() {
var ot = document.getElementById('filter-ot');
var wf = document.getElementById('filter-wf');
var uml = document.getElementById('filter-umlaut');
if (!ot || !wf) return;
var allowed = [];
if (ot.checked) allowed.push('ot');
if (wf.checked) allowed.push('wf');
var items = document.querySelectorAll('.word-list li');
var visible = 0;
items.forEach(function(li) {
var sources = (li.dataset.sources || '').split(/\s+/).filter(Boolean);
var hasUmlaut = li.dataset.umlaut === '1';
var showSource = allowed.length === 0 ? false : sources.some(function(s){ return allowed.indexOf(s) !== -1; });
var showUmlaut = uml && uml.checked ? true : !hasUmlaut; // an => alle, aus => ohne Umlaute
var show = showSource && showUmlaut;
li.style.display = show ? '' : 'none';
if (show) visible++;
});
var countEl = document.getElementById('visible-count');
if (countEl) countEl.textContent = String(visible);
}
function updateLettersList(listElementId, lettersString) {
var ul = document.getElementById(listElementId);
if (!ul) return;
while (ul.firstChild) ul.removeChild(ul.firstChild);
var letters = (lettersString || '')
.split('')
.filter(function(ch){ return !!ch; })
.map(function(ch){ return ch.toLowerCase(); })
.sort(function(a,b){ return a.localeCompare(b, 'de'); });
letters.forEach(function(ch){
var li = document.createElement('li');
var badge = document.createElement('span');
badge.className = 'badge';
var lower = ch.toLowerCase();
badge.textContent = lower.toUpperCase();
badge.setAttribute('data-letter', lower);
badge.title = 'Zum Entfernen klicken';
badge.setAttribute('aria-label', "Buchstabe '" + lower.toUpperCase() + "' entfernen");
// Drag & Drop für Includes-Liste aktivieren
if (listElementId === 'includes-list') {
badge.draggable = true;
badge.addEventListener('dragstart', function(e){
try {
e.dataTransfer.setData('text/plain', lower);
e.dataTransfer.setData('text/list', listElementId);
e.dataTransfer.effectAllowed = 'move';
} catch (err) {}
});
}
li.appendChild(badge);
ul.appendChild(li);
});
}
function removeLetterFromList(hiddenId, listId, letter) {
var hidden = document.getElementById(hiddenId);
if (!hidden) return;
var current = hidden.value || '';
var next = (current || '').split('').filter(function(ch){ return ch !== letter; }).join('');
hidden.value = next;
updateLettersList(listId, next);
}
function addLetterFromInput(inputId, hiddenId, listId) {
var input = document.getElementById(inputId);
var hidden = document.getElementById(hiddenId);
if (!input || !hidden) return;
var raw = (input.value || '').trim();
if (!raw) return;
var ch = raw[0];
if (!/[A-Za-zÄÖÜäöüß]/.test(ch)) { input.value = ''; return; }
var lower = ch.toLowerCase();
var current = hidden.value || '';
if (current.indexOf(lower) === -1) {
hidden.value = current + lower;
updateLettersList(listId, hidden.value);
}
input.value = '';
input.focus();
}
// Drag-Status für Positionsfelder
var dragSourcePosInput = null;
var dragAccepted = false;
window.addEventListener('load', function() {
var ot = document.getElementById('filter-ot');
var wf = document.getElementById('filter-wf');
var uml = document.getElementById('filter-umlaut');
if (ot) ot.addEventListener('change', applyFilters);
if (wf) wf.addEventListener('change', applyFilters);
if (uml) uml.addEventListener('change', applyFilters);
applyFilters();
// Status der Positionsfelder (filled) initial setzen und bei Eingaben aktualisieren
function updatePosFilledState() {
for (var i = 1; i <= 5; i++) {
var el = document.getElementById('pos' + i);
if (!el) continue;
if ((el.value || '').trim()) el.classList.add('filled');
else el.classList.remove('filled');
}
}
updatePosFilledState();
for (var i = 1; i <= 5; i++) {
(function(idx){
var el = document.getElementById('pos' + idx);
if (!el) return;
el.addEventListener('input', updatePosFilledState);
el.addEventListener('change', updatePosFilledState);
})(i);
}
// Listen initial aus Hidden-Feldern rendern
var hiddenInc = document.getElementById('includes');
var hiddenExc = document.getElementById('excludes');
updateLettersList('includes-list', hiddenInc ? hiddenInc.value : '');
updateLettersList('excludes-list', hiddenExc ? hiddenExc.value : '');
// Add-Buttons verdrahten
var addIncBtn = document.getElementById('includes-add-button');
if (addIncBtn) addIncBtn.addEventListener('click', function(){ addLetterFromInput('includes-input-one', 'includes', 'includes-list'); });
var addExcBtn = document.getElementById('excludes-add-button');
if (addExcBtn) addExcBtn.addEventListener('click', function(){ addLetterFromInput('excludes-input-one', 'excludes', 'excludes-list'); });
// Enter-Handling in den Ein-Feld-Eingaben
var incInputOne = document.getElementById('includes-input-one');
if (incInputOne) incInputOne.addEventListener('keydown', function(e){ if (e.key === 'Enter') { e.preventDefault(); addLetterFromInput('includes-input-one', 'includes', 'includes-list'); } });
var excInputOne = document.getElementById('excludes-input-one');
if (excInputOne) excInputOne.addEventListener('keydown', function(e){ if (e.key === 'Enter') { e.preventDefault(); addLetterFromInput('excludes-input-one', 'excludes', 'excludes-list'); } });
// Entfernen per Klick auf Badge (Event Delegation)
var incList = document.getElementById('includes-list');
if (incList) incList.addEventListener('click', function(e){
var t = e.target;
if (t && t.classList && t.classList.contains('badge')) {
var letter = (t.getAttribute('data-letter') || t.textContent || '').trim().toLowerCase();
if (letter) removeLetterFromList('includes', 'includes-list', letter);
}
});
var excList = document.getElementById('excludes-list');
if (excList) excList.addEventListener('click', function(e){
var t = e.target;
if (t && t.classList && t.classList.contains('badge')) {
var letter = (t.getAttribute('data-letter') || t.textContent || '').trim().toLowerCase();
if (letter) removeLetterFromList('excludes', 'excludes-list', letter);
}
});
// Drop-Ziele für Positionsfelder (pos1..pos5)
for (var i = 1; i <= 5; i++) {
(function(idx){
var input = document.getElementById('pos' + idx);
if (!input) return;
// Als Drag-Quelle nutzbar machen
input.draggable = true;
input.addEventListener('dragstart', function(e){
var val = (input.value || '').trim().toLowerCase();
if (!val) { try { e.preventDefault(); } catch (err) {} return; }
dragSourcePosInput = input;
dragAccepted = false;
try {
e.dataTransfer.setData('text/plain', val);
e.dataTransfer.setData('text/source', 'pos');
e.dataTransfer.effectAllowed = 'move';
} catch (err) {}
});
input.addEventListener('dragend', function(){
if (dragSourcePosInput === input && !dragAccepted) {
input.value = '';
input.classList.remove('filled');
}
dragSourcePosInput = null;
dragAccepted = false;
});
input.addEventListener('dragover', function(e){
e.preventDefault();
try { e.dataTransfer.dropEffect = 'move'; } catch (err) {}
input.classList.add('drop-target');
});
input.addEventListener('dragleave', function(){ input.classList.remove('drop-target'); });
input.addEventListener('drop', function(e){
e.preventDefault();
input.classList.remove('drop-target');
var letter = (e.dataTransfer.getData('text/plain') || '').trim().toLowerCase();
var sourceList = e.dataTransfer.getData('text/list') || '';
var sourceType = e.dataTransfer.getData('text/source') || '';
if (letter && /[a-zäöüß]/i.test(letter) && (sourceList === 'includes-list' || sourceType === 'pos')) {
dragAccepted = true;
input.value = letter;
if ((input.value || '').trim()) input.classList.add('filled');
if (sourceList === 'includes-list') {
removeLetterFromList('includes', 'includes-list', letter);
}
if (sourceType === 'pos' && dragSourcePosInput && dragSourcePosInput !== input) {
dragSourcePosInput.value = '';
dragSourcePosInput.classList.remove('filled');
}
input.focus();
}
});
})(i);
}
// Reset-Button Funktionalität
var resetButton = document.getElementById('reset-button');
if (resetButton) {
resetButton.addEventListener('click', function() {
// Alle Positionsfelder zurücksetzen
for (var i = 1; i <= 5; i++) {
var posField = document.getElementById('pos' + i);
if (posField) { posField.value = ''; posField.classList.remove('filled'); }
}
// Weitere Felder zurücksetzen
var includesField = document.getElementById('includes');
if (includesField) includesField.value = '';
var includesList = document.getElementById('includes-list');
if (includesList) includesList.innerHTML = '';
var incInput = document.getElementById('includes-input-one');
if (incInput) incInput.value = '';
var excludesField = document.getElementById('excludes');
if (excludesField) excludesField.value = '';
var excludesList = document.getElementById('excludes-list');
if (excludesList) excludesList.innerHTML = '';
var excInput = document.getElementById('excludes-input-one');
if (excInput) excInput.value = '';
// Suchergebnisse ausblenden
var resultsDiv = document.getElementById('results');
if (resultsDiv) {
resultsDiv.style.display = 'none';
}
});
}
});
</script>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js');
});
}
</script>
</body>
</html>