669 lines
32 KiB
HTML
669 lines
32 KiB
HTML
<!DOCTYPE html>
|
||
<!-- saved from url=(0024)https://qr.alster.space/ -->
|
||
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Just a QR Code</title>
|
||
<link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' fill='%23ffffff' rx='3' ry='3'/><rect x='8' y='8' width='4' height='4' fill='%23000000'/><rect x='12' y='8' width='4' height='4' fill='%23000000'/><rect x='16' y='8' width='4' height='4' fill='%23000000'/><rect x='8' y='12' width='4' height='4' fill='%23000000'/><rect x='8' y='16' width='4' height='4' fill='%23000000'/><rect x='20' y='8' width='4' height='4' fill='%23000000'/><rect x='16' y='12' width='4' height='4' fill='%23000000'/><rect x='20' y='16' width='4' height='4' fill='%23000000'/><rect x='8' y='20' width='4' height='4' fill='%23000000'/><rect x='12' y='20' width='4' height='4' fill='%23000000'/><rect x='16' y='20' width='4' height='4' fill='%23000000'/><rect x='20' y='20' width='4' height='4' fill='%23000000'/><rect x='20' y='12' width='4' height='4' fill='%23000000'/></svg>">
|
||
<script src="./assets/qrious.min.js.Download"></script>
|
||
<!--
|
||
Licensed under the Creative Commons Attribution-NonCommercial (BY-NC) 4.0 license.
|
||
Feel free to use this for anything noncommercial.
|
||
Credit appreciated but not required.
|
||
If you don't like the news, go out and make some of your own.
|
||
-->
|
||
<style>
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||
line-height: 1.6;
|
||
max-width: 800px;
|
||
margin: 0 auto;
|
||
padding: 20px;
|
||
background-color: #f5f5f7;
|
||
color: #333;
|
||
}
|
||
|
||
h1 {
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
color: #1d1d1f;
|
||
cursor: default;
|
||
user-select: none;
|
||
}
|
||
|
||
.container {
|
||
background-color: white;
|
||
border-radius: 10px;
|
||
padding: 30px;
|
||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||
position: relative;
|
||
}
|
||
|
||
.input-section {
|
||
margin-bottom: 40px;
|
||
}
|
||
|
||
label {
|
||
display: block;
|
||
margin-bottom: 5px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
input, select {
|
||
width: 100%;
|
||
padding: 10px;
|
||
margin-bottom: 15px;
|
||
border: 1px solid #ccc;
|
||
border-radius: 4px;
|
||
box-sizing: border-box;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.text-input-wrapper {
|
||
position: relative;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.text-input-wrapper input {
|
||
padding-right: 40px;
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.clear-button {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 0;
|
||
height: 100%;
|
||
width: 40px;
|
||
background: none;
|
||
border: none;
|
||
color: #666;
|
||
font-size: 18px;
|
||
cursor: pointer;
|
||
display: none;
|
||
padding: 0;
|
||
}
|
||
|
||
.clear-button:hover {
|
||
color: #333;
|
||
background: none;
|
||
}
|
||
|
||
.options-row {
|
||
display: flex;
|
||
gap: 15px;
|
||
}
|
||
|
||
.options-col {
|
||
flex: 1;
|
||
}
|
||
|
||
.color-input-wrapper {
|
||
position: relative;
|
||
}
|
||
|
||
input[type="color"] {
|
||
width: 100%;
|
||
height: 40px;
|
||
padding: 0;
|
||
border: 1px solid #ccc;
|
||
border-radius: 4px;
|
||
background-color: transparent;
|
||
cursor: pointer;
|
||
}
|
||
|
||
input[type="color"]::-webkit-color-swatch-wrapper {
|
||
padding: 0;
|
||
}
|
||
|
||
input[type="color"]::-webkit-color-swatch {
|
||
border: none;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.output-section {
|
||
text-align: center;
|
||
margin-top: 30px;
|
||
}
|
||
|
||
#qrcode-container {
|
||
text-align: center;
|
||
}
|
||
|
||
#qrcode, #qrcode-img {
|
||
margin: 0 auto;
|
||
padding: 15px;
|
||
background-color: white;
|
||
border-radius: 4px;
|
||
display: block;
|
||
}
|
||
|
||
.download-btn {
|
||
background-color: #ff9500;
|
||
margin-top: 15px;
|
||
display: none;
|
||
}
|
||
|
||
.download-btn:hover {
|
||
background-color: #e68600;
|
||
}
|
||
|
||
.error {
|
||
color: #e74c3c;
|
||
margin-top: 5px;
|
||
}
|
||
|
||
button {
|
||
background-color: #0071e3;
|
||
color: white;
|
||
border: none;
|
||
padding: 12px 20px;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
transition: background-color 0.2s;
|
||
width: 100%;
|
||
}
|
||
|
||
button:hover {
|
||
background-color: #0058b9;
|
||
}
|
||
|
||
.info-button {
|
||
position: absolute;
|
||
top: 20px;
|
||
right: 20px;
|
||
width: 30px;
|
||
height: 30px;
|
||
border-radius: 50%;
|
||
background: none;
|
||
border: none;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
font-size: 18px;
|
||
color: #999;
|
||
transition: color 0.2s ease;
|
||
z-index: 10;
|
||
}
|
||
|
||
.info-button:hover {
|
||
color: #333;
|
||
background: none;
|
||
}
|
||
|
||
.info-panel {
|
||
position: fixed;
|
||
top: 0;
|
||
right: -350px;
|
||
width: 330px;
|
||
height: 100%;
|
||
background-color: white;
|
||
box-shadow: -2px 0 10px rgba(0,0,0,0.1);
|
||
transition: right 0.3s ease;
|
||
z-index: 100;
|
||
overflow-y: auto;
|
||
padding: 20px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.info-panel.open {
|
||
right: 0;
|
||
}
|
||
|
||
.info-panel-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 1px solid #eee;
|
||
}
|
||
|
||
.info-panel-header h2 {
|
||
margin: 0;
|
||
color: #333;
|
||
}
|
||
|
||
.info-close {
|
||
background: none;
|
||
border: none;
|
||
font-size: 22px;
|
||
color: #666;
|
||
cursor: pointer;
|
||
width: auto;
|
||
padding: 5px;
|
||
}
|
||
|
||
.info-close:hover {
|
||
color: #333;
|
||
background: none;
|
||
}
|
||
|
||
.info-content h3 {
|
||
font-size: 16px;
|
||
margin-top: 20px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.info-content p {
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
color: #555;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.footer {
|
||
margin-top: 30px;
|
||
text-align: center;
|
||
font-size: 14px;
|
||
color: #666;
|
||
}
|
||
|
||
.credit {
|
||
margin-top: 5px;
|
||
font-size: 12px;
|
||
color: #888;
|
||
}
|
||
|
||
.credit a {
|
||
text-decoration: none;
|
||
}
|
||
|
||
.credit a:hover {
|
||
text-decoration: underline;
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<h1 id="title">Just a QR Code</h1>
|
||
<button id="info-button" class="info-button" aria-label="Information">?</button>
|
||
|
||
<div class="input-section">
|
||
<label for="text">Text oder URL:</label>
|
||
<div class="text-input-wrapper">
|
||
<input type="text" id="text" placeholder="https://example.com" value="" autofocus="">
|
||
<button type="button" id="clear-text" class="clear-button" aria-label="Clear text" style="display: none;">✕</button>
|
||
</div>
|
||
|
||
<div class="options-row">
|
||
<div class="options-col">
|
||
<label for="wifi-ssid">WLAN SSID:</label>
|
||
<input type="text" id="wifi-ssid" placeholder="">
|
||
</div>
|
||
|
||
<div class="options-col">
|
||
<label for="wifi-password">WLAN Passwort:</label>
|
||
<input type="password" id="wifi-password" placeholder="">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="options-row">
|
||
<div class="options-col">
|
||
<label for="size">QR Code Grösse:</label>
|
||
<select id="size">
|
||
<option value="128">Klein (128×128)</option>
|
||
<option value="256" selected="">Mittel (256×256)</option>
|
||
<option value="512">Groß (512×512)</option>
|
||
<option value="1024">XL (1024×1024)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="options-col">
|
||
<label for="errorCorrection">Fehlerkorrektur:</label>
|
||
<select id="errorCorrection">
|
||
<option value="L">Klein (7%)</option>
|
||
<option value="M" selected="">Mittel (15%)</option>
|
||
<option value="Q">Viertel (25%)</option>
|
||
<option value="H">Hoch (30%)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="options-row">
|
||
<div class="options-col">
|
||
<label for="foreground">Vodergrundfarbe:</label>
|
||
<div class="color-input-wrapper">
|
||
<input type="color" id="foreground" value="#000000">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="options-col">
|
||
<label for="background">Hintergrundfarbe:</label>
|
||
<div class="color-input-wrapper">
|
||
<input type="color" id="background" value="#ffffff">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="output-section">
|
||
<div id="qrcode-container">
|
||
<canvas id="qrcode" style="display: none;" height="256" width="256"></canvas>
|
||
<img id="qrcode-img" style="display: block;" alt="Generated QR Code" src="data:image/png;base64,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">
|
||
</div>
|
||
<div id="error-message" class="error"></div>
|
||
<button id="download" class="download-btn" style="display: inline-block;">Download QR Code</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="info-panel" id="info-panel">
|
||
<div class="info-panel-header">
|
||
<h2>About</h2>
|
||
<button class="info-close" id="info-close">✕</button>
|
||
</div>
|
||
<div class="info-content">
|
||
<p>I needed to make a QR code again. It's one of those tasks that's useful enough to pop up regularly, but not frequently enough for you to have a go-to solution. When the need arises, you end up googling "QR code generator" and picking the least sketchy-looking option from the results. A couple ads and some invisible trackers are just the price you pay, right?</p>
|
||
|
||
<p>Not today.</p>
|
||
|
||
<p>Rather than bemoan the fact that there's no easy way to get just a QR code without the low-key parasitic monetization, I found a library (qrious) and put together just enough Javascript code to generate QR codes in the browser. No ads, no trackers, no sending your data to someone else's server for who-knows-what-reason.</p>
|
||
|
||
<p>Go ahead and inspect the page source. Save it to your computer, copy it, remix it, whatever you want.</p>
|
||
|
||
<p>This is my act of resistance: I'll pay for the domain name and hosting just so you and I don't ever have to give one more click or tracker data-point to some questionable "free" site again. If you find this useful, you can pay it back by making or doing something of your own and giving it away freely.</p>
|
||
|
||
<p>This is the web we were trying to build.</p>
|
||
|
||
<p>Be excellent to each other.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="footer">
|
||
Dieser QR-Code-Generator funktioniert vollständig in deinem Browser. Es werden keine Daten an einen Server gesendet. <i>Quellcode und README bei <a href="https://gitea.elpatron.me/elpatron/QR-Code-Generator" target="_blank">Gitea</a></i>.
|
||
<p class="credit">Made with ❤️ and 🍪 by Markus. Self hosted on <a href="https://unraid.net" target="_blank">Unraid</a> for <a href="https://medisoftware.de" target="_blank">medisoftware</a>. Credits: <a href="https://qr.alster.space/" target="_blank">alsternerd</a></p>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
|
||
const textInput = document.getElementById('text');
|
||
const clearTextBtn = document.getElementById('clear-text');
|
||
const wifiSsidInput = document.getElementById('wifi-ssid');
|
||
const wifiPasswordInput = document.getElementById('wifi-password');
|
||
const sizeSelect = document.getElementById('size');
|
||
const errorCorrectionSelect = document.getElementById('errorCorrection');
|
||
const foregroundColor = document.getElementById('foreground');
|
||
const backgroundColor = document.getElementById('background');
|
||
const downloadBtn = document.getElementById('download');
|
||
const qrcodeCanvas = document.getElementById('qrcode');
|
||
const qrcodeImg = document.getElementById('qrcode-img');
|
||
const errorMessage = document.getElementById('error-message');
|
||
const infoButton = document.getElementById('info-button');
|
||
const infoPanel = document.getElementById('info-panel');
|
||
const infoClose = document.getElementById('info-close');
|
||
const titleElement = document.getElementById('title');
|
||
|
||
// Title Easter egg
|
||
let titleClickCount = 0;
|
||
let titleTimeoutId = null;
|
||
|
||
titleElement.addEventListener('click', function() {
|
||
titleClickCount++;
|
||
clearTimeout(titleTimeoutId);
|
||
|
||
if (titleClickCount === 1) {
|
||
this.textContent = "Just a [bleep]ing QR Code";
|
||
|
||
titleTimeoutId = setTimeout(() => {
|
||
this.textContent = "Just a QR Code";
|
||
titleClickCount = 0;
|
||
}, 500);
|
||
}
|
||
});
|
||
|
||
// Info panel toggle
|
||
infoButton.addEventListener('click', function() {
|
||
infoPanel.classList.add('open');
|
||
});
|
||
|
||
infoClose.addEventListener('click', function() {
|
||
infoPanel.classList.remove('open');
|
||
});
|
||
|
||
// Close info panel when clicking outside
|
||
document.addEventListener('click', function(event) {
|
||
if (infoPanel.classList.contains('open') &&
|
||
!infoPanel.contains(event.target) &&
|
||
event.target !== infoButton) {
|
||
infoPanel.classList.remove('open');
|
||
}
|
||
});
|
||
|
||
// Close info panel with Escape key
|
||
document.addEventListener('keydown', function(event) {
|
||
if (event.key === 'Escape' && infoPanel.classList.contains('open')) {
|
||
infoPanel.classList.remove('open');
|
||
}
|
||
});
|
||
|
||
// Set random compliment as default text and select it
|
||
textInput.value = 'https://medisoftware.de';
|
||
textInput.select();
|
||
|
||
// Show/hide clear button based on text input content
|
||
function toggleClearButton() {
|
||
if (textInput.value.length > 0) {
|
||
clearTextBtn.style.display = 'block';
|
||
} else {
|
||
clearTextBtn.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
// Initialize clear button visibility
|
||
toggleClearButton();
|
||
|
||
// Clear text when clear button is clicked
|
||
clearTextBtn.addEventListener('click', function() {
|
||
textInput.value = '';
|
||
wifiSsidInput.value = '';
|
||
wifiPasswordInput.value = '';
|
||
toggleClearButton();
|
||
textInput.focus();
|
||
generateQRCode();
|
||
});
|
||
|
||
// Update clear button visibility when text changes
|
||
textInput.addEventListener('input', function() {
|
||
toggleClearButton();
|
||
// Only generate QR code if no wifi data is being entered
|
||
const wifiSsid = wifiSsidInput.value.trim();
|
||
const wifiPassword = wifiPasswordInput.value.trim();
|
||
if (!wifiSsid && !wifiPassword) {
|
||
generateQRCode();
|
||
}
|
||
});
|
||
|
||
let qr = null;
|
||
|
||
// Generate QR code when settings change
|
||
sizeSelect.addEventListener('change', generateQRCode);
|
||
errorCorrectionSelect.addEventListener('change', generateQRCode);
|
||
foregroundColor.addEventListener('input', generateQRCode);
|
||
backgroundColor.addEventListener('input', generateQRCode);
|
||
|
||
// Generate QR code when wifi settings change
|
||
wifiSsidInput.addEventListener('input', function() {
|
||
updateTextDisplay();
|
||
generateQRCode();
|
||
});
|
||
wifiPasswordInput.addEventListener('input', function() {
|
||
updateTextDisplay();
|
||
generateQRCode();
|
||
});
|
||
|
||
// Download QR code as image
|
||
downloadBtn.addEventListener('click', downloadQRCode);
|
||
|
||
// Or check for URL parameters if present
|
||
const urlParams = new URLSearchParams(window.location.search);
|
||
if (urlParams.has('text')) {
|
||
textInput.value = urlParams.get('text');
|
||
}
|
||
if (urlParams.has('ssid')) {
|
||
wifiSsidInput.value = urlParams.get('ssid');
|
||
}
|
||
if (urlParams.has('password')) {
|
||
wifiPasswordInput.value = urlParams.get('password');
|
||
}
|
||
if (urlParams.has('size')) {
|
||
const sizeValue = urlParams.get('size');
|
||
if (['128', '256', '512', '1024'].includes(sizeValue)) {
|
||
sizeSelect.value = sizeValue;
|
||
}
|
||
}
|
||
if (urlParams.has('errorCorrection')) {
|
||
const ecValue = urlParams.get('errorCorrection').toUpperCase();
|
||
if (['L', 'M', 'Q', 'H'].includes(ecValue)) {
|
||
errorCorrectionSelect.value = ecValue;
|
||
}
|
||
}
|
||
if (urlParams.has('foreground')) {
|
||
const fgValue = urlParams.get('foreground');
|
||
if (/^#[0-9A-F]{6}$/i.test(fgValue)) {
|
||
foregroundColor.value = fgValue;
|
||
}
|
||
}
|
||
if (urlParams.has('background')) {
|
||
const bgValue = urlParams.get('background');
|
||
if (/^#[0-9A-F]{6}$/i.test(bgValue)) {
|
||
backgroundColor.value = bgValue;
|
||
}
|
||
}
|
||
generateQRCode();
|
||
|
||
function generateQRCode() {
|
||
const text = textInput.value.trim();
|
||
const wifiSsid = wifiSsidInput.value.trim();
|
||
const wifiPassword = wifiPasswordInput.value.trim();
|
||
const size = parseInt(sizeSelect.value);
|
||
const errorCorrection = errorCorrectionSelect.value.toLowerCase();
|
||
const fgColor = foregroundColor.value;
|
||
const bgColor = backgroundColor.value;
|
||
|
||
// Update the gabe link color to match foreground color
|
||
const gabeLink = document.querySelector('.credit a');
|
||
if (gabeLink) {
|
||
gabeLink.style.color = fgColor;
|
||
}
|
||
|
||
// Clear previous error
|
||
errorMessage.textContent = '';
|
||
downloadBtn.style.display = 'none';
|
||
|
||
// Determine what to encode
|
||
let qrText = text;
|
||
|
||
// If wifi credentials are provided, generate wifi QR code
|
||
if (wifiSsid && wifiPassword) {
|
||
// Wifi QR code format: WIFI:S:<SSID>;T:WPA;P:<password>;;
|
||
qrText = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:WPA;P:${escapeSpecialChars(wifiPassword)};;`;
|
||
} else if (wifiSsid && !wifiPassword) {
|
||
// SSID only (open network)
|
||
qrText = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:nopass;;`;
|
||
} else if (!wifiSsid && wifiPassword) {
|
||
// Password without SSID is invalid
|
||
errorMessage.textContent = 'Bitte geben Sie eine SSID ein, wenn Sie ein Passwort angeben';
|
||
qrcodeCanvas.style.display = 'none';
|
||
qrcodeImg.style.display = 'none';
|
||
return;
|
||
}
|
||
|
||
// Validate input
|
||
if (qrText === '') {
|
||
errorMessage.textContent = 'Bitte geben Sie Text/URL oder Wifi-Daten ein';
|
||
// Hide both canvas and image
|
||
qrcodeCanvas.style.display = 'none';
|
||
qrcodeImg.style.display = 'none';
|
||
return;
|
||
}
|
||
|
||
// Create new QR code on canvas
|
||
try {
|
||
if (!qr) {
|
||
qr = new QRious({
|
||
element: qrcodeCanvas,
|
||
size: size,
|
||
value: qrText,
|
||
foreground: fgColor,
|
||
background: bgColor,
|
||
level: errorCorrection
|
||
});
|
||
} else {
|
||
qr.set({
|
||
size: size,
|
||
value: qrText,
|
||
foreground: fgColor,
|
||
background: bgColor,
|
||
level: errorCorrection
|
||
});
|
||
}
|
||
|
||
// Convert canvas to image
|
||
const dataURL = qrcodeCanvas.toDataURL('image/png');
|
||
qrcodeImg.src = dataURL;
|
||
|
||
// Show image (hide canvas) and download button
|
||
qrcodeCanvas.style.display = 'none';
|
||
qrcodeImg.style.display = 'block';
|
||
downloadBtn.style.display = 'inline-block';
|
||
} catch (err) {
|
||
errorMessage.textContent = 'Error generating QR code: ' + err.message;
|
||
qrcodeCanvas.style.display = 'none';
|
||
qrcodeImg.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
// Helper function to escape special characters in wifi strings
|
||
function escapeSpecialChars(str) {
|
||
return str.replace(/[\\;:,]/g, '\\$&');
|
||
}
|
||
|
||
function downloadQRCode() {
|
||
// Use the image source for download
|
||
const link = document.createElement('a');
|
||
link.download = 'qrcode.png';
|
||
link.href = qrcodeImg.src;
|
||
|
||
// Simulate click to trigger download
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
|
||
// Show success feedback
|
||
const originalText = downloadBtn.textContent;
|
||
downloadBtn.textContent = 'Downloaded!';
|
||
|
||
// Reset button after a short delay
|
||
setTimeout(() => {
|
||
downloadBtn.textContent = originalText;
|
||
}, 2000);
|
||
}
|
||
|
||
// Function to update the text display with current QR code content
|
||
function updateTextDisplay() {
|
||
const wifiSsid = wifiSsidInput.value.trim();
|
||
const wifiPassword = wifiPasswordInput.value.trim();
|
||
|
||
if (wifiSsid && wifiPassword) {
|
||
// Show wifi QR code format
|
||
textInput.value = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:WPA;P:${escapeSpecialChars(wifiPassword)};;`;
|
||
} else if (wifiSsid && !wifiPassword) {
|
||
// Show open network format
|
||
textInput.value = `WIFI:S:${escapeSpecialChars(wifiSsid)};T:nopass;;`;
|
||
} else {
|
||
// If no wifi data, keep original text input value
|
||
// Don't change it here to avoid overwriting user input
|
||
}
|
||
|
||
// Update clear button visibility
|
||
toggleClearButton();
|
||
}
|
||
});
|
||
</script>
|
||
|
||
|
||
</body></html> |