-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.js
More file actions
134 lines (116 loc) · 4.58 KB
/
app.js
File metadata and controls
134 lines (116 loc) · 4.58 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
(() => {
const textInput = document.getElementById("qr-text");
const hiddenInput = document.getElementById("qr-hidden");
const qrImage = document.getElementById("qr-image");
const eclSelect = document.getElementById("qr-ecl");
const scaleInput = document.getElementById("qr-scale");
const marginInput = document.getElementById("qr-margin");
const canvas = document.getElementById("qr-canvas");
const status = document.getElementById("qr-status");
const versionField = document.getElementById("qr-version");
const sizeField = document.getElementById("qr-size");
const eclField = document.getElementById("qr-ecl-display");
const maskField = document.getElementById("qr-mask");
const eclMap = {
L: qrcodegen.QrCode.Ecc.LOW,
M: qrcodegen.QrCode.Ecc.MEDIUM,
Q: qrcodegen.QrCode.Ecc.QUARTILE,
H: qrcodegen.QrCode.Ecc.HIGH,
};
const eclDisplay = new Map([
[qrcodegen.QrCode.Ecc.LOW, "Low (7% tolerance)"],
[qrcodegen.QrCode.Ecc.MEDIUM, "Medium (15% tolerance)"],
[qrcodegen.QrCode.Ecc.QUARTILE, "Quartile (25% tolerance)"],
[qrcodegen.QrCode.Ecc.HIGH, "High (30% tolerance)"],
]);
const renderQr = () => {
const message = textInput.value.trim();
const hiddenMessage = hiddenInput.value;
const scale = clamp(parseInt(scaleInput.value, 10) || 8, 2, 20);
const margin = clamp(parseInt(marginInput.value, 10) || 4, 0, 10);
if (!message) {
status.textContent = "Enter text to generate a QR code.";
clearCanvas(canvas);
resetMeta();
return;
}
const ecl = eclMap[eclSelect.value] ?? qrcodegen.QrCode.Ecc.LOW;
try {
const qr = qrcodegen.QrCode.encodeText(message, ecl, hiddenMessage);
drawCanvas(qr, canvas, scale, margin);
status.textContent = `QR code generated at ${formatTimestamp(new Date())}.`;
setMeta(qr);
} catch (error) {
clearCanvas(canvas);
status.textContent = `Unable to generate QR code: ${error.message}`;
resetMeta();
}
};
const drawCanvas = (qr, canvasEl, scale, margin) => {
const ctx = canvasEl.getContext("2d");
const size = (qr.size + margin * 2) * scale;
canvasEl.width = size;
canvasEl.height = size;
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = "#111";
for (let y = 0; y < qr.size; y += 1) {
for (let x = 0; x < qr.size; x += 1) {
if (qr.getModule(x, y)) {
ctx.fillRect((margin + x) * scale, (margin + y) * scale, scale, scale);
}
}
}
const dataUrl = canvasEl.toDataURL("image/png");
qrImage.src = dataUrl;
qrImage.width = size;
qrImage.height = size;
qrImage.alt = "QR code preview. Long-press to save or open in a new tab.";
};
const setMeta = (qr) => {
versionField.textContent = qr.version;
sizeField.textContent = `${qr.size} × ${qr.size}`;
eclField.textContent = eclDisplay.get(qr.errorCorrectionLevel) ?? "Unknown";
maskField.textContent = qr.mask;
};
const resetMeta = () => {
versionField.textContent = "–";
sizeField.textContent = "–";
eclField.textContent = "–";
maskField.textContent = "–";
};
const clearCanvas = (canvasEl) => {
const ctx = canvasEl.getContext("2d");
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
qrImage.removeAttribute("src");
};
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
const formatTimestamp = (date) => {
const pad = (value) => String(value).padStart(2, "0");
const year = date.getFullYear();
const month = pad(date.getMonth() + 1);
const day = pad(date.getDate());
const hours = pad(date.getHours());
const minutes = pad(date.getMinutes());
const seconds = pad(date.getSeconds());
const offsetMinutes = date.getTimezoneOffset();
const totalMinutes = -offsetMinutes;
const sign = totalMinutes >= 0 ? "+" : "-";
const absoluteMinutes = Math.abs(totalMinutes);
const offsetHours = Math.floor(absoluteMinutes / 60);
const offsetRemainingMinutes = absoluteMinutes % 60;
const formattedOffset =
offsetRemainingMinutes === 0
? `${sign}${offsetHours}`
: `${sign}${offsetHours}:${pad(offsetRemainingMinutes)}`;
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} UTC${formattedOffset}`;
};
["input", "change"].forEach((eventName) => {
textInput.addEventListener(eventName, renderQr);
hiddenInput.addEventListener(eventName, renderQr);
eclSelect.addEventListener(eventName, renderQr);
scaleInput.addEventListener(eventName, renderQr);
marginInput.addEventListener(eventName, renderQr);
});
renderQr();
})();