Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
341 lines (298 loc) · 17.4 KB
/
Copy pathindex.html
File metadata and controls
341 lines (298 loc) · 17.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Free QR Code Generator - Create and download QR codes for URLs and WiFi networks. Simple, fast, and secure.">
<meta name="keywords" content="QR code generator, QR code creator, free QR code, HTTPS QR code, WiFi QR code, QR code download">
<meta name="author" content="Good Heart Tech">
<meta name="color-scheme" content="light">
<meta name="theme-color" content="#FFFFFF">
<meta property="og:title" content="QR Code Generator - Good Heart Tech">
<meta property="og:description" content="Free QR Code Generator - Create and download QR codes for URLs and WiFi networks. Simple, fast, and secure.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://goodhearttech.org/qr-generator">
<title>QR Code Generator | Good Heart Tech</title>
<link rel="icon" type="image/svg+xml" href="https://graphics.goodhearttech.org/GHT/favicons/favicon.svg">
<script src="lib/qrcode.min.js"></script>
<link rel="stylesheet" href="ght-variables.css">
<link rel="stylesheet" href="ght-buttons.css">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: var(--ght-font-family-body);
background-color: var(--ght-palette-white);
color: var(--ght-palette-charcoal);
min-height: 100vh;
display: flex;
flex-direction: column;
}
main.container {
background-color: var(--ght-palette-white);
padding: 20px;
width: 100%;
max-width: 800px;
margin: 0 auto;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
.attribution { margin-bottom: 15px; font-size: 1.1em; }
.attribution a { color: var(--ght-palette-primary-dark); font-weight: var(--ght-font-weight-semibold); text-decoration: underline; }
.attribution a:hover { color: var(--ght-palette-rich-black); }
h1 { color: var(--ght-palette-rich-black); margin-bottom: 20px; font-size: 2.5em; font-weight: var(--ght-font-weight-bold); }
.tabs { display: flex; gap: 10px; margin-bottom: 20px; width: 100%; }
.tab { flex: 1; }
.tab.active, .tab.active:hover { color: var(--ght-palette-white); background: var(--ght-palette-primary-dark); border-color: var(--ght-palette-primary-dark); }
.input-group { margin-bottom: 20px; width: 100%; max-width: 700px; margin-left: auto; margin-right: auto; }
.input-group label { display: block; text-align: left; margin-bottom: 5px; color: var(--ght-palette-rich-black); font-weight: var(--ght-font-weight-semibold); }
input, select {
width: 100%;
padding: 15px;
border: 2px solid var(--ght-palette-primary-dark);
border-radius: 10px;
font-size: 16px;
font-family: inherit;
outline: none;
transition: all 0.3s ease;
background-color: var(--ght-palette-white);
color: var(--ght-palette-rich-black);
}
input:focus, select:focus { border-color: var(--ght-palette-rich-black); box-shadow: 0 0 0 3px var(--ght-palette-primary); }
.error {
color: #9B1C1C;
background-color: #FDECEC;
border-radius: 8px;
padding: 8px 12px;
margin-top: 10px;
font-size: 14px;
text-align: left;
display: none;
}
.form-group .ght-btn { margin: 5px; }
/* QR code: dark modules on a light background so it stays scannable */
#qrcode {
margin-top: 20px;
padding: 20px;
background-color: #FFFFFF;
border: 1px solid var(--ght-palette-primary-tint);
border-radius: 10px;
display: none;
width: 100%;
max-width: 500px;
}
#qrcode.visible { display: flex; justify-content: center; align-items: center; }
#qrcode img { max-width: 100%; height: auto; margin-bottom: 20px; display: block; }
.download-buttons { display: none; gap: 10px; justify-content: center; margin-top: 20px; width: 100%; }
.form-group { display: none; width: 100%; }
.form-group.active { display: flex; flex-direction: column; align-items: center; }
.info-message {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
background-color: var(--ght-palette-primary-tint);
border-radius: 10px;
padding: 10px;
margin: 10px 0 20px 0;
color: var(--ght-palette-charcoal);
font-size: 0.9em;
width: 100%;
text-align: left;
}
.info-message svg { flex: none; color: var(--ght-palette-primary-dark); }
@media (max-width: 600px) { h1 { font-size: 2em; } }
/* Good Heart Tech standard app footer, light mode */
.ght-footer { width: 100%; margin-top: auto; padding: 22px 24px 26px; text-align: center; background: color-mix(in srgb, var(--ght-palette-primary-tint) 30%, white); border-top: 1px solid var(--ght-palette-primary-tint); color: var(--ght-palette-charcoal); }
.ght-footer-note { max-width: 52rem; margin: 0 auto 16px; font-size: .8125rem; font-style: italic; }
.ght-footer-row { display: flex; align-items: center; justify-content: center; gap: 20px; flex-wrap: wrap; }
.ght-footer-logo img { display: block; height: 30px; width: auto; }
.ght-footer-copy { font-size: .9375rem; }
.ght-footer-donate { display: inline-flex; line-height: 1.2; align-items: center; gap: 8px; padding: 9px 22px; border-radius: 8px; background: var(--ght-palette-primary-dark); color: var(--ght-palette-white); font-weight: var(--ght-font-weight-semibold); text-decoration: none; }
.ght-footer-donate:hover { background: var(--ght-palette-rich-black); color: var(--ght-palette-white); }
.ght-footer-github { display: inline-flex; color: var(--ght-palette-rich-black); }
.ght-footer-github:hover { color: var(--ght-palette-primary-dark); }
.ght-footer a:focus-visible { outline: 3px solid var(--ght-palette-primary); outline-offset: 3px; }
</style>
</head>
<body>
<main class="container">
<h1>QR Code Generator</h1>
<div class="attribution">
Created and provided by <a href="https://goodhearttech.org/" target="_blank" rel="noopener noreferrer">Good Heart Tech</a>, a nonprofit providing free IT services to other nonprofits.
</div>
<div class="info-message">
<svg aria-hidden="true" viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4z"/></svg><span>All processing occurs locally within the browser. No queries or results are recorded on our systems</span>
</div>
<div class="tabs">
<button type="button" class="ght-btn tab active" onclick="switchTab('url')" aria-pressed="true">URL or App Link</button>
<button type="button" class="ght-btn tab" onclick="switchTab('wifi')" aria-pressed="false">WiFi</button>
</div>
<div class="form-group active" id="urlForm">
<div class="input-group">
<label for="urlInput">Enter URL or App Link</label>
<input type="text" id="urlInput" placeholder="https:// or app://" />
<div class="error" id="urlError" role="alert">Error: Please enter a valid URL or app link (must contain ://)</div>
</div>
<button type="button" class="ght-btn ght-btn--primary" onclick="generateUrlQR()" id="generateUrlBtn"><svg aria-hidden="true" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M3 3h8v8H3V3zm2 2v4h4V5H5zm8-2h8v8h-8V3zm2 2v4h4V5h-4zM3 13h8v8H3v-8zm2 2v4h4v-4H5zm8-2h2v2h-2v-2zm2 2h2v2h-2v-2zm2-2h4v2h-4v-2zm-4 4h2v2h-2v-2zm4 0h2v4h-4v-2h2v-2zm-4 4h2v2h-2v-2z"/></svg> Generate QR code</button>
</div>
<div class="form-group" id="wifiForm">
<div class="input-group">
<label for="ssidInput">Network Name (SSID)</label>
<input type="text" id="ssidInput" placeholder="Enter WiFi network name" />
</div>
<div class="input-group">
<label for="passwordInput">Password</label>
<input type="password" id="passwordInput" placeholder="Enter WiFi password" />
</div>
<div class="input-group">
<label for="encryptionInput">Encryption Type</label>
<select id="encryptionInput">
<option value="WPA">WPA/WPA2</option>
<option value="WEP">WEP</option>
<option value="nopass">No Password</option>
</select>
</div>
<button type="button" class="ght-btn ght-btn--primary" onclick="generateWifiQR()" id="generateWifiBtn"><svg aria-hidden="true" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M3 3h8v8H3V3zm2 2v4h4V5H5zm8-2h8v8h-8V3zm2 2v4h4V5h-4zM3 13h8v8H3v-8zm2 2v4h4v-4H5zm8-2h2v2h-2v-2zm2 2h2v2h-2v-2zm2-2h4v2h-4v-2zm-4 4h2v2h-2v-2zm4 0h2v4h-4v-2h2v-2zm-4 4h2v2h-2v-2z"/></svg> Generate QR code</button>
</div>
<div id="qrcode" role="img"></div>
<div class="download-buttons" id="downloadButtons">
<button type="button" class="ght-btn" onclick="downloadQR('png')"><svg aria-hidden="true" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M5 20h14v-2H5v2zM19 9h-4V3H9v6H5l7 7 7-7z"/></svg> Download as PNG</button>
<button type="button" class="ght-btn" onclick="downloadQR('jpeg')"><svg aria-hidden="true" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M5 20h14v-2H5v2zM19 9h-4V3H9v6H5l7 7 7-7z"/></svg> Download as JPEG</button>
</div>
</main>
<footer class="ght-footer">
<p class="ght-footer-note">This tool is for informational use only. Accuracy is not guaranteed, and it may become outdated or stop functioning. Use at your own discretion.</p>
<div class="ght-footer-row">
<a class="ght-footer-logo" href="https://goodhearttech.org/"><img src="assets/ght-lockup.svg" alt="Good Heart Tech" height="30"></a>
<span class="ght-footer-copy">© <span id="ght-year">2026</span> All rights reserved.</span>
<a class="ght-footer-donate" href="https://goodhearttech.org/donate/">
<svg aria-hidden="true" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg> Donate
</a>
<a class="ght-footer-github" href="https://github.com/Good-Heart-Tech/QR-Code-Generator" aria-label="QR Code Generator on GitHub">
<svg aria-hidden="true" viewBox="0 0 24 24" width="26" height="26" fill="currentColor"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
</a>
</div>
</footer>
<script>
function switchTab(tab) {
// Update tabs
document.querySelectorAll('.tab').forEach(t => { t.classList.remove('active'); t.setAttribute('aria-pressed', 'false'); });
const activeTab = document.querySelector(`.tab:nth-child(${tab === 'url' ? '1' : '2'})`);
activeTab.classList.add('active');
activeTab.setAttribute('aria-pressed', 'true');
// Update forms
document.querySelectorAll('.form-group').forEach(f => f.classList.remove('active'));
document.getElementById(`${tab}Form`).classList.add('active');
// Clear QR code and download buttons
document.getElementById('qrcode').classList.remove('visible');
document.getElementById('downloadButtons').style.display = 'none';
}
function isValidUrl(url) {
try {
// Check if the URL contains :// which is required for both web URLs and app links
if (!url.includes('://')) {
return false;
}
// For web URLs, validate them properly
if (url.startsWith('http://') || url.startsWith('https://')) {
new URL(url);
return true;
}
// For app links, just ensure they have a protocol and content after ://
const parts = url.split('://');
return parts.length === 2 && parts[0].length > 0 && parts[1].length > 0;
} catch {
return false;
}
}
function generateUrlQR() {
const urlInput = document.getElementById('urlInput');
const errorMessage = document.getElementById('urlError');
const qrcodeDiv = document.getElementById('qrcode');
const downloadButtons = document.getElementById('downloadButtons');
const url = urlInput.value.trim();
if (!isValidUrl(url)) {
errorMessage.style.display = 'block';
qrcodeDiv.classList.remove('visible');
downloadButtons.style.display = 'none';
return;
}
errorMessage.style.display = 'none';
qrcodeDiv.innerHTML = '';
new QRCode(qrcodeDiv, {
text: url,
width: 300,
height: 300,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
qrcodeDiv.classList.add('visible');
downloadButtons.style.display = 'flex';
}
function generateWifiQR() {
const ssid = document.getElementById('ssidInput').value.trim();
const password = document.getElementById('passwordInput').value.trim();
const encryption = document.getElementById('encryptionInput').value;
const qrcodeDiv = document.getElementById('qrcode');
const downloadButtons = document.getElementById('downloadButtons');
if (!ssid) {
alert('Please enter a network name');
qrcodeDiv.classList.remove('visible');
downloadButtons.style.display = 'none';
return;
}
const wifiString = `WIFI:S:${ssid};T:${encryption};P:${password};;`;
qrcodeDiv.innerHTML = '';
new QRCode(qrcodeDiv, {
text: wifiString,
width: 300,
height: 300,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
qrcodeDiv.classList.add('visible');
downloadButtons.style.display = 'flex';
}
function downloadQR(format) {
const img = document.querySelector('#qrcode img');
if (!img || !img.src) return;
// Redraw onto a white canvas so JPEG is a real JPEG (no transparency)
const canvas = document.createElement('canvas');
canvas.width = img.naturalWidth || 300;
canvas.height = img.naturalHeight || 300;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
const link = document.createElement('a');
link.download = `qr-code.${format === 'jpeg' ? 'jpg' : 'png'}`;
link.href = canvas.toDataURL(format === 'jpeg' ? 'image/jpeg' : 'image/png', 0.95);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// Add event listeners for Enter key
document.getElementById('urlInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
generateUrlQR();
}
});
document.getElementById('ssidInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
generateWifiQR();
}
});
document.getElementById('passwordInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
generateWifiQR();
}
});
// Set the current year dynamically
document.getElementById('ght-year').textContent = new Date().getFullYear();
</script>
</body>
</html>