Compare commits

...

15 Commits

23 changed files with 1485 additions and 45 deletions

9
.gitmodules vendored Normal file
View File

@ -0,0 +1,9 @@
[submodule "external/LZMA-JS"]
path = external/LZMA-JS
url = https://github.com/LZMA-JS/LZMA-JS.git
[submodule "external/base64.js"]
path = external/base64.js
url = https://gist.github.com/72c423f727d395eeaa09697058238727.git
[submodule "external/qr-scanner"]
path = external/qr-scanner
url = https://github.com/nimiq/qr-scanner.git

View File

@ -4,5 +4,5 @@ from . import consumers
websocket_urlpatterns = [
re_path(r'camera/ws/(?P<kind>host|client)/(?P<room_name>\w+)/$',
consumers.VideoConsumer),
consumers.VideoConsumer.as_asgi()),
]

1
camera/static/js/base64.js Symbolic link
View File

@ -0,0 +1 @@
../../../external/base64.js/base64.js

View File

@ -0,0 +1 @@
../../../external/LZMA-JS/src/lzma_worker.js

175
camera/static/js/prefix Normal file
View File

@ -0,0 +1,175 @@
{"description":{"type":"answer","sdp":"v=0
o=- 0 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0
a=msid-semantic: WMS
m=audio 9 UDP/TLS/RTP/SAVPF 109 9 0 8 101
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:0 1 udp 0 a.local 0 typ host generation 0 network-cost 999
a=ice-ufrag:
a=ice-pwd:
a=ice-options:trickle
a=fingerprint:sha-256 000:01:12:23:34:45:56:67:78:89:9A:AB:BC:CD:DE:EF:F0:02:03:04:05:06:07:08:09:0A:0B:0C:0D:0E:0F
a=setup:active
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
a=recvonly
a=rtcp-mux
a=rtpmap:109 opus/48000/2
a=fmtp:109 minptime=10;useinbandfec=1
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
"}}
{"description":{"type":"offer","sdp":"v=0
o=mozilla...THIS_IS_SDPARTA-68.8.1 0 0 IN IP4 0.0.0.0
s=-
t=0 0
a=sendrecv
a=fingerprint:sha-256 0:10:11:12:13:14:15:16:17:18:19:1A:1B:1C:1D:1E:1F:20:21:22:23:24:25:26:27:28:29:2A:2B:2C:2D
a=group:BUNDLE 0
a=ice-options:trickle
a=msid-semantic:WMS *
m=audio 40000 UDP/TLS/RTP/SAVPF 109 9 0 8 101
c=IN IP4 192.168.0.0
a=candidate:0 1 UDP 2100000000 192.168.0.0 40000 typ host
a=candidate:1 1 UDP 2100000000 ::1 40000 typ host
a=candidate:2 1 TCP 2100000000 192.168.0.0 40000 typ host tcptype passive
a=candidate:2 1 TCP 2100000000 192.168.0.0 9 typ host tcptype active
a=candidate:3 1 TCP 2100000000 ::1 40000 typ host tcptype passive
a=candidate:3 1 TCP 2100000000 ::1 9 typ host tcptype active
a=candidate:0 2 UDP 2100000000 192.168.0.0 40000 typ host
a=candidate:1 2 UDP 2100000000 ::1 40000 typ host
a=candidate:2 2 TCP 2100000000 192.168.0.0 40000 typ host tcptype passive
a=candidate:2 2 TCP 2100000000 192.168.0.0 9 typ host tcptype active
a=candidate:3 2 TCP 2100000000 ::1 40000 typ host tcptype passive
a=candidate:3 2 TCP 2100000000 ::1 9 typ host tcptype active
a=sendrecv
a=end-of-candidates
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2/recvonly urn:ietf:params:rtp-hdrext:csrc-audio-level
a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
a=fmtp:109 maxplaybackrate=48000;stereo=1;useinbandfec=1
a=fmtp:101 0-15
a=ice-pwd:
a=ice-ufrag:
a=mid:0
a=msid:{}
a=rtcp:40000 IN IP4 192.168.0.0
a=rtcp-mux
a=rtpmap:109 opus/48000/2
a=rtpmap:9 G722/8000/1
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:101 telephone-event/8000
a=setup:actpass
a=ssrc:0 cname:{}
"}}
{"description":{"type":"answer","sdp":"v=0
o=- 0 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0
a=msid-semantic: WMS
m=video 9 UDP/TLS/RTP/SAVPF 120 121
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=candidate:0 1 udp 2100000000 abc.local 40000 typ host generation 0 network-cost 999
a=ice-ufrag:
a=ice-pwd:
a=ice-options:trickle
a=fingerprint:sha-256 0:2E:2F:30:31:32:33:34:35:36:37:38:39:3A:3B:3C:3D:3E:3F:40:41:42:43:44:45:46:47:48:49:4A:4B
a=setup:active
a=mid:0
a=extmap:5 urn:ietf:params:rtp-hdrext:toffset
a=extmap:4 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
a=recvonly
a=rtcp-mux
a=rtpmap:120 VP8/90000
a=rtcp-fb:120 goog-remb
a=rtcp-fb:120 ccm fir
a=rtcp-fb:120 nack
a=rtcp-fb:120 nack pli
a=rtpmap:121 VP9/90000
a=rtcp-fb:121 goog-remb
a=rtcp-fb:121 ccm fir
a=rtcp-fb:121 nack
a=rtcp-fb:121 nack pli
a=fmtp:121 profile-id=0
"}}
{"description":{"type":"offer","sdp":"v=0
o=mozilla...THIS_IS_SDPARTA-68.8.1 0 0 IN IP4 0.0.0.0
s=-
t=0 0
a=sendrecv
a=fingerprint:sha-256 0:4C:4D:4E:4F:50:51:52:53:54:55:56:57:58:59:5A:5B:5C:5D:5E:5F:60:61:62:63:64:65:66:67:68:69
a=group:BUNDLE 0
a=ice-options:trickle
a=msid-semantic:WMS *
m=video 40000 UDP/TLS/RTP/SAVPF 120 121
c=IN IP4 192.168.0.0
a=candidate:0 1 UDP 2100000000 192.168.0.0 40000 typ host
a=candidate:1 1 UDP 2100000000 ::1 40000 typ host
a=candidate:2 1 TCP 2100000000 192.168.0.0 40000 typ host tcptype passive
a=candidate:2 1 TCP 2100000000 192.168.0.0 9 typ host tcptype active
a=candidate:3 1 TCP 2100000000 ::1 40000 typ host tcptype passive
a=candidate:3 1 TCP 2100000000 ::1 9 typ host tcptype active
a=candidate:0 2 UDP 2100000000 192.168.0.0 40000 typ host
a=candidate:1 2 UDP 2100000000 ::1 40000 typ host
a=candidate:2 2 TCP 2100000000 192.168.0.0 40000 typ host tcptype passive
a=candidate:2 2 TCP 2100000000 192.168.0.0 9 typ host tcptype active
a=candidate:3 2 TCP 2100000000 ::1 40000 typ host tcptype passive
a=candidate:3 2 TCP 2100000000 ::1 9 typ host tcptype active
a=sendrecv
a=end-of-candidates
a=extmap:3 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:4 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:5 urn:ietf:params:rtp-hdrext:toffset
a=fmtp:120 max-fs=12288;max-fr=60
a=fmtp:121 max-fs=12288;max-fr=60
a=ice-pwd:
a=ice-ufrag:
a=mid:0
a=msid:{}
a=rtcp:40000 IN IP4 192.168.0.0
a=rtcp-fb:120 nack
a=rtcp-fb:120 nack pli
a=rtcp-fb:120 ccm fir
a=rtcp-fb:120 goog-remb
a=rtcp-fb:121 nack
a=rtcp-fb:121 nack pli
a=rtcp-fb:121 ccm fir
a=rtcp-fb:121 goog-remb
a=rtcp-mux
a=rtpmap:120 VP8/90000
a=rtpmap:121 VP9/90000
a=setup:actpass
a=ssrc:0 cname:{}
"}}
{"description":{"type":"offer","sdp":"v=0
o=mozilla...THIS_IS_SDPARTA-75.0 0 0 IN IP4 0.0.0.0
s=-
t=0 0
a=sendrecv
a=fingerprint:sha-256 6A:6B:6C:6D:6E:6F:70:71:72:73:74:75:76:77:78:79:7A:7B:7C:7D:7E:7F:80:81:82:83:84:85:86:87:88:89
a=group:BUNDLE 0
a=ice-options:trickle
a=msid-semantic:WMS *
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
c=IN IP4 0.0.0.0
a=candidate:0 1 UDP 2100000000 0.local 40000 typ host
a=candidate:1 1 TCP 2100000000 0.local 9 typ host tcptype active
a=sendrecv
a=end-of-candidates
a=ice-pwd:
a=ice-ufrag:
a=mid:0
a=setup:actpass
a=sctp-port:5000
a=max-message-size:
"}}

View File

@ -0,0 +1,47 @@
async function loadLZMAPrefixCompressor(prefixFilename) {
class PrefixCompressor {
constructor(prefix, compress, decompress, bytesToIgnore) {
this.prefix = prefix;
this.prefixCompressed = compress(prefix);
this.bytesToIgnore = bytesToIgnore;
this.baseCompress = compress;
this.baseDecompress = decompress;
}
compress(str) {
const c = this.baseCompress(this.prefix + str);
for (var i = this.bytesToIgnore;
i < this.prefixCompressed.length
&& this.prefixCompressed[i] == c[i];
i++) { }
const omittedBytes = this.prefixCompressed.length - i;
const res = c.subarray(i - 1);
res[0] = omittedBytes;
return res;
}
decompress(arr) {
const omittedBytes = arr[0];
const prefixBytes = this.prefixCompressed.length-omittedBytes;
const c = new Uint8Array(prefixBytes + arr.length-1);
c.set(this.prefixCompressed.subarray(0, prefixBytes));
c.set(arr.subarray(1), prefixBytes);
const d = this.baseDecompress(c);
return d.substring(this.prefix.length);
}
}
async function loadDictionary() {
const response = await fetch(prefixFilename);
return await response.text();
}
const prefix = await loadDictionary();
return new PrefixCompressor(prefix, str => {
const res = new Uint8Array(LZMA.compress(str, 9));
// Clear uncompressed size as it will be invalid.
for (var i = 5; i < 13; i++) res[i] = 255;
return res;
}, arr => {
return LZMA.decompress(arr).toString();
}, 12);
}

View File

@ -0,0 +1 @@
../../../external/qr-scanner-binaryData/qr-scanner-worker.min.js

View File

@ -0,0 +1 @@
../../../external/qr-scanner/qr-scanner-worker.min.js.map

1
camera/static/js/qr-scanner.min.js vendored Symbolic link
View File

@ -0,0 +1 @@
../../../external/qr-scanner/qr-scanner.min.js

View File

@ -0,0 +1 @@
../../../external/qr-scanner/qr-scanner.min.js.map

View File

@ -0,0 +1 @@
../../../external/qr-code-generator/qrcodegen.js

View File

@ -3,18 +3,23 @@
<head>
<meta charset="utf-8"/>
<title>Minimal WebRTC</title>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
<style>
body.justVideo {
margin: 0;
overflow: hidden;
}
#qrcodelink {
body.justVideo #status, body.justVideo form {
display: none;
}
.qrcontainer {
position: sticky;
top: 0;
right: 0;
float: right;
}
#qrcode {
.qrcontainer svg path {
min-width: 30vw;
max-width: 100vw;
max-height: 50vh;
}
@ -51,17 +56,37 @@ form label {
background: lightyellow;
}
</style>
<script type="text/javascript" src="static/js/qrcodegen.js"></script>
<script type="text/javascript" src="static/js/lzma_worker.js"></script>
<script type="text/javascript" src="static/js/prefixCompressor.js"></script>
</head>
<body>
<a id="qrcodelink" style="display:none;"><img id="qrcode" /></a>
<a class="qrcontainer" id="qrcodelink" style="display:none;">
<svg xmlns="http://www.w3.org/2000/svg" id="qrcode" style="width:20em; height:20em;" stroke="none">
<rect width="100%" height="100%" fill="#FFFFFF"/>
<path d="" fill="#000000"/>
</svg>
</a>
<div class="qrcontainer" id="qrcodeObjContainer" style="display:none;">
<svg xmlns="http://www.w3.org/2000/svg" id="qrcodeObj" style="width:20em; height:20em;" stroke="none">
<rect width="100%" height="100%" fill="#FFFFFF"/>
<path d="" fill="#000000"/>
</svg> <br/>
<textarea id="localOffer" readonly></textarea>
<button id="copyLocalOffer">Copy</button>
</div>
<form name="settings">
<label>
Serverless mode:
<input name="serverless" type="checkbox" value="true" />
</label>
<label>
Recieve remote video:
<select name="client-video">
<option value="none">none</option>
<option value="true">any camera</option>
<option value="environment" selected>rear camera</option>
<option value="user">front camera</option>
<option value="true" selected>any camera</option>
<option value="screen">screen share</option>
</select>
</label>
@ -74,6 +99,8 @@ form label {
<select name="host-video">
<option value="none" selected>none</option>
<option value="true">any camera</option>
<option value="environment">rear camera</option>
<option value="user">front camera</option>
<option value="screen">screen share</option>
</select>
</label>
@ -81,6 +108,17 @@ form label {
Transmit audio:
<input name="host-audio" type="checkbox" value="true" />
</label>
<label>
Debug mode (show log even after starting video):
<input name="debug" type="checkbox" value="true" />
</label>
</form>
<form id="serverlessOffer" style="display: none;" onsubmit="return false;">
<label>
Paste offer here:
<textarea name="remoteOffer"></textarea>
<button id="remoteOfferScan">Scan QR code</button>
</label>
</form>
<div id="status"></div>
<div id="videos">
@ -88,19 +126,42 @@ form label {
<button id="start" style="display:none;">Start Streaming</button>
<video id="remoteView" width="100%" autoplay muted style="display:none;"></video>
<video id="selfView" width="200" height="150" autoplay muted style="display:none;"></video>
<video id="qrscan" width="100%" autoplay muted style="display:none;"></video>
</div>
<script >
<script type="module" >
import * as b64 from "./static/js/base64.js";
import QrScanner from "./static/js/qr-scanner.min.js";
QrScanner.WORKER_PATH = "./static/js/qr-scanner-worker.min.js"
async function initialize() {
const create = (container, type) => container.appendChild(document.createElement(type));
const QRGen = qrcodegen.QrCode;
var compressor = null;
async function getCompressor() {
if (compressor == null) {
compressor = await loadLZMAPrefixCompressor('static/js/prefix');
}
return compressor;
}
const body = document.querySelector("body");
const out = document.getElementById("status");
const qrcode = document.getElementById("qrcode");
const qrcodelink = document.getElementById("qrcodelink");
const qrcodeObjContainer = document.getElementById("qrcodeObjContainer");
const qrcodeObj = document.getElementById("qrcodeObj");
const remoteView = document.getElementById("remoteView");
const selfView = document.getElementById("selfView");
const qrscan = document.getElementById("qrscan");
const start = document.getElementById("start");
const unmute = document.getElementById("unmute");
const form = document.forms["settings"];
const remoteOfferForm = document.forms["serverlessOffer"];
const remoteOffer = remoteOfferForm.elements["remoteOffer"];
const remoteOfferScan = document.getElementById("remoteOfferScan");
const localOfferArea = document.getElementById("localOffer");
const localOfferCopyButton = document.getElementById("copyLocalOffer");
function _log(str, tag) {
const logEntry = document.createElement(tag);
@ -134,18 +195,64 @@ form label {
}
for (const el of form.elements) {
el.addEventListener("change", _ => {
el.addEventListener("change", e => {
window.location.hash = JSON.stringify(readSettingsForm(false));
if (e.target.name == 'serverless') {
window.location.reload(false);
}
});
}
function getRoomName() {
JSON.parse(document.getElementById('room-name').textContent);
function isServerless() {
return settings && 'serverless' in settings && settings.serverless;
}
function withHash(hash) {
return window.location.href.split('#')[0] + '#' + hash;
}
function displayQR(qrcode, val) {
const encodeFunc = val instanceof Uint8Array
? QRGen.encodeBinary
: QRGen.encodeText;
const qr = encodeFunc(val, QRGen.Ecc.MEDIUM);
const code = qr.toSvgString(1);
const viewBox = (/ viewBox="([^"]*)"/.exec(code))[1];
const pathD = (/ d="([^"]*)"/.exec(code))[1];
qrcode.setAttribute("viewBox", viewBox);
qrcode.querySelector("path").setAttribute("d", pathD);
}
function displayQRUrl(url) {
qrcodelink.href = url;
displayQR(qrcode, url);
qrcodelink.style.display = '';
}
function displayQRUrlForObj(obj) {
const json = JSON.stringify(obj);
log("Encoding message in QR code/link:");
logPre(json);
const compressed = compressor.compress(json);
const encoded = b64.bytesToBase64(compressed);
displayQRUrl(withHash('^' + encoded));
}
function displayQRCodeForObj(obj) {
const json = JSON.stringify(obj);
log("Encoding message in QR code for copy/paste:");
logPre(json);
const compressed = compressor.compress(json);
const encoded = b64.bytesToBase64(compressed);
displayQR(qrcodeObj, compressed);
localOfferArea.value = encoded;
localOfferCopyButton.onclick = _ => {
localOfferArea.focus();
localOfferArea.select();
document.execCommand('copy');
};
qrcodeObjContainer.style.display = '';
}
let roomName = window.location.hash;
let isHost = roomName === undefined || !roomName;
if (roomName.startsWith("#{")) {
if (roomName && roomName.startsWith("#{")) {
roomName = undefined;
isHost = true;
@ -167,6 +274,25 @@ form label {
log("Failed to read settings from hash: " + error);
}
}
var firstMessage = undefined;
if (roomName && roomName.startsWith("#^")) {
roomName = undefined;
isHost = false;
try {
const hash = decodeURI(window.location.hash);
log("Reading first message from hash:");
logPre(hash);
log("Decoded base64:");
const decoded = (await getCompressor()).decompress(b64.base64ToBytes(hash.substring(2)));
logPre(decoded);
firstMessage = decoded;
settings = {serverless: true};
} catch (error) {
log("Failed to read message from hash: " + error);
}
}
if (isHost) {
// From https://stackoverflow.com/a/1349426
function makeid(length) {
@ -179,17 +305,23 @@ form label {
return result;
}
if (!isServerless()) {
roomName = makeid(8);
qrcodelink.href = window.location.href.split('#')[0] + '#' + roomName;
qrcode.src = window.location.href.split('#')[0] + roomName + '/qr';
qrcodelink.style.display = '';
displayQRUrl(withHash(roomName));
}
} else {
if (!isServerless()) {
roomName = roomName.substring(1);
}
qrcodelink.style.display = 'none';
form.style.display = 'none';
}
if (isServerless()) {
log("In serverless mode.");
} else {
log("Room: " + roomName);
}
var webSocket = undefined;
@ -199,6 +331,10 @@ form label {
const method = dcReady ? "dataConnection" : "webSocket";
log("Sending message via " + method + "...");
logPre(toSend);
if (isServerless() && !dcReady) {
log("ERROR: Attempted to use webSocket in serverless mode.");
return;
}
(dcReady ? dc : webSocket).send(toSend);
}
@ -207,7 +343,12 @@ form label {
function sendOffer() {
if (pc.iceGatheringState == "complete") {
sendJson({
const sendFunc = !isServerless() || dc && dc.readyState == "open"
? sendJson
: isHost
? displayQRUrlForObj
: displayQRCodeForObj;
sendFunc({
description: pc.localDescription
});
}
@ -232,7 +373,10 @@ form label {
// ... but only once icegathering is complete.
pc.onnegotiationneeded = async function () {
log("In pc.onnegotiationneeded...");
await pc.setLocalDescription(await pc.createOffer());
const useOffer = (!settings || !('separateIce' in settings)
|| !settings.separateIce);
await pc.setLocalDescription(
await (useOffer ? pc.createOffer() : pc.createAnswer()));
sendOffer();
}
@ -245,11 +389,11 @@ form label {
if (stream.getVideoTracks().length > 0) {
remoteView.style.display = '';
out.style.display = 'none';
form.style.display = 'none';
videos.style.display = '';
if (!settings.debug) {
body.classList.add('justVideo');
}
}
};
pc.ondatachannel = e => {
@ -258,6 +402,7 @@ form label {
receiveMessage({source: "dataChannel", data: e.data});
}
log('Data channel initialized.');
qrcodeObjContainer.style.display = 'none';
}
if (isHost) {
@ -266,6 +411,8 @@ form label {
receiveMessage({source: "dataChannel", data: e.data});
}
dc.onopen = e => {
remoteOffer.disabled = true;
remoteOfferScan.disabled = true;
log("Data channel open, sending settings...")
settings = readSettingsForm(true);
sendJson({settings});
@ -306,7 +453,7 @@ form label {
: videoSettings == 'true'
? true
: { advanced: [{facingMode: videoSettings}] };
log("Created videoConstraints.");
log("Created videoConstraints: " + JSON.stringify(videoConstraints));
if (!videoConstraints && !audioSettings) return;
const stream = videoSettings == 'screen'
@ -327,8 +474,13 @@ form label {
log("Added track.");
pc.addTrack(track, stream);
}
log('End of startStreaming(), creating answer...');
if (settings && 'separateIce' in settings && settings.separateIce) {
await pc.setLocalDescription(await pc.createAnswer());
}
}
function startStreamingWithErorrHandling(fromButton) {
try {
startStreaming(fromButton)
.then(() => {
log("startStreaming() finished.");
@ -336,6 +488,9 @@ form label {
.catch(e => {
log("startStreaming() errored: " + e.message);
});
} catch (e) {
log("Error in startStreaming(): " + e);
}
}
start.addEventListener("click", _ => {
@ -354,12 +509,20 @@ form label {
settings = data.settings;
startStreamingWithErorrHandling(false);
} else if (data.description) {
try {
if (pc == undefined) pc = createRTCPeerConnection();
await pc.setRemoteDescription(data.description);
if (data.description.type == "offer") {
log("Got an offer...");
if (!settings || !('separateIce' in settings) || !settings.separateIce) {
await pc.setLocalDescription(await pc.createAnswer());
sendOffer();
} else {
log("separateIce mode, so delaying answer.");
}
}
} catch (e) {
log("Error accepting remote offer/answer: " + e);
}
}
};
@ -388,6 +551,7 @@ form label {
return webSocket;
}
if (!isServerless()) {
webSocket = createWebSocket();
if (!isHost) {
@ -396,8 +560,42 @@ form label {
// so here just notify the host to start the process.
webSocket.onopen = _ => sendJson({ready: true});
}
} else if (isHost) {
await getCompressor();
pc = createRTCPeerConnection();
remoteOffer.value = '';
remoteOffer.onchange = _ => {
try {
const decoded = b64.base64ToBytes(remoteOffer.value);
const decompressed = compressor.decompress(decoded);
receiveMessage({source: 'textarea', data: decompressed});
} catch (e) {
log("Error decoding remote offer: " + e);
}
};
remoteOfferScan.onclick = _ => {
qrscan.style.display = '';
const qrScanner = new QrScanner(qrscan, (result) => {
qrscan.style.display = 'none';
log('Decoded qr code.');
try {
const decompressed = compressor.decompress(new Uint8Array(result.binaryData));
receiveMessage({source: 'qr', data: decompressed});
qrScanner.destroy();
} catch(error) {
log('Error interpreting QR code: ' + error);
}
});
qrScanner.start();
};
remoteOfferForm.style.display = '';
} else {
receiveMessage({source: 'URL', data: firstMessage});
}
log("Finished <script> block.");
}
initialize();
</script>
</body>
</html>

View File

@ -79,7 +79,7 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/static/'
STATIC_URL = '/camera/static/'
ASGI_APPLICATION = 'camera_site.routing.application'
CHANNEL_LAYERS = {

1
external/LZMA-JS vendored Submodule

@ -0,0 +1 @@
Subproject commit 8f98fe85a1ef78ab6e9d26ab85ce338f50095a23

1
external/base64.js vendored Submodule

@ -0,0 +1 @@
Subproject commit 74d3cbf82481545bc26c104de2419f4ee30c7dd7

4
external/qr-code-generator/README vendored Normal file
View File

@ -0,0 +1,4 @@
Source: https://github.com/nayuki/QR-Code-generator
Downloaded from https://www.nayuki.io/res/qr-code-generator-library/qrcodegen.js
MIT Licensed

902
external/qr-code-generator/qrcodegen.js vendored Normal file
View File

@ -0,0 +1,902 @@
/*
* QR Code generator library (compiled from TypeScript)
*
* Copyright (c) 2019 Project Nayuki. (MIT License)
* https://www.nayuki.io/page/qr-code-generator-library
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
* - The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* - The Software is provided "as is", without warranty of any kind, express or
* implied, including but not limited to the warranties of merchantability,
* fitness for a particular purpose and noninfringement. In no event shall the
* authors or copyright holders be liable for any claim, damages or other
* liability, whether in an action of contract, tort or otherwise, arising from,
* out of or in connection with the Software or the use or other dealings in the
* Software.
*/
"use strict";
var qrcodegen;
(function (qrcodegen) {
/*---- QR Code symbol class ----*/
/*
* A QR Code symbol, which is a type of two-dimension barcode.
* Invented by Denso Wave and described in the ISO/IEC 18004 standard.
* Instances of this class represent an immutable square grid of black and white cells.
* The class provides static factory functions to create a QR Code from text or binary data.
* The class covers the QR Code Model 2 specification, supporting all versions (sizes)
* from 1 to 40, all 4 error correction levels, and 4 character encoding modes.
*
* Ways to create a QR Code object:
* - High level: Take the payload data and call QrCode.encodeText() or QrCode.encodeBinary().
* - Mid level: Custom-make the list of segments and call QrCode.encodeSegments().
* - Low level: Custom-make the array of data codeword bytes (including
* segment headers and final padding, excluding error correction codewords),
* supply the appropriate version number, and call the QrCode() constructor.
* (Note that all ways require supplying the desired error correction level.)
*/
var QrCode = /** @class */ (function () {
/*-- Constructor (low level) and fields --*/
// Creates a new QR Code with the given version number,
// error correction level, data codeword bytes, and mask number.
// This is a low-level API that most users should not use directly.
// A mid-level API is the encodeSegments() function.
function QrCode(
// The version number of this QR Code, which is between 1 and 40 (inclusive).
// This determines the size of this barcode.
version,
// The error correction level used in this QR Code.
errorCorrectionLevel, dataCodewords,
// The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive).
// Even if a QR Code is created with automatic masking requested (mask = -1),
// the resulting object still has a mask value between 0 and 7.
mask) {
this.version = version;
this.errorCorrectionLevel = errorCorrectionLevel;
this.mask = mask;
// The modules of this QR Code (false = white, true = black).
// Immutable after constructor finishes. Accessed through getModule().
this.modules = [];
// Indicates function modules that are not subjected to masking. Discarded when constructor finishes.
this.isFunction = [];
// Check scalar arguments
if (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION)
throw "Version value out of range";
if (mask < -1 || mask > 7)
throw "Mask value out of range";
this.size = version * 4 + 17;
// Initialize both grids to be size*size arrays of Boolean false
var row = [];
for (var i = 0; i < this.size; i++)
row.push(false);
for (var i = 0; i < this.size; i++) {
this.modules.push(row.slice()); // Initially all white
this.isFunction.push(row.slice());
}
// Compute ECC, draw modules
this.drawFunctionPatterns();
var allCodewords = this.addEccAndInterleave(dataCodewords);
this.drawCodewords(allCodewords);
// Do masking
if (mask == -1) { // Automatically choose best mask
var minPenalty = 1000000000;
for (var i = 0; i < 8; i++) {
this.applyMask(i);
this.drawFormatBits(i);
var penalty = this.getPenaltyScore();
if (penalty < minPenalty) {
mask = i;
minPenalty = penalty;
}
this.applyMask(i); // Undoes the mask due to XOR
}
}
if (mask < 0 || mask > 7)
throw "Assertion error";
this.mask = mask;
this.applyMask(mask); // Apply the final choice of mask
this.drawFormatBits(mask); // Overwrite old format bits
this.isFunction = [];
}
/*-- Static factory functions (high level) --*/
// Returns a QR Code representing the given Unicode text string at the given error correction level.
// As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer
// Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible
// QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the
// ecl argument if it can be done without increasing the version.
QrCode.encodeText = function (text, ecl) {
var segs = qrcodegen.QrSegment.makeSegments(text);
return QrCode.encodeSegments(segs, ecl);
};
// Returns a QR Code representing the given binary data at the given error correction level.
// This function always encodes using the binary segment mode, not any text mode. The maximum number of
// bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.
// The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.
QrCode.encodeBinary = function (data, ecl) {
var seg = qrcodegen.QrSegment.makeBytes(data);
return QrCode.encodeSegments([seg], ecl);
};
/*-- Static factory functions (mid level) --*/
// Returns a QR Code representing the given segments with the given encoding parameters.
// The smallest possible QR Code version within the given range is automatically
// chosen for the output. Iff boostEcl is true, then the ECC level of the result
// may be higher than the ecl argument if it can be done without increasing the
// version. The mask number is either between 0 to 7 (inclusive) to force that
// mask, or -1 to automatically choose an appropriate mask (which may be slow).
// This function allows the user to create a custom sequence of segments that switches
// between modes (such as alphanumeric and byte) to encode text in less space.
// This is a mid-level API; the high-level API is encodeText() and encodeBinary().
QrCode.encodeSegments = function (segs, ecl, minVersion, maxVersion, mask, boostEcl) {
if (minVersion === void 0) { minVersion = 1; }
if (maxVersion === void 0) { maxVersion = 40; }
if (mask === void 0) { mask = -1; }
if (boostEcl === void 0) { boostEcl = true; }
if (!(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION)
|| mask < -1 || mask > 7)
throw "Invalid value";
// Find the minimal version number to use
var version;
var dataUsedBits;
for (version = minVersion;; version++) {
var dataCapacityBits_1 = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available
var usedBits = QrSegment.getTotalBits(segs, version);
if (usedBits <= dataCapacityBits_1) {
dataUsedBits = usedBits;
break; // This version number is found to be suitable
}
if (version >= maxVersion) // All versions in the range could not fit the given data
throw "Data too long";
}
// Increase the error correction level while the data still fits in the current version number
for (var _i = 0, _a = [QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH]; _i < _a.length; _i++) { // From low to high
var newEcl = _a[_i];
if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8)
ecl = newEcl;
}
// Concatenate all segments to create the data bit string
var bb = [];
for (var _b = 0, segs_1 = segs; _b < segs_1.length; _b++) {
var seg = segs_1[_b];
appendBits(seg.mode.modeBits, 4, bb);
appendBits(seg.numChars, seg.mode.numCharCountBits(version), bb);
for (var _c = 0, _d = seg.getData(); _c < _d.length; _c++) {
var b = _d[_c];
bb.push(b);
}
}
if (bb.length != dataUsedBits)
throw "Assertion error";
// Add terminator and pad up to a byte if applicable
var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8;
if (bb.length > dataCapacityBits)
throw "Assertion error";
appendBits(0, Math.min(4, dataCapacityBits - bb.length), bb);
appendBits(0, (8 - bb.length % 8) % 8, bb);
if (bb.length % 8 != 0)
throw "Assertion error";
// Pad with alternating bytes until data capacity is reached
for (var padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11)
appendBits(padByte, 8, bb);
// Pack bits into bytes in big endian
var dataCodewords = [];
while (dataCodewords.length * 8 < bb.length)
dataCodewords.push(0);
bb.forEach(function (b, i) {
return dataCodewords[i >>> 3] |= b << (7 - (i & 7));
});
// Create the QR Code object
return new QrCode(version, ecl, dataCodewords, mask);
};
/*-- Accessor methods --*/
// Returns the color of the module (pixel) at the given coordinates, which is false
// for white or true for black. The top left corner has the coordinates (x=0, y=0).
// If the given coordinates are out of bounds, then false (white) is returned.
QrCode.prototype.getModule = function (x, y) {
return 0 <= x && x < this.size && 0 <= y && y < this.size && this.modules[y][x];
};
/*-- Public instance methods --*/
// Draws this QR Code, with the given module scale and border modules, onto the given HTML
// canvas element. The canvas's width and height is resized to (this.size + border * 2) * scale.
// The drawn image is be purely black and white, and fully opaque.
// The scale must be a positive integer and the border must be a non-negative integer.
QrCode.prototype.drawCanvas = function (scale, border, canvas) {
if (scale <= 0 || border < 0)
throw "Value out of range";
var width = (this.size + border * 2) * scale;
canvas.width = width;
canvas.height = width;
var ctx = canvas.getContext("2d");
for (var y = -border; y < this.size + border; y++) {
for (var x = -border; x < this.size + border; x++) {
ctx.fillStyle = this.getModule(x, y) ? "#000000" : "#FFFFFF";
ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale);
}
}
};
// Returns a string of SVG code for an image depicting this QR Code, with the given number
// of border modules. The string always uses Unix newlines (\n), regardless of the platform.
QrCode.prototype.toSvgString = function (border) {
if (border < 0)
throw "Border must be non-negative";
var parts = [];
for (var y = 0; y < this.size; y++) {
for (var x = 0; x < this.size; x++) {
if (this.getModule(x, y))
parts.push("M" + (x + border) + "," + (y + border) + "h1v1h-1z");
}
}
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 " + (this.size + border * 2) + " " + (this.size + border * 2) + "\" stroke=\"none\">\n\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n\t<path d=\"" + parts.join(" ") + "\" fill=\"#000000\"/>\n</svg>\n";
};
/*-- Private helper methods for constructor: Drawing function modules --*/
// Reads this object's version field, and draws and marks all function modules.
QrCode.prototype.drawFunctionPatterns = function () {
// Draw horizontal and vertical timing patterns
for (var i = 0; i < this.size; i++) {
this.setFunctionModule(6, i, i % 2 == 0);
this.setFunctionModule(i, 6, i % 2 == 0);
}
// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
this.drawFinderPattern(3, 3);
this.drawFinderPattern(this.size - 4, 3);
this.drawFinderPattern(3, this.size - 4);
// Draw numerous alignment patterns
var alignPatPos = this.getAlignmentPatternPositions();
var numAlign = alignPatPos.length;
for (var i = 0; i < numAlign; i++) {
for (var j = 0; j < numAlign; j++) {
// Don't draw on the three finder corners
if (!(i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0))
this.drawAlignmentPattern(alignPatPos[i], alignPatPos[j]);
}
}
// Draw configuration data
this.drawFormatBits(0); // Dummy mask value; overwritten later in the constructor
this.drawVersion();
};
// Draws two copies of the format bits (with its own error correction code)
// based on the given mask and this object's error correction level field.
QrCode.prototype.drawFormatBits = function (mask) {
// Calculate error correction code and pack bits
var data = this.errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3
var rem = data;
for (var i = 0; i < 10; i++)
rem = (rem << 1) ^ ((rem >>> 9) * 0x537);
var bits = (data << 10 | rem) ^ 0x5412; // uint15
if (bits >>> 15 != 0)
throw "Assertion error";
// Draw first copy
for (var i = 0; i <= 5; i++)
this.setFunctionModule(8, i, getBit(bits, i));
this.setFunctionModule(8, 7, getBit(bits, 6));
this.setFunctionModule(8, 8, getBit(bits, 7));
this.setFunctionModule(7, 8, getBit(bits, 8));
for (var i = 9; i < 15; i++)
this.setFunctionModule(14 - i, 8, getBit(bits, i));
// Draw second copy
for (var i = 0; i < 8; i++)
this.setFunctionModule(this.size - 1 - i, 8, getBit(bits, i));
for (var i = 8; i < 15; i++)
this.setFunctionModule(8, this.size - 15 + i, getBit(bits, i));
this.setFunctionModule(8, this.size - 8, true); // Always black
};
// Draws two copies of the version bits (with its own error correction code),
// based on this object's version field, iff 7 <= version <= 40.
QrCode.prototype.drawVersion = function () {
if (this.version < 7)
return;
// Calculate error correction code and pack bits
var rem = this.version; // version is uint6, in the range [7, 40]
for (var i = 0; i < 12; i++)
rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25);
var bits = this.version << 12 | rem; // uint18
if (bits >>> 18 != 0)
throw "Assertion error";
// Draw two copies
for (var i = 0; i < 18; i++) {
var color = getBit(bits, i);
var a = this.size - 11 + i % 3;
var b = Math.floor(i / 3);
this.setFunctionModule(a, b, color);
this.setFunctionModule(b, a, color);
}
};
// Draws a 9*9 finder pattern including the border separator,
// with the center module at (x, y). Modules can be out of bounds.
QrCode.prototype.drawFinderPattern = function (x, y) {
for (var dy = -4; dy <= 4; dy++) {
for (var dx = -4; dx <= 4; dx++) {
var dist = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm
var xx = x + dx;
var yy = y + dy;
if (0 <= xx && xx < this.size && 0 <= yy && yy < this.size)
this.setFunctionModule(xx, yy, dist != 2 && dist != 4);
}
}
};
// Draws a 5*5 alignment pattern, with the center module
// at (x, y). All modules must be in bounds.
QrCode.prototype.drawAlignmentPattern = function (x, y) {
for (var dy = -2; dy <= 2; dy++) {
for (var dx = -2; dx <= 2; dx++)
this.setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dx), Math.abs(dy)) != 1);
}
};
// Sets the color of a module and marks it as a function module.
// Only used by the constructor. Coordinates must be in bounds.
QrCode.prototype.setFunctionModule = function (x, y, isBlack) {
this.modules[y][x] = isBlack;
this.isFunction[y][x] = true;
};
/*-- Private helper methods for constructor: Codewords and masking --*/
// Returns a new byte string representing the given data with the appropriate error correction
// codewords appended to it, based on this object's version and error correction level.
QrCode.prototype.addEccAndInterleave = function (data) {
var ver = this.version;
var ecl = this.errorCorrectionLevel;
if (data.length != QrCode.getNumDataCodewords(ver, ecl))
throw "Invalid argument";
// Calculate parameter numbers
var numBlocks = QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver];
var blockEccLen = QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver];
var rawCodewords = Math.floor(QrCode.getNumRawDataModules(ver) / 8);
var numShortBlocks = numBlocks - rawCodewords % numBlocks;
var shortBlockLen = Math.floor(rawCodewords / numBlocks);
// Split data into blocks and append ECC to each block
var blocks = [];
var rsDiv = QrCode.reedSolomonComputeDivisor(blockEccLen);
for (var i = 0, k = 0; i < numBlocks; i++) {
var dat = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1));
k += dat.length;
var ecc = QrCode.reedSolomonComputeRemainder(dat, rsDiv);
if (i < numShortBlocks)
dat.push(0);
blocks.push(dat.concat(ecc));
}
// Interleave (not concatenate) the bytes from every block into a single sequence
var result = [];
for (var i = 0; i < blocks[0].length; i++) {
for (var j = 0; j < blocks.length; j++) {
// Skip the padding byte in short blocks
if (i != shortBlockLen - blockEccLen || j >= numShortBlocks)
result.push(blocks[j][i]);
}
}
if (result.length != rawCodewords)
throw "Assertion error";
return result;
};
// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
// data area of this QR Code. Function modules need to be marked off before this is called.
QrCode.prototype.drawCodewords = function (data) {
if (data.length != Math.floor(QrCode.getNumRawDataModules(this.version) / 8))
throw "Invalid argument";
var i = 0; // Bit index into the data
// Do the funny zigzag scan
for (var right = this.size - 1; right >= 1; right -= 2) { // Index of right column in each column pair
if (right == 6)
right = 5;
for (var vert = 0; vert < this.size; vert++) { // Vertical counter
for (var j = 0; j < 2; j++) {
var x = right - j; // Actual x coordinate
var upward = ((right + 1) & 2) == 0;
var y = upward ? this.size - 1 - vert : vert; // Actual y coordinate
if (!this.isFunction[y][x] && i < data.length * 8) {
this.modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7));
i++;
}
// If this QR Code has any remainder bits (0 to 7), they were assigned as
// 0/false/white by the constructor and are left unchanged by this method
}
}
}
if (i != data.length * 8)
throw "Assertion error";
};
// XORs the codeword modules in this QR Code with the given mask pattern.
// The function modules must be marked and the codeword bits must be drawn
// before masking. Due to the arithmetic of XOR, calling applyMask() with
// the same mask value a second time will undo the mask. A final well-formed
// QR Code needs exactly one (not zero, two, etc.) mask applied.
QrCode.prototype.applyMask = function (mask) {
if (mask < 0 || mask > 7)
throw "Mask value out of range";
for (var y = 0; y < this.size; y++) {
for (var x = 0; x < this.size; x++) {
var invert = void 0;
switch (mask) {
case 0:
invert = (x + y) % 2 == 0;
break;
case 1:
invert = y % 2 == 0;
break;
case 2:
invert = x % 3 == 0;
break;
case 3:
invert = (x + y) % 3 == 0;
break;
case 4:
invert = (Math.floor(x / 3) + Math.floor(y / 2)) % 2 == 0;
break;
case 5:
invert = x * y % 2 + x * y % 3 == 0;
break;
case 6:
invert = (x * y % 2 + x * y % 3) % 2 == 0;
break;
case 7:
invert = ((x + y) % 2 + x * y % 3) % 2 == 0;
break;
default: throw "Assertion error";
}
if (!this.isFunction[y][x] && invert)
this.modules[y][x] = !this.modules[y][x];
}
}
};
// Calculates and returns the penalty score based on state of this QR Code's current modules.
// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
QrCode.prototype.getPenaltyScore = function () {
var result = 0;
// Adjacent modules in row having same color, and finder-like patterns
for (var y = 0; y < this.size; y++) {
var runColor = false;
var runX = 0;
var runHistory = [0, 0, 0, 0, 0, 0, 0];
var padRun = this.size;
for (var x = 0; x < this.size; x++) {
if (this.modules[y][x] == runColor) {
runX++;
if (runX == 5)
result += QrCode.PENALTY_N1;
else if (runX > 5)
result++;
}
else {
QrCode.finderPenaltyAddHistory(runX + padRun, runHistory);
padRun = 0;
if (!runColor)
result += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3;
runColor = this.modules[y][x];
runX = 1;
}
}
result += this.finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * QrCode.PENALTY_N3;
}
// Adjacent modules in column having same color, and finder-like patterns
for (var x = 0; x < this.size; x++) {
var runColor = false;
var runY = 0;
var runHistory = [0, 0, 0, 0, 0, 0, 0];
var padRun = this.size;
for (var y = 0; y < this.size; y++) {
if (this.modules[y][x] == runColor) {
runY++;
if (runY == 5)
result += QrCode.PENALTY_N1;
else if (runY > 5)
result++;
}
else {
QrCode.finderPenaltyAddHistory(runY + padRun, runHistory);
padRun = 0;
if (!runColor)
result += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3;
runColor = this.modules[y][x];
runY = 1;
}
}
result += this.finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * QrCode.PENALTY_N3;
}
// 2*2 blocks of modules having same color
for (var y = 0; y < this.size - 1; y++) {
for (var x = 0; x < this.size - 1; x++) {
var color = this.modules[y][x];
if (color == this.modules[y][x + 1] &&
color == this.modules[y + 1][x] &&
color == this.modules[y + 1][x + 1])
result += QrCode.PENALTY_N2;
}
}
// Balance of black and white modules
var black = 0;
for (var _i = 0, _a = this.modules; _i < _a.length; _i++) {
var row = _a[_i];
for (var _b = 0, row_1 = row; _b < row_1.length; _b++) {
var color = row_1[_b];
if (color)
black++;
}
}
var total = this.size * this.size; // Note that size is odd, so black/total != 1/2
// Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)%
var k = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1;
result += k * QrCode.PENALTY_N4;
return result;
};
/*-- Private helper functions --*/
// Returns an ascending list of positions of alignment patterns for this version number.
// Each position is in the range [0,177), and are used on both the x and y axes.
// This could be implemented as lookup table of 40 variable-length lists of integers.
QrCode.prototype.getAlignmentPatternPositions = function () {
if (this.version == 1)
return [];
else {
var numAlign = Math.floor(this.version / 7) + 2;
var step = (this.version == 32) ? 26 :
Math.ceil((this.size - 13) / (numAlign * 2 - 2)) * 2;
var result = [6];
for (var pos = this.size - 7; result.length < numAlign; pos -= step)
result.splice(1, 0, pos);
return result;
}
};
// Returns the number of data bits that can be stored in a QR Code of the given version number, after
// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8.
// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.
QrCode.getNumRawDataModules = function (ver) {
if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION)
throw "Version number out of range";
var result = (16 * ver + 128) * ver + 64;
if (ver >= 2) {
var numAlign = Math.floor(ver / 7) + 2;
result -= (25 * numAlign - 10) * numAlign - 55;
if (ver >= 7)
result -= 36;
}
return result;
};
// Returns the number of 8-bit data (i.e. not error correction) codewords contained in any
// QR Code of the given version number and error correction level, with remainder bits discarded.
// This stateless pure function could be implemented as a (40*4)-cell lookup table.
QrCode.getNumDataCodewords = function (ver, ecl) {
return Math.floor(QrCode.getNumRawDataModules(ver) / 8) -
QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] *
QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver];
};
// Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be
// implemented as a lookup table over all possible parameter values, instead of as an algorithm.
QrCode.reedSolomonComputeDivisor = function (degree) {
if (degree < 1 || degree > 255)
throw "Degree out of range";
// Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1.
// For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93].
var result = [];
for (var i = 0; i < degree - 1; i++)
result.push(0);
result.push(1); // Start off with the monomial x^0
// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
// and drop the highest monomial term which is always 1x^degree.
// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
var root = 1;
for (var i = 0; i < degree; i++) {
// Multiply the current product by (x - r^i)
for (var j = 0; j < result.length; j++) {
result[j] = QrCode.reedSolomonMultiply(result[j], root);
if (j + 1 < result.length)
result[j] ^= result[j + 1];
}
root = QrCode.reedSolomonMultiply(root, 0x02);
}
return result;
};
// Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.
QrCode.reedSolomonComputeRemainder = function (data, divisor) {
var result = divisor.map(function (_) { return 0; });
var _loop_1 = function (b) {
var factor = b ^ result.shift();
result.push(0);
divisor.forEach(function (coef, i) {
return result[i] ^= QrCode.reedSolomonMultiply(coef, factor);
});
};
for (var _i = 0, data_1 = data; _i < data_1.length; _i++) {
var b = data_1[_i];
_loop_1(b);
}
return result;
};
// Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result
// are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8.
QrCode.reedSolomonMultiply = function (x, y) {
if (x >>> 8 != 0 || y >>> 8 != 0)
throw "Byte out of range";
// Russian peasant multiplication
var z = 0;
for (var i = 7; i >= 0; i--) {
z = (z << 1) ^ ((z >>> 7) * 0x11D);
z ^= ((y >>> i) & 1) * x;
}
if (z >>> 8 != 0)
throw "Assertion error";
return z;
};
// Can only be called immediately after a white run is added, and
// returns either 0, 1, or 2. A helper function for getPenaltyScore().
QrCode.prototype.finderPenaltyCountPatterns = function (runHistory) {
var n = runHistory[1];
if (n > this.size * 3)
throw "Assertion error";
var core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n;
return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0)
+ (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0);
};
// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore().
QrCode.prototype.finderPenaltyTerminateAndCount = function (currentRunColor, currentRunLength, runHistory) {
if (currentRunColor) { // Terminate black run
QrCode.finderPenaltyAddHistory(currentRunLength, runHistory);
currentRunLength = 0;
}
currentRunLength += this.size; // Add white border to final run
QrCode.finderPenaltyAddHistory(currentRunLength, runHistory);
return this.finderPenaltyCountPatterns(runHistory);
};
// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore().
QrCode.finderPenaltyAddHistory = function (currentRunLength, runHistory) {
runHistory.pop();
runHistory.unshift(currentRunLength);
};
/*-- Constants and tables --*/
// The minimum version number supported in the QR Code Model 2 standard.
QrCode.MIN_VERSION = 1;
// The maximum version number supported in the QR Code Model 2 standard.
QrCode.MAX_VERSION = 40;
// For use in getPenaltyScore(), when evaluating which mask is best.
QrCode.PENALTY_N1 = 3;
QrCode.PENALTY_N2 = 3;
QrCode.PENALTY_N3 = 40;
QrCode.PENALTY_N4 = 10;
QrCode.ECC_CODEWORDS_PER_BLOCK = [
// Version: (note that index 0 is for padding, and is set to an illegal value)
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
[-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],
[-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28],
[-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],
[-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30],
];
QrCode.NUM_ERROR_CORRECTION_BLOCKS = [
// Version: (note that index 0 is for padding, and is set to an illegal value)
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
[-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25],
[-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49],
[-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68],
[-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81],
];
return QrCode;
}());
qrcodegen.QrCode = QrCode;
// Appends the given number of low-order bits of the given value
// to the given buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len.
function appendBits(val, len, bb) {
if (len < 0 || len > 31 || val >>> len != 0)
throw "Value out of range";
for (var i = len - 1; i >= 0; i--) // Append bit by bit
bb.push((val >>> i) & 1);
}
// Returns true iff the i'th bit of x is set to 1.
function getBit(x, i) {
return ((x >>> i) & 1) != 0;
}
/*---- Data segment class ----*/
/*
* A segment of character/binary/control data in a QR Code symbol.
* Instances of this class are immutable.
* The mid-level way to create a segment is to take the payload data
* and call a static factory function such as QrSegment.makeNumeric().
* The low-level way to create a segment is to custom-make the bit buffer
* and call the QrSegment() constructor with appropriate values.
* This segment class imposes no length restrictions, but QR Codes have restrictions.
* Even in the most favorable conditions, a QR Code can only hold 7089 characters of data.
* Any segment longer than this is meaningless for the purpose of generating QR Codes.
*/
var QrSegment = /** @class */ (function () {
/*-- Constructor (low level) and fields --*/
// Creates a new QR Code segment with the given attributes and data.
// The character count (numChars) must agree with the mode and the bit buffer length,
// but the constraint isn't checked. The given bit buffer is cloned and stored.
function QrSegment(
// The mode indicator of this segment.
mode,
// The length of this segment's unencoded data. Measured in characters for
// numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode.
// Always zero or positive. Not the same as the data's bit length.
numChars,
// The data bits of this segment. Accessed through getData().
bitData) {
this.mode = mode;
this.numChars = numChars;
this.bitData = bitData;
if (numChars < 0)
throw "Invalid argument";
this.bitData = bitData.slice(); // Make defensive copy
}
/*-- Static factory functions (mid level) --*/
// Returns a segment representing the given binary data encoded in
// byte mode. All input byte arrays are acceptable. Any text string
// can be converted to UTF-8 bytes and encoded as a byte mode segment.
QrSegment.makeBytes = function (data) {
var bb = [];
for (var _i = 0, data_2 = data; _i < data_2.length; _i++) {
var b = data_2[_i];
appendBits(b, 8, bb);
}
return new QrSegment(QrSegment.Mode.BYTE, data.length, bb);
};
// Returns a segment representing the given string of decimal digits encoded in numeric mode.
QrSegment.makeNumeric = function (digits) {
if (!this.NUMERIC_REGEX.test(digits))
throw "String contains non-numeric characters";
var bb = [];
for (var i = 0; i < digits.length;) { // Consume up to 3 digits per iteration
var n = Math.min(digits.length - i, 3);
appendBits(parseInt(digits.substr(i, n), 10), n * 3 + 1, bb);
i += n;
}
return new QrSegment(QrSegment.Mode.NUMERIC, digits.length, bb);
};
// Returns a segment representing the given text string encoded in alphanumeric mode.
// The characters allowed are: 0 to 9, A to Z (uppercase only), space,
// dollar, percent, asterisk, plus, hyphen, period, slash, colon.
QrSegment.makeAlphanumeric = function (text) {
if (!this.ALPHANUMERIC_REGEX.test(text))
throw "String contains unencodable characters in alphanumeric mode";
var bb = [];
var i;
for (i = 0; i + 2 <= text.length; i += 2) { // Process groups of 2
var temp = QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45;
temp += QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1));
appendBits(temp, 11, bb);
}
if (i < text.length) // 1 character remaining
appendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6, bb);
return new QrSegment(QrSegment.Mode.ALPHANUMERIC, text.length, bb);
};
// Returns a new mutable list of zero or more segments to represent the given Unicode text string.
// The result may use various segment modes and switch modes to optimize the length of the bit stream.
QrSegment.makeSegments = function (text) {
// Select the most efficient segment encoding automatically
if (text == "")
return [];
else if (this.NUMERIC_REGEX.test(text))
return [QrSegment.makeNumeric(text)];
else if (this.ALPHANUMERIC_REGEX.test(text))
return [QrSegment.makeAlphanumeric(text)];
else
return [QrSegment.makeBytes(QrSegment.toUtf8ByteArray(text))];
};
// Returns a segment representing an Extended Channel Interpretation
// (ECI) designator with the given assignment value.
QrSegment.makeEci = function (assignVal) {
var bb = [];
if (assignVal < 0)
throw "ECI assignment value out of range";
else if (assignVal < (1 << 7))
appendBits(assignVal, 8, bb);
else if (assignVal < (1 << 14)) {
appendBits(2, 2, bb);
appendBits(assignVal, 14, bb);
}
else if (assignVal < 1000000) {
appendBits(6, 3, bb);
appendBits(assignVal, 21, bb);
}
else
throw "ECI assignment value out of range";
return new QrSegment(QrSegment.Mode.ECI, 0, bb);
};
/*-- Methods --*/
// Returns a new copy of the data bits of this segment.
QrSegment.prototype.getData = function () {
return this.bitData.slice(); // Make defensive copy
};
// (Package-private) Calculates and returns the number of bits needed to encode the given segments at
// the given version. The result is infinity if a segment has too many characters to fit its length field.
QrSegment.getTotalBits = function (segs, version) {
var result = 0;
for (var _i = 0, segs_2 = segs; _i < segs_2.length; _i++) {
var seg = segs_2[_i];
var ccbits = seg.mode.numCharCountBits(version);
if (seg.numChars >= (1 << ccbits))
return Infinity; // The segment's length doesn't fit the field's bit width
result += 4 + ccbits + seg.bitData.length;
}
return result;
};
// Returns a new array of bytes representing the given string encoded in UTF-8.
QrSegment.toUtf8ByteArray = function (str) {
str = encodeURI(str);
var result = [];
for (var i = 0; i < str.length; i++) {
if (str.charAt(i) != "%")
result.push(str.charCodeAt(i));
else {
result.push(parseInt(str.substr(i + 1, 2), 16));
i += 2;
}
}
return result;
};
/*-- Constants --*/
// Describes precisely all strings that are encodable in numeric mode. To test
// whether a string s is encodable: let ok: boolean = NUMERIC_REGEX.test(s);
// A string is encodable iff each character is in the range 0 to 9.
QrSegment.NUMERIC_REGEX = /^[0-9]*$/;
// Describes precisely all strings that are encodable in alphanumeric mode. To test
// whether a string s is encodable: let ok: boolean = ALPHANUMERIC_REGEX.test(s);
// A string is encodable iff each character is in the following set: 0 to 9, A to Z
// (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.
QrSegment.ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+.\/:-]*$/;
// The set of all legal characters in alphanumeric mode,
// where each character value maps to the index in the string.
QrSegment.ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
return QrSegment;
}());
qrcodegen.QrSegment = QrSegment;
})(qrcodegen || (qrcodegen = {}));
/*---- Public helper enumeration ----*/
(function (qrcodegen) {
var QrCode;
(function (QrCode) {
/*
* The error correction level in a QR Code symbol. Immutable.
*/
var Ecc = /** @class */ (function () {
/*-- Constructor and fields --*/
function Ecc(
// In the range 0 to 3 (unsigned 2-bit integer).
ordinal,
// (Package-private) In the range 0 to 3 (unsigned 2-bit integer).
formatBits) {
this.ordinal = ordinal;
this.formatBits = formatBits;
}
/*-- Constants --*/
Ecc.LOW = new Ecc(0, 1); // The QR Code can tolerate about 7% erroneous codewords
Ecc.MEDIUM = new Ecc(1, 0); // The QR Code can tolerate about 15% erroneous codewords
Ecc.QUARTILE = new Ecc(2, 3); // The QR Code can tolerate about 25% erroneous codewords
Ecc.HIGH = new Ecc(3, 2); // The QR Code can tolerate about 30% erroneous codewords
return Ecc;
}());
QrCode.Ecc = Ecc;
})(QrCode = qrcodegen.QrCode || (qrcodegen.QrCode = {}));
})(qrcodegen || (qrcodegen = {}));
/*---- Public helper enumeration ----*/
(function (qrcodegen) {
var QrSegment;
(function (QrSegment) {
/*
* Describes how a segment's data bits are interpreted. Immutable.
*/
var Mode = /** @class */ (function () {
/*-- Constructor and fields --*/
function Mode(
// The mode indicator bits, which is a uint4 value (range 0 to 15).
modeBits,
// Number of character count bits for three different version ranges.
numBitsCharCount) {
this.modeBits = modeBits;
this.numBitsCharCount = numBitsCharCount;
}
/*-- Method --*/
// (Package-private) Returns the bit width of the character count field for a segment in
// this mode in a QR Code at the given version number. The result is in the range [0, 16].
Mode.prototype.numCharCountBits = function (ver) {
return this.numBitsCharCount[Math.floor((ver + 7) / 17)];
};
/*-- Constants --*/
Mode.NUMERIC = new Mode(0x1, [10, 12, 14]);
Mode.ALPHANUMERIC = new Mode(0x2, [9, 11, 13]);
Mode.BYTE = new Mode(0x4, [8, 16, 16]);
Mode.KANJI = new Mode(0x8, [8, 10, 12]);
Mode.ECI = new Mode(0x7, [0, 0, 0]);
return Mode;
}());
QrSegment.Mode = Mode;
})(QrSegment = qrcodegen.QrSegment || (qrcodegen.QrSegment = {}));
})(qrcodegen || (qrcodegen = {}));

1
external/qr-scanner vendored Submodule

@ -0,0 +1 @@
Subproject commit ecd8a3425f5d222ded65a0c6a560a437e53c204c

6
external/qr-scanner-binaryData/README vendored Normal file
View File

@ -0,0 +1,6 @@
./qr-scanner-worker.min.js is modified from
../qr-scanner/qr-scanner-worker.min.js. The only change is that instead
of returning result.data, it returns result to allow access to
result.binaryData.
MIT Licensed

View File

@ -0,0 +1,86 @@
'use strict';(function(){function S(a,b){let c=[],d="";b=a.readBits([8,16,16][b]);for(let d=0;d<b;d++){let b=a.readBits(8);c.push(b)}try{d+=decodeURIComponent(c.map((a)=>`%${("0"+a.toString(16)).substr(-2)}`).join(""))}catch(e){}return{bytes:c,text:d}}function T(a,b){a=new U(a);b=9>=b?0:26>=b?1:2;let c={text:"",bytes:[],chunks:[]};for(;4<=a.available();){var d=a.readBits(4);if(d===y.Terminator)return c;if(d===y.ECI)0===a.readBits(1)?c.chunks.push({type:u.ECI,assignmentNumber:a.readBits(7)}):0===a.readBits(1)?
c.chunks.push({type:u.ECI,assignmentNumber:a.readBits(14)}):0===a.readBits(1)?c.chunks.push({type:u.ECI,assignmentNumber:a.readBits(21)}):c.chunks.push({type:u.ECI,assignmentNumber:-1});else if(d===y.Numeric){var e=a;d=[];for(var g="",f=e.readBits([10,12,14][b]);3<=f;){var h=e.readBits(10);if(1E3<=h)throw Error("Invalid numeric value above 999");var k=Math.floor(h/100),m=Math.floor(h/10)%10;h%=10;d.push(48+k,48+m,48+h);g+=k.toString()+m.toString()+h.toString();f-=3}if(2===f){f=e.readBits(7);if(100<=
f)throw Error("Invalid numeric value above 99");e=Math.floor(f/10);f%=10;d.push(48+e,48+f);g+=e.toString()+f.toString()}else if(1===f){e=e.readBits(4);if(10<=e)throw Error("Invalid numeric value above 9");d.push(48+e);g+=e.toString()}d={bytes:d,text:g};c.text+=d.text;c.bytes.push(...d.bytes);c.chunks.push({type:u.Numeric,text:d.text})}else if(d===y.Alphanumeric){e=a;d=[];g="";for(f=e.readBits([9,11,13][b]);2<=f;)m=e.readBits(11),k=Math.floor(m/45),m%=45,d.push(B[k].charCodeAt(0),B[m].charCodeAt(0)),
g+=B[k]+B[m],f-=2;1===f&&(e=e.readBits(6),d.push(B[e].charCodeAt(0)),g+=B[e]);d={bytes:d,text:g};c.text+=d.text;c.bytes.push(...d.bytes);c.chunks.push({type:u.Alphanumeric,text:d.text})}else if(d===y.Byte)d=S(a,b),c.text+=d.text,c.bytes.push(...d.bytes),c.chunks.push({type:u.Byte,bytes:d.bytes,text:d.text});else if(d===y.Kanji){g=a;d=[];e=g.readBits([8,10,12][b]);for(f=0;f<e;f++)k=g.readBits(13),k=Math.floor(k/192)<<8|k%192,k=7936>k?k+33088:k+49472,d.push(k>>8,k&255);g=(new TextDecoder("shift-jis")).decode(Uint8Array.from(d));
d={bytes:d,text:g};c.text+=d.text;c.bytes.push(...d.bytes);c.chunks.push({type:u.Kanji,bytes:d.bytes,text:d.text})}}if(0===a.available()||0===a.readBits(a.available()))return c}function J(a,b){return a^b}function V(a,b,c,d){b.degree()<c.degree()&&([b,c]=[c,b]);let e=a.zero;for(var g=a.one;c.degree()>=d/2;){var f=b;let d=e;b=c;e=g;if(b.isZero())return null;c=f;g=a.zero;f=b.getCoefficient(b.degree());for(f=a.inverse(f);c.degree()>=b.degree()&&!c.isZero();){let d=c.degree()-b.degree(),e=a.multiply(c.getCoefficient(c.degree()),
f);g=g.addOrSubtract(a.buildMonomial(d,e));c=c.addOrSubtract(b.multiplyByMonomial(d,e))}g=g.multiplyPoly(e).addOrSubtract(d);if(c.degree()>=b.degree())return null}d=g.getCoefficient(0);if(0===d)return null;a=a.inverse(d);return[g.multiply(a),c.multiply(a)]}function W(a,b){let c=new Uint8ClampedArray(a.length);c.set(a);a=new X(285,256,0);var d=new w(a,c),e=new Uint8ClampedArray(b),g=!1;for(var f=0;f<b;f++){var h=d.evaluateAt(a.exp(f+a.generatorBase));e[e.length-1-f]=h;0!==h&&(g=!0)}if(!g)return c;
d=new w(a,e);d=V(a,a.buildMonomial(b,1),d,b);if(null===d)return null;b=d[0];f=b.degree();if(1===f)b=[b.getCoefficient(1)];else{e=Array(f);g=0;for(h=1;h<a.size&&g<f;h++)0===b.evaluateAt(h)&&(e[g]=a.inverse(h),g++);b=g!==f?null:e}if(null==b)return null;d=d[1];e=b.length;g=Array(e);for(f=0;f<e;f++){h=a.inverse(b[f]);let c=1;for(let d=0;d<e;d++)f!==d&&(c=a.multiply(c,J(1,a.multiply(b[d],h))));g[f]=a.multiply(d.evaluateAt(h),a.inverse(c));0!==a.generatorBase&&(g[f]=a.multiply(g[f],h))}d=g;for(e=0;e<b.length;e++){g=
c.length-1-a.log(b[e]);if(0>g)return null;c[g]^=d[e]}return c}function E(a,b){a^=b;for(b=0;a;)b++,a&=a-1;return b}function C(a,b){return b<<1|a}function Y(a,b,c){c=Z[c.dataMask];let d=a.height;var e=17+4*b.versionNumber,g=A.createEmpty(e,e);g.setRegion(0,0,9,9,!0);g.setRegion(e-8,0,8,9,!0);g.setRegion(0,e-8,9,8,!0);for(var f of b.alignmentPatternCenters)for(var h of b.alignmentPatternCenters)6===f&&6===h||6===f&&h===e-7||f===e-7&&6===h||g.setRegion(f-2,h-2,5,5,!0);g.setRegion(6,9,1,e-17,!0);g.setRegion(9,
6,e-17,1,!0);6<b.versionNumber&&(g.setRegion(e-11,0,3,6,!0),g.setRegion(0,e-11,6,3,!0));b=g;f=[];e=h=0;g=!0;for(let k=d-1;0<k;k-=2){6===k&&k--;for(let m=0;m<d;m++){let p=g?d-1-m:m;for(let d=0;2>d;d++){let g=k-d;if(!b.get(g,p)){e++;let b=a.get(g,p);c({y:p,x:g})&&(b=!b);h=h<<1|b;8===e&&(f.push(h),h=e=0)}}}g=!g}return f}function aa(a){var b=a.height,c=Math.floor((b-17)/4);if(6>=c)return K[c-1];c=0;for(var d=5;0<=d;d--)for(var e=b-9;e>=b-11;e--)c=C(a.get(e,d),c);d=0;for(e=5;0<=e;e--)for(let c=b-9;c>=
b-11;c--)d=C(a.get(e,c),d);a=Infinity;let g;for(let e of K){if(e.infoBits===c||e.infoBits===d)return e;b=E(c,e.infoBits);b<a&&(g=e,a=b);b=E(d,e.infoBits);b<a&&(g=e,a=b)}if(3>=a)return g}function ba(a){let b=0;for(var c=0;8>=c;c++)6!==c&&(b=C(a.get(c,8),b));for(c=7;0<=c;c--)6!==c&&(b=C(a.get(8,c),b));var d=a.height;c=0;for(var e=d-1;e>=d-7;e--)c=C(a.get(8,e),c);for(e=d-8;e<d;e++)c=C(a.get(e,8),c);a=Infinity;d=null;for(let {bits:g,formatInfo:f}of ca){if(g===b||g===c)return f;e=E(b,g);e<a&&(d=f,a=e);
b!==c&&(e=E(c,g),e<a&&(d=f,a=e))}return 3>=a?d:null}function da(a,b,c){let d=b.errorCorrectionLevels[c],e=[],g=0;d.ecBlocks.forEach((a)=>{for(let b=0;b<a.numBlocks;b++)e.push({numDataCodewords:a.dataCodewordsPerBlock,codewords:[]}),g+=a.dataCodewordsPerBlock+d.ecCodewordsPerBlock});if(a.length<g)return null;a=a.slice(0,g);b=d.ecBlocks[0].dataCodewordsPerBlock;for(c=0;c<b;c++)for(var f of e)f.codewords.push(a.shift());if(1<d.ecBlocks.length)for(f=d.ecBlocks[0].numBlocks,b=d.ecBlocks[1].numBlocks,c=
0;c<b;c++)e[f+c].codewords.push(a.shift());for(;0<a.length;)for(let b of e)b.codewords.push(a.shift());return e}function L(a){let b=aa(a);if(!b)return null;var c=ba(a);if(!c)return null;a=Y(a,b,c);var d=da(a,b,c.errorCorrectionLevel);if(!d)return null;c=d.reduce((a,b)=>a+b.numDataCodewords,0);c=new Uint8ClampedArray(c);a=0;for(let b of d){d=W(b.codewords,b.codewords.length-b.numDataCodewords);if(!d)return null;for(let e=0;e<b.numDataCodewords;e++)c[a++]=d[e]}try{return T(c,b.versionNumber)}catch(e){return null}}
function M(a,b,c,d){var e=a.x-b.x+c.x-d.x;let g=a.y-b.y+c.y-d.y;if(0===e&&0===g)return{a11:b.x-a.x,a12:b.y-a.y,a13:0,a21:c.x-b.x,a22:c.y-b.y,a23:0,a31:a.x,a32:a.y,a33:1};{let h=b.x-c.x;var f=d.x-c.x;let k=b.y-c.y,m=d.y-c.y;c=h*m-f*k;f=(e*m-f*g)/c;e=(h*g-e*k)/c;return{a11:b.x-a.x+f*b.x,a12:b.y-a.y+f*b.y,a13:f,a21:d.x-a.x+e*d.x,a22:d.y-a.y+e*d.y,a23:e,a31:a.x,a32:a.y,a33:1}}}function ea(a,b,c,d){a=M(a,b,c,d);return{a11:a.a22*a.a33-a.a23*a.a32,a12:a.a13*a.a32-a.a12*a.a33,a13:a.a12*a.a23-a.a13*a.a22,
a21:a.a23*a.a31-a.a21*a.a33,a22:a.a11*a.a33-a.a13*a.a31,a23:a.a13*a.a21-a.a11*a.a23,a31:a.a21*a.a32-a.a22*a.a31,a32:a.a12*a.a31-a.a11*a.a32,a33:a.a11*a.a22-a.a12*a.a21}}function fa(a,b){var c=ea({x:3.5,y:3.5},{x:b.dimension-3.5,y:3.5},{x:b.dimension-6.5,y:b.dimension-6.5},{x:3.5,y:b.dimension-3.5}),d=M(b.topLeft,b.topRight,b.alignmentPattern,b.bottomLeft),e=d.a11*c.a11+d.a21*c.a12+d.a31*c.a13,g=d.a12*c.a11+d.a22*c.a12+d.a32*c.a13,f=d.a13*c.a11+d.a23*c.a12+d.a33*c.a13,h=d.a11*c.a21+d.a21*c.a22+d.a31*
c.a23,k=d.a12*c.a21+d.a22*c.a22+d.a32*c.a23,m=d.a13*c.a21+d.a23*c.a22+d.a33*c.a23,p=d.a11*c.a31+d.a21*c.a32+d.a31*c.a33,n=d.a12*c.a31+d.a22*c.a32+d.a32*c.a33,l=d.a13*c.a31+d.a23*c.a32+d.a33*c.a33;c=A.createEmpty(b.dimension,b.dimension);d=(a,b)=>{const c=f*a+m*b+l;return{x:(e*a+h*b+p)/c,y:(g*a+k*b+n)/c}};for(let e=0;e<b.dimension;e++)for(let f=0;f<b.dimension;f++){let b=d(f+.5,e+.5);c.set(f,e,a.get(Math.floor(b.x),Math.floor(b.y)))}return{matrix:c,mappingFunction:d}}function t(a){return a.reduce((a,
c)=>a+c)}function ha(a,b,c){let d=x(a,b),e=x(b,c),g=x(a,c),f,h,k;e>=d&&e>=g?[f,h,k]=[b,a,c]:g>=e&&g>=d?[f,h,k]=[a,b,c]:[f,h,k]=[a,c,b];0>(k.x-h.x)*(f.y-h.y)-(k.y-h.y)*(f.x-h.x)&&([f,k]=[k,f]);return{bottomLeft:f,topLeft:h,topRight:k}}function ia(a,b,c,d){d=(t(z(a,c,d,5))/7+t(z(a,b,d,5))/7+t(z(c,a,d,5))/7+t(z(b,a,d,5))/7)/4;if(1>d)throw Error("Invalid module size");b=Math.round(x(a,b)/d);a=Math.round(x(a,c)/d);a=Math.floor((b+a)/2)+7;switch(a%4){case 0:a++;break;case 2:a--}return{dimension:a,moduleSize:d}}
function N(a,b,c,d){let e=[{x:Math.floor(a.x),y:Math.floor(a.y)}];var g=Math.abs(b.y-a.y)>Math.abs(b.x-a.x);if(g){var f=Math.floor(a.y);var h=Math.floor(a.x);a=Math.floor(b.y);b=Math.floor(b.x)}else f=Math.floor(a.x),h=Math.floor(a.y),a=Math.floor(b.x),b=Math.floor(b.y);let k=Math.abs(a-f),m=Math.abs(b-h),p=Math.floor(-k/2),n=f<a?1:-1,l=h<b?1:-1,q=!0;for(let r=f,F=h;r!==a+n;r+=n){f=g?F:r;h=g?r:F;if(c.get(f,h)!==q&&(q=!q,e.push({x:f,y:h}),e.length===d+1))break;p+=m;if(0<p){if(F===b)break;F+=l;p-=k}}c=
[];for(g=0;g<d;g++)e[g]&&e[g+1]?c.push(x(e[g],e[g+1])):c.push(0);return c}function z(a,b,c,d){let e=b.y-a.y,g=b.x-a.x;b=N(a,b,c,Math.ceil(d/2));a=N(a,{x:a.x-g,y:a.y-e},c,Math.ceil(d/2));c=b.shift()+a.shift()-1;return a.concat(c).concat(...b)}function G(a,b){let c=t(a)/t(b),d=0;b.forEach((b,g)=>{d+=Math.pow(a[g]-b*c,2)});return{averageSize:c,error:d}}function O(a,b,c){try{let d=z(a,{x:-1,y:a.y},c,b.length),e=z(a,{x:a.x,y:-1},c,b.length),g=z(a,{x:Math.max(0,a.x-a.y)-1,y:Math.max(0,a.y-a.x)-1},c,b.length),
f=z(a,{x:Math.min(c.width,a.x+a.y)+1,y:Math.min(c.height,a.y+a.x)+1},c,b.length),h=G(d,b),k=G(e,b),m=G(g,b),p=G(f,b),n=(h.averageSize+k.averageSize+m.averageSize+p.averageSize)/4;return Math.sqrt(h.error*h.error+k.error*k.error+m.error*m.error+p.error*p.error)+(Math.pow(h.averageSize-n,2)+Math.pow(k.averageSize-n,2)+Math.pow(m.averageSize-n,2)+Math.pow(p.averageSize-n,2))/n}catch(d){return Infinity}}function ja(a){var b=[],c=[],d=[],e=[];for(let r=0;r<=a.height;r++){let k=0,m=!1,l=[0,0,0,0,0];for(let b=
-1;b<=a.width;b++){var g=a.get(b,r);if(g===m)k++;else{l=[l[1],l[2],l[3],l[4],k];k=1;m=g;var f=t(l)/7;f=Math.abs(l[0]-f)<f&&Math.abs(l[1]-f)<f&&Math.abs(l[2]-3*f)<3*f&&Math.abs(l[3]-f)<f&&Math.abs(l[4]-f)<f&&!g;var h=t(l.slice(-3))/3;g=Math.abs(l[2]-h)<h&&Math.abs(l[3]-h)<h&&Math.abs(l[4]-h)<h&&g;if(f){let a=b-l[3]-l[4],d=a-l[2];f={startX:d,endX:a,y:r};h=c.filter((b)=>d>=b.bottom.startX&&d<=b.bottom.endX||a>=b.bottom.startX&&d<=b.bottom.endX||d<=b.bottom.startX&&a>=b.bottom.endX&&1.5>l[2]/(b.bottom.endX-
b.bottom.startX)&&.5<l[2]/(b.bottom.endX-b.bottom.startX));0<h.length?h[0].bottom=f:c.push({top:f,bottom:f})}if(g){let a=b-l[4],c=a-l[3];g={startX:c,y:r,endX:a};f=e.filter((b)=>c>=b.bottom.startX&&c<=b.bottom.endX||a>=b.bottom.startX&&c<=b.bottom.endX||c<=b.bottom.startX&&a>=b.bottom.endX&&1.5>l[2]/(b.bottom.endX-b.bottom.startX)&&.5<l[2]/(b.bottom.endX-b.bottom.startX));0<f.length?f[0].bottom=g:e.push({top:g,bottom:g})}}}b.push(...c.filter((a)=>a.bottom.y!==r&&2<=a.bottom.y-a.top.y));c=c.filter((a)=>
a.bottom.y===r);d.push(...e.filter((a)=>a.bottom.y!==r));e=e.filter((a)=>a.bottom.y===r)}b.push(...c.filter((a)=>2<=a.bottom.y-a.top.y));d.push(...e);b=b.filter((a)=>2<=a.bottom.y-a.top.y).map((b)=>{const c=(b.top.startX+b.top.endX+b.bottom.startX+b.bottom.endX)/4,d=(b.top.y+b.bottom.y+1)/2;if(a.get(Math.round(c),Math.round(d)))return b=[b.top.endX-b.top.startX,b.bottom.endX-b.bottom.startX,b.bottom.y-b.top.y+1],b=t(b)/b.length,{score:O({x:Math.round(c),y:Math.round(d)},[1,1,3,1,1],a),x:c,y:d,size:b}}).filter((a)=>
!!a).sort((a,b)=>a.score-b.score).map((a,b,c)=>{if(4<b)return null;c=c.filter((a,c)=>b!==c).map((b)=>({x:b.x,y:b.y,score:b.score+Math.pow(b.size-a.size,2)/a.size,size:b.size})).sort((a,b)=>a.score-b.score);if(2>c.length)return null;const d=a.score+c[0].score+c[1].score;return{points:[a].concat(c.slice(0,2)),score:d}}).filter((a)=>!!a).sort((a,b)=>a.score-b.score);if(0===b.length)return null;var {topRight:k,topLeft:m,bottomLeft:p}=ha(b[0].points[0],b[0].points[1],b[0].points[2]);let n;try{({dimension:n,
moduleSize:l}=ia(m,k,p,a))}catch(r){return null}b=k.x-m.x+p.x;c=k.y-m.y+p.y;var l=(x(m,p)+x(m,k))/2/l;e=1-3/l;let q={x:m.x+e*(b-m.x),y:m.y+e*(c-m.y)};d=d.map((b)=>{const c=(b.top.startX+b.top.endX+b.bottom.startX+b.bottom.endX)/4,d=(b.top.y+b.bottom.y+1)/2;if(a.get(Math.floor(c),Math.floor(d)))return t([b.top.endX-b.top.startX,b.bottom.endX-b.bottom.startX,b.bottom.y-b.top.y+1]),b=O({x:Math.floor(c),y:Math.floor(d)},[1,1,1],a)+x({x:c,y:d},q),{x:c,y:d,score:b}}).filter((a)=>!!a).sort((a,b)=>a.score-
b.score);d=15<=l&&d.length?d[0]:q;return{alignmentPattern:{x:d.x,y:d.y},bottomLeft:{x:p.x,y:p.y},dimension:n,topLeft:{x:m.x,y:m.y},topRight:{x:k.x,y:k.y}}}function P(a){let b=ja(a);if(!b)return null;a=fa(a,b);var c=a.matrix;if(null==c)c=null;else{var d=L(c);if(d)c=d;else{for(d=0;d<c.width;d++)for(let a=d+1;a<c.height;a++)c.get(d,a)!==c.get(a,d)&&(c.set(d,a,!c.get(d,a)),c.set(a,d,!c.get(a,d)));c=L(c)}}return c?{binaryData:c.bytes,data:c.text,chunks:c.chunks,location:{topRightCorner:a.mappingFunction(b.dimension,
0),topLeftCorner:a.mappingFunction(0,0),bottomRightCorner:a.mappingFunction(b.dimension,b.dimension),bottomLeftCorner:a.mappingFunction(0,b.dimension),topRightFinderPattern:b.topRight,topLeftFinderPattern:b.topLeft,bottomLeftFinderPattern:b.bottomLeft,bottomRightAlignmentPattern:b.alignmentPattern}}:null}function Q(a,b){Object.keys(b).forEach((c)=>{a[c]=b[c]})}function I(a,b,c,d={}){let e=Object.create(null);Q(e,ka);Q(e,d);d="onlyInvert"===e.inversionAttempts||"invertFirst"===e.inversionAttempts;
var g="attemptBoth"===e.inversionAttempts||"invertFirst"===e.inversionAttempts;var f=e.greyScaleWeights,h=e.canOverwriteImage,k=b*c;if(a.length!==4*k)throw Error("Malformed data passed to binarizer.");var m=0;if(h){var p=new Uint8ClampedArray(a.buffer,m,k);m+=k}p=new R(b,c,p);if(f.useIntegerApproximation)for(var n=0;n<c;n++)for(var l=0;l<b;l++){var q=4*(n*b+l);p.set(l,n,f.red*a[q]+f.green*a[q+1]+f.blue*a[q+2]+128>>8)}else for(n=0;n<c;n++)for(l=0;l<b;l++)q=4*(n*b+l),p.set(l,n,f.red*a[q]+f.green*a[q+
1]+f.blue*a[q+2]);f=Math.ceil(b/8);n=Math.ceil(c/8);l=f*n;if(h){var r=new Uint8ClampedArray(a.buffer,m,l);m+=l}r=new R(f,n,r);for(l=0;l<n;l++)for(q=0;q<f;q++){var u=0,v=Infinity,t=0;for(let a=0;8>a;a++)for(let b=0;8>b;b++){let c=p.get(8*q+b,8*l+a);u+=c;v=Math.min(v,c);t=Math.max(t,c)}u/=Math.pow(8,2);24>=t-v&&(u=v/2,0<l&&0<q&&(t=(r.get(q,l-1)+2*r.get(q-1,l)+r.get(q-1,l-1))/4,v<t&&(u=t)));r.set(q,l,u)}h?(l=new Uint8ClampedArray(a.buffer,m,k),m+=k,l=new A(l,b)):l=A.createEmpty(b,c);q=null;g&&(h?(a=
new Uint8ClampedArray(a.buffer,m,k),q=new A(a,b)):q=A.createEmpty(b,c));for(b=0;b<n;b++)for(a=0;a<f;a++){c=f-3;c=2>a?2:a>c?c:a;h=n-3;h=2>b?2:b>h?h:b;k=0;for(m=-2;2>=m;m++)for(v=-2;2>=v;v++)k+=r.get(c+m,h+v);c=k/25;for(h=0;8>h;h++)for(k=0;8>k;k++)m=8*a+h,v=8*b+k,t=p.get(m,v),l.set(m,v,t<=c),g&&q.set(m,v,!(t<=c))}g=g?{binarized:l,inverted:q}:{binarized:l};let {binarized:w,inverted:x}=g;(g=P(d?x:w))||"attemptBoth"!==e.inversionAttempts&&"invertFirst"!==e.inversionAttempts||(g=P(d?w:x));return g}class A{static createEmpty(a,
b){return new A(new Uint8ClampedArray(a*b),a)}constructor(a,b){this.width=b;this.height=a.length/b;this.data=a}get(a,b){return 0>a||a>=this.width||0>b||b>=this.height?!1:!!this.data[b*this.width+a]}set(a,b,c){this.data[b*this.width+a]=c?1:0}setRegion(a,b,c,d,e){for(let g=b;g<b+d;g++)for(let b=a;b<a+c;b++)this.set(b,g,!!e)}}class R{constructor(a,b,c){this.width=a;a*=b;if(c&&c.length!==a)throw Error("Wrong buffer size");this.data=c||new Uint8ClampedArray(a)}get(a,b){return this.data[b*this.width+a]}set(a,
b,c){this.data[b*this.width+a]=c}}class U{constructor(a){this.bitOffset=this.byteOffset=0;this.bytes=a}readBits(a){if(1>a||32<a||a>this.available())throw Error("Cannot read "+a.toString()+" bits");var b=0;if(0<this.bitOffset){b=8-this.bitOffset;var c=a<b?a:b;b-=c;b=(this.bytes[this.byteOffset]&255>>8-c<<b)>>b;a-=c;this.bitOffset+=c;8===this.bitOffset&&(this.bitOffset=0,this.byteOffset++)}if(0<a){for(;8<=a;)b=b<<8|this.bytes[this.byteOffset]&255,this.byteOffset++,a-=8;0<a&&(c=8-a,b=b<<a|(this.bytes[this.byteOffset]&
255>>c<<c)>>c,this.bitOffset+=a)}return b}available(){return 8*(this.bytes.length-this.byteOffset)-this.bitOffset}}var u;(function(a){a.Numeric="numeric";a.Alphanumeric="alphanumeric";a.Byte="byte";a.Kanji="kanji";a.ECI="eci"})(u||(u={}));var y;(function(a){a[a.Terminator=0]="Terminator";a[a.Numeric=1]="Numeric";a[a.Alphanumeric=2]="Alphanumeric";a[a.Byte=4]="Byte";a[a.Kanji=8]="Kanji";a[a.ECI=7]="ECI"})(y||(y={}));let B="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".split("");class w{constructor(a,
b){if(0===b.length)throw Error("No coefficients.");this.field=a;let c=b.length;if(1<c&&0===b[0]){let d=1;for(;d<c&&0===b[d];)d++;if(d===c)this.coefficients=a.zero.coefficients;else for(this.coefficients=new Uint8ClampedArray(c-d),a=0;a<this.coefficients.length;a++)this.coefficients[a]=b[d+a]}else this.coefficients=b}degree(){return this.coefficients.length-1}isZero(){return 0===this.coefficients[0]}getCoefficient(a){return this.coefficients[this.coefficients.length-1-a]}addOrSubtract(a){if(this.isZero())return a;
if(a.isZero())return this;let b=this.coefficients;a=a.coefficients;b.length>a.length&&([b,a]=[a,b]);let c=new Uint8ClampedArray(a.length),d=a.length-b.length;for(var e=0;e<d;e++)c[e]=a[e];for(e=d;e<a.length;e++)c[e]=b[e-d]^a[e];return new w(this.field,c)}multiply(a){if(0===a)return this.field.zero;if(1===a)return this;let b=this.coefficients.length,c=new Uint8ClampedArray(b);for(let d=0;d<b;d++)c[d]=this.field.multiply(this.coefficients[d],a);return new w(this.field,c)}multiplyPoly(a){if(this.isZero()||
a.isZero())return this.field.zero;let b=this.coefficients,c=b.length;a=a.coefficients;let d=a.length,e=new Uint8ClampedArray(c+d-1);for(let h=0;h<c;h++){let c=b[h];for(let b=0;b<d;b++){var g=h+b,f=this.field.multiply(c,a[b]);e[g]=e[h+b]^f}}return new w(this.field,e)}multiplyByMonomial(a,b){if(0>a)throw Error("Invalid degree less than 0");if(0===b)return this.field.zero;let c=this.coefficients.length;a=new Uint8ClampedArray(c+a);for(let d=0;d<c;d++)a[d]=this.field.multiply(this.coefficients[d],b);
return new w(this.field,a)}evaluateAt(a){let b=0;if(0===a)return this.getCoefficient(0);let c=this.coefficients.length;if(1===a)return this.coefficients.forEach((a)=>{b^=a}),b;b=this.coefficients[0];for(let d=1;d<c;d++)b=J(this.field.multiply(a,b),this.coefficients[d]);return b}}class X{constructor(a,b,c){this.primitive=a;this.size=b;this.generatorBase=c;this.expTable=Array(this.size);this.logTable=Array(this.size);a=1;for(b=0;b<this.size;b++)this.expTable[b]=a,a*=2,a>=this.size&&(a=(a^this.primitive)&
this.size-1);for(a=0;a<this.size-1;a++)this.logTable[this.expTable[a]]=a;this.zero=new w(this,Uint8ClampedArray.from([0]));this.one=new w(this,Uint8ClampedArray.from([1]))}multiply(a,b){return 0===a||0===b?0:this.expTable[(this.logTable[a]+this.logTable[b])%(this.size-1)]}inverse(a){if(0===a)throw Error("Can't invert 0");return this.expTable[this.size-this.logTable[a]-1]}buildMonomial(a,b){if(0>a)throw Error("Invalid monomial degree less than 0");if(0===b)return this.zero;a=new Uint8ClampedArray(a+
1);a[0]=b;return new w(this,a)}log(a){if(0===a)throw Error("Can't take log(0)");return this.logTable[a]}exp(a){return this.expTable[a]}}let K=[{infoBits:null,versionNumber:1,alignmentPatternCenters:[],errorCorrectionLevels:[{ecCodewordsPerBlock:7,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:13,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:13}]},{ecCodewordsPerBlock:17,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:9}]}]},
{infoBits:null,versionNumber:2,alignmentPatternCenters:[6,18],errorCorrectionLevels:[{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:34}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:28}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]}]},{infoBits:null,versionNumber:3,alignmentPatternCenters:[6,22],errorCorrectionLevels:[{ecCodewordsPerBlock:15,ecBlocks:[{numBlocks:1,
dataCodewordsPerBlock:55}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:13}]}]},{infoBits:null,versionNumber:4,alignmentPatternCenters:[6,26],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:80}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:32}]},{ecCodewordsPerBlock:26,
ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:9}]}]},{infoBits:null,versionNumber:5,alignmentPatternCenters:[6,30],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:43}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:22,
ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:11},{numBlocks:2,dataCodewordsPerBlock:12}]}]},{infoBits:null,versionNumber:6,alignmentPatternCenters:[6,34],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:27}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:31892,versionNumber:7,
alignmentPatternCenters:[6,22,38],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:78}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:31}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:13},{numBlocks:1,dataCodewordsPerBlock:14}]}]},{infoBits:34236,versionNumber:8,alignmentPatternCenters:[6,24,42],
errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:97}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:38},{numBlocks:2,dataCodewordsPerBlock:39}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:18},{numBlocks:2,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:14},{numBlocks:2,dataCodewordsPerBlock:15}]}]},{infoBits:39577,versionNumber:9,alignmentPatternCenters:[6,
26,46],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:36},{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:16},{numBlocks:4,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:12},{numBlocks:4,dataCodewordsPerBlock:13}]}]},{infoBits:42195,versionNumber:10,alignmentPatternCenters:[6,
28,50],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68},{numBlocks:2,dataCodewordsPerBlock:69}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:43},{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]}]},{infoBits:48118,
versionNumber:11,alignmentPatternCenters:[6,30,54],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:81}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:50},{numBlocks:4,dataCodewordsPerBlock:51}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:22},{numBlocks:4,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:12},{numBlocks:8,dataCodewordsPerBlock:13}]}]},{infoBits:51042,
versionNumber:12,alignmentPatternCenters:[6,32,58],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:92},{numBlocks:2,dataCodewordsPerBlock:93}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:36},{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:20},{numBlocks:6,dataCodewordsPerBlock:21}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:14},{numBlocks:4,
dataCodewordsPerBlock:15}]}]},{infoBits:55367,versionNumber:13,alignmentPatternCenters:[6,34,62],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:107}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:37},{numBlocks:1,dataCodewordsPerBlock:38}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:20},{numBlocks:4,dataCodewordsPerBlock:21}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:11},{numBlocks:4,
dataCodewordsPerBlock:12}]}]},{infoBits:58893,versionNumber:14,alignmentPatternCenters:[6,26,46,66],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:115},{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:40},{numBlocks:5,dataCodewordsPerBlock:41}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:16},{numBlocks:5,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,
dataCodewordsPerBlock:12},{numBlocks:5,dataCodewordsPerBlock:13}]}]},{infoBits:63784,versionNumber:15,alignmentPatternCenters:[6,26,48,70],errorCorrectionLevels:[{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:87},{numBlocks:1,dataCodewordsPerBlock:88}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:41},{numBlocks:5,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},
{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:7,dataCodewordsPerBlock:13}]}]},{infoBits:68472,versionNumber:16,alignmentPatternCenters:[6,26,50,74],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:98},{numBlocks:1,dataCodewordsPerBlock:99}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:19},
{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},{infoBits:70749,versionNumber:17,alignmentPatternCenters:[6,30,54,78],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},{numBlocks:1,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,
ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22},{numBlocks:15,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:17,dataCodewordsPerBlock:15}]}]},{infoBits:76311,versionNumber:18,alignmentPatternCenters:[6,30,56,82],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:120},{numBlocks:1,dataCodewordsPerBlock:121}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:43},{numBlocks:4,
dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},{numBlocks:1,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:19,dataCodewordsPerBlock:15}]}]},{infoBits:79154,versionNumber:19,alignmentPatternCenters:[6,30,58,86],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:113},{numBlocks:4,dataCodewordsPerBlock:114}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,
dataCodewordsPerBlock:44},{numBlocks:11,dataCodewordsPerBlock:45}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:21},{numBlocks:4,dataCodewordsPerBlock:22}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:13},{numBlocks:16,dataCodewordsPerBlock:14}]}]},{infoBits:84390,versionNumber:20,alignmentPatternCenters:[6,34,62,90],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},
{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:41},{numBlocks:13,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},{numBlocks:5,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:15},{numBlocks:10,dataCodewordsPerBlock:16}]}]},{infoBits:87683,versionNumber:21,alignmentPatternCenters:[6,28,50,72,94],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:116},
{numBlocks:4,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:16},{numBlocks:6,dataCodewordsPerBlock:17}]}]},{infoBits:92361,versionNumber:22,alignmentPatternCenters:[6,26,50,74,98],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:111},
{numBlocks:7,dataCodewordsPerBlock:112}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:13}]}]},{infoBits:96236,versionNumber:23,alignmentPatternCenters:[6,30,54,74,102],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:121},{numBlocks:5,dataCodewordsPerBlock:122}]},
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:47},{numBlocks:14,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:16,dataCodewordsPerBlock:15},{numBlocks:14,dataCodewordsPerBlock:16}]}]},{infoBits:102084,versionNumber:24,alignmentPatternCenters:[6,28,54,80,106],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:117},
{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:45},{numBlocks:14,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:30,dataCodewordsPerBlock:16},{numBlocks:2,dataCodewordsPerBlock:17}]}]},{infoBits:102881,versionNumber:25,alignmentPatternCenters:[6,32,58,84,110],errorCorrectionLevels:[{ecCodewordsPerBlock:26,
ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:106},{numBlocks:4,dataCodewordsPerBlock:107}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:47},{numBlocks:13,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},{infoBits:110507,versionNumber:26,alignmentPatternCenters:[6,
30,58,86,114],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:114},{numBlocks:2,dataCodewordsPerBlock:115}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:46},{numBlocks:4,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:28,dataCodewordsPerBlock:22},{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:33,dataCodewordsPerBlock:16},{numBlocks:4,dataCodewordsPerBlock:17}]}]},
{infoBits:110734,versionNumber:27,alignmentPatternCenters:[6,34,62,90,118],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:23},{numBlocks:26,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:15},
{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:117786,versionNumber:28,alignmentPatternCenters:[6,26,50,74,98,122],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:117},{numBlocks:10,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:45},{numBlocks:23,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:24},{numBlocks:31,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,
ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:31,dataCodewordsPerBlock:16}]}]},{infoBits:119615,versionNumber:29,alignmentPatternCenters:[6,30,54,78,102,126],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:116},{numBlocks:7,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:21,dataCodewordsPerBlock:45},{numBlocks:7,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:23},{numBlocks:37,
dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:15},{numBlocks:26,dataCodewordsPerBlock:16}]}]},{infoBits:126325,versionNumber:30,alignmentPatternCenters:[6,26,52,78,104,130],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:115},{numBlocks:10,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:47},{numBlocks:10,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,
ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},{numBlocks:25,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},{numBlocks:25,dataCodewordsPerBlock:16}]}]},{infoBits:127568,versionNumber:31,alignmentPatternCenters:[6,30,56,82,108,134],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:3,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:46},
{numBlocks:29,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:24},{numBlocks:1,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:133589,versionNumber:32,alignmentPatternCenters:[6,34,60,86,112,138],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,
dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:24},{numBlocks:35,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:15},{numBlocks:35,dataCodewordsPerBlock:16}]}]},{infoBits:136944,versionNumber:33,alignmentPatternCenters:[6,30,58,86,114,142],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115},{numBlocks:1,dataCodewordsPerBlock:116}]},
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:21,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:24},{numBlocks:19,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:141498,versionNumber:34,alignmentPatternCenters:[6,34,62,90,118,146],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},
{numBlocks:6,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:44,dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:59,dataCodewordsPerBlock:16},{numBlocks:1,dataCodewordsPerBlock:17}]}]},{infoBits:145311,versionNumber:35,alignmentPatternCenters:[6,30,54,78,102,126,150],errorCorrectionLevels:[{ecCodewordsPerBlock:30,
ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:121},{numBlocks:7,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:47},{numBlocks:26,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:39,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:41,dataCodewordsPerBlock:16}]}]},{infoBits:150283,versionNumber:36,alignmentPatternCenters:[6,
24,50,76,102,128,154],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:121},{numBlocks:14,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:47},{numBlocks:34,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:46,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:15},{numBlocks:64,dataCodewordsPerBlock:16}]}]},
{infoBits:152622,versionNumber:37,alignmentPatternCenters:[6,28,54,80,106,132,158],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:46},{numBlocks:14,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:49,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:24,
dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:158308,versionNumber:38,alignmentPatternCenters:[6,32,58,84,110,136,162],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:122},{numBlocks:18,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:46},{numBlocks:32,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:48,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},
{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:15},{numBlocks:32,dataCodewordsPerBlock:16}]}]},{infoBits:161089,versionNumber:39,alignmentPatternCenters:[6,26,54,82,110,138,166],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:40,dataCodewordsPerBlock:47},{numBlocks:7,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:43,
dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:15},{numBlocks:67,dataCodewordsPerBlock:16}]}]},{infoBits:167017,versionNumber:40,alignmentPatternCenters:[6,30,58,86,114,142,170],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:118},{numBlocks:6,dataCodewordsPerBlock:119}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:18,dataCodewordsPerBlock:47},{numBlocks:31,dataCodewordsPerBlock:48}]},
{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:24},{numBlocks:34,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:15},{numBlocks:61,dataCodewordsPerBlock:16}]}]}],ca=[{bits:21522,formatInfo:{errorCorrectionLevel:1,dataMask:0}},{bits:20773,formatInfo:{errorCorrectionLevel:1,dataMask:1}},{bits:24188,formatInfo:{errorCorrectionLevel:1,dataMask:2}},{bits:23371,formatInfo:{errorCorrectionLevel:1,dataMask:3}},{bits:17913,formatInfo:{errorCorrectionLevel:1,
dataMask:4}},{bits:16590,formatInfo:{errorCorrectionLevel:1,dataMask:5}},{bits:20375,formatInfo:{errorCorrectionLevel:1,dataMask:6}},{bits:19104,formatInfo:{errorCorrectionLevel:1,dataMask:7}},{bits:30660,formatInfo:{errorCorrectionLevel:0,dataMask:0}},{bits:29427,formatInfo:{errorCorrectionLevel:0,dataMask:1}},{bits:32170,formatInfo:{errorCorrectionLevel:0,dataMask:2}},{bits:30877,formatInfo:{errorCorrectionLevel:0,dataMask:3}},{bits:26159,formatInfo:{errorCorrectionLevel:0,dataMask:4}},{bits:25368,
formatInfo:{errorCorrectionLevel:0,dataMask:5}},{bits:27713,formatInfo:{errorCorrectionLevel:0,dataMask:6}},{bits:26998,formatInfo:{errorCorrectionLevel:0,dataMask:7}},{bits:5769,formatInfo:{errorCorrectionLevel:3,dataMask:0}},{bits:5054,formatInfo:{errorCorrectionLevel:3,dataMask:1}},{bits:7399,formatInfo:{errorCorrectionLevel:3,dataMask:2}},{bits:6608,formatInfo:{errorCorrectionLevel:3,dataMask:3}},{bits:1890,formatInfo:{errorCorrectionLevel:3,dataMask:4}},{bits:597,formatInfo:{errorCorrectionLevel:3,
dataMask:5}},{bits:3340,formatInfo:{errorCorrectionLevel:3,dataMask:6}},{bits:2107,formatInfo:{errorCorrectionLevel:3,dataMask:7}},{bits:13663,formatInfo:{errorCorrectionLevel:2,dataMask:0}},{bits:12392,formatInfo:{errorCorrectionLevel:2,dataMask:1}},{bits:16177,formatInfo:{errorCorrectionLevel:2,dataMask:2}},{bits:14854,formatInfo:{errorCorrectionLevel:2,dataMask:3}},{bits:9396,formatInfo:{errorCorrectionLevel:2,dataMask:4}},{bits:8579,formatInfo:{errorCorrectionLevel:2,dataMask:5}},{bits:11994,
formatInfo:{errorCorrectionLevel:2,dataMask:6}},{bits:11245,formatInfo:{errorCorrectionLevel:2,dataMask:7}}],Z=[(a)=>0===(a.y+a.x)%2,(a)=>0===a.y%2,(a)=>0===a.x%3,(a)=>0===(a.y+a.x)%3,(a)=>0===(Math.floor(a.y/2)+Math.floor(a.x/3))%2,(a)=>0===a.x*a.y%2+a.x*a.y%3,(a)=>0===(a.y*a.x%2+a.y*a.x%3)%2,(a)=>0===((a.y+a.x)%2+a.y*a.x%3)%2],x=(a,b)=>Math.sqrt(Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2)),ka={inversionAttempts:"attemptBoth",greyScaleWeights:{red:.2126,green:.7152,blue:.0722,useIntegerApproximation:!1},
canOverwriteImage:!0};I.default=I;let H="dontInvert",D={red:77,green:150,blue:29,useIntegerApproximation:!0};self.onmessage=(a)=>{let b=a.data.data;switch(a.data.type){case "decode":a=I(b.data,b.width,b.height,{inversionAttempts:H,greyScaleWeights:D});self.postMessage({type:"qrResult",data:a?a:null});break;case "grayscaleWeights":D.red=b.red;D.green=b.green;D.blue=b.blue;D.useIntegerApproximation=b.useIntegerApproximation;break;case "inversionMode":switch(b){case "original":H="dontInvert";break;
case "invert":H="attemptBoth";break;case "both":H="attemptBoth";break;default:throw Error("Invalid inversion mode");}break;case "close":self.close()}}})();
//# sourceMappingURL=qr-scanner-worker.min.js.map

View File

@ -18,10 +18,11 @@ server {
server_name _;
location / {
try_files $uri $uri/ @proxy_to_app;
location /camera/ {
alias /path/to/minimal-webrtc/wwwroot/;
try_files $uri $uri/ @minimal_webrtc;
}
location @proxy_to_app {
location @minimal_webrtc {
proxy_pass http://minimal-webrtc;
proxy_http_version 1.1;

1
wwwroot/index.html Symbolic link
View File

@ -0,0 +1 @@
../camera/templates/index.html

1
wwwroot/static Symbolic link
View File

@ -0,0 +1 @@
../camera/static