Spaces:
Running
Running
CyberMax commited on
Deploy utm-builder to the hub
Browse files- utm-builder/app.js +172 -0
- utm-builder/icon-192.png +0 -0
- utm-builder/icon-512.png +0 -0
- utm-builder/icon.svg +1 -0
- utm-builder/index.html +162 -0
- utm-builder/manifest.webmanifest +15 -0
- utm-builder/og.png +0 -0
- utm-builder/robots.txt +3 -0
- utm-builder/sitemap.xml +2 -0
- utm-builder/style.css +75 -0
- utm-builder/sw.js +20 -0
- utm-builder/utm.js +132 -0
- utm-builder/vendor/LICENSE-qrcode-generator.txt +25 -0
- utm-builder/vendor/qrcode.js +2297 -0
utm-builder/app.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* UTM Builder UI. © 2026 CyberMax. All rights reserved. */
|
| 2 |
+
(function () {
|
| 3 |
+
"use strict";
|
| 4 |
+
var U = window.UtmCore;
|
| 5 |
+
var el = function (id) { return document.getElementById(id); };
|
| 6 |
+
var KEY = "cybermax-utm:v1:";
|
| 7 |
+
function load(k, d) { try { var v = localStorage.getItem(KEY + k); return v ? JSON.parse(v) : d; } catch (e) { return d; } }
|
| 8 |
+
function save(k, v) { try { localStorage.setItem(KEY + k, JSON.stringify(v)); } catch (e) { /* ignore */ } }
|
| 9 |
+
function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { "&": "&", "<": "<", ">": ">", '"': """ }[c]; }); }
|
| 10 |
+
var toastT;
|
| 11 |
+
function toast(m) { var t = el("toast"); t.textContent = m; t.classList.add("show"); clearTimeout(toastT); toastT = setTimeout(function () { t.classList.remove("show"); }, 1600); }
|
| 12 |
+
function copy(text, msg) {
|
| 13 |
+
var done = function () { toast(msg || "Copied"); };
|
| 14 |
+
if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).then(done, function () { fallback(text); done(); });
|
| 15 |
+
else { fallback(text); done(); }
|
| 16 |
+
}
|
| 17 |
+
function fallback(text) { var ta = document.createElement("textarea"); ta.value = text; document.body.appendChild(ta); ta.select(); try { document.execCommand("copy"); } catch (e) { /* ignore */ } ta.remove(); }
|
| 18 |
+
function download(name, text, type) {
|
| 19 |
+
var a = document.createElement("a");
|
| 20 |
+
a.href = URL.createObjectURL(new Blob([text], { type: type }));
|
| 21 |
+
a.download = name; document.body.appendChild(a); a.click(); a.remove();
|
| 22 |
+
setTimeout(function () { URL.revokeObjectURL(a.href); }, 1000);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
// ---------- tabs ----------
|
| 26 |
+
function showTab(name) {
|
| 27 |
+
document.querySelectorAll(".tab").forEach(function (t) { t.setAttribute("aria-selected", String(t.dataset.tab === name)); });
|
| 28 |
+
document.querySelectorAll(".panel").forEach(function (p) { p.hidden = p.id !== "panel-" + name; });
|
| 29 |
+
if (name === "history") renderHistory();
|
| 30 |
+
}
|
| 31 |
+
document.querySelectorAll(".tab").forEach(function (t) { t.onclick = function () { showTab(t.dataset.tab); }; });
|
| 32 |
+
|
| 33 |
+
// ---------- builder ----------
|
| 34 |
+
var fields = ["url"].concat(U.KEYS);
|
| 35 |
+
function params() { var p = {}; U.KEYS.forEach(function (k) { p[k] = el("f-" + k).value; }); return p; }
|
| 36 |
+
function opts() { return { lower: el("o-lower").checked, space: el("o-space").value }; }
|
| 37 |
+
var current = "";
|
| 38 |
+
|
| 39 |
+
var presets = el("presets");
|
| 40 |
+
Object.keys(U.PRESETS).forEach(function (name) {
|
| 41 |
+
var b = document.createElement("button");
|
| 42 |
+
b.type = "button"; b.className = "chip"; b.textContent = name;
|
| 43 |
+
b.onclick = function () {
|
| 44 |
+
var p = U.PRESETS[name];
|
| 45 |
+
for (var k in p) el("f-" + k).value = p[k];
|
| 46 |
+
presets.querySelectorAll(".chip").forEach(function (c) { c.classList.toggle("on", c === b); });
|
| 47 |
+
update();
|
| 48 |
+
};
|
| 49 |
+
presets.appendChild(b);
|
| 50 |
+
});
|
| 51 |
+
|
| 52 |
+
function update() {
|
| 53 |
+
var r = U.build(el("f-url").value, params(), opts());
|
| 54 |
+
var out = el("out");
|
| 55 |
+
if (r.error || !r.tagged) {
|
| 56 |
+
current = "";
|
| 57 |
+
out.textContent = el("f-url").value.trim() ? (r.error || "Add a source, medium and campaign.") : "Fill in the URL, source, medium and campaign.";
|
| 58 |
+
} else {
|
| 59 |
+
current = r.url;
|
| 60 |
+
out.textContent = r.url;
|
| 61 |
+
}
|
| 62 |
+
el("warn").innerHTML = (r.warnings || []).filter(function () { return !!current; }).map(function (w) { return "⚠ " + esc(w); }).join("<br>");
|
| 63 |
+
el("btn-copy").disabled = !current;
|
| 64 |
+
el("btn-qr").disabled = !current;
|
| 65 |
+
var open = el("btn-open");
|
| 66 |
+
if (current) { open.href = current; open.removeAttribute("aria-disabled"); } else { open.removeAttribute("href"); open.setAttribute("aria-disabled", "true"); }
|
| 67 |
+
drawQr(current);
|
| 68 |
+
var state = {}; fields.forEach(function (f) { state[f] = el("f-" + f).value; });
|
| 69 |
+
state.lower = el("o-lower").checked; state.space = el("o-space").value;
|
| 70 |
+
save("form", state);
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
function drawQr(text) {
|
| 74 |
+
var c = el("qr"), ctx = c.getContext("2d");
|
| 75 |
+
ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, c.width, c.height);
|
| 76 |
+
if (!text) {
|
| 77 |
+
ctx.fillStyle = "#9aa4b6"; ctx.font = "14px system-ui, sans-serif"; ctx.textAlign = "center";
|
| 78 |
+
ctx.fillText("QR code appears here", c.width / 2, c.height / 2);
|
| 79 |
+
return;
|
| 80 |
+
}
|
| 81 |
+
try {
|
| 82 |
+
var qr = window.qrcode(0, "M");
|
| 83 |
+
qr.addData(text); qr.make();
|
| 84 |
+
var n = qr.getModuleCount(), quiet = 4, size = Math.floor(c.width / (n + quiet * 2));
|
| 85 |
+
var off = Math.floor((c.width - size * n) / 2);
|
| 86 |
+
ctx.fillStyle = "#000";
|
| 87 |
+
for (var r = 0; r < n; r++) for (var col = 0; col < n; col++) if (qr.isDark(r, col)) ctx.fillRect(off + col * size, off + r * size, size, size);
|
| 88 |
+
} catch (e) {
|
| 89 |
+
ctx.fillStyle = "#b25e00"; ctx.font = "13px system-ui, sans-serif"; ctx.textAlign = "center";
|
| 90 |
+
ctx.fillText("Link too long for a QR code", c.width / 2, c.height / 2);
|
| 91 |
+
}
|
| 92 |
+
}
|
| 93 |
+
function qrPng() {
|
| 94 |
+
// Re-render at 1024px for print quality.
|
| 95 |
+
var qr = window.qrcode(0, "M"); qr.addData(current); qr.make();
|
| 96 |
+
var n = qr.getModuleCount(), cell = Math.floor(1024 / (n + 8)), size = cell * (n + 8);
|
| 97 |
+
var c = document.createElement("canvas"); c.width = c.height = size;
|
| 98 |
+
var ctx = c.getContext("2d"); ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, size, size); ctx.fillStyle = "#000";
|
| 99 |
+
for (var r = 0; r < n; r++) for (var col = 0; col < n; col++) if (qr.isDark(r, col)) ctx.fillRect((col + 4) * cell, (r + 4) * cell, cell, cell);
|
| 100 |
+
var a = document.createElement("a"); a.href = c.toDataURL("image/png");
|
| 101 |
+
var camp = (el("f-utm_campaign").value || "link").toLowerCase().replace(/[^a-z0-9_-]+/g, "-");
|
| 102 |
+
a.download = "qr-" + camp + ".png"; document.body.appendChild(a); a.click(); a.remove();
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
fields.forEach(function (f) { el("f-" + f).addEventListener("input", update); });
|
| 106 |
+
el("o-lower").addEventListener("change", update);
|
| 107 |
+
el("o-space").addEventListener("change", update);
|
| 108 |
+
el("btn-copy").onclick = function () { if (!current) return; copy(current, "Link copied"); addHistory(current); };
|
| 109 |
+
el("btn-qr").onclick = function () { if (current) qrPng(); };
|
| 110 |
+
el("btn-reset").onclick = function () { fields.forEach(function (f) { el("f-" + f).value = ""; }); presets.querySelectorAll(".chip").forEach(function (c) { c.classList.remove("on"); }); update(); el("f-url").focus(); };
|
| 111 |
+
|
| 112 |
+
// ---------- bulk ----------
|
| 113 |
+
var bulkRows = [];
|
| 114 |
+
el("btn-bulk").onclick = function () {
|
| 115 |
+
bulkRows = U.bulk(el("bulk-in").value, params(), opts());
|
| 116 |
+
var tb = el("bulk-table").querySelector("tbody");
|
| 117 |
+
tb.innerHTML = bulkRows.map(function (r, i) {
|
| 118 |
+
return "<tr><td class='num'>" + (i + 1) + "</td><td style='word-break:break-all'>" + esc(r.url || r.input) + "</td><td>" +
|
| 119 |
+
(r.error ? "<span style='color:var(--warn)'>" + esc(r.error) + "</span>" : esc(r.warnings.filter(function (w) { return !/^Added https/.test(w); }).slice(0, 2).join(" "))) + "</td></tr>";
|
| 120 |
+
}).join("");
|
| 121 |
+
el("bulk-table").hidden = !bulkRows.length;
|
| 122 |
+
var okN = bulkRows.filter(function (r) { return !r.error; }).length;
|
| 123 |
+
el("btn-bulk-copy").disabled = el("btn-bulk-csv").disabled = !okN;
|
| 124 |
+
toast(bulkRows.length ? okN + " of " + bulkRows.length + " links built" : "Paste some URLs first");
|
| 125 |
+
};
|
| 126 |
+
el("btn-bulk-copy").onclick = function () { copy(bulkRows.filter(function (r) { return !r.error; }).map(function (r) { return r.url; }).join("\n"), "All links copied"); };
|
| 127 |
+
el("btn-bulk-csv").onclick = function () { download("utm-links.csv", U.toCsv(bulkRows), "text/csv"); };
|
| 128 |
+
|
| 129 |
+
// ---------- decode ----------
|
| 130 |
+
var decoded = null;
|
| 131 |
+
el("dec-in").addEventListener("input", function () {
|
| 132 |
+
var v = el("dec-in").value.trim(), tb = el("dec-table").querySelector("tbody");
|
| 133 |
+
decoded = v ? U.parse(v) : null;
|
| 134 |
+
if (!decoded || decoded.error) { tb.innerHTML = v ? "<tr><td>" + esc(decoded.error) + "</td></tr>" : ""; el("dec-clean").textContent = ""; return; }
|
| 135 |
+
var keys = Object.keys(decoded.utm);
|
| 136 |
+
tb.innerHTML = keys.length ? keys.map(function (k) { return "<tr><th>" + esc(k) + "</th><td>" + esc(decoded.utm[k]) + "</td></tr>"; }).join("")
|
| 137 |
+
: "<tr><td>No UTM parameters in this link.</td></tr>";
|
| 138 |
+
el("dec-clean").textContent = decoded.clean;
|
| 139 |
+
});
|
| 140 |
+
el("btn-dec-edit").onclick = function () {
|
| 141 |
+
if (!decoded || decoded.error) return;
|
| 142 |
+
el("f-url").value = decoded.clean;
|
| 143 |
+
U.KEYS.forEach(function (k) { el("f-" + k).value = decoded.utm[k] || ""; });
|
| 144 |
+
showTab("single"); update();
|
| 145 |
+
};
|
| 146 |
+
|
| 147 |
+
// ---------- history ----------
|
| 148 |
+
function addHistory(url) {
|
| 149 |
+
var h = load("history", []).filter(function (x) { return x.url !== url; });
|
| 150 |
+
h.unshift({ url: url, at: new Date().toISOString().slice(0, 16).replace("T", " ") });
|
| 151 |
+
save("history", h.slice(0, 25));
|
| 152 |
+
}
|
| 153 |
+
function renderHistory() {
|
| 154 |
+
var h = load("history", []), tb = el("hist-table").querySelector("tbody");
|
| 155 |
+
tb.innerHTML = h.length ? h.map(function (x, i) {
|
| 156 |
+
return "<tr><td style='white-space:nowrap;color:var(--muted)'>" + esc(x.at) + "</td><td style='word-break:break-all'>" + esc(x.url) + "</td><td><button class='chip' data-i='" + i + "'>Copy</button></td></tr>";
|
| 157 |
+
}).join("") : "<tr><td>Links you copy will appear here.</td></tr>";
|
| 158 |
+
tb.querySelectorAll("button").forEach(function (b) { b.onclick = function () { copy(h[+b.dataset.i].url, "Link copied"); }; });
|
| 159 |
+
}
|
| 160 |
+
el("btn-hist-clear").onclick = function () { save("history", []); renderHistory(); };
|
| 161 |
+
|
| 162 |
+
// ---------- restore ----------
|
| 163 |
+
var st = load("form", null);
|
| 164 |
+
if (st) {
|
| 165 |
+
fields.forEach(function (f) { if (st[f]) el("f-" + f).value = st[f]; });
|
| 166 |
+
el("o-lower").checked = st.lower !== false;
|
| 167 |
+
if (st.space) el("o-space").value = st.space;
|
| 168 |
+
}
|
| 169 |
+
update();
|
| 170 |
+
window.UtmApp = { update: update, current: function () { return current; } };
|
| 171 |
+
if ("serviceWorker" in navigator && location.protocol === "https:") navigator.serviceWorker.register("sw.js").catch(function () {});
|
| 172 |
+
})();
|
utm-builder/icon-192.png
ADDED
|
|
utm-builder/icon-512.png
ADDED
|
|
utm-builder/icon.svg
ADDED
|
|
utm-builder/index.html
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
| 6 |
+
<title>Free UTM Builder — Bulk Campaign URL Builder + QR Code</title>
|
| 7 |
+
<meta name="description" content="Free UTM builder: tag campaign URLs for Google Analytics 4, build hundreds at once from a list or CSV, make a QR code, and decode tagged links. No sign-up.">
|
| 8 |
+
<meta name="keywords" content="utm builder, utm generator, campaign url builder, bulk utm builder, utm qr code generator, google analytics utm, ga4 utm parameters">
|
| 9 |
+
<meta name="theme-color" content="#3056d3">
|
| 10 |
+
<link rel="canonical" href="https://shaw276-cybermax.static.hf.space/utm-builder/">
|
| 11 |
+
<link rel="manifest" href="manifest.webmanifest">
|
| 12 |
+
<link rel="icon" href="icon.svg" type="image/svg+xml">
|
| 13 |
+
<link rel="apple-touch-icon" href="icon-192.png">
|
| 14 |
+
<meta property="og:type" content="website">
|
| 15 |
+
<meta property="og:title" content="Free UTM Builder — bulk links + QR codes">
|
| 16 |
+
<meta property="og:description" content="Tag one link or a thousand for GA4, with QR codes and a link decoder. Free, private, no sign-up.">
|
| 17 |
+
<meta property="og:url" content="https://shaw276-cybermax.static.hf.space/utm-builder/">
|
| 18 |
+
<meta property="og:image" content="https://shaw276-cybermax.static.hf.space/utm-builder/og.png">
|
| 19 |
+
<meta name="twitter:card" content="summary_large_image">
|
| 20 |
+
<script type="application/ld+json">
|
| 21 |
+
{"@context":"https://schema.org","@graph":[
|
| 22 |
+
{"@type":"WebApplication","name":"CyberMax UTM Builder","url":"https://shaw276-cybermax.static.hf.space/utm-builder/","applicationCategory":"BusinessApplication","operatingSystem":"Any","description":"Free UTM campaign URL builder with bulk mode, QR codes and a UTM decoder.","offers":{"@type":"Offer","price":"0","priceCurrency":"USD"},"publisher":{"@type":"Organization","name":"CyberMax"}},
|
| 23 |
+
{"@type":"FAQPage","mainEntity":[
|
| 24 |
+
{"@type":"Question","name":"What are UTM parameters?","acceptedAnswer":{"@type":"Answer","text":"UTM parameters are tags added to a link (utm_source, utm_medium, utm_campaign, utm_term, utm_content, utm_id) that tell analytics tools like Google Analytics 4 where a visit came from."}},
|
| 25 |
+
{"@type":"Question","name":"Which UTM parameters are required?","acceptedAnswer":{"@type":"Answer","text":"Google Analytics only needs utm_source, but you should always set utm_source, utm_medium and utm_campaign so GA4 can assign the visit to the right channel and campaign."}},
|
| 26 |
+
{"@type":"Question","name":"Should UTM values be lowercase?","acceptedAnswer":{"@type":"Answer","text":"Yes. UTM values are case-sensitive, so Facebook and facebook show up as two different sources. This builder lowercases values by default."}},
|
| 27 |
+
{"@type":"Question","name":"Is my data sent anywhere?","acceptedAnswer":{"@type":"Answer","text":"No. Every link is built in your browser. Nothing is uploaded or stored on a server."}}]}
|
| 28 |
+
]}
|
| 29 |
+
</script>
|
| 30 |
+
<link rel="stylesheet" href="style.css">
|
| 31 |
+
</head>
|
| 32 |
+
<body>
|
| 33 |
+
<header class="top"><div class="wrap">
|
| 34 |
+
<div class="brand"><span class="dot">?u</span> UTM Builder <small>by CyberMax</small></div>
|
| 35 |
+
<a class="btn ghost" href="#faq">FAQ</a>
|
| 36 |
+
</div></header>
|
| 37 |
+
|
| 38 |
+
<main class="wrap">
|
| 39 |
+
<h1>Free UTM Builder: campaign URLs, bulk tagging and QR codes</h1>
|
| 40 |
+
<p class="lede">Tag links for Google Analytics 4 in seconds. Build one link or hundreds at once, catch mistakes that break your reports, and download a QR code. Everything runs in your browser.</p>
|
| 41 |
+
|
| 42 |
+
<div class="tabs" role="tablist">
|
| 43 |
+
<button class="tab" role="tab" aria-selected="true" data-tab="single" id="tab-single">Builder</button>
|
| 44 |
+
<button class="tab" role="tab" aria-selected="false" data-tab="bulk" id="tab-bulk">Bulk</button>
|
| 45 |
+
<button class="tab" role="tab" aria-selected="false" data-tab="decode" id="tab-decode">Decode a link</button>
|
| 46 |
+
<button class="tab" role="tab" aria-selected="false" data-tab="history" id="tab-history">History</button>
|
| 47 |
+
</div>
|
| 48 |
+
|
| 49 |
+
<section class="panel" id="panel-single" role="tabpanel">
|
| 50 |
+
<div class="grid">
|
| 51 |
+
<div class="card">
|
| 52 |
+
<h2>1. Your link and campaign</h2>
|
| 53 |
+
<label for="f-url">Website URL</label>
|
| 54 |
+
<input type="url" id="f-url" placeholder="https://example.com/landing-page" autocomplete="url" inputmode="url">
|
| 55 |
+
<label>Quick presets</label>
|
| 56 |
+
<div class="chips" id="presets"></div>
|
| 57 |
+
<div class="row2">
|
| 58 |
+
<div><label for="f-utm_source">Source <span class="opt">utm_source</span></label><input type="text" id="f-utm_source" placeholder="google, newsletter"></div>
|
| 59 |
+
<div><label for="f-utm_medium">Medium <span class="opt">utm_medium</span></label><input type="text" id="f-utm_medium" placeholder="cpc, email, social" list="mediums"></div>
|
| 60 |
+
</div>
|
| 61 |
+
<datalist id="mediums"><option value="cpc"><option value="email"><option value="social"><option value="paid_social"><option value="display"><option value="affiliate"><option value="referral"><option value="video"><option value="sms"><option value="print"></datalist>
|
| 62 |
+
<label for="f-utm_campaign">Campaign name <span class="opt">utm_campaign</span></label>
|
| 63 |
+
<input type="text" id="f-utm_campaign" placeholder="spring_sale_2026">
|
| 64 |
+
<div class="row3">
|
| 65 |
+
<div><label for="f-utm_id">Campaign ID <span class="opt">optional</span></label><input type="text" id="f-utm_id" placeholder="abc.123"></div>
|
| 66 |
+
<div><label for="f-utm_term">Term <span class="opt">optional</span></label><input type="text" id="f-utm_term" placeholder="running shoes"></div>
|
| 67 |
+
<div><label for="f-utm_content">Content <span class="opt">optional</span></label><input type="text" id="f-utm_content" placeholder="hero_button"></div>
|
| 68 |
+
</div>
|
| 69 |
+
<label class="check"><input type="checkbox" id="o-lower" checked> Force lowercase (recommended)</label>
|
| 70 |
+
<label for="o-space">Replace spaces with</label>
|
| 71 |
+
<select id="o-space"><option value="-">Hyphen (spring-sale)</option><option value="_">Underscore (spring_sale)</option><option value="keep">Keep spaces (%20)</option></select>
|
| 72 |
+
</div>
|
| 73 |
+
<div class="card">
|
| 74 |
+
<h2>2. Your tagged link</h2>
|
| 75 |
+
<div class="out" id="out" aria-live="polite">Fill in the URL, source, medium and campaign.</div>
|
| 76 |
+
<div class="warn" id="warn"></div>
|
| 77 |
+
<div class="btns">
|
| 78 |
+
<button class="btn" id="btn-copy" disabled>Copy link</button>
|
| 79 |
+
<a class="btn ghost" id="btn-open" target="_blank" rel="noopener" aria-disabled="true">Test link ↗</a>
|
| 80 |
+
<button class="btn ghost" id="btn-reset">Clear</button>
|
| 81 |
+
</div>
|
| 82 |
+
<h2 style="margin-top:18px">QR code</h2>
|
| 83 |
+
<canvas id="qr" width="220" height="220" aria-label="QR code for the tagged link" style="background:#fff;border-radius:10px;border:1px solid var(--line);max-width:100%"></canvas>
|
| 84 |
+
<div class="btns"><button class="btn ghost" id="btn-qr" disabled>Download QR (PNG)</button></div>
|
| 85 |
+
<p class="hint">Tip: for printed QR codes use source = where it's printed (poster, flyer) and medium = print.</p>
|
| 86 |
+
</div>
|
| 87 |
+
</div>
|
| 88 |
+
</section>
|
| 89 |
+
|
| 90 |
+
<section class="panel" id="panel-bulk" role="tabpanel" hidden>
|
| 91 |
+
<div class="card">
|
| 92 |
+
<h2>Bulk UTM builder</h2>
|
| 93 |
+
<p class="hint">One URL per line, or CSV columns <code>url,source,medium,campaign,term,content</code> (a header row is optional). Blank columns use the values from the Builder tab. Up to 5,000 rows.</p>
|
| 94 |
+
<textarea id="bulk-in" placeholder="https://example.com/pricing https://example.com/blog/post,linkedin,social,launch_week example.com/docs,newsletter,email,october_digest,,footer_link"></textarea>
|
| 95 |
+
<div class="btns">
|
| 96 |
+
<button class="btn" id="btn-bulk">Build links</button>
|
| 97 |
+
<button class="btn ghost" id="btn-bulk-copy" disabled>Copy all</button>
|
| 98 |
+
<button class="btn ghost" id="btn-bulk-csv" disabled>Download CSV</button>
|
| 99 |
+
</div>
|
| 100 |
+
<div class="table-wrap" style="margin-top:12px"><table id="bulk-table" hidden><thead><tr><th>#</th><th>Tagged URL</th><th>Notes</th></tr></thead><tbody></tbody></table></div>
|
| 101 |
+
</div>
|
| 102 |
+
</section>
|
| 103 |
+
|
| 104 |
+
<section class="panel" id="panel-decode" role="tabpanel" hidden>
|
| 105 |
+
<div class="card">
|
| 106 |
+
<h2>Decode a tagged link</h2>
|
| 107 |
+
<label for="dec-in">Paste any URL</label>
|
| 108 |
+
<input type="url" id="dec-in" placeholder="https://example.com/?utm_source=google&utm_medium=cpc&utm_campaign=brand">
|
| 109 |
+
<div class="table-wrap" style="margin-top:12px"><table id="dec-table"><tbody></tbody></table></div>
|
| 110 |
+
<label>Clean URL (UTM tags removed)</label>
|
| 111 |
+
<div class="out" id="dec-clean"></div>
|
| 112 |
+
<div class="btns"><button class="btn ghost" id="btn-dec-edit">Edit in builder</button></div>
|
| 113 |
+
</div>
|
| 114 |
+
</section>
|
| 115 |
+
|
| 116 |
+
<section class="panel" id="panel-history" role="tabpanel" hidden>
|
| 117 |
+
<div class="card">
|
| 118 |
+
<h2>Recent links</h2>
|
| 119 |
+
<p class="hint">Your last 25 copied links, saved only in this browser.</p>
|
| 120 |
+
<div class="table-wrap"><table id="hist-table"><tbody></tbody></table></div>
|
| 121 |
+
<div class="btns"><button class="btn ghost" id="btn-hist-clear">Clear history</button></div>
|
| 122 |
+
</div>
|
| 123 |
+
</section>
|
| 124 |
+
|
| 125 |
+
<aside class="related">
|
| 126 |
+
<h2>Plan the campaign, not just the link</h2>
|
| 127 |
+
<p>Pick <b>utm_term</b> and ad keywords from real searches with <a href="https://apify.com/cybermax/keyword-ideas" target="_blank" rel="noopener">Keyfern keyword ideas</a> (Google, YouTube and Amazon autocomplete), and time launches with <a href="https://apify.com/cybermax/google-trends" target="_blank" rel="noopener">Swellmeter Google Trends</a>. Both are paid CyberMax tools on the Apify Store, charged per result, and a first run costs under $0.01.</p>
|
| 128 |
+
</aside>
|
| 129 |
+
|
| 130 |
+
<section class="seo" id="faq">
|
| 131 |
+
<h2>How to build a UTM link</h2>
|
| 132 |
+
<p>Paste the page URL, choose where the link will be shared (source), the type of traffic (medium) and a campaign name. The builder adds <code>utm_source</code>, <code>utm_medium</code> and <code>utm_campaign</code> to your link. Existing query parameters and #anchors are kept.</p>
|
| 133 |
+
<h3>What are UTM parameters?</h3>
|
| 134 |
+
<p>Tags on a link that tell Google Analytics 4, Matomo, Plausible and other analytics tools where a visitor came from: <b>utm_source</b> (google, newsletter), <b>utm_medium</b> (cpc, email, social), <b>utm_campaign</b> (spring_sale), and optionally <b>utm_term</b> (the keyword), <b>utm_content</b> (which button or ad variant) and <b>utm_id</b> (a campaign ID).</p>
|
| 135 |
+
<h3>Why do my UTM visits show as "Unassigned" in GA4?</h3>
|
| 136 |
+
<p>GA4 sorts traffic into channels using the medium. Values it doesn't recognise, like "newsletter" as a medium or "Paid-Social", can land in "Unassigned". The builder warns you when a medium probably won't match GA4's default channel groups.</p>
|
| 137 |
+
<h3>Should UTM values be lowercase?</h3>
|
| 138 |
+
<p>Yes. UTM values are case-sensitive, so "Facebook" and "facebook" count as two sources. Lowercase is on by default.</p>
|
| 139 |
+
<h3>Can I build UTM links in bulk?</h3>
|
| 140 |
+
<p>Yes. Open the Bulk tab and paste a list of URLs, or a CSV with a source, medium and campaign for each row. You'll get a table you can copy or download as CSV for a spreadsheet.</p>
|
| 141 |
+
<h3>Is it private?</h3>
|
| 142 |
+
<p>Yes. The links are built in your browser. Nothing is uploaded, and the history stays on your device. The app works offline once loaded.</p>
|
| 143 |
+
</section>
|
| 144 |
+
|
| 145 |
+
<footer class="more">
|
| 146 |
+
<div class="more-title">More from CyberMax</div>
|
| 147 |
+
<div class="more-links">
|
| 148 |
+
<a href="https://shaw276-cybermax.static.hf.space/profit-margin-calculator/" target="_blank" rel="noopener">Profit Margin Calculator</a>
|
| 149 |
+
<a href="https://shaw276-cybermax.static.hf.space/salary-to-hourly-calculator/" target="_blank" rel="noopener">Salary to Hourly Calculator</a>
|
| 150 |
+
<a href="https://shaw276-cybermax.static.hf.space/sumbloom/" target="_blank" rel="noopener">Sumbloom · daily number puzzle</a>
|
| 151 |
+
<a href="https://shaw276-cybermax.static.hf.space/honeyfold/" target="_blank" rel="noopener">Honeyfold · idle bee game</a>
|
| 152 |
+
<a href="https://huggingface.co/spaces/shaw276/cybermax" target="_blank" rel="noopener">All CyberMax tools</a>
|
| 153 |
+
</div>
|
| 154 |
+
<div class="copy">© 2026 CyberMax. All rights reserved. QR codes by qrcode-generator (MIT, Kazuhiko Arase). "QR Code" is a registered trademark of DENSO WAVE INCORPORATED.</div>
|
| 155 |
+
</footer>
|
| 156 |
+
</main>
|
| 157 |
+
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
| 158 |
+
<script src="vendor/qrcode.js"></script>
|
| 159 |
+
<script src="utm.js"></script>
|
| 160 |
+
<script src="app.js"></script>
|
| 161 |
+
</body>
|
| 162 |
+
</html>
|
utm-builder/manifest.webmanifest
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "UTM Builder by CyberMax",
|
| 3 |
+
"short_name": "UTM Builder",
|
| 4 |
+
"description": "Build UTM campaign links one at a time or in bulk, with QR codes and a link decoder.",
|
| 5 |
+
"start_url": "./",
|
| 6 |
+
"scope": "./",
|
| 7 |
+
"display": "standalone",
|
| 8 |
+
"background_color": "#f6f7fb",
|
| 9 |
+
"theme_color": "#3056d3",
|
| 10 |
+
"icons": [
|
| 11 |
+
{"src": "icon.svg", "sizes": "any", "type": "image/svg+xml"},
|
| 12 |
+
{"src": "icon-192.png", "sizes": "192x192", "type": "image/png"},
|
| 13 |
+
{"src": "icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable"}
|
| 14 |
+
]
|
| 15 |
+
}
|
utm-builder/og.png
ADDED
|
utm-builder/robots.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
User-agent: *
|
| 2 |
+
Allow: /
|
| 3 |
+
Sitemap: https://shaw276-cybermax.static.hf.space/utm-builder/sitemap.xml
|
utm-builder/sitemap.xml
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"><url><loc>https://shaw276-cybermax.static.hf.space/utm-builder/</loc><lastmod>2026-09-24</lastmod><changefreq>monthly</changefreq></url></urlset>
|
utm-builder/style.css
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* CyberMax web app base. © 2026 CyberMax. All rights reserved. */
|
| 2 |
+
:root{
|
| 3 |
+
--bg:#f6f7fb;--surface:#fff;--ink:#1d2433;--muted:#5d6678;--line:#e1e5ee;--accent:#3056d3;--accent-ink:#fff;
|
| 4 |
+
--accent-soft:#e8edfc;--good:#1f8a4c;--warn:#b25e00;--warn-soft:#fff3e0;--code:#f0f2f7;color-scheme:light;
|
| 5 |
+
}
|
| 6 |
+
@media (prefers-color-scheme:dark){
|
| 7 |
+
:root{--bg:#12151c;--surface:#1b2029;--ink:#e8ecf4;--muted:#9aa4b6;--line:#2d3440;--accent:#7c9cff;--accent-ink:#0d1220;
|
| 8 |
+
--accent-soft:#232c45;--good:#5ccf8b;--warn:#ffb25c;--warn-soft:#3a2b18;--code:#232833;color-scheme:dark}
|
| 9 |
+
}
|
| 10 |
+
*{box-sizing:border-box}
|
| 11 |
+
html,body{margin:0}
|
| 12 |
+
body{background:var(--bg);color:var(--ink);font:15px/1.5 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
|
| 13 |
+
a{color:var(--accent)}
|
| 14 |
+
button,input,select,textarea{font:inherit;color:inherit}
|
| 15 |
+
.wrap{max-width:1040px;margin:0 auto;padding:0 16px}
|
| 16 |
+
.top{background:var(--surface);border-bottom:1px solid var(--line)}
|
| 17 |
+
.top .wrap{display:flex;align-items:center;justify-content:space-between;gap:12px;padding-top:12px;padding-bottom:12px}
|
| 18 |
+
.brand{font-weight:800;font-size:18px;display:flex;align-items:center;gap:8px}
|
| 19 |
+
.brand .dot{width:26px;height:26px;border-radius:8px;background:var(--accent);color:var(--accent-ink);display:inline-flex;align-items:center;justify-content:center;font-size:14px}
|
| 20 |
+
.brand small{font-weight:600;color:var(--muted);font-size:13px}
|
| 21 |
+
h1{font-size:clamp(24px,4.5vw,34px);line-height:1.15;margin:22px 0 6px}
|
| 22 |
+
.lede{color:var(--muted);margin:0 0 16px;max-width:720px}
|
| 23 |
+
.tabs{display:flex;gap:4px;border-bottom:1px solid var(--line);margin-bottom:16px;overflow-x:auto}
|
| 24 |
+
.tab{border:0;background:none;padding:10px 14px;font-weight:700;color:var(--muted);border-bottom:3px solid transparent;cursor:pointer;white-space:nowrap}
|
| 25 |
+
.tab[aria-selected="true"]{color:var(--accent);border-bottom-color:var(--accent)}
|
| 26 |
+
.panel[hidden]{display:none}
|
| 27 |
+
.grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:16px}
|
| 28 |
+
.card{background:var(--surface);border:1px solid var(--line);border-radius:14px;padding:16px}
|
| 29 |
+
.card h2{font-size:16px;margin:0 0 12px}
|
| 30 |
+
label{display:block;font-size:13px;font-weight:700;margin:10px 0 4px}
|
| 31 |
+
label .opt{font-weight:500;color:var(--muted)}
|
| 32 |
+
.hint{font-size:12px;color:var(--muted);margin-top:3px}
|
| 33 |
+
input[type=text],input[type=url],input[type=number],select,textarea{width:100%;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:var(--bg);outline:none;min-width:0}
|
| 34 |
+
input:focus,select:focus,textarea:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
|
| 35 |
+
textarea{min-height:110px;resize:vertical;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px}
|
| 36 |
+
.row2{display:grid;grid-template-columns:1fr 1fr;gap:10px}
|
| 37 |
+
.row3{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
|
| 38 |
+
.chips{display:flex;flex-wrap:wrap;gap:6px}
|
| 39 |
+
.chip{border:1px solid var(--line);background:var(--bg);border-radius:999px;padding:5px 11px;font-size:13px;font-weight:600;cursor:pointer}
|
| 40 |
+
.chip.on,.chip:hover{border-color:var(--accent);color:var(--accent)}
|
| 41 |
+
.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;border:0;border-radius:10px;background:var(--accent);color:var(--accent-ink);font-weight:700;padding:10px 16px;cursor:pointer;text-decoration:none}
|
| 42 |
+
.btn.ghost{background:var(--surface);color:var(--ink);border:1px solid var(--line)}
|
| 43 |
+
.btn:disabled{opacity:.5;cursor:default}
|
| 44 |
+
.btns{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}
|
| 45 |
+
.out{background:var(--code);border-radius:10px;padding:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;word-break:break-all;min-height:46px}
|
| 46 |
+
.warn{background:var(--warn-soft);color:var(--warn);border-radius:10px;padding:8px 12px;font-size:13px;margin-top:10px}
|
| 47 |
+
.warn:empty{display:none}
|
| 48 |
+
.check{display:flex;align-items:center;gap:8px;font-weight:600;font-size:13px;margin:8px 0}
|
| 49 |
+
.check input{width:18px;height:18px}
|
| 50 |
+
table{width:100%;border-collapse:collapse;font-size:13px}
|
| 51 |
+
th,td{text-align:left;padding:7px 8px;border-bottom:1px solid var(--line);vertical-align:top}
|
| 52 |
+
th{font-size:12px;color:var(--muted);text-transform:uppercase;letter-spacing:.5px}
|
| 53 |
+
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums}
|
| 54 |
+
.table-wrap{overflow-x:auto}
|
| 55 |
+
.big{font-size:clamp(26px,5vw,38px);font-weight:800;font-variant-numeric:tabular-nums;line-height:1.1}
|
| 56 |
+
.stat{background:var(--bg);border-radius:12px;padding:12px}
|
| 57 |
+
.stat .k{font-size:12px;color:var(--muted);font-weight:700;text-transform:uppercase;letter-spacing:.5px}
|
| 58 |
+
.stat .v{font-size:22px;font-weight:800;font-variant-numeric:tabular-nums}
|
| 59 |
+
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px}
|
| 60 |
+
.related{border:1px dashed var(--accent);background:var(--accent-soft);border-radius:14px;padding:14px 16px;margin:18px 0}
|
| 61 |
+
.related h2{font-size:15px;margin:0 0 6px}
|
| 62 |
+
.related p{margin:4px 0;font-size:14px}
|
| 63 |
+
.seo{color:var(--muted);font-size:14px;margin-top:10px}
|
| 64 |
+
.seo h2{color:var(--ink);font-size:18px;margin:22px 0 6px}
|
| 65 |
+
.seo h3{color:var(--ink);font-size:15px;margin:14px 0 4px}
|
| 66 |
+
.more{border-top:1px solid var(--line);margin-top:28px;padding:18px 0 22px;text-align:center;font-size:14px}
|
| 67 |
+
.more-title{font-weight:700;margin-bottom:8px}
|
| 68 |
+
.more-links{display:flex;flex-wrap:wrap;gap:8px;justify-content:center}
|
| 69 |
+
.more-links a{color:var(--ink);background:var(--surface);border:1px solid var(--line);border-radius:999px;padding:6px 12px;text-decoration:none}
|
| 70 |
+
.more-links a:hover{border-color:var(--accent)}
|
| 71 |
+
.copy{margin-top:12px;color:var(--muted);font-size:12px}
|
| 72 |
+
.toast{position:fixed;bottom:18px;left:50%;transform:translateX(-50%);background:var(--ink);color:var(--bg);padding:10px 16px;border-radius:10px;font-weight:600;opacity:0;pointer-events:none;transition:opacity .2s;z-index:20}
|
| 73 |
+
.toast.show{opacity:1}
|
| 74 |
+
@media (max-width:760px){.grid,.row3{grid-template-columns:minmax(0,1fr)}}
|
| 75 |
+
@media (max-width:420px){.row2{grid-template-columns:minmax(0,1fr)}}
|
utm-builder/sw.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* UTM Builder offline cache. © 2026 CyberMax. All rights reserved. */
|
| 2 |
+
var CACHE = "utm-builder-v1";
|
| 3 |
+
var FILES = ["./", "index.html", "style.css", "utm.js", "app.js", "vendor/qrcode.js", "manifest.webmanifest", "icon.svg", "icon-192.png", "icon-512.png"];
|
| 4 |
+
self.addEventListener("install", function (e) {
|
| 5 |
+
e.waitUntil(caches.open(CACHE).then(function (c) { return c.addAll(FILES); }).then(function () { return self.skipWaiting(); }));
|
| 6 |
+
});
|
| 7 |
+
self.addEventListener("activate", function (e) {
|
| 8 |
+
e.waitUntil(caches.keys().then(function (keys) {
|
| 9 |
+
return Promise.all(keys.filter(function (k) { return k !== CACHE; }).map(function (k) { return caches.delete(k); }));
|
| 10 |
+
}).then(function () { return self.clients.claim(); }));
|
| 11 |
+
});
|
| 12 |
+
self.addEventListener("fetch", function (e) {
|
| 13 |
+
if (e.request.method !== "GET" || new URL(e.request.url).origin !== location.origin) return;
|
| 14 |
+
// Network first so updates arrive quickly; cache as offline fallback.
|
| 15 |
+
e.respondWith(fetch(e.request).then(function (r) {
|
| 16 |
+
var copy = r.clone();
|
| 17 |
+
caches.open(CACHE).then(function (c) { c.put(e.request, copy); });
|
| 18 |
+
return r;
|
| 19 |
+
}).catch(function () { return caches.match(e.request, { ignoreSearch: true }); }));
|
| 20 |
+
});
|
utm-builder/utm.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* UTM Builder core (pure, no DOM). © 2026 CyberMax. All rights reserved. */
|
| 2 |
+
(function (root) {
|
| 3 |
+
"use strict";
|
| 4 |
+
|
| 5 |
+
var KEYS = ["utm_source", "utm_medium", "utm_campaign", "utm_id", "utm_term", "utm_content"];
|
| 6 |
+
// Mediums that GA4's default channel groups recognise (patterns from Google's published rules, simplified).
|
| 7 |
+
var KNOWN_MEDIUM = /^(.*cp.*|ppc|retargeting|paid.*|e[-_ ]?mail|display|banner|expandable|interstitial|affiliate|referral|app|link|social|social[-_ ]network|social[-_ ]media|sm|organic|video|audio|sms|push|mobile|notification|qr|print|offline)$/;
|
| 8 |
+
|
| 9 |
+
var PRESETS = {
|
| 10 |
+
"Google Ads": { utm_source: "google", utm_medium: "cpc" },
|
| 11 |
+
"Microsoft Ads": { utm_source: "bing", utm_medium: "cpc" },
|
| 12 |
+
"Meta Ads": { utm_source: "facebook", utm_medium: "paid_social" },
|
| 13 |
+
"Instagram bio": { utm_source: "instagram", utm_medium: "social" },
|
| 14 |
+
"LinkedIn post": { utm_source: "linkedin", utm_medium: "social" },
|
| 15 |
+
"X / Twitter": { utm_source: "twitter", utm_medium: "social" },
|
| 16 |
+
"TikTok": { utm_source: "tiktok", utm_medium: "social" },
|
| 17 |
+
"YouTube": { utm_source: "youtube", utm_medium: "video" },
|
| 18 |
+
"Newsletter": { utm_source: "newsletter", utm_medium: "email" },
|
| 19 |
+
"QR / print": { utm_source: "qr", utm_medium: "print" },
|
| 20 |
+
"Affiliate": { utm_source: "partner", utm_medium: "affiliate" }
|
| 21 |
+
};
|
| 22 |
+
|
| 23 |
+
function clean(v, opts) {
|
| 24 |
+
v = String(v == null ? "" : v).trim();
|
| 25 |
+
if (opts.lower) v = v.toLowerCase();
|
| 26 |
+
if (opts.space && opts.space !== "keep") v = v.replace(/\s+/g, opts.space);
|
| 27 |
+
return v;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
function normalizeBase(base) {
|
| 31 |
+
base = String(base || "").trim();
|
| 32 |
+
if (!base) return { error: "Enter the page URL you want to track." };
|
| 33 |
+
var added = false;
|
| 34 |
+
if (!/^[a-z][a-z0-9+.-]*:\/\//i.test(base)) { base = "https://" + base; added = true; }
|
| 35 |
+
var u;
|
| 36 |
+
try { u = new URL(base); } catch (e) { return { error: "That doesn't look like a valid URL." }; }
|
| 37 |
+
if (!/^https?:$/.test(u.protocol)) return { error: "Use an http:// or https:// URL." };
|
| 38 |
+
if (!/\./.test(u.hostname) && u.hostname !== "localhost") return { error: "That URL has no domain name." };
|
| 39 |
+
return { url: u, added: added };
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
// Build a tagged URL. params: {utm_source, ...}; opts: {lower: bool, space: "-"|"_"|"%20"|"keep"}.
|
| 43 |
+
function build(base, params, opts) {
|
| 44 |
+
opts = opts || { lower: true, space: "-" };
|
| 45 |
+
var warnings = [];
|
| 46 |
+
var nb = normalizeBase(base);
|
| 47 |
+
if (nb.error) return { url: "", error: nb.error, warnings: [] };
|
| 48 |
+
var u = nb.url;
|
| 49 |
+
if (nb.added) warnings.push("Added https:// to the URL.");
|
| 50 |
+
var replaced = [];
|
| 51 |
+
var kept = [];
|
| 52 |
+
u.search.replace(/^\?/, "").split("&").forEach(function (pair) {
|
| 53 |
+
if (!pair) return;
|
| 54 |
+
var k = decodeURIComponent(pair.split("=")[0].replace(/\+/g, " ")).toLowerCase();
|
| 55 |
+
if (KEYS.indexOf(k) >= 0) replaced.push(k); else kept.push(pair);
|
| 56 |
+
});
|
| 57 |
+
var tags = [];
|
| 58 |
+
KEYS.forEach(function (k) {
|
| 59 |
+
var raw = params[k];
|
| 60 |
+
var v = clean(raw, opts);
|
| 61 |
+
if (v) tags.push(k + "=" + encodeURIComponent(v).replace(/%2F/gi, "/"));
|
| 62 |
+
if (raw && /\s/.test(String(raw).trim()) && opts.space === "keep") warnings.push(k + " contains spaces; they'll be encoded as %20.");
|
| 63 |
+
if (raw && !opts.lower && /[A-Z]/.test(raw)) warnings.push(k + " has capitals. Analytics treats \"Facebook\" and \"facebook\" as different sources.");
|
| 64 |
+
});
|
| 65 |
+
["utm_source", "utm_medium", "utm_campaign"].forEach(function (k) {
|
| 66 |
+
if (!clean(params[k], opts)) warnings.push(k + " is empty. GA4 needs source, medium and campaign to report the visit properly.");
|
| 67 |
+
});
|
| 68 |
+
var med = clean(params.utm_medium, opts);
|
| 69 |
+
if (med && !KNOWN_MEDIUM.test(med.toLowerCase())) warnings.push("utm_medium \"" + med + "\" isn't one GA4's default channel groups recognise, so these visits may show as \"Unassigned\". Try cpc, email, social, paid_social, display, affiliate or referral.");
|
| 70 |
+
if (replaced.length) warnings.push("Replaced existing " + replaced.join(", ") + " in the URL.");
|
| 71 |
+
var search = kept.concat(tags).join("&");
|
| 72 |
+
var out = u.origin + u.pathname + (search ? "?" + search : "") + u.hash;
|
| 73 |
+
if (out.length > 2000) warnings.push("The URL is over 2,000 characters; some platforms truncate long links.");
|
| 74 |
+
return { url: out, warnings: warnings, tagged: tags.length > 0 };
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
function parse(url) {
|
| 78 |
+
var nb = normalizeBase(url);
|
| 79 |
+
if (nb.error) return { error: nb.error };
|
| 80 |
+
var u = nb.url, utm = {}, other = [];
|
| 81 |
+
u.searchParams.forEach(function (v, k) {
|
| 82 |
+
if (/^utm_/i.test(k)) utm[k.toLowerCase()] = v; else other.push(k + "=" + v);
|
| 83 |
+
});
|
| 84 |
+
var cleanUrl = u.origin + u.pathname;
|
| 85 |
+
var rest = [];
|
| 86 |
+
u.search.replace(/^\?/, "").split("&").forEach(function (p) { if (p && !/^utm_/i.test(p)) rest.push(p); });
|
| 87 |
+
if (rest.length) cleanUrl += "?" + rest.join("&");
|
| 88 |
+
cleanUrl += u.hash;
|
| 89 |
+
return { utm: utm, clean: cleanUrl, other: other };
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
// CSV line splitter with quotes.
|
| 93 |
+
function splitCsv(line) {
|
| 94 |
+
var out = [], cur = "", q = false;
|
| 95 |
+
for (var i = 0; i < line.length; i++) {
|
| 96 |
+
var c = line[i];
|
| 97 |
+
if (q) { if (c === '"' && line[i + 1] === '"') { cur += '"'; i++; } else if (c === '"') q = false; else cur += c; }
|
| 98 |
+
else if (c === '"') q = true;
|
| 99 |
+
else if (c === "," || c === "\t") { out.push(cur); cur = ""; }
|
| 100 |
+
else cur += c;
|
| 101 |
+
}
|
| 102 |
+
out.push(cur);
|
| 103 |
+
return out.map(function (s) { return s.trim(); });
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
// Bulk: each line "url" or "url,source,medium,campaign,term,content". Blank columns use defaults.
|
| 107 |
+
var BULK_COLS = ["url", "utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
|
| 108 |
+
function bulk(text, defaults, opts) {
|
| 109 |
+
var lines = String(text || "").split(/\r?\n/).map(function (l) { return l.trim(); }).filter(Boolean);
|
| 110 |
+
var cols = BULK_COLS;
|
| 111 |
+
if (lines.length && /^url\b/i.test(lines[0])) {
|
| 112 |
+
cols = splitCsv(lines.shift()).map(function (h) { h = h.toLowerCase().replace(/^utm_/, ""); return h === "url" ? "url" : "utm_" + h; });
|
| 113 |
+
}
|
| 114 |
+
return lines.slice(0, 5000).map(function (line) {
|
| 115 |
+
var cells = splitCsv(line), p = {};
|
| 116 |
+
KEYS.forEach(function (k) { p[k] = defaults[k] || ""; });
|
| 117 |
+
var url = "";
|
| 118 |
+
cols.forEach(function (c, i) { if (c === "url") url = cells[i] || ""; else if (cells[i]) p[c] = cells[i]; });
|
| 119 |
+
var r = build(url, p, opts);
|
| 120 |
+
return { input: url, url: r.url, error: r.error || "", warnings: r.warnings || [] };
|
| 121 |
+
});
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
function csvCell(s) { s = String(s == null ? "" : s); return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s; }
|
| 125 |
+
function toCsv(rows) {
|
| 126 |
+
return "input_url,tagged_url,error\n" + rows.map(function (r) { return [r.input, r.url, r.error].map(csvCell).join(","); }).join("\n") + "\n";
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
var api = { KEYS: KEYS, PRESETS: PRESETS, build: build, parse: parse, bulk: bulk, toCsv: toCsv, splitCsv: splitCsv };
|
| 130 |
+
root.UtmCore = api;
|
| 131 |
+
if (typeof module !== "undefined") module.exports = api;
|
| 132 |
+
})(typeof window !== "undefined" ? window : globalThis);
|
utm-builder/vendor/LICENSE-qrcode-generator.txt
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
qrcode-generator 1.4.4 (vendor/qrcode.js) — https://github.com/kazuhikoarase/qrcode-generator
|
| 2 |
+
|
| 3 |
+
The MIT License (MIT)
|
| 4 |
+
|
| 5 |
+
Copyright (c) 2009 Kazuhiko Arase
|
| 6 |
+
|
| 7 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 8 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 9 |
+
in the Software without restriction, including without limitation the rights
|
| 10 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 11 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 12 |
+
furnished to do so, subject to the following conditions:
|
| 13 |
+
|
| 14 |
+
The above copyright notice and this permission notice shall be included in
|
| 15 |
+
all copies or substantial portions of the Software.
|
| 16 |
+
|
| 17 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 18 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 19 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 20 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 21 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 22 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
| 23 |
+
THE SOFTWARE.
|
| 24 |
+
|
| 25 |
+
The word "QR Code" is a registered trademark of DENSO WAVE INCORPORATED.
|
utm-builder/vendor/qrcode.js
ADDED
|
@@ -0,0 +1,2297 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//---------------------------------------------------------------------
|
| 2 |
+
//
|
| 3 |
+
// QR Code Generator for JavaScript
|
| 4 |
+
//
|
| 5 |
+
// Copyright (c) 2009 Kazuhiko Arase
|
| 6 |
+
//
|
| 7 |
+
// URL: http://www.d-project.com/
|
| 8 |
+
//
|
| 9 |
+
// Licensed under the MIT license:
|
| 10 |
+
// http://www.opensource.org/licenses/mit-license.php
|
| 11 |
+
//
|
| 12 |
+
// The word 'QR Code' is registered trademark of
|
| 13 |
+
// DENSO WAVE INCORPORATED
|
| 14 |
+
// http://www.denso-wave.com/qrcode/faqpatent-e.html
|
| 15 |
+
//
|
| 16 |
+
//---------------------------------------------------------------------
|
| 17 |
+
|
| 18 |
+
var qrcode = function() {
|
| 19 |
+
|
| 20 |
+
//---------------------------------------------------------------------
|
| 21 |
+
// qrcode
|
| 22 |
+
//---------------------------------------------------------------------
|
| 23 |
+
|
| 24 |
+
/**
|
| 25 |
+
* qrcode
|
| 26 |
+
* @param typeNumber 1 to 40
|
| 27 |
+
* @param errorCorrectionLevel 'L','M','Q','H'
|
| 28 |
+
*/
|
| 29 |
+
var qrcode = function(typeNumber, errorCorrectionLevel) {
|
| 30 |
+
|
| 31 |
+
var PAD0 = 0xEC;
|
| 32 |
+
var PAD1 = 0x11;
|
| 33 |
+
|
| 34 |
+
var _typeNumber = typeNumber;
|
| 35 |
+
var _errorCorrectionLevel = QRErrorCorrectionLevel[errorCorrectionLevel];
|
| 36 |
+
var _modules = null;
|
| 37 |
+
var _moduleCount = 0;
|
| 38 |
+
var _dataCache = null;
|
| 39 |
+
var _dataList = [];
|
| 40 |
+
|
| 41 |
+
var _this = {};
|
| 42 |
+
|
| 43 |
+
var makeImpl = function(test, maskPattern) {
|
| 44 |
+
|
| 45 |
+
_moduleCount = _typeNumber * 4 + 17;
|
| 46 |
+
_modules = function(moduleCount) {
|
| 47 |
+
var modules = new Array(moduleCount);
|
| 48 |
+
for (var row = 0; row < moduleCount; row += 1) {
|
| 49 |
+
modules[row] = new Array(moduleCount);
|
| 50 |
+
for (var col = 0; col < moduleCount; col += 1) {
|
| 51 |
+
modules[row][col] = null;
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
return modules;
|
| 55 |
+
}(_moduleCount);
|
| 56 |
+
|
| 57 |
+
setupPositionProbePattern(0, 0);
|
| 58 |
+
setupPositionProbePattern(_moduleCount - 7, 0);
|
| 59 |
+
setupPositionProbePattern(0, _moduleCount - 7);
|
| 60 |
+
setupPositionAdjustPattern();
|
| 61 |
+
setupTimingPattern();
|
| 62 |
+
setupTypeInfo(test, maskPattern);
|
| 63 |
+
|
| 64 |
+
if (_typeNumber >= 7) {
|
| 65 |
+
setupTypeNumber(test);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
if (_dataCache == null) {
|
| 69 |
+
_dataCache = createData(_typeNumber, _errorCorrectionLevel, _dataList);
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
mapData(_dataCache, maskPattern);
|
| 73 |
+
};
|
| 74 |
+
|
| 75 |
+
var setupPositionProbePattern = function(row, col) {
|
| 76 |
+
|
| 77 |
+
for (var r = -1; r <= 7; r += 1) {
|
| 78 |
+
|
| 79 |
+
if (row + r <= -1 || _moduleCount <= row + r) continue;
|
| 80 |
+
|
| 81 |
+
for (var c = -1; c <= 7; c += 1) {
|
| 82 |
+
|
| 83 |
+
if (col + c <= -1 || _moduleCount <= col + c) continue;
|
| 84 |
+
|
| 85 |
+
if ( (0 <= r && r <= 6 && (c == 0 || c == 6) )
|
| 86 |
+
|| (0 <= c && c <= 6 && (r == 0 || r == 6) )
|
| 87 |
+
|| (2 <= r && r <= 4 && 2 <= c && c <= 4) ) {
|
| 88 |
+
_modules[row + r][col + c] = true;
|
| 89 |
+
} else {
|
| 90 |
+
_modules[row + r][col + c] = false;
|
| 91 |
+
}
|
| 92 |
+
}
|
| 93 |
+
}
|
| 94 |
+
};
|
| 95 |
+
|
| 96 |
+
var getBestMaskPattern = function() {
|
| 97 |
+
|
| 98 |
+
var minLostPoint = 0;
|
| 99 |
+
var pattern = 0;
|
| 100 |
+
|
| 101 |
+
for (var i = 0; i < 8; i += 1) {
|
| 102 |
+
|
| 103 |
+
makeImpl(true, i);
|
| 104 |
+
|
| 105 |
+
var lostPoint = QRUtil.getLostPoint(_this);
|
| 106 |
+
|
| 107 |
+
if (i == 0 || minLostPoint > lostPoint) {
|
| 108 |
+
minLostPoint = lostPoint;
|
| 109 |
+
pattern = i;
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
return pattern;
|
| 114 |
+
};
|
| 115 |
+
|
| 116 |
+
var setupTimingPattern = function() {
|
| 117 |
+
|
| 118 |
+
for (var r = 8; r < _moduleCount - 8; r += 1) {
|
| 119 |
+
if (_modules[r][6] != null) {
|
| 120 |
+
continue;
|
| 121 |
+
}
|
| 122 |
+
_modules[r][6] = (r % 2 == 0);
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
for (var c = 8; c < _moduleCount - 8; c += 1) {
|
| 126 |
+
if (_modules[6][c] != null) {
|
| 127 |
+
continue;
|
| 128 |
+
}
|
| 129 |
+
_modules[6][c] = (c % 2 == 0);
|
| 130 |
+
}
|
| 131 |
+
};
|
| 132 |
+
|
| 133 |
+
var setupPositionAdjustPattern = function() {
|
| 134 |
+
|
| 135 |
+
var pos = QRUtil.getPatternPosition(_typeNumber);
|
| 136 |
+
|
| 137 |
+
for (var i = 0; i < pos.length; i += 1) {
|
| 138 |
+
|
| 139 |
+
for (var j = 0; j < pos.length; j += 1) {
|
| 140 |
+
|
| 141 |
+
var row = pos[i];
|
| 142 |
+
var col = pos[j];
|
| 143 |
+
|
| 144 |
+
if (_modules[row][col] != null) {
|
| 145 |
+
continue;
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
for (var r = -2; r <= 2; r += 1) {
|
| 149 |
+
|
| 150 |
+
for (var c = -2; c <= 2; c += 1) {
|
| 151 |
+
|
| 152 |
+
if (r == -2 || r == 2 || c == -2 || c == 2
|
| 153 |
+
|| (r == 0 && c == 0) ) {
|
| 154 |
+
_modules[row + r][col + c] = true;
|
| 155 |
+
} else {
|
| 156 |
+
_modules[row + r][col + c] = false;
|
| 157 |
+
}
|
| 158 |
+
}
|
| 159 |
+
}
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
};
|
| 163 |
+
|
| 164 |
+
var setupTypeNumber = function(test) {
|
| 165 |
+
|
| 166 |
+
var bits = QRUtil.getBCHTypeNumber(_typeNumber);
|
| 167 |
+
|
| 168 |
+
for (var i = 0; i < 18; i += 1) {
|
| 169 |
+
var mod = (!test && ( (bits >> i) & 1) == 1);
|
| 170 |
+
_modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod;
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
for (var i = 0; i < 18; i += 1) {
|
| 174 |
+
var mod = (!test && ( (bits >> i) & 1) == 1);
|
| 175 |
+
_modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
|
| 176 |
+
}
|
| 177 |
+
};
|
| 178 |
+
|
| 179 |
+
var setupTypeInfo = function(test, maskPattern) {
|
| 180 |
+
|
| 181 |
+
var data = (_errorCorrectionLevel << 3) | maskPattern;
|
| 182 |
+
var bits = QRUtil.getBCHTypeInfo(data);
|
| 183 |
+
|
| 184 |
+
// vertical
|
| 185 |
+
for (var i = 0; i < 15; i += 1) {
|
| 186 |
+
|
| 187 |
+
var mod = (!test && ( (bits >> i) & 1) == 1);
|
| 188 |
+
|
| 189 |
+
if (i < 6) {
|
| 190 |
+
_modules[i][8] = mod;
|
| 191 |
+
} else if (i < 8) {
|
| 192 |
+
_modules[i + 1][8] = mod;
|
| 193 |
+
} else {
|
| 194 |
+
_modules[_moduleCount - 15 + i][8] = mod;
|
| 195 |
+
}
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
// horizontal
|
| 199 |
+
for (var i = 0; i < 15; i += 1) {
|
| 200 |
+
|
| 201 |
+
var mod = (!test && ( (bits >> i) & 1) == 1);
|
| 202 |
+
|
| 203 |
+
if (i < 8) {
|
| 204 |
+
_modules[8][_moduleCount - i - 1] = mod;
|
| 205 |
+
} else if (i < 9) {
|
| 206 |
+
_modules[8][15 - i - 1 + 1] = mod;
|
| 207 |
+
} else {
|
| 208 |
+
_modules[8][15 - i - 1] = mod;
|
| 209 |
+
}
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
// fixed module
|
| 213 |
+
_modules[_moduleCount - 8][8] = (!test);
|
| 214 |
+
};
|
| 215 |
+
|
| 216 |
+
var mapData = function(data, maskPattern) {
|
| 217 |
+
|
| 218 |
+
var inc = -1;
|
| 219 |
+
var row = _moduleCount - 1;
|
| 220 |
+
var bitIndex = 7;
|
| 221 |
+
var byteIndex = 0;
|
| 222 |
+
var maskFunc = QRUtil.getMaskFunction(maskPattern);
|
| 223 |
+
|
| 224 |
+
for (var col = _moduleCount - 1; col > 0; col -= 2) {
|
| 225 |
+
|
| 226 |
+
if (col == 6) col -= 1;
|
| 227 |
+
|
| 228 |
+
while (true) {
|
| 229 |
+
|
| 230 |
+
for (var c = 0; c < 2; c += 1) {
|
| 231 |
+
|
| 232 |
+
if (_modules[row][col - c] == null) {
|
| 233 |
+
|
| 234 |
+
var dark = false;
|
| 235 |
+
|
| 236 |
+
if (byteIndex < data.length) {
|
| 237 |
+
dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1);
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
var mask = maskFunc(row, col - c);
|
| 241 |
+
|
| 242 |
+
if (mask) {
|
| 243 |
+
dark = !dark;
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
_modules[row][col - c] = dark;
|
| 247 |
+
bitIndex -= 1;
|
| 248 |
+
|
| 249 |
+
if (bitIndex == -1) {
|
| 250 |
+
byteIndex += 1;
|
| 251 |
+
bitIndex = 7;
|
| 252 |
+
}
|
| 253 |
+
}
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
row += inc;
|
| 257 |
+
|
| 258 |
+
if (row < 0 || _moduleCount <= row) {
|
| 259 |
+
row -= inc;
|
| 260 |
+
inc = -inc;
|
| 261 |
+
break;
|
| 262 |
+
}
|
| 263 |
+
}
|
| 264 |
+
}
|
| 265 |
+
};
|
| 266 |
+
|
| 267 |
+
var createBytes = function(buffer, rsBlocks) {
|
| 268 |
+
|
| 269 |
+
var offset = 0;
|
| 270 |
+
|
| 271 |
+
var maxDcCount = 0;
|
| 272 |
+
var maxEcCount = 0;
|
| 273 |
+
|
| 274 |
+
var dcdata = new Array(rsBlocks.length);
|
| 275 |
+
var ecdata = new Array(rsBlocks.length);
|
| 276 |
+
|
| 277 |
+
for (var r = 0; r < rsBlocks.length; r += 1) {
|
| 278 |
+
|
| 279 |
+
var dcCount = rsBlocks[r].dataCount;
|
| 280 |
+
var ecCount = rsBlocks[r].totalCount - dcCount;
|
| 281 |
+
|
| 282 |
+
maxDcCount = Math.max(maxDcCount, dcCount);
|
| 283 |
+
maxEcCount = Math.max(maxEcCount, ecCount);
|
| 284 |
+
|
| 285 |
+
dcdata[r] = new Array(dcCount);
|
| 286 |
+
|
| 287 |
+
for (var i = 0; i < dcdata[r].length; i += 1) {
|
| 288 |
+
dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset];
|
| 289 |
+
}
|
| 290 |
+
offset += dcCount;
|
| 291 |
+
|
| 292 |
+
var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
|
| 293 |
+
var rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1);
|
| 294 |
+
|
| 295 |
+
var modPoly = rawPoly.mod(rsPoly);
|
| 296 |
+
ecdata[r] = new Array(rsPoly.getLength() - 1);
|
| 297 |
+
for (var i = 0; i < ecdata[r].length; i += 1) {
|
| 298 |
+
var modIndex = i + modPoly.getLength() - ecdata[r].length;
|
| 299 |
+
ecdata[r][i] = (modIndex >= 0)? modPoly.getAt(modIndex) : 0;
|
| 300 |
+
}
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
var totalCodeCount = 0;
|
| 304 |
+
for (var i = 0; i < rsBlocks.length; i += 1) {
|
| 305 |
+
totalCodeCount += rsBlocks[i].totalCount;
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
var data = new Array(totalCodeCount);
|
| 309 |
+
var index = 0;
|
| 310 |
+
|
| 311 |
+
for (var i = 0; i < maxDcCount; i += 1) {
|
| 312 |
+
for (var r = 0; r < rsBlocks.length; r += 1) {
|
| 313 |
+
if (i < dcdata[r].length) {
|
| 314 |
+
data[index] = dcdata[r][i];
|
| 315 |
+
index += 1;
|
| 316 |
+
}
|
| 317 |
+
}
|
| 318 |
+
}
|
| 319 |
+
|
| 320 |
+
for (var i = 0; i < maxEcCount; i += 1) {
|
| 321 |
+
for (var r = 0; r < rsBlocks.length; r += 1) {
|
| 322 |
+
if (i < ecdata[r].length) {
|
| 323 |
+
data[index] = ecdata[r][i];
|
| 324 |
+
index += 1;
|
| 325 |
+
}
|
| 326 |
+
}
|
| 327 |
+
}
|
| 328 |
+
|
| 329 |
+
return data;
|
| 330 |
+
};
|
| 331 |
+
|
| 332 |
+
var createData = function(typeNumber, errorCorrectionLevel, dataList) {
|
| 333 |
+
|
| 334 |
+
var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectionLevel);
|
| 335 |
+
|
| 336 |
+
var buffer = qrBitBuffer();
|
| 337 |
+
|
| 338 |
+
for (var i = 0; i < dataList.length; i += 1) {
|
| 339 |
+
var data = dataList[i];
|
| 340 |
+
buffer.put(data.getMode(), 4);
|
| 341 |
+
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) );
|
| 342 |
+
data.write(buffer);
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
// calc num max data.
|
| 346 |
+
var totalDataCount = 0;
|
| 347 |
+
for (var i = 0; i < rsBlocks.length; i += 1) {
|
| 348 |
+
totalDataCount += rsBlocks[i].dataCount;
|
| 349 |
+
}
|
| 350 |
+
|
| 351 |
+
if (buffer.getLengthInBits() > totalDataCount * 8) {
|
| 352 |
+
throw 'code length overflow. ('
|
| 353 |
+
+ buffer.getLengthInBits()
|
| 354 |
+
+ '>'
|
| 355 |
+
+ totalDataCount * 8
|
| 356 |
+
+ ')';
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
// end code
|
| 360 |
+
if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
|
| 361 |
+
buffer.put(0, 4);
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
// padding
|
| 365 |
+
while (buffer.getLengthInBits() % 8 != 0) {
|
| 366 |
+
buffer.putBit(false);
|
| 367 |
+
}
|
| 368 |
+
|
| 369 |
+
// padding
|
| 370 |
+
while (true) {
|
| 371 |
+
|
| 372 |
+
if (buffer.getLengthInBits() >= totalDataCount * 8) {
|
| 373 |
+
break;
|
| 374 |
+
}
|
| 375 |
+
buffer.put(PAD0, 8);
|
| 376 |
+
|
| 377 |
+
if (buffer.getLengthInBits() >= totalDataCount * 8) {
|
| 378 |
+
break;
|
| 379 |
+
}
|
| 380 |
+
buffer.put(PAD1, 8);
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
return createBytes(buffer, rsBlocks);
|
| 384 |
+
};
|
| 385 |
+
|
| 386 |
+
_this.addData = function(data, mode) {
|
| 387 |
+
|
| 388 |
+
mode = mode || 'Byte';
|
| 389 |
+
|
| 390 |
+
var newData = null;
|
| 391 |
+
|
| 392 |
+
switch(mode) {
|
| 393 |
+
case 'Numeric' :
|
| 394 |
+
newData = qrNumber(data);
|
| 395 |
+
break;
|
| 396 |
+
case 'Alphanumeric' :
|
| 397 |
+
newData = qrAlphaNum(data);
|
| 398 |
+
break;
|
| 399 |
+
case 'Byte' :
|
| 400 |
+
newData = qr8BitByte(data);
|
| 401 |
+
break;
|
| 402 |
+
case 'Kanji' :
|
| 403 |
+
newData = qrKanji(data);
|
| 404 |
+
break;
|
| 405 |
+
default :
|
| 406 |
+
throw 'mode:' + mode;
|
| 407 |
+
}
|
| 408 |
+
|
| 409 |
+
_dataList.push(newData);
|
| 410 |
+
_dataCache = null;
|
| 411 |
+
};
|
| 412 |
+
|
| 413 |
+
_this.isDark = function(row, col) {
|
| 414 |
+
if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) {
|
| 415 |
+
throw row + ',' + col;
|
| 416 |
+
}
|
| 417 |
+
return _modules[row][col];
|
| 418 |
+
};
|
| 419 |
+
|
| 420 |
+
_this.getModuleCount = function() {
|
| 421 |
+
return _moduleCount;
|
| 422 |
+
};
|
| 423 |
+
|
| 424 |
+
_this.make = function() {
|
| 425 |
+
if (_typeNumber < 1) {
|
| 426 |
+
var typeNumber = 1;
|
| 427 |
+
|
| 428 |
+
for (; typeNumber < 40; typeNumber++) {
|
| 429 |
+
var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, _errorCorrectionLevel);
|
| 430 |
+
var buffer = qrBitBuffer();
|
| 431 |
+
|
| 432 |
+
for (var i = 0; i < _dataList.length; i++) {
|
| 433 |
+
var data = _dataList[i];
|
| 434 |
+
buffer.put(data.getMode(), 4);
|
| 435 |
+
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) );
|
| 436 |
+
data.write(buffer);
|
| 437 |
+
}
|
| 438 |
+
|
| 439 |
+
var totalDataCount = 0;
|
| 440 |
+
for (var i = 0; i < rsBlocks.length; i++) {
|
| 441 |
+
totalDataCount += rsBlocks[i].dataCount;
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
if (buffer.getLengthInBits() <= totalDataCount * 8) {
|
| 445 |
+
break;
|
| 446 |
+
}
|
| 447 |
+
}
|
| 448 |
+
|
| 449 |
+
_typeNumber = typeNumber;
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
makeImpl(false, getBestMaskPattern() );
|
| 453 |
+
};
|
| 454 |
+
|
| 455 |
+
_this.createTableTag = function(cellSize, margin) {
|
| 456 |
+
|
| 457 |
+
cellSize = cellSize || 2;
|
| 458 |
+
margin = (typeof margin == 'undefined')? cellSize * 4 : margin;
|
| 459 |
+
|
| 460 |
+
var qrHtml = '';
|
| 461 |
+
|
| 462 |
+
qrHtml += '<table style="';
|
| 463 |
+
qrHtml += ' border-width: 0px; border-style: none;';
|
| 464 |
+
qrHtml += ' border-collapse: collapse;';
|
| 465 |
+
qrHtml += ' padding: 0px; margin: ' + margin + 'px;';
|
| 466 |
+
qrHtml += '">';
|
| 467 |
+
qrHtml += '<tbody>';
|
| 468 |
+
|
| 469 |
+
for (var r = 0; r < _this.getModuleCount(); r += 1) {
|
| 470 |
+
|
| 471 |
+
qrHtml += '<tr>';
|
| 472 |
+
|
| 473 |
+
for (var c = 0; c < _this.getModuleCount(); c += 1) {
|
| 474 |
+
qrHtml += '<td style="';
|
| 475 |
+
qrHtml += ' border-width: 0px; border-style: none;';
|
| 476 |
+
qrHtml += ' border-collapse: collapse;';
|
| 477 |
+
qrHtml += ' padding: 0px; margin: 0px;';
|
| 478 |
+
qrHtml += ' width: ' + cellSize + 'px;';
|
| 479 |
+
qrHtml += ' height: ' + cellSize + 'px;';
|
| 480 |
+
qrHtml += ' background-color: ';
|
| 481 |
+
qrHtml += _this.isDark(r, c)? '#000000' : '#ffffff';
|
| 482 |
+
qrHtml += ';';
|
| 483 |
+
qrHtml += '"/>';
|
| 484 |
+
}
|
| 485 |
+
|
| 486 |
+
qrHtml += '</tr>';
|
| 487 |
+
}
|
| 488 |
+
|
| 489 |
+
qrHtml += '</tbody>';
|
| 490 |
+
qrHtml += '</table>';
|
| 491 |
+
|
| 492 |
+
return qrHtml;
|
| 493 |
+
};
|
| 494 |
+
|
| 495 |
+
_this.createSvgTag = function(cellSize, margin, alt, title) {
|
| 496 |
+
|
| 497 |
+
var opts = {};
|
| 498 |
+
if (typeof arguments[0] == 'object') {
|
| 499 |
+
// Called by options.
|
| 500 |
+
opts = arguments[0];
|
| 501 |
+
// overwrite cellSize and margin.
|
| 502 |
+
cellSize = opts.cellSize;
|
| 503 |
+
margin = opts.margin;
|
| 504 |
+
alt = opts.alt;
|
| 505 |
+
title = opts.title;
|
| 506 |
+
}
|
| 507 |
+
|
| 508 |
+
cellSize = cellSize || 2;
|
| 509 |
+
margin = (typeof margin == 'undefined')? cellSize * 4 : margin;
|
| 510 |
+
|
| 511 |
+
// Compose alt property surrogate
|
| 512 |
+
alt = (typeof alt === 'string') ? {text: alt} : alt || {};
|
| 513 |
+
alt.text = alt.text || null;
|
| 514 |
+
alt.id = (alt.text) ? alt.id || 'qrcode-description' : null;
|
| 515 |
+
|
| 516 |
+
// Compose title property surrogate
|
| 517 |
+
title = (typeof title === 'string') ? {text: title} : title || {};
|
| 518 |
+
title.text = title.text || null;
|
| 519 |
+
title.id = (title.text) ? title.id || 'qrcode-title' : null;
|
| 520 |
+
|
| 521 |
+
var size = _this.getModuleCount() * cellSize + margin * 2;
|
| 522 |
+
var c, mc, r, mr, qrSvg='', rect;
|
| 523 |
+
|
| 524 |
+
rect = 'l' + cellSize + ',0 0,' + cellSize +
|
| 525 |
+
' -' + cellSize + ',0 0,-' + cellSize + 'z ';
|
| 526 |
+
|
| 527 |
+
qrSvg += '<svg version="1.1" xmlns="http://www.w3.org/2000/svg"';
|
| 528 |
+
qrSvg += !opts.scalable ? ' width="' + size + 'px" height="' + size + 'px"' : '';
|
| 529 |
+
qrSvg += ' viewBox="0 0 ' + size + ' ' + size + '" ';
|
| 530 |
+
qrSvg += ' preserveAspectRatio="xMinYMin meet"';
|
| 531 |
+
qrSvg += (title.text || alt.text) ? ' role="img" aria-labelledby="' +
|
| 532 |
+
escapeXml([title.id, alt.id].join(' ').trim() ) + '"' : '';
|
| 533 |
+
qrSvg += '>';
|
| 534 |
+
qrSvg += (title.text) ? '<title id="' + escapeXml(title.id) + '">' +
|
| 535 |
+
escapeXml(title.text) + '</title>' : '';
|
| 536 |
+
qrSvg += (alt.text) ? '<description id="' + escapeXml(alt.id) + '">' +
|
| 537 |
+
escapeXml(alt.text) + '</description>' : '';
|
| 538 |
+
qrSvg += '<rect width="100%" height="100%" fill="white" cx="0" cy="0"/>';
|
| 539 |
+
qrSvg += '<path d="';
|
| 540 |
+
|
| 541 |
+
for (r = 0; r < _this.getModuleCount(); r += 1) {
|
| 542 |
+
mr = r * cellSize + margin;
|
| 543 |
+
for (c = 0; c < _this.getModuleCount(); c += 1) {
|
| 544 |
+
if (_this.isDark(r, c) ) {
|
| 545 |
+
mc = c*cellSize+margin;
|
| 546 |
+
qrSvg += 'M' + mc + ',' + mr + rect;
|
| 547 |
+
}
|
| 548 |
+
}
|
| 549 |
+
}
|
| 550 |
+
|
| 551 |
+
qrSvg += '" stroke="transparent" fill="black"/>';
|
| 552 |
+
qrSvg += '</svg>';
|
| 553 |
+
|
| 554 |
+
return qrSvg;
|
| 555 |
+
};
|
| 556 |
+
|
| 557 |
+
_this.createDataURL = function(cellSize, margin) {
|
| 558 |
+
|
| 559 |
+
cellSize = cellSize || 2;
|
| 560 |
+
margin = (typeof margin == 'undefined')? cellSize * 4 : margin;
|
| 561 |
+
|
| 562 |
+
var size = _this.getModuleCount() * cellSize + margin * 2;
|
| 563 |
+
var min = margin;
|
| 564 |
+
var max = size - margin;
|
| 565 |
+
|
| 566 |
+
return createDataURL(size, size, function(x, y) {
|
| 567 |
+
if (min <= x && x < max && min <= y && y < max) {
|
| 568 |
+
var c = Math.floor( (x - min) / cellSize);
|
| 569 |
+
var r = Math.floor( (y - min) / cellSize);
|
| 570 |
+
return _this.isDark(r, c)? 0 : 1;
|
| 571 |
+
} else {
|
| 572 |
+
return 1;
|
| 573 |
+
}
|
| 574 |
+
} );
|
| 575 |
+
};
|
| 576 |
+
|
| 577 |
+
_this.createImgTag = function(cellSize, margin, alt) {
|
| 578 |
+
|
| 579 |
+
cellSize = cellSize || 2;
|
| 580 |
+
margin = (typeof margin == 'undefined')? cellSize * 4 : margin;
|
| 581 |
+
|
| 582 |
+
var size = _this.getModuleCount() * cellSize + margin * 2;
|
| 583 |
+
|
| 584 |
+
var img = '';
|
| 585 |
+
img += '<img';
|
| 586 |
+
img += '\u0020src="';
|
| 587 |
+
img += _this.createDataURL(cellSize, margin);
|
| 588 |
+
img += '"';
|
| 589 |
+
img += '\u0020width="';
|
| 590 |
+
img += size;
|
| 591 |
+
img += '"';
|
| 592 |
+
img += '\u0020height="';
|
| 593 |
+
img += size;
|
| 594 |
+
img += '"';
|
| 595 |
+
if (alt) {
|
| 596 |
+
img += '\u0020alt="';
|
| 597 |
+
img += escapeXml(alt);
|
| 598 |
+
img += '"';
|
| 599 |
+
}
|
| 600 |
+
img += '/>';
|
| 601 |
+
|
| 602 |
+
return img;
|
| 603 |
+
};
|
| 604 |
+
|
| 605 |
+
var escapeXml = function(s) {
|
| 606 |
+
var escaped = '';
|
| 607 |
+
for (var i = 0; i < s.length; i += 1) {
|
| 608 |
+
var c = s.charAt(i);
|
| 609 |
+
switch(c) {
|
| 610 |
+
case '<': escaped += '<'; break;
|
| 611 |
+
case '>': escaped += '>'; break;
|
| 612 |
+
case '&': escaped += '&'; break;
|
| 613 |
+
case '"': escaped += '"'; break;
|
| 614 |
+
default : escaped += c; break;
|
| 615 |
+
}
|
| 616 |
+
}
|
| 617 |
+
return escaped;
|
| 618 |
+
};
|
| 619 |
+
|
| 620 |
+
var _createHalfASCII = function(margin) {
|
| 621 |
+
var cellSize = 1;
|
| 622 |
+
margin = (typeof margin == 'undefined')? cellSize * 2 : margin;
|
| 623 |
+
|
| 624 |
+
var size = _this.getModuleCount() * cellSize + margin * 2;
|
| 625 |
+
var min = margin;
|
| 626 |
+
var max = size - margin;
|
| 627 |
+
|
| 628 |
+
var y, x, r1, r2, p;
|
| 629 |
+
|
| 630 |
+
var blocks = {
|
| 631 |
+
'██': '█',
|
| 632 |
+
'█ ': '▀',
|
| 633 |
+
' █': '▄',
|
| 634 |
+
' ': ' '
|
| 635 |
+
};
|
| 636 |
+
|
| 637 |
+
var blocksLastLineNoMargin = {
|
| 638 |
+
'██': '▀',
|
| 639 |
+
'█ ': '▀',
|
| 640 |
+
' █': ' ',
|
| 641 |
+
' ': ' '
|
| 642 |
+
};
|
| 643 |
+
|
| 644 |
+
var ascii = '';
|
| 645 |
+
for (y = 0; y < size; y += 2) {
|
| 646 |
+
r1 = Math.floor((y - min) / cellSize);
|
| 647 |
+
r2 = Math.floor((y + 1 - min) / cellSize);
|
| 648 |
+
for (x = 0; x < size; x += 1) {
|
| 649 |
+
p = '█';
|
| 650 |
+
|
| 651 |
+
if (min <= x && x < max && min <= y && y < max && _this.isDark(r1, Math.floor((x - min) / cellSize))) {
|
| 652 |
+
p = ' ';
|
| 653 |
+
}
|
| 654 |
+
|
| 655 |
+
if (min <= x && x < max && min <= y+1 && y+1 < max && _this.isDark(r2, Math.floor((x - min) / cellSize))) {
|
| 656 |
+
p += ' ';
|
| 657 |
+
}
|
| 658 |
+
else {
|
| 659 |
+
p += '█';
|
| 660 |
+
}
|
| 661 |
+
|
| 662 |
+
// Output 2 characters per pixel, to create full square. 1 character per pixels gives only half width of square.
|
| 663 |
+
ascii += (margin < 1 && y+1 >= max) ? blocksLastLineNoMargin[p] : blocks[p];
|
| 664 |
+
}
|
| 665 |
+
|
| 666 |
+
ascii += '\n';
|
| 667 |
+
}
|
| 668 |
+
|
| 669 |
+
if (size % 2 && margin > 0) {
|
| 670 |
+
return ascii.substring(0, ascii.length - size - 1) + Array(size+1).join('▀');
|
| 671 |
+
}
|
| 672 |
+
|
| 673 |
+
return ascii.substring(0, ascii.length-1);
|
| 674 |
+
};
|
| 675 |
+
|
| 676 |
+
_this.createASCII = function(cellSize, margin) {
|
| 677 |
+
cellSize = cellSize || 1;
|
| 678 |
+
|
| 679 |
+
if (cellSize < 2) {
|
| 680 |
+
return _createHalfASCII(margin);
|
| 681 |
+
}
|
| 682 |
+
|
| 683 |
+
cellSize -= 1;
|
| 684 |
+
margin = (typeof margin == 'undefined')? cellSize * 2 : margin;
|
| 685 |
+
|
| 686 |
+
var size = _this.getModuleCount() * cellSize + margin * 2;
|
| 687 |
+
var min = margin;
|
| 688 |
+
var max = size - margin;
|
| 689 |
+
|
| 690 |
+
var y, x, r, p;
|
| 691 |
+
|
| 692 |
+
var white = Array(cellSize+1).join('██');
|
| 693 |
+
var black = Array(cellSize+1).join(' ');
|
| 694 |
+
|
| 695 |
+
var ascii = '';
|
| 696 |
+
var line = '';
|
| 697 |
+
for (y = 0; y < size; y += 1) {
|
| 698 |
+
r = Math.floor( (y - min) / cellSize);
|
| 699 |
+
line = '';
|
| 700 |
+
for (x = 0; x < size; x += 1) {
|
| 701 |
+
p = 1;
|
| 702 |
+
|
| 703 |
+
if (min <= x && x < max && min <= y && y < max && _this.isDark(r, Math.floor((x - min) / cellSize))) {
|
| 704 |
+
p = 0;
|
| 705 |
+
}
|
| 706 |
+
|
| 707 |
+
// Output 2 characters per pixel, to create full square. 1 character per pixels gives only half width of square.
|
| 708 |
+
line += p ? white : black;
|
| 709 |
+
}
|
| 710 |
+
|
| 711 |
+
for (r = 0; r < cellSize; r += 1) {
|
| 712 |
+
ascii += line + '\n';
|
| 713 |
+
}
|
| 714 |
+
}
|
| 715 |
+
|
| 716 |
+
return ascii.substring(0, ascii.length-1);
|
| 717 |
+
};
|
| 718 |
+
|
| 719 |
+
_this.renderTo2dContext = function(context, cellSize) {
|
| 720 |
+
cellSize = cellSize || 2;
|
| 721 |
+
var length = _this.getModuleCount();
|
| 722 |
+
for (var row = 0; row < length; row++) {
|
| 723 |
+
for (var col = 0; col < length; col++) {
|
| 724 |
+
context.fillStyle = _this.isDark(row, col) ? 'black' : 'white';
|
| 725 |
+
context.fillRect(row * cellSize, col * cellSize, cellSize, cellSize);
|
| 726 |
+
}
|
| 727 |
+
}
|
| 728 |
+
}
|
| 729 |
+
|
| 730 |
+
return _this;
|
| 731 |
+
};
|
| 732 |
+
|
| 733 |
+
//---------------------------------------------------------------------
|
| 734 |
+
// qrcode.stringToBytes
|
| 735 |
+
//---------------------------------------------------------------------
|
| 736 |
+
|
| 737 |
+
qrcode.stringToBytesFuncs = {
|
| 738 |
+
'default' : function(s) {
|
| 739 |
+
var bytes = [];
|
| 740 |
+
for (var i = 0; i < s.length; i += 1) {
|
| 741 |
+
var c = s.charCodeAt(i);
|
| 742 |
+
bytes.push(c & 0xff);
|
| 743 |
+
}
|
| 744 |
+
return bytes;
|
| 745 |
+
}
|
| 746 |
+
};
|
| 747 |
+
|
| 748 |
+
qrcode.stringToBytes = qrcode.stringToBytesFuncs['default'];
|
| 749 |
+
|
| 750 |
+
//---------------------------------------------------------------------
|
| 751 |
+
// qrcode.createStringToBytes
|
| 752 |
+
//---------------------------------------------------------------------
|
| 753 |
+
|
| 754 |
+
/**
|
| 755 |
+
* @param unicodeData base64 string of byte array.
|
| 756 |
+
* [16bit Unicode],[16bit Bytes], ...
|
| 757 |
+
* @param numChars
|
| 758 |
+
*/
|
| 759 |
+
qrcode.createStringToBytes = function(unicodeData, numChars) {
|
| 760 |
+
|
| 761 |
+
// create conversion map.
|
| 762 |
+
|
| 763 |
+
var unicodeMap = function() {
|
| 764 |
+
|
| 765 |
+
var bin = base64DecodeInputStream(unicodeData);
|
| 766 |
+
var read = function() {
|
| 767 |
+
var b = bin.read();
|
| 768 |
+
if (b == -1) throw 'eof';
|
| 769 |
+
return b;
|
| 770 |
+
};
|
| 771 |
+
|
| 772 |
+
var count = 0;
|
| 773 |
+
var unicodeMap = {};
|
| 774 |
+
while (true) {
|
| 775 |
+
var b0 = bin.read();
|
| 776 |
+
if (b0 == -1) break;
|
| 777 |
+
var b1 = read();
|
| 778 |
+
var b2 = read();
|
| 779 |
+
var b3 = read();
|
| 780 |
+
var k = String.fromCharCode( (b0 << 8) | b1);
|
| 781 |
+
var v = (b2 << 8) | b3;
|
| 782 |
+
unicodeMap[k] = v;
|
| 783 |
+
count += 1;
|
| 784 |
+
}
|
| 785 |
+
if (count != numChars) {
|
| 786 |
+
throw count + ' != ' + numChars;
|
| 787 |
+
}
|
| 788 |
+
|
| 789 |
+
return unicodeMap;
|
| 790 |
+
}();
|
| 791 |
+
|
| 792 |
+
var unknownChar = '?'.charCodeAt(0);
|
| 793 |
+
|
| 794 |
+
return function(s) {
|
| 795 |
+
var bytes = [];
|
| 796 |
+
for (var i = 0; i < s.length; i += 1) {
|
| 797 |
+
var c = s.charCodeAt(i);
|
| 798 |
+
if (c < 128) {
|
| 799 |
+
bytes.push(c);
|
| 800 |
+
} else {
|
| 801 |
+
var b = unicodeMap[s.charAt(i)];
|
| 802 |
+
if (typeof b == 'number') {
|
| 803 |
+
if ( (b & 0xff) == b) {
|
| 804 |
+
// 1byte
|
| 805 |
+
bytes.push(b);
|
| 806 |
+
} else {
|
| 807 |
+
// 2bytes
|
| 808 |
+
bytes.push(b >>> 8);
|
| 809 |
+
bytes.push(b & 0xff);
|
| 810 |
+
}
|
| 811 |
+
} else {
|
| 812 |
+
bytes.push(unknownChar);
|
| 813 |
+
}
|
| 814 |
+
}
|
| 815 |
+
}
|
| 816 |
+
return bytes;
|
| 817 |
+
};
|
| 818 |
+
};
|
| 819 |
+
|
| 820 |
+
//---------------------------------------------------------------------
|
| 821 |
+
// QRMode
|
| 822 |
+
//---------------------------------------------------------------------
|
| 823 |
+
|
| 824 |
+
var QRMode = {
|
| 825 |
+
MODE_NUMBER : 1 << 0,
|
| 826 |
+
MODE_ALPHA_NUM : 1 << 1,
|
| 827 |
+
MODE_8BIT_BYTE : 1 << 2,
|
| 828 |
+
MODE_KANJI : 1 << 3
|
| 829 |
+
};
|
| 830 |
+
|
| 831 |
+
//---------------------------------------------------------------------
|
| 832 |
+
// QRErrorCorrectionLevel
|
| 833 |
+
//---------------------------------------------------------------------
|
| 834 |
+
|
| 835 |
+
var QRErrorCorrectionLevel = {
|
| 836 |
+
L : 1,
|
| 837 |
+
M : 0,
|
| 838 |
+
Q : 3,
|
| 839 |
+
H : 2
|
| 840 |
+
};
|
| 841 |
+
|
| 842 |
+
//---------------------------------------------------------------------
|
| 843 |
+
// QRMaskPattern
|
| 844 |
+
//---------------------------------------------------------------------
|
| 845 |
+
|
| 846 |
+
var QRMaskPattern = {
|
| 847 |
+
PATTERN000 : 0,
|
| 848 |
+
PATTERN001 : 1,
|
| 849 |
+
PATTERN010 : 2,
|
| 850 |
+
PATTERN011 : 3,
|
| 851 |
+
PATTERN100 : 4,
|
| 852 |
+
PATTERN101 : 5,
|
| 853 |
+
PATTERN110 : 6,
|
| 854 |
+
PATTERN111 : 7
|
| 855 |
+
};
|
| 856 |
+
|
| 857 |
+
//---------------------------------------------------------------------
|
| 858 |
+
// QRUtil
|
| 859 |
+
//---------------------------------------------------------------------
|
| 860 |
+
|
| 861 |
+
var QRUtil = function() {
|
| 862 |
+
|
| 863 |
+
var PATTERN_POSITION_TABLE = [
|
| 864 |
+
[],
|
| 865 |
+
[6, 18],
|
| 866 |
+
[6, 22],
|
| 867 |
+
[6, 26],
|
| 868 |
+
[6, 30],
|
| 869 |
+
[6, 34],
|
| 870 |
+
[6, 22, 38],
|
| 871 |
+
[6, 24, 42],
|
| 872 |
+
[6, 26, 46],
|
| 873 |
+
[6, 28, 50],
|
| 874 |
+
[6, 30, 54],
|
| 875 |
+
[6, 32, 58],
|
| 876 |
+
[6, 34, 62],
|
| 877 |
+
[6, 26, 46, 66],
|
| 878 |
+
[6, 26, 48, 70],
|
| 879 |
+
[6, 26, 50, 74],
|
| 880 |
+
[6, 30, 54, 78],
|
| 881 |
+
[6, 30, 56, 82],
|
| 882 |
+
[6, 30, 58, 86],
|
| 883 |
+
[6, 34, 62, 90],
|
| 884 |
+
[6, 28, 50, 72, 94],
|
| 885 |
+
[6, 26, 50, 74, 98],
|
| 886 |
+
[6, 30, 54, 78, 102],
|
| 887 |
+
[6, 28, 54, 80, 106],
|
| 888 |
+
[6, 32, 58, 84, 110],
|
| 889 |
+
[6, 30, 58, 86, 114],
|
| 890 |
+
[6, 34, 62, 90, 118],
|
| 891 |
+
[6, 26, 50, 74, 98, 122],
|
| 892 |
+
[6, 30, 54, 78, 102, 126],
|
| 893 |
+
[6, 26, 52, 78, 104, 130],
|
| 894 |
+
[6, 30, 56, 82, 108, 134],
|
| 895 |
+
[6, 34, 60, 86, 112, 138],
|
| 896 |
+
[6, 30, 58, 86, 114, 142],
|
| 897 |
+
[6, 34, 62, 90, 118, 146],
|
| 898 |
+
[6, 30, 54, 78, 102, 126, 150],
|
| 899 |
+
[6, 24, 50, 76, 102, 128, 154],
|
| 900 |
+
[6, 28, 54, 80, 106, 132, 158],
|
| 901 |
+
[6, 32, 58, 84, 110, 136, 162],
|
| 902 |
+
[6, 26, 54, 82, 110, 138, 166],
|
| 903 |
+
[6, 30, 58, 86, 114, 142, 170]
|
| 904 |
+
];
|
| 905 |
+
var G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0);
|
| 906 |
+
var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0);
|
| 907 |
+
var G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1);
|
| 908 |
+
|
| 909 |
+
var _this = {};
|
| 910 |
+
|
| 911 |
+
var getBCHDigit = function(data) {
|
| 912 |
+
var digit = 0;
|
| 913 |
+
while (data != 0) {
|
| 914 |
+
digit += 1;
|
| 915 |
+
data >>>= 1;
|
| 916 |
+
}
|
| 917 |
+
return digit;
|
| 918 |
+
};
|
| 919 |
+
|
| 920 |
+
_this.getBCHTypeInfo = function(data) {
|
| 921 |
+
var d = data << 10;
|
| 922 |
+
while (getBCHDigit(d) - getBCHDigit(G15) >= 0) {
|
| 923 |
+
d ^= (G15 << (getBCHDigit(d) - getBCHDigit(G15) ) );
|
| 924 |
+
}
|
| 925 |
+
return ( (data << 10) | d) ^ G15_MASK;
|
| 926 |
+
};
|
| 927 |
+
|
| 928 |
+
_this.getBCHTypeNumber = function(data) {
|
| 929 |
+
var d = data << 12;
|
| 930 |
+
while (getBCHDigit(d) - getBCHDigit(G18) >= 0) {
|
| 931 |
+
d ^= (G18 << (getBCHDigit(d) - getBCHDigit(G18) ) );
|
| 932 |
+
}
|
| 933 |
+
return (data << 12) | d;
|
| 934 |
+
};
|
| 935 |
+
|
| 936 |
+
_this.getPatternPosition = function(typeNumber) {
|
| 937 |
+
return PATTERN_POSITION_TABLE[typeNumber - 1];
|
| 938 |
+
};
|
| 939 |
+
|
| 940 |
+
_this.getMaskFunction = function(maskPattern) {
|
| 941 |
+
|
| 942 |
+
switch (maskPattern) {
|
| 943 |
+
|
| 944 |
+
case QRMaskPattern.PATTERN000 :
|
| 945 |
+
return function(i, j) { return (i + j) % 2 == 0; };
|
| 946 |
+
case QRMaskPattern.PATTERN001 :
|
| 947 |
+
return function(i, j) { return i % 2 == 0; };
|
| 948 |
+
case QRMaskPattern.PATTERN010 :
|
| 949 |
+
return function(i, j) { return j % 3 == 0; };
|
| 950 |
+
case QRMaskPattern.PATTERN011 :
|
| 951 |
+
return function(i, j) { return (i + j) % 3 == 0; };
|
| 952 |
+
case QRMaskPattern.PATTERN100 :
|
| 953 |
+
return function(i, j) { return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0; };
|
| 954 |
+
case QRMaskPattern.PATTERN101 :
|
| 955 |
+
return function(i, j) { return (i * j) % 2 + (i * j) % 3 == 0; };
|
| 956 |
+
case QRMaskPattern.PATTERN110 :
|
| 957 |
+
return function(i, j) { return ( (i * j) % 2 + (i * j) % 3) % 2 == 0; };
|
| 958 |
+
case QRMaskPattern.PATTERN111 :
|
| 959 |
+
return function(i, j) { return ( (i * j) % 3 + (i + j) % 2) % 2 == 0; };
|
| 960 |
+
|
| 961 |
+
default :
|
| 962 |
+
throw 'bad maskPattern:' + maskPattern;
|
| 963 |
+
}
|
| 964 |
+
};
|
| 965 |
+
|
| 966 |
+
_this.getErrorCorrectPolynomial = function(errorCorrectLength) {
|
| 967 |
+
var a = qrPolynomial([1], 0);
|
| 968 |
+
for (var i = 0; i < errorCorrectLength; i += 1) {
|
| 969 |
+
a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0) );
|
| 970 |
+
}
|
| 971 |
+
return a;
|
| 972 |
+
};
|
| 973 |
+
|
| 974 |
+
_this.getLengthInBits = function(mode, type) {
|
| 975 |
+
|
| 976 |
+
if (1 <= type && type < 10) {
|
| 977 |
+
|
| 978 |
+
// 1 - 9
|
| 979 |
+
|
| 980 |
+
switch(mode) {
|
| 981 |
+
case QRMode.MODE_NUMBER : return 10;
|
| 982 |
+
case QRMode.MODE_ALPHA_NUM : return 9;
|
| 983 |
+
case QRMode.MODE_8BIT_BYTE : return 8;
|
| 984 |
+
case QRMode.MODE_KANJI : return 8;
|
| 985 |
+
default :
|
| 986 |
+
throw 'mode:' + mode;
|
| 987 |
+
}
|
| 988 |
+
|
| 989 |
+
} else if (type < 27) {
|
| 990 |
+
|
| 991 |
+
// 10 - 26
|
| 992 |
+
|
| 993 |
+
switch(mode) {
|
| 994 |
+
case QRMode.MODE_NUMBER : return 12;
|
| 995 |
+
case QRMode.MODE_ALPHA_NUM : return 11;
|
| 996 |
+
case QRMode.MODE_8BIT_BYTE : return 16;
|
| 997 |
+
case QRMode.MODE_KANJI : return 10;
|
| 998 |
+
default :
|
| 999 |
+
throw 'mode:' + mode;
|
| 1000 |
+
}
|
| 1001 |
+
|
| 1002 |
+
} else if (type < 41) {
|
| 1003 |
+
|
| 1004 |
+
// 27 - 40
|
| 1005 |
+
|
| 1006 |
+
switch(mode) {
|
| 1007 |
+
case QRMode.MODE_NUMBER : return 14;
|
| 1008 |
+
case QRMode.MODE_ALPHA_NUM : return 13;
|
| 1009 |
+
case QRMode.MODE_8BIT_BYTE : return 16;
|
| 1010 |
+
case QRMode.MODE_KANJI : return 12;
|
| 1011 |
+
default :
|
| 1012 |
+
throw 'mode:' + mode;
|
| 1013 |
+
}
|
| 1014 |
+
|
| 1015 |
+
} else {
|
| 1016 |
+
throw 'type:' + type;
|
| 1017 |
+
}
|
| 1018 |
+
};
|
| 1019 |
+
|
| 1020 |
+
_this.getLostPoint = function(qrcode) {
|
| 1021 |
+
|
| 1022 |
+
var moduleCount = qrcode.getModuleCount();
|
| 1023 |
+
|
| 1024 |
+
var lostPoint = 0;
|
| 1025 |
+
|
| 1026 |
+
// LEVEL1
|
| 1027 |
+
|
| 1028 |
+
for (var row = 0; row < moduleCount; row += 1) {
|
| 1029 |
+
for (var col = 0; col < moduleCount; col += 1) {
|
| 1030 |
+
|
| 1031 |
+
var sameCount = 0;
|
| 1032 |
+
var dark = qrcode.isDark(row, col);
|
| 1033 |
+
|
| 1034 |
+
for (var r = -1; r <= 1; r += 1) {
|
| 1035 |
+
|
| 1036 |
+
if (row + r < 0 || moduleCount <= row + r) {
|
| 1037 |
+
continue;
|
| 1038 |
+
}
|
| 1039 |
+
|
| 1040 |
+
for (var c = -1; c <= 1; c += 1) {
|
| 1041 |
+
|
| 1042 |
+
if (col + c < 0 || moduleCount <= col + c) {
|
| 1043 |
+
continue;
|
| 1044 |
+
}
|
| 1045 |
+
|
| 1046 |
+
if (r == 0 && c == 0) {
|
| 1047 |
+
continue;
|
| 1048 |
+
}
|
| 1049 |
+
|
| 1050 |
+
if (dark == qrcode.isDark(row + r, col + c) ) {
|
| 1051 |
+
sameCount += 1;
|
| 1052 |
+
}
|
| 1053 |
+
}
|
| 1054 |
+
}
|
| 1055 |
+
|
| 1056 |
+
if (sameCount > 5) {
|
| 1057 |
+
lostPoint += (3 + sameCount - 5);
|
| 1058 |
+
}
|
| 1059 |
+
}
|
| 1060 |
+
};
|
| 1061 |
+
|
| 1062 |
+
// LEVEL2
|
| 1063 |
+
|
| 1064 |
+
for (var row = 0; row < moduleCount - 1; row += 1) {
|
| 1065 |
+
for (var col = 0; col < moduleCount - 1; col += 1) {
|
| 1066 |
+
var count = 0;
|
| 1067 |
+
if (qrcode.isDark(row, col) ) count += 1;
|
| 1068 |
+
if (qrcode.isDark(row + 1, col) ) count += 1;
|
| 1069 |
+
if (qrcode.isDark(row, col + 1) ) count += 1;
|
| 1070 |
+
if (qrcode.isDark(row + 1, col + 1) ) count += 1;
|
| 1071 |
+
if (count == 0 || count == 4) {
|
| 1072 |
+
lostPoint += 3;
|
| 1073 |
+
}
|
| 1074 |
+
}
|
| 1075 |
+
}
|
| 1076 |
+
|
| 1077 |
+
// LEVEL3
|
| 1078 |
+
|
| 1079 |
+
for (var row = 0; row < moduleCount; row += 1) {
|
| 1080 |
+
for (var col = 0; col < moduleCount - 6; col += 1) {
|
| 1081 |
+
if (qrcode.isDark(row, col)
|
| 1082 |
+
&& !qrcode.isDark(row, col + 1)
|
| 1083 |
+
&& qrcode.isDark(row, col + 2)
|
| 1084 |
+
&& qrcode.isDark(row, col + 3)
|
| 1085 |
+
&& qrcode.isDark(row, col + 4)
|
| 1086 |
+
&& !qrcode.isDark(row, col + 5)
|
| 1087 |
+
&& qrcode.isDark(row, col + 6) ) {
|
| 1088 |
+
lostPoint += 40;
|
| 1089 |
+
}
|
| 1090 |
+
}
|
| 1091 |
+
}
|
| 1092 |
+
|
| 1093 |
+
for (var col = 0; col < moduleCount; col += 1) {
|
| 1094 |
+
for (var row = 0; row < moduleCount - 6; row += 1) {
|
| 1095 |
+
if (qrcode.isDark(row, col)
|
| 1096 |
+
&& !qrcode.isDark(row + 1, col)
|
| 1097 |
+
&& qrcode.isDark(row + 2, col)
|
| 1098 |
+
&& qrcode.isDark(row + 3, col)
|
| 1099 |
+
&& qrcode.isDark(row + 4, col)
|
| 1100 |
+
&& !qrcode.isDark(row + 5, col)
|
| 1101 |
+
&& qrcode.isDark(row + 6, col) ) {
|
| 1102 |
+
lostPoint += 40;
|
| 1103 |
+
}
|
| 1104 |
+
}
|
| 1105 |
+
}
|
| 1106 |
+
|
| 1107 |
+
// LEVEL4
|
| 1108 |
+
|
| 1109 |
+
var darkCount = 0;
|
| 1110 |
+
|
| 1111 |
+
for (var col = 0; col < moduleCount; col += 1) {
|
| 1112 |
+
for (var row = 0; row < moduleCount; row += 1) {
|
| 1113 |
+
if (qrcode.isDark(row, col) ) {
|
| 1114 |
+
darkCount += 1;
|
| 1115 |
+
}
|
| 1116 |
+
}
|
| 1117 |
+
}
|
| 1118 |
+
|
| 1119 |
+
var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
|
| 1120 |
+
lostPoint += ratio * 10;
|
| 1121 |
+
|
| 1122 |
+
return lostPoint;
|
| 1123 |
+
};
|
| 1124 |
+
|
| 1125 |
+
return _this;
|
| 1126 |
+
}();
|
| 1127 |
+
|
| 1128 |
+
//---------------------------------------------------------------------
|
| 1129 |
+
// QRMath
|
| 1130 |
+
//---------------------------------------------------------------------
|
| 1131 |
+
|
| 1132 |
+
var QRMath = function() {
|
| 1133 |
+
|
| 1134 |
+
var EXP_TABLE = new Array(256);
|
| 1135 |
+
var LOG_TABLE = new Array(256);
|
| 1136 |
+
|
| 1137 |
+
// initialize tables
|
| 1138 |
+
for (var i = 0; i < 8; i += 1) {
|
| 1139 |
+
EXP_TABLE[i] = 1 << i;
|
| 1140 |
+
}
|
| 1141 |
+
for (var i = 8; i < 256; i += 1) {
|
| 1142 |
+
EXP_TABLE[i] = EXP_TABLE[i - 4]
|
| 1143 |
+
^ EXP_TABLE[i - 5]
|
| 1144 |
+
^ EXP_TABLE[i - 6]
|
| 1145 |
+
^ EXP_TABLE[i - 8];
|
| 1146 |
+
}
|
| 1147 |
+
for (var i = 0; i < 255; i += 1) {
|
| 1148 |
+
LOG_TABLE[EXP_TABLE[i] ] = i;
|
| 1149 |
+
}
|
| 1150 |
+
|
| 1151 |
+
var _this = {};
|
| 1152 |
+
|
| 1153 |
+
_this.glog = function(n) {
|
| 1154 |
+
|
| 1155 |
+
if (n < 1) {
|
| 1156 |
+
throw 'glog(' + n + ')';
|
| 1157 |
+
}
|
| 1158 |
+
|
| 1159 |
+
return LOG_TABLE[n];
|
| 1160 |
+
};
|
| 1161 |
+
|
| 1162 |
+
_this.gexp = function(n) {
|
| 1163 |
+
|
| 1164 |
+
while (n < 0) {
|
| 1165 |
+
n += 255;
|
| 1166 |
+
}
|
| 1167 |
+
|
| 1168 |
+
while (n >= 256) {
|
| 1169 |
+
n -= 255;
|
| 1170 |
+
}
|
| 1171 |
+
|
| 1172 |
+
return EXP_TABLE[n];
|
| 1173 |
+
};
|
| 1174 |
+
|
| 1175 |
+
return _this;
|
| 1176 |
+
}();
|
| 1177 |
+
|
| 1178 |
+
//---------------------------------------------------------------------
|
| 1179 |
+
// qrPolynomial
|
| 1180 |
+
//---------------------------------------------------------------------
|
| 1181 |
+
|
| 1182 |
+
function qrPolynomial(num, shift) {
|
| 1183 |
+
|
| 1184 |
+
if (typeof num.length == 'undefined') {
|
| 1185 |
+
throw num.length + '/' + shift;
|
| 1186 |
+
}
|
| 1187 |
+
|
| 1188 |
+
var _num = function() {
|
| 1189 |
+
var offset = 0;
|
| 1190 |
+
while (offset < num.length && num[offset] == 0) {
|
| 1191 |
+
offset += 1;
|
| 1192 |
+
}
|
| 1193 |
+
var _num = new Array(num.length - offset + shift);
|
| 1194 |
+
for (var i = 0; i < num.length - offset; i += 1) {
|
| 1195 |
+
_num[i] = num[i + offset];
|
| 1196 |
+
}
|
| 1197 |
+
return _num;
|
| 1198 |
+
}();
|
| 1199 |
+
|
| 1200 |
+
var _this = {};
|
| 1201 |
+
|
| 1202 |
+
_this.getAt = function(index) {
|
| 1203 |
+
return _num[index];
|
| 1204 |
+
};
|
| 1205 |
+
|
| 1206 |
+
_this.getLength = function() {
|
| 1207 |
+
return _num.length;
|
| 1208 |
+
};
|
| 1209 |
+
|
| 1210 |
+
_this.multiply = function(e) {
|
| 1211 |
+
|
| 1212 |
+
var num = new Array(_this.getLength() + e.getLength() - 1);
|
| 1213 |
+
|
| 1214 |
+
for (var i = 0; i < _this.getLength(); i += 1) {
|
| 1215 |
+
for (var j = 0; j < e.getLength(); j += 1) {
|
| 1216 |
+
num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i) ) + QRMath.glog(e.getAt(j) ) );
|
| 1217 |
+
}
|
| 1218 |
+
}
|
| 1219 |
+
|
| 1220 |
+
return qrPolynomial(num, 0);
|
| 1221 |
+
};
|
| 1222 |
+
|
| 1223 |
+
_this.mod = function(e) {
|
| 1224 |
+
|
| 1225 |
+
if (_this.getLength() - e.getLength() < 0) {
|
| 1226 |
+
return _this;
|
| 1227 |
+
}
|
| 1228 |
+
|
| 1229 |
+
var ratio = QRMath.glog(_this.getAt(0) ) - QRMath.glog(e.getAt(0) );
|
| 1230 |
+
|
| 1231 |
+
var num = new Array(_this.getLength() );
|
| 1232 |
+
for (var i = 0; i < _this.getLength(); i += 1) {
|
| 1233 |
+
num[i] = _this.getAt(i);
|
| 1234 |
+
}
|
| 1235 |
+
|
| 1236 |
+
for (var i = 0; i < e.getLength(); i += 1) {
|
| 1237 |
+
num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i) ) + ratio);
|
| 1238 |
+
}
|
| 1239 |
+
|
| 1240 |
+
// recursive call
|
| 1241 |
+
return qrPolynomial(num, 0).mod(e);
|
| 1242 |
+
};
|
| 1243 |
+
|
| 1244 |
+
return _this;
|
| 1245 |
+
};
|
| 1246 |
+
|
| 1247 |
+
//---------------------------------------------------------------------
|
| 1248 |
+
// QRRSBlock
|
| 1249 |
+
//---------------------------------------------------------------------
|
| 1250 |
+
|
| 1251 |
+
var QRRSBlock = function() {
|
| 1252 |
+
|
| 1253 |
+
var RS_BLOCK_TABLE = [
|
| 1254 |
+
|
| 1255 |
+
// L
|
| 1256 |
+
// M
|
| 1257 |
+
// Q
|
| 1258 |
+
// H
|
| 1259 |
+
|
| 1260 |
+
// 1
|
| 1261 |
+
[1, 26, 19],
|
| 1262 |
+
[1, 26, 16],
|
| 1263 |
+
[1, 26, 13],
|
| 1264 |
+
[1, 26, 9],
|
| 1265 |
+
|
| 1266 |
+
// 2
|
| 1267 |
+
[1, 44, 34],
|
| 1268 |
+
[1, 44, 28],
|
| 1269 |
+
[1, 44, 22],
|
| 1270 |
+
[1, 44, 16],
|
| 1271 |
+
|
| 1272 |
+
// 3
|
| 1273 |
+
[1, 70, 55],
|
| 1274 |
+
[1, 70, 44],
|
| 1275 |
+
[2, 35, 17],
|
| 1276 |
+
[2, 35, 13],
|
| 1277 |
+
|
| 1278 |
+
// 4
|
| 1279 |
+
[1, 100, 80],
|
| 1280 |
+
[2, 50, 32],
|
| 1281 |
+
[2, 50, 24],
|
| 1282 |
+
[4, 25, 9],
|
| 1283 |
+
|
| 1284 |
+
// 5
|
| 1285 |
+
[1, 134, 108],
|
| 1286 |
+
[2, 67, 43],
|
| 1287 |
+
[2, 33, 15, 2, 34, 16],
|
| 1288 |
+
[2, 33, 11, 2, 34, 12],
|
| 1289 |
+
|
| 1290 |
+
// 6
|
| 1291 |
+
[2, 86, 68],
|
| 1292 |
+
[4, 43, 27],
|
| 1293 |
+
[4, 43, 19],
|
| 1294 |
+
[4, 43, 15],
|
| 1295 |
+
|
| 1296 |
+
// 7
|
| 1297 |
+
[2, 98, 78],
|
| 1298 |
+
[4, 49, 31],
|
| 1299 |
+
[2, 32, 14, 4, 33, 15],
|
| 1300 |
+
[4, 39, 13, 1, 40, 14],
|
| 1301 |
+
|
| 1302 |
+
// 8
|
| 1303 |
+
[2, 121, 97],
|
| 1304 |
+
[2, 60, 38, 2, 61, 39],
|
| 1305 |
+
[4, 40, 18, 2, 41, 19],
|
| 1306 |
+
[4, 40, 14, 2, 41, 15],
|
| 1307 |
+
|
| 1308 |
+
// 9
|
| 1309 |
+
[2, 146, 116],
|
| 1310 |
+
[3, 58, 36, 2, 59, 37],
|
| 1311 |
+
[4, 36, 16, 4, 37, 17],
|
| 1312 |
+
[4, 36, 12, 4, 37, 13],
|
| 1313 |
+
|
| 1314 |
+
// 10
|
| 1315 |
+
[2, 86, 68, 2, 87, 69],
|
| 1316 |
+
[4, 69, 43, 1, 70, 44],
|
| 1317 |
+
[6, 43, 19, 2, 44, 20],
|
| 1318 |
+
[6, 43, 15, 2, 44, 16],
|
| 1319 |
+
|
| 1320 |
+
// 11
|
| 1321 |
+
[4, 101, 81],
|
| 1322 |
+
[1, 80, 50, 4, 81, 51],
|
| 1323 |
+
[4, 50, 22, 4, 51, 23],
|
| 1324 |
+
[3, 36, 12, 8, 37, 13],
|
| 1325 |
+
|
| 1326 |
+
// 12
|
| 1327 |
+
[2, 116, 92, 2, 117, 93],
|
| 1328 |
+
[6, 58, 36, 2, 59, 37],
|
| 1329 |
+
[4, 46, 20, 6, 47, 21],
|
| 1330 |
+
[7, 42, 14, 4, 43, 15],
|
| 1331 |
+
|
| 1332 |
+
// 13
|
| 1333 |
+
[4, 133, 107],
|
| 1334 |
+
[8, 59, 37, 1, 60, 38],
|
| 1335 |
+
[8, 44, 20, 4, 45, 21],
|
| 1336 |
+
[12, 33, 11, 4, 34, 12],
|
| 1337 |
+
|
| 1338 |
+
// 14
|
| 1339 |
+
[3, 145, 115, 1, 146, 116],
|
| 1340 |
+
[4, 64, 40, 5, 65, 41],
|
| 1341 |
+
[11, 36, 16, 5, 37, 17],
|
| 1342 |
+
[11, 36, 12, 5, 37, 13],
|
| 1343 |
+
|
| 1344 |
+
// 15
|
| 1345 |
+
[5, 109, 87, 1, 110, 88],
|
| 1346 |
+
[5, 65, 41, 5, 66, 42],
|
| 1347 |
+
[5, 54, 24, 7, 55, 25],
|
| 1348 |
+
[11, 36, 12, 7, 37, 13],
|
| 1349 |
+
|
| 1350 |
+
// 16
|
| 1351 |
+
[5, 122, 98, 1, 123, 99],
|
| 1352 |
+
[7, 73, 45, 3, 74, 46],
|
| 1353 |
+
[15, 43, 19, 2, 44, 20],
|
| 1354 |
+
[3, 45, 15, 13, 46, 16],
|
| 1355 |
+
|
| 1356 |
+
// 17
|
| 1357 |
+
[1, 135, 107, 5, 136, 108],
|
| 1358 |
+
[10, 74, 46, 1, 75, 47],
|
| 1359 |
+
[1, 50, 22, 15, 51, 23],
|
| 1360 |
+
[2, 42, 14, 17, 43, 15],
|
| 1361 |
+
|
| 1362 |
+
// 18
|
| 1363 |
+
[5, 150, 120, 1, 151, 121],
|
| 1364 |
+
[9, 69, 43, 4, 70, 44],
|
| 1365 |
+
[17, 50, 22, 1, 51, 23],
|
| 1366 |
+
[2, 42, 14, 19, 43, 15],
|
| 1367 |
+
|
| 1368 |
+
// 19
|
| 1369 |
+
[3, 141, 113, 4, 142, 114],
|
| 1370 |
+
[3, 70, 44, 11, 71, 45],
|
| 1371 |
+
[17, 47, 21, 4, 48, 22],
|
| 1372 |
+
[9, 39, 13, 16, 40, 14],
|
| 1373 |
+
|
| 1374 |
+
// 20
|
| 1375 |
+
[3, 135, 107, 5, 136, 108],
|
| 1376 |
+
[3, 67, 41, 13, 68, 42],
|
| 1377 |
+
[15, 54, 24, 5, 55, 25],
|
| 1378 |
+
[15, 43, 15, 10, 44, 16],
|
| 1379 |
+
|
| 1380 |
+
// 21
|
| 1381 |
+
[4, 144, 116, 4, 145, 117],
|
| 1382 |
+
[17, 68, 42],
|
| 1383 |
+
[17, 50, 22, 6, 51, 23],
|
| 1384 |
+
[19, 46, 16, 6, 47, 17],
|
| 1385 |
+
|
| 1386 |
+
// 22
|
| 1387 |
+
[2, 139, 111, 7, 140, 112],
|
| 1388 |
+
[17, 74, 46],
|
| 1389 |
+
[7, 54, 24, 16, 55, 25],
|
| 1390 |
+
[34, 37, 13],
|
| 1391 |
+
|
| 1392 |
+
// 23
|
| 1393 |
+
[4, 151, 121, 5, 152, 122],
|
| 1394 |
+
[4, 75, 47, 14, 76, 48],
|
| 1395 |
+
[11, 54, 24, 14, 55, 25],
|
| 1396 |
+
[16, 45, 15, 14, 46, 16],
|
| 1397 |
+
|
| 1398 |
+
// 24
|
| 1399 |
+
[6, 147, 117, 4, 148, 118],
|
| 1400 |
+
[6, 73, 45, 14, 74, 46],
|
| 1401 |
+
[11, 54, 24, 16, 55, 25],
|
| 1402 |
+
[30, 46, 16, 2, 47, 17],
|
| 1403 |
+
|
| 1404 |
+
// 25
|
| 1405 |
+
[8, 132, 106, 4, 133, 107],
|
| 1406 |
+
[8, 75, 47, 13, 76, 48],
|
| 1407 |
+
[7, 54, 24, 22, 55, 25],
|
| 1408 |
+
[22, 45, 15, 13, 46, 16],
|
| 1409 |
+
|
| 1410 |
+
// 26
|
| 1411 |
+
[10, 142, 114, 2, 143, 115],
|
| 1412 |
+
[19, 74, 46, 4, 75, 47],
|
| 1413 |
+
[28, 50, 22, 6, 51, 23],
|
| 1414 |
+
[33, 46, 16, 4, 47, 17],
|
| 1415 |
+
|
| 1416 |
+
// 27
|
| 1417 |
+
[8, 152, 122, 4, 153, 123],
|
| 1418 |
+
[22, 73, 45, 3, 74, 46],
|
| 1419 |
+
[8, 53, 23, 26, 54, 24],
|
| 1420 |
+
[12, 45, 15, 28, 46, 16],
|
| 1421 |
+
|
| 1422 |
+
// 28
|
| 1423 |
+
[3, 147, 117, 10, 148, 118],
|
| 1424 |
+
[3, 73, 45, 23, 74, 46],
|
| 1425 |
+
[4, 54, 24, 31, 55, 25],
|
| 1426 |
+
[11, 45, 15, 31, 46, 16],
|
| 1427 |
+
|
| 1428 |
+
// 29
|
| 1429 |
+
[7, 146, 116, 7, 147, 117],
|
| 1430 |
+
[21, 73, 45, 7, 74, 46],
|
| 1431 |
+
[1, 53, 23, 37, 54, 24],
|
| 1432 |
+
[19, 45, 15, 26, 46, 16],
|
| 1433 |
+
|
| 1434 |
+
// 30
|
| 1435 |
+
[5, 145, 115, 10, 146, 116],
|
| 1436 |
+
[19, 75, 47, 10, 76, 48],
|
| 1437 |
+
[15, 54, 24, 25, 55, 25],
|
| 1438 |
+
[23, 45, 15, 25, 46, 16],
|
| 1439 |
+
|
| 1440 |
+
// 31
|
| 1441 |
+
[13, 145, 115, 3, 146, 116],
|
| 1442 |
+
[2, 74, 46, 29, 75, 47],
|
| 1443 |
+
[42, 54, 24, 1, 55, 25],
|
| 1444 |
+
[23, 45, 15, 28, 46, 16],
|
| 1445 |
+
|
| 1446 |
+
// 32
|
| 1447 |
+
[17, 145, 115],
|
| 1448 |
+
[10, 74, 46, 23, 75, 47],
|
| 1449 |
+
[10, 54, 24, 35, 55, 25],
|
| 1450 |
+
[19, 45, 15, 35, 46, 16],
|
| 1451 |
+
|
| 1452 |
+
// 33
|
| 1453 |
+
[17, 145, 115, 1, 146, 116],
|
| 1454 |
+
[14, 74, 46, 21, 75, 47],
|
| 1455 |
+
[29, 54, 24, 19, 55, 25],
|
| 1456 |
+
[11, 45, 15, 46, 46, 16],
|
| 1457 |
+
|
| 1458 |
+
// 34
|
| 1459 |
+
[13, 145, 115, 6, 146, 116],
|
| 1460 |
+
[14, 74, 46, 23, 75, 47],
|
| 1461 |
+
[44, 54, 24, 7, 55, 25],
|
| 1462 |
+
[59, 46, 16, 1, 47, 17],
|
| 1463 |
+
|
| 1464 |
+
// 35
|
| 1465 |
+
[12, 151, 121, 7, 152, 122],
|
| 1466 |
+
[12, 75, 47, 26, 76, 48],
|
| 1467 |
+
[39, 54, 24, 14, 55, 25],
|
| 1468 |
+
[22, 45, 15, 41, 46, 16],
|
| 1469 |
+
|
| 1470 |
+
// 36
|
| 1471 |
+
[6, 151, 121, 14, 152, 122],
|
| 1472 |
+
[6, 75, 47, 34, 76, 48],
|
| 1473 |
+
[46, 54, 24, 10, 55, 25],
|
| 1474 |
+
[2, 45, 15, 64, 46, 16],
|
| 1475 |
+
|
| 1476 |
+
// 37
|
| 1477 |
+
[17, 152, 122, 4, 153, 123],
|
| 1478 |
+
[29, 74, 46, 14, 75, 47],
|
| 1479 |
+
[49, 54, 24, 10, 55, 25],
|
| 1480 |
+
[24, 45, 15, 46, 46, 16],
|
| 1481 |
+
|
| 1482 |
+
// 38
|
| 1483 |
+
[4, 152, 122, 18, 153, 123],
|
| 1484 |
+
[13, 74, 46, 32, 75, 47],
|
| 1485 |
+
[48, 54, 24, 14, 55, 25],
|
| 1486 |
+
[42, 45, 15, 32, 46, 16],
|
| 1487 |
+
|
| 1488 |
+
// 39
|
| 1489 |
+
[20, 147, 117, 4, 148, 118],
|
| 1490 |
+
[40, 75, 47, 7, 76, 48],
|
| 1491 |
+
[43, 54, 24, 22, 55, 25],
|
| 1492 |
+
[10, 45, 15, 67, 46, 16],
|
| 1493 |
+
|
| 1494 |
+
// 40
|
| 1495 |
+
[19, 148, 118, 6, 149, 119],
|
| 1496 |
+
[18, 75, 47, 31, 76, 48],
|
| 1497 |
+
[34, 54, 24, 34, 55, 25],
|
| 1498 |
+
[20, 45, 15, 61, 46, 16]
|
| 1499 |
+
];
|
| 1500 |
+
|
| 1501 |
+
var qrRSBlock = function(totalCount, dataCount) {
|
| 1502 |
+
var _this = {};
|
| 1503 |
+
_this.totalCount = totalCount;
|
| 1504 |
+
_this.dataCount = dataCount;
|
| 1505 |
+
return _this;
|
| 1506 |
+
};
|
| 1507 |
+
|
| 1508 |
+
var _this = {};
|
| 1509 |
+
|
| 1510 |
+
var getRsBlockTable = function(typeNumber, errorCorrectionLevel) {
|
| 1511 |
+
|
| 1512 |
+
switch(errorCorrectionLevel) {
|
| 1513 |
+
case QRErrorCorrectionLevel.L :
|
| 1514 |
+
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
|
| 1515 |
+
case QRErrorCorrectionLevel.M :
|
| 1516 |
+
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
|
| 1517 |
+
case QRErrorCorrectionLevel.Q :
|
| 1518 |
+
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
|
| 1519 |
+
case QRErrorCorrectionLevel.H :
|
| 1520 |
+
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
|
| 1521 |
+
default :
|
| 1522 |
+
return undefined;
|
| 1523 |
+
}
|
| 1524 |
+
};
|
| 1525 |
+
|
| 1526 |
+
_this.getRSBlocks = function(typeNumber, errorCorrectionLevel) {
|
| 1527 |
+
|
| 1528 |
+
var rsBlock = getRsBlockTable(typeNumber, errorCorrectionLevel);
|
| 1529 |
+
|
| 1530 |
+
if (typeof rsBlock == 'undefined') {
|
| 1531 |
+
throw 'bad rs block @ typeNumber:' + typeNumber +
|
| 1532 |
+
'/errorCorrectionLevel:' + errorCorrectionLevel;
|
| 1533 |
+
}
|
| 1534 |
+
|
| 1535 |
+
var length = rsBlock.length / 3;
|
| 1536 |
+
|
| 1537 |
+
var list = [];
|
| 1538 |
+
|
| 1539 |
+
for (var i = 0; i < length; i += 1) {
|
| 1540 |
+
|
| 1541 |
+
var count = rsBlock[i * 3 + 0];
|
| 1542 |
+
var totalCount = rsBlock[i * 3 + 1];
|
| 1543 |
+
var dataCount = rsBlock[i * 3 + 2];
|
| 1544 |
+
|
| 1545 |
+
for (var j = 0; j < count; j += 1) {
|
| 1546 |
+
list.push(qrRSBlock(totalCount, dataCount) );
|
| 1547 |
+
}
|
| 1548 |
+
}
|
| 1549 |
+
|
| 1550 |
+
return list;
|
| 1551 |
+
};
|
| 1552 |
+
|
| 1553 |
+
return _this;
|
| 1554 |
+
}();
|
| 1555 |
+
|
| 1556 |
+
//---------------------------------------------------------------------
|
| 1557 |
+
// qrBitBuffer
|
| 1558 |
+
//---------------------------------------------------------------------
|
| 1559 |
+
|
| 1560 |
+
var qrBitBuffer = function() {
|
| 1561 |
+
|
| 1562 |
+
var _buffer = [];
|
| 1563 |
+
var _length = 0;
|
| 1564 |
+
|
| 1565 |
+
var _this = {};
|
| 1566 |
+
|
| 1567 |
+
_this.getBuffer = function() {
|
| 1568 |
+
return _buffer;
|
| 1569 |
+
};
|
| 1570 |
+
|
| 1571 |
+
_this.getAt = function(index) {
|
| 1572 |
+
var bufIndex = Math.floor(index / 8);
|
| 1573 |
+
return ( (_buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1;
|
| 1574 |
+
};
|
| 1575 |
+
|
| 1576 |
+
_this.put = function(num, length) {
|
| 1577 |
+
for (var i = 0; i < length; i += 1) {
|
| 1578 |
+
_this.putBit( ( (num >>> (length - i - 1) ) & 1) == 1);
|
| 1579 |
+
}
|
| 1580 |
+
};
|
| 1581 |
+
|
| 1582 |
+
_this.getLengthInBits = function() {
|
| 1583 |
+
return _length;
|
| 1584 |
+
};
|
| 1585 |
+
|
| 1586 |
+
_this.putBit = function(bit) {
|
| 1587 |
+
|
| 1588 |
+
var bufIndex = Math.floor(_length / 8);
|
| 1589 |
+
if (_buffer.length <= bufIndex) {
|
| 1590 |
+
_buffer.push(0);
|
| 1591 |
+
}
|
| 1592 |
+
|
| 1593 |
+
if (bit) {
|
| 1594 |
+
_buffer[bufIndex] |= (0x80 >>> (_length % 8) );
|
| 1595 |
+
}
|
| 1596 |
+
|
| 1597 |
+
_length += 1;
|
| 1598 |
+
};
|
| 1599 |
+
|
| 1600 |
+
return _this;
|
| 1601 |
+
};
|
| 1602 |
+
|
| 1603 |
+
//---------------------------------------------------------------------
|
| 1604 |
+
// qrNumber
|
| 1605 |
+
//---------------------------------------------------------------------
|
| 1606 |
+
|
| 1607 |
+
var qrNumber = function(data) {
|
| 1608 |
+
|
| 1609 |
+
var _mode = QRMode.MODE_NUMBER;
|
| 1610 |
+
var _data = data;
|
| 1611 |
+
|
| 1612 |
+
var _this = {};
|
| 1613 |
+
|
| 1614 |
+
_this.getMode = function() {
|
| 1615 |
+
return _mode;
|
| 1616 |
+
};
|
| 1617 |
+
|
| 1618 |
+
_this.getLength = function(buffer) {
|
| 1619 |
+
return _data.length;
|
| 1620 |
+
};
|
| 1621 |
+
|
| 1622 |
+
_this.write = function(buffer) {
|
| 1623 |
+
|
| 1624 |
+
var data = _data;
|
| 1625 |
+
|
| 1626 |
+
var i = 0;
|
| 1627 |
+
|
| 1628 |
+
while (i + 2 < data.length) {
|
| 1629 |
+
buffer.put(strToNum(data.substring(i, i + 3) ), 10);
|
| 1630 |
+
i += 3;
|
| 1631 |
+
}
|
| 1632 |
+
|
| 1633 |
+
if (i < data.length) {
|
| 1634 |
+
if (data.length - i == 1) {
|
| 1635 |
+
buffer.put(strToNum(data.substring(i, i + 1) ), 4);
|
| 1636 |
+
} else if (data.length - i == 2) {
|
| 1637 |
+
buffer.put(strToNum(data.substring(i, i + 2) ), 7);
|
| 1638 |
+
}
|
| 1639 |
+
}
|
| 1640 |
+
};
|
| 1641 |
+
|
| 1642 |
+
var strToNum = function(s) {
|
| 1643 |
+
var num = 0;
|
| 1644 |
+
for (var i = 0; i < s.length; i += 1) {
|
| 1645 |
+
num = num * 10 + chatToNum(s.charAt(i) );
|
| 1646 |
+
}
|
| 1647 |
+
return num;
|
| 1648 |
+
};
|
| 1649 |
+
|
| 1650 |
+
var chatToNum = function(c) {
|
| 1651 |
+
if ('0' <= c && c <= '9') {
|
| 1652 |
+
return c.charCodeAt(0) - '0'.charCodeAt(0);
|
| 1653 |
+
}
|
| 1654 |
+
throw 'illegal char :' + c;
|
| 1655 |
+
};
|
| 1656 |
+
|
| 1657 |
+
return _this;
|
| 1658 |
+
};
|
| 1659 |
+
|
| 1660 |
+
//---------------------------------------------------------------------
|
| 1661 |
+
// qrAlphaNum
|
| 1662 |
+
//---------------------------------------------------------------------
|
| 1663 |
+
|
| 1664 |
+
var qrAlphaNum = function(data) {
|
| 1665 |
+
|
| 1666 |
+
var _mode = QRMode.MODE_ALPHA_NUM;
|
| 1667 |
+
var _data = data;
|
| 1668 |
+
|
| 1669 |
+
var _this = {};
|
| 1670 |
+
|
| 1671 |
+
_this.getMode = function() {
|
| 1672 |
+
return _mode;
|
| 1673 |
+
};
|
| 1674 |
+
|
| 1675 |
+
_this.getLength = function(buffer) {
|
| 1676 |
+
return _data.length;
|
| 1677 |
+
};
|
| 1678 |
+
|
| 1679 |
+
_this.write = function(buffer) {
|
| 1680 |
+
|
| 1681 |
+
var s = _data;
|
| 1682 |
+
|
| 1683 |
+
var i = 0;
|
| 1684 |
+
|
| 1685 |
+
while (i + 1 < s.length) {
|
| 1686 |
+
buffer.put(
|
| 1687 |
+
getCode(s.charAt(i) ) * 45 +
|
| 1688 |
+
getCode(s.charAt(i + 1) ), 11);
|
| 1689 |
+
i += 2;
|
| 1690 |
+
}
|
| 1691 |
+
|
| 1692 |
+
if (i < s.length) {
|
| 1693 |
+
buffer.put(getCode(s.charAt(i) ), 6);
|
| 1694 |
+
}
|
| 1695 |
+
};
|
| 1696 |
+
|
| 1697 |
+
var getCode = function(c) {
|
| 1698 |
+
|
| 1699 |
+
if ('0' <= c && c <= '9') {
|
| 1700 |
+
return c.charCodeAt(0) - '0'.charCodeAt(0);
|
| 1701 |
+
} else if ('A' <= c && c <= 'Z') {
|
| 1702 |
+
return c.charCodeAt(0) - 'A'.charCodeAt(0) + 10;
|
| 1703 |
+
} else {
|
| 1704 |
+
switch (c) {
|
| 1705 |
+
case ' ' : return 36;
|
| 1706 |
+
case '$' : return 37;
|
| 1707 |
+
case '%' : return 38;
|
| 1708 |
+
case '*' : return 39;
|
| 1709 |
+
case '+' : return 40;
|
| 1710 |
+
case '-' : return 41;
|
| 1711 |
+
case '.' : return 42;
|
| 1712 |
+
case '/' : return 43;
|
| 1713 |
+
case ':' : return 44;
|
| 1714 |
+
default :
|
| 1715 |
+
throw 'illegal char :' + c;
|
| 1716 |
+
}
|
| 1717 |
+
}
|
| 1718 |
+
};
|
| 1719 |
+
|
| 1720 |
+
return _this;
|
| 1721 |
+
};
|
| 1722 |
+
|
| 1723 |
+
//---------------------------------------------------------------------
|
| 1724 |
+
// qr8BitByte
|
| 1725 |
+
//---------------------------------------------------------------------
|
| 1726 |
+
|
| 1727 |
+
var qr8BitByte = function(data) {
|
| 1728 |
+
|
| 1729 |
+
var _mode = QRMode.MODE_8BIT_BYTE;
|
| 1730 |
+
var _data = data;
|
| 1731 |
+
var _bytes = qrcode.stringToBytes(data);
|
| 1732 |
+
|
| 1733 |
+
var _this = {};
|
| 1734 |
+
|
| 1735 |
+
_this.getMode = function() {
|
| 1736 |
+
return _mode;
|
| 1737 |
+
};
|
| 1738 |
+
|
| 1739 |
+
_this.getLength = function(buffer) {
|
| 1740 |
+
return _bytes.length;
|
| 1741 |
+
};
|
| 1742 |
+
|
| 1743 |
+
_this.write = function(buffer) {
|
| 1744 |
+
for (var i = 0; i < _bytes.length; i += 1) {
|
| 1745 |
+
buffer.put(_bytes[i], 8);
|
| 1746 |
+
}
|
| 1747 |
+
};
|
| 1748 |
+
|
| 1749 |
+
return _this;
|
| 1750 |
+
};
|
| 1751 |
+
|
| 1752 |
+
//---------------------------------------------------------------------
|
| 1753 |
+
// qrKanji
|
| 1754 |
+
//---------------------------------------------------------------------
|
| 1755 |
+
|
| 1756 |
+
var qrKanji = function(data) {
|
| 1757 |
+
|
| 1758 |
+
var _mode = QRMode.MODE_KANJI;
|
| 1759 |
+
var _data = data;
|
| 1760 |
+
|
| 1761 |
+
var stringToBytes = qrcode.stringToBytesFuncs['SJIS'];
|
| 1762 |
+
if (!stringToBytes) {
|
| 1763 |
+
throw 'sjis not supported.';
|
| 1764 |
+
}
|
| 1765 |
+
!function(c, code) {
|
| 1766 |
+
// self test for sjis support.
|
| 1767 |
+
var test = stringToBytes(c);
|
| 1768 |
+
if (test.length != 2 || ( (test[0] << 8) | test[1]) != code) {
|
| 1769 |
+
throw 'sjis not supported.';
|
| 1770 |
+
}
|
| 1771 |
+
}('\u53cb', 0x9746);
|
| 1772 |
+
|
| 1773 |
+
var _bytes = stringToBytes(data);
|
| 1774 |
+
|
| 1775 |
+
var _this = {};
|
| 1776 |
+
|
| 1777 |
+
_this.getMode = function() {
|
| 1778 |
+
return _mode;
|
| 1779 |
+
};
|
| 1780 |
+
|
| 1781 |
+
_this.getLength = function(buffer) {
|
| 1782 |
+
return ~~(_bytes.length / 2);
|
| 1783 |
+
};
|
| 1784 |
+
|
| 1785 |
+
_this.write = function(buffer) {
|
| 1786 |
+
|
| 1787 |
+
var data = _bytes;
|
| 1788 |
+
|
| 1789 |
+
var i = 0;
|
| 1790 |
+
|
| 1791 |
+
while (i + 1 < data.length) {
|
| 1792 |
+
|
| 1793 |
+
var c = ( (0xff & data[i]) << 8) | (0xff & data[i + 1]);
|
| 1794 |
+
|
| 1795 |
+
if (0x8140 <= c && c <= 0x9FFC) {
|
| 1796 |
+
c -= 0x8140;
|
| 1797 |
+
} else if (0xE040 <= c && c <= 0xEBBF) {
|
| 1798 |
+
c -= 0xC140;
|
| 1799 |
+
} else {
|
| 1800 |
+
throw 'illegal char at ' + (i + 1) + '/' + c;
|
| 1801 |
+
}
|
| 1802 |
+
|
| 1803 |
+
c = ( (c >>> 8) & 0xff) * 0xC0 + (c & 0xff);
|
| 1804 |
+
|
| 1805 |
+
buffer.put(c, 13);
|
| 1806 |
+
|
| 1807 |
+
i += 2;
|
| 1808 |
+
}
|
| 1809 |
+
|
| 1810 |
+
if (i < data.length) {
|
| 1811 |
+
throw 'illegal char at ' + (i + 1);
|
| 1812 |
+
}
|
| 1813 |
+
};
|
| 1814 |
+
|
| 1815 |
+
return _this;
|
| 1816 |
+
};
|
| 1817 |
+
|
| 1818 |
+
//=====================================================================
|
| 1819 |
+
// GIF Support etc.
|
| 1820 |
+
//
|
| 1821 |
+
|
| 1822 |
+
//---------------------------------------------------------------------
|
| 1823 |
+
// byteArrayOutputStream
|
| 1824 |
+
//---------------------------------------------------------------------
|
| 1825 |
+
|
| 1826 |
+
var byteArrayOutputStream = function() {
|
| 1827 |
+
|
| 1828 |
+
var _bytes = [];
|
| 1829 |
+
|
| 1830 |
+
var _this = {};
|
| 1831 |
+
|
| 1832 |
+
_this.writeByte = function(b) {
|
| 1833 |
+
_bytes.push(b & 0xff);
|
| 1834 |
+
};
|
| 1835 |
+
|
| 1836 |
+
_this.writeShort = function(i) {
|
| 1837 |
+
_this.writeByte(i);
|
| 1838 |
+
_this.writeByte(i >>> 8);
|
| 1839 |
+
};
|
| 1840 |
+
|
| 1841 |
+
_this.writeBytes = function(b, off, len) {
|
| 1842 |
+
off = off || 0;
|
| 1843 |
+
len = len || b.length;
|
| 1844 |
+
for (var i = 0; i < len; i += 1) {
|
| 1845 |
+
_this.writeByte(b[i + off]);
|
| 1846 |
+
}
|
| 1847 |
+
};
|
| 1848 |
+
|
| 1849 |
+
_this.writeString = function(s) {
|
| 1850 |
+
for (var i = 0; i < s.length; i += 1) {
|
| 1851 |
+
_this.writeByte(s.charCodeAt(i) );
|
| 1852 |
+
}
|
| 1853 |
+
};
|
| 1854 |
+
|
| 1855 |
+
_this.toByteArray = function() {
|
| 1856 |
+
return _bytes;
|
| 1857 |
+
};
|
| 1858 |
+
|
| 1859 |
+
_this.toString = function() {
|
| 1860 |
+
var s = '';
|
| 1861 |
+
s += '[';
|
| 1862 |
+
for (var i = 0; i < _bytes.length; i += 1) {
|
| 1863 |
+
if (i > 0) {
|
| 1864 |
+
s += ',';
|
| 1865 |
+
}
|
| 1866 |
+
s += _bytes[i];
|
| 1867 |
+
}
|
| 1868 |
+
s += ']';
|
| 1869 |
+
return s;
|
| 1870 |
+
};
|
| 1871 |
+
|
| 1872 |
+
return _this;
|
| 1873 |
+
};
|
| 1874 |
+
|
| 1875 |
+
//---------------------------------------------------------------------
|
| 1876 |
+
// base64EncodeOutputStream
|
| 1877 |
+
//---------------------------------------------------------------------
|
| 1878 |
+
|
| 1879 |
+
var base64EncodeOutputStream = function() {
|
| 1880 |
+
|
| 1881 |
+
var _buffer = 0;
|
| 1882 |
+
var _buflen = 0;
|
| 1883 |
+
var _length = 0;
|
| 1884 |
+
var _base64 = '';
|
| 1885 |
+
|
| 1886 |
+
var _this = {};
|
| 1887 |
+
|
| 1888 |
+
var writeEncoded = function(b) {
|
| 1889 |
+
_base64 += String.fromCharCode(encode(b & 0x3f) );
|
| 1890 |
+
};
|
| 1891 |
+
|
| 1892 |
+
var encode = function(n) {
|
| 1893 |
+
if (n < 0) {
|
| 1894 |
+
// error.
|
| 1895 |
+
} else if (n < 26) {
|
| 1896 |
+
return 0x41 + n;
|
| 1897 |
+
} else if (n < 52) {
|
| 1898 |
+
return 0x61 + (n - 26);
|
| 1899 |
+
} else if (n < 62) {
|
| 1900 |
+
return 0x30 + (n - 52);
|
| 1901 |
+
} else if (n == 62) {
|
| 1902 |
+
return 0x2b;
|
| 1903 |
+
} else if (n == 63) {
|
| 1904 |
+
return 0x2f;
|
| 1905 |
+
}
|
| 1906 |
+
throw 'n:' + n;
|
| 1907 |
+
};
|
| 1908 |
+
|
| 1909 |
+
_this.writeByte = function(n) {
|
| 1910 |
+
|
| 1911 |
+
_buffer = (_buffer << 8) | (n & 0xff);
|
| 1912 |
+
_buflen += 8;
|
| 1913 |
+
_length += 1;
|
| 1914 |
+
|
| 1915 |
+
while (_buflen >= 6) {
|
| 1916 |
+
writeEncoded(_buffer >>> (_buflen - 6) );
|
| 1917 |
+
_buflen -= 6;
|
| 1918 |
+
}
|
| 1919 |
+
};
|
| 1920 |
+
|
| 1921 |
+
_this.flush = function() {
|
| 1922 |
+
|
| 1923 |
+
if (_buflen > 0) {
|
| 1924 |
+
writeEncoded(_buffer << (6 - _buflen) );
|
| 1925 |
+
_buffer = 0;
|
| 1926 |
+
_buflen = 0;
|
| 1927 |
+
}
|
| 1928 |
+
|
| 1929 |
+
if (_length % 3 != 0) {
|
| 1930 |
+
// padding
|
| 1931 |
+
var padlen = 3 - _length % 3;
|
| 1932 |
+
for (var i = 0; i < padlen; i += 1) {
|
| 1933 |
+
_base64 += '=';
|
| 1934 |
+
}
|
| 1935 |
+
}
|
| 1936 |
+
};
|
| 1937 |
+
|
| 1938 |
+
_this.toString = function() {
|
| 1939 |
+
return _base64;
|
| 1940 |
+
};
|
| 1941 |
+
|
| 1942 |
+
return _this;
|
| 1943 |
+
};
|
| 1944 |
+
|
| 1945 |
+
//---------------------------------------------------------------------
|
| 1946 |
+
// base64DecodeInputStream
|
| 1947 |
+
//---------------------------------------------------------------------
|
| 1948 |
+
|
| 1949 |
+
var base64DecodeInputStream = function(str) {
|
| 1950 |
+
|
| 1951 |
+
var _str = str;
|
| 1952 |
+
var _pos = 0;
|
| 1953 |
+
var _buffer = 0;
|
| 1954 |
+
var _buflen = 0;
|
| 1955 |
+
|
| 1956 |
+
var _this = {};
|
| 1957 |
+
|
| 1958 |
+
_this.read = function() {
|
| 1959 |
+
|
| 1960 |
+
while (_buflen < 8) {
|
| 1961 |
+
|
| 1962 |
+
if (_pos >= _str.length) {
|
| 1963 |
+
if (_buflen == 0) {
|
| 1964 |
+
return -1;
|
| 1965 |
+
}
|
| 1966 |
+
throw 'unexpected end of file./' + _buflen;
|
| 1967 |
+
}
|
| 1968 |
+
|
| 1969 |
+
var c = _str.charAt(_pos);
|
| 1970 |
+
_pos += 1;
|
| 1971 |
+
|
| 1972 |
+
if (c == '=') {
|
| 1973 |
+
_buflen = 0;
|
| 1974 |
+
return -1;
|
| 1975 |
+
} else if (c.match(/^\s$/) ) {
|
| 1976 |
+
// ignore if whitespace.
|
| 1977 |
+
continue;
|
| 1978 |
+
}
|
| 1979 |
+
|
| 1980 |
+
_buffer = (_buffer << 6) | decode(c.charCodeAt(0) );
|
| 1981 |
+
_buflen += 6;
|
| 1982 |
+
}
|
| 1983 |
+
|
| 1984 |
+
var n = (_buffer >>> (_buflen - 8) ) & 0xff;
|
| 1985 |
+
_buflen -= 8;
|
| 1986 |
+
return n;
|
| 1987 |
+
};
|
| 1988 |
+
|
| 1989 |
+
var decode = function(c) {
|
| 1990 |
+
if (0x41 <= c && c <= 0x5a) {
|
| 1991 |
+
return c - 0x41;
|
| 1992 |
+
} else if (0x61 <= c && c <= 0x7a) {
|
| 1993 |
+
return c - 0x61 + 26;
|
| 1994 |
+
} else if (0x30 <= c && c <= 0x39) {
|
| 1995 |
+
return c - 0x30 + 52;
|
| 1996 |
+
} else if (c == 0x2b) {
|
| 1997 |
+
return 62;
|
| 1998 |
+
} else if (c == 0x2f) {
|
| 1999 |
+
return 63;
|
| 2000 |
+
} else {
|
| 2001 |
+
throw 'c:' + c;
|
| 2002 |
+
}
|
| 2003 |
+
};
|
| 2004 |
+
|
| 2005 |
+
return _this;
|
| 2006 |
+
};
|
| 2007 |
+
|
| 2008 |
+
//---------------------------------------------------------------------
|
| 2009 |
+
// gifImage (B/W)
|
| 2010 |
+
//---------------------------------------------------------------------
|
| 2011 |
+
|
| 2012 |
+
var gifImage = function(width, height) {
|
| 2013 |
+
|
| 2014 |
+
var _width = width;
|
| 2015 |
+
var _height = height;
|
| 2016 |
+
var _data = new Array(width * height);
|
| 2017 |
+
|
| 2018 |
+
var _this = {};
|
| 2019 |
+
|
| 2020 |
+
_this.setPixel = function(x, y, pixel) {
|
| 2021 |
+
_data[y * _width + x] = pixel;
|
| 2022 |
+
};
|
| 2023 |
+
|
| 2024 |
+
_this.write = function(out) {
|
| 2025 |
+
|
| 2026 |
+
//---------------------------------
|
| 2027 |
+
// GIF Signature
|
| 2028 |
+
|
| 2029 |
+
out.writeString('GIF87a');
|
| 2030 |
+
|
| 2031 |
+
//---------------------------------
|
| 2032 |
+
// Screen Descriptor
|
| 2033 |
+
|
| 2034 |
+
out.writeShort(_width);
|
| 2035 |
+
out.writeShort(_height);
|
| 2036 |
+
|
| 2037 |
+
out.writeByte(0x80); // 2bit
|
| 2038 |
+
out.writeByte(0);
|
| 2039 |
+
out.writeByte(0);
|
| 2040 |
+
|
| 2041 |
+
//---------------------------------
|
| 2042 |
+
// Global Color Map
|
| 2043 |
+
|
| 2044 |
+
// black
|
| 2045 |
+
out.writeByte(0x00);
|
| 2046 |
+
out.writeByte(0x00);
|
| 2047 |
+
out.writeByte(0x00);
|
| 2048 |
+
|
| 2049 |
+
// white
|
| 2050 |
+
out.writeByte(0xff);
|
| 2051 |
+
out.writeByte(0xff);
|
| 2052 |
+
out.writeByte(0xff);
|
| 2053 |
+
|
| 2054 |
+
//---------------------------------
|
| 2055 |
+
// Image Descriptor
|
| 2056 |
+
|
| 2057 |
+
out.writeString(',');
|
| 2058 |
+
out.writeShort(0);
|
| 2059 |
+
out.writeShort(0);
|
| 2060 |
+
out.writeShort(_width);
|
| 2061 |
+
out.writeShort(_height);
|
| 2062 |
+
out.writeByte(0);
|
| 2063 |
+
|
| 2064 |
+
//---------------------------------
|
| 2065 |
+
// Local Color Map
|
| 2066 |
+
|
| 2067 |
+
//---------------------------------
|
| 2068 |
+
// Raster Data
|
| 2069 |
+
|
| 2070 |
+
var lzwMinCodeSize = 2;
|
| 2071 |
+
var raster = getLZWRaster(lzwMinCodeSize);
|
| 2072 |
+
|
| 2073 |
+
out.writeByte(lzwMinCodeSize);
|
| 2074 |
+
|
| 2075 |
+
var offset = 0;
|
| 2076 |
+
|
| 2077 |
+
while (raster.length - offset > 255) {
|
| 2078 |
+
out.writeByte(255);
|
| 2079 |
+
out.writeBytes(raster, offset, 255);
|
| 2080 |
+
offset += 255;
|
| 2081 |
+
}
|
| 2082 |
+
|
| 2083 |
+
out.writeByte(raster.length - offset);
|
| 2084 |
+
out.writeBytes(raster, offset, raster.length - offset);
|
| 2085 |
+
out.writeByte(0x00);
|
| 2086 |
+
|
| 2087 |
+
//---------------------------------
|
| 2088 |
+
// GIF Terminator
|
| 2089 |
+
out.writeString(';');
|
| 2090 |
+
};
|
| 2091 |
+
|
| 2092 |
+
var bitOutputStream = function(out) {
|
| 2093 |
+
|
| 2094 |
+
var _out = out;
|
| 2095 |
+
var _bitLength = 0;
|
| 2096 |
+
var _bitBuffer = 0;
|
| 2097 |
+
|
| 2098 |
+
var _this = {};
|
| 2099 |
+
|
| 2100 |
+
_this.write = function(data, length) {
|
| 2101 |
+
|
| 2102 |
+
if ( (data >>> length) != 0) {
|
| 2103 |
+
throw 'length over';
|
| 2104 |
+
}
|
| 2105 |
+
|
| 2106 |
+
while (_bitLength + length >= 8) {
|
| 2107 |
+
_out.writeByte(0xff & ( (data << _bitLength) | _bitBuffer) );
|
| 2108 |
+
length -= (8 - _bitLength);
|
| 2109 |
+
data >>>= (8 - _bitLength);
|
| 2110 |
+
_bitBuffer = 0;
|
| 2111 |
+
_bitLength = 0;
|
| 2112 |
+
}
|
| 2113 |
+
|
| 2114 |
+
_bitBuffer = (data << _bitLength) | _bitBuffer;
|
| 2115 |
+
_bitLength = _bitLength + length;
|
| 2116 |
+
};
|
| 2117 |
+
|
| 2118 |
+
_this.flush = function() {
|
| 2119 |
+
if (_bitLength > 0) {
|
| 2120 |
+
_out.writeByte(_bitBuffer);
|
| 2121 |
+
}
|
| 2122 |
+
};
|
| 2123 |
+
|
| 2124 |
+
return _this;
|
| 2125 |
+
};
|
| 2126 |
+
|
| 2127 |
+
var getLZWRaster = function(lzwMinCodeSize) {
|
| 2128 |
+
|
| 2129 |
+
var clearCode = 1 << lzwMinCodeSize;
|
| 2130 |
+
var endCode = (1 << lzwMinCodeSize) + 1;
|
| 2131 |
+
var bitLength = lzwMinCodeSize + 1;
|
| 2132 |
+
|
| 2133 |
+
// Setup LZWTable
|
| 2134 |
+
var table = lzwTable();
|
| 2135 |
+
|
| 2136 |
+
for (var i = 0; i < clearCode; i += 1) {
|
| 2137 |
+
table.add(String.fromCharCode(i) );
|
| 2138 |
+
}
|
| 2139 |
+
table.add(String.fromCharCode(clearCode) );
|
| 2140 |
+
table.add(String.fromCharCode(endCode) );
|
| 2141 |
+
|
| 2142 |
+
var byteOut = byteArrayOutputStream();
|
| 2143 |
+
var bitOut = bitOutputStream(byteOut);
|
| 2144 |
+
|
| 2145 |
+
// clear code
|
| 2146 |
+
bitOut.write(clearCode, bitLength);
|
| 2147 |
+
|
| 2148 |
+
var dataIndex = 0;
|
| 2149 |
+
|
| 2150 |
+
var s = String.fromCharCode(_data[dataIndex]);
|
| 2151 |
+
dataIndex += 1;
|
| 2152 |
+
|
| 2153 |
+
while (dataIndex < _data.length) {
|
| 2154 |
+
|
| 2155 |
+
var c = String.fromCharCode(_data[dataIndex]);
|
| 2156 |
+
dataIndex += 1;
|
| 2157 |
+
|
| 2158 |
+
if (table.contains(s + c) ) {
|
| 2159 |
+
|
| 2160 |
+
s = s + c;
|
| 2161 |
+
|
| 2162 |
+
} else {
|
| 2163 |
+
|
| 2164 |
+
bitOut.write(table.indexOf(s), bitLength);
|
| 2165 |
+
|
| 2166 |
+
if (table.size() < 0xfff) {
|
| 2167 |
+
|
| 2168 |
+
if (table.size() == (1 << bitLength) ) {
|
| 2169 |
+
bitLength += 1;
|
| 2170 |
+
}
|
| 2171 |
+
|
| 2172 |
+
table.add(s + c);
|
| 2173 |
+
}
|
| 2174 |
+
|
| 2175 |
+
s = c;
|
| 2176 |
+
}
|
| 2177 |
+
}
|
| 2178 |
+
|
| 2179 |
+
bitOut.write(table.indexOf(s), bitLength);
|
| 2180 |
+
|
| 2181 |
+
// end code
|
| 2182 |
+
bitOut.write(endCode, bitLength);
|
| 2183 |
+
|
| 2184 |
+
bitOut.flush();
|
| 2185 |
+
|
| 2186 |
+
return byteOut.toByteArray();
|
| 2187 |
+
};
|
| 2188 |
+
|
| 2189 |
+
var lzwTable = function() {
|
| 2190 |
+
|
| 2191 |
+
var _map = {};
|
| 2192 |
+
var _size = 0;
|
| 2193 |
+
|
| 2194 |
+
var _this = {};
|
| 2195 |
+
|
| 2196 |
+
_this.add = function(key) {
|
| 2197 |
+
if (_this.contains(key) ) {
|
| 2198 |
+
throw 'dup key:' + key;
|
| 2199 |
+
}
|
| 2200 |
+
_map[key] = _size;
|
| 2201 |
+
_size += 1;
|
| 2202 |
+
};
|
| 2203 |
+
|
| 2204 |
+
_this.size = function() {
|
| 2205 |
+
return _size;
|
| 2206 |
+
};
|
| 2207 |
+
|
| 2208 |
+
_this.indexOf = function(key) {
|
| 2209 |
+
return _map[key];
|
| 2210 |
+
};
|
| 2211 |
+
|
| 2212 |
+
_this.contains = function(key) {
|
| 2213 |
+
return typeof _map[key] != 'undefined';
|
| 2214 |
+
};
|
| 2215 |
+
|
| 2216 |
+
return _this;
|
| 2217 |
+
};
|
| 2218 |
+
|
| 2219 |
+
return _this;
|
| 2220 |
+
};
|
| 2221 |
+
|
| 2222 |
+
var createDataURL = function(width, height, getPixel) {
|
| 2223 |
+
var gif = gifImage(width, height);
|
| 2224 |
+
for (var y = 0; y < height; y += 1) {
|
| 2225 |
+
for (var x = 0; x < width; x += 1) {
|
| 2226 |
+
gif.setPixel(x, y, getPixel(x, y) );
|
| 2227 |
+
}
|
| 2228 |
+
}
|
| 2229 |
+
|
| 2230 |
+
var b = byteArrayOutputStream();
|
| 2231 |
+
gif.write(b);
|
| 2232 |
+
|
| 2233 |
+
var base64 = base64EncodeOutputStream();
|
| 2234 |
+
var bytes = b.toByteArray();
|
| 2235 |
+
for (var i = 0; i < bytes.length; i += 1) {
|
| 2236 |
+
base64.writeByte(bytes[i]);
|
| 2237 |
+
}
|
| 2238 |
+
base64.flush();
|
| 2239 |
+
|
| 2240 |
+
return 'data:image/gif;base64,' + base64;
|
| 2241 |
+
};
|
| 2242 |
+
|
| 2243 |
+
//---------------------------------------------------------------------
|
| 2244 |
+
// returns qrcode function.
|
| 2245 |
+
|
| 2246 |
+
return qrcode;
|
| 2247 |
+
}();
|
| 2248 |
+
|
| 2249 |
+
// multibyte support
|
| 2250 |
+
!function() {
|
| 2251 |
+
|
| 2252 |
+
qrcode.stringToBytesFuncs['UTF-8'] = function(s) {
|
| 2253 |
+
// http://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array
|
| 2254 |
+
function toUTF8Array(str) {
|
| 2255 |
+
var utf8 = [];
|
| 2256 |
+
for (var i=0; i < str.length; i++) {
|
| 2257 |
+
var charcode = str.charCodeAt(i);
|
| 2258 |
+
if (charcode < 0x80) utf8.push(charcode);
|
| 2259 |
+
else if (charcode < 0x800) {
|
| 2260 |
+
utf8.push(0xc0 | (charcode >> 6),
|
| 2261 |
+
0x80 | (charcode & 0x3f));
|
| 2262 |
+
}
|
| 2263 |
+
else if (charcode < 0xd800 || charcode >= 0xe000) {
|
| 2264 |
+
utf8.push(0xe0 | (charcode >> 12),
|
| 2265 |
+
0x80 | ((charcode>>6) & 0x3f),
|
| 2266 |
+
0x80 | (charcode & 0x3f));
|
| 2267 |
+
}
|
| 2268 |
+
// surrogate pair
|
| 2269 |
+
else {
|
| 2270 |
+
i++;
|
| 2271 |
+
// UTF-16 encodes 0x10000-0x10FFFF by
|
| 2272 |
+
// subtracting 0x10000 and splitting the
|
| 2273 |
+
// 20 bits of 0x0-0xFFFFF into two halves
|
| 2274 |
+
charcode = 0x10000 + (((charcode & 0x3ff)<<10)
|
| 2275 |
+
| (str.charCodeAt(i) & 0x3ff));
|
| 2276 |
+
utf8.push(0xf0 | (charcode >>18),
|
| 2277 |
+
0x80 | ((charcode>>12) & 0x3f),
|
| 2278 |
+
0x80 | ((charcode>>6) & 0x3f),
|
| 2279 |
+
0x80 | (charcode & 0x3f));
|
| 2280 |
+
}
|
| 2281 |
+
}
|
| 2282 |
+
return utf8;
|
| 2283 |
+
}
|
| 2284 |
+
return toUTF8Array(s);
|
| 2285 |
+
};
|
| 2286 |
+
|
| 2287 |
+
}();
|
| 2288 |
+
|
| 2289 |
+
(function (factory) {
|
| 2290 |
+
if (typeof define === 'function' && define.amd) {
|
| 2291 |
+
define([], factory);
|
| 2292 |
+
} else if (typeof exports === 'object') {
|
| 2293 |
+
module.exports = factory();
|
| 2294 |
+
}
|
| 2295 |
+
}(function () {
|
| 2296 |
+
return qrcode;
|
| 2297 |
+
}));
|