Compare commits
15 Commits
feature/da
...
master
Author | SHA1 | Date | |
---|---|---|---|
c4bb684b41 | |||
bf4749499e | |||
f025675b9b | |||
614eb52224 | |||
131c9b3bf1 | |||
90565985ae | |||
4f3ae7abd6 | |||
834031dca7 | |||
225a307ac7 | |||
a3528ec43e | |||
3f393d8dc2 | |||
6300f20407 | |||
c4cddaaefd | |||
5669593cb1 | |||
01446ba255 |
9
.gitmodules
vendored
Normal file
9
.gitmodules
vendored
Normal 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
|
|
@ -4,5 +4,5 @@ from . import consumers
|
||||||
|
|
||||||
websocket_urlpatterns = [
|
websocket_urlpatterns = [
|
||||||
re_path(r'camera/ws/(?P<kind>host|client)/(?P<room_name>\w+)/$',
|
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
1
camera/static/js/base64.js
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../external/base64.js/base64.js
|
1
camera/static/js/lzma_worker.js
Symbolic link
1
camera/static/js/lzma_worker.js
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../external/LZMA-JS/src/lzma_worker.js
|
175
camera/static/js/prefix
Normal file
175
camera/static/js/prefix
Normal 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:
|
||||||
|
"}}
|
47
camera/static/js/prefixCompressor.js
Normal file
47
camera/static/js/prefixCompressor.js
Normal 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);
|
||||||
|
}
|
1
camera/static/js/qr-scanner-worker.min.js
vendored
Symbolic link
1
camera/static/js/qr-scanner-worker.min.js
vendored
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../external/qr-scanner-binaryData/qr-scanner-worker.min.js
|
1
camera/static/js/qr-scanner-worker.min.js.map
Symbolic link
1
camera/static/js/qr-scanner-worker.min.js.map
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../external/qr-scanner/qr-scanner-worker.min.js.map
|
1
camera/static/js/qr-scanner.min.js
vendored
Symbolic link
1
camera/static/js/qr-scanner.min.js
vendored
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../external/qr-scanner/qr-scanner.min.js
|
1
camera/static/js/qr-scanner.min.js.map
Symbolic link
1
camera/static/js/qr-scanner.min.js.map
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../external/qr-scanner/qr-scanner.min.js.map
|
1
camera/static/js/qrcodegen.js
Symbolic link
1
camera/static/js/qrcodegen.js
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../external/qr-code-generator/qrcodegen.js
|
|
@ -3,18 +3,23 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<title>Minimal WebRTC</title>
|
<title>Minimal WebRTC</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
|
||||||
<style>
|
<style>
|
||||||
body.justVideo {
|
body.justVideo {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
#qrcodelink {
|
body.justVideo #status, body.justVideo form {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.qrcontainer {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
#qrcode {
|
.qrcontainer svg path {
|
||||||
|
min-width: 30vw;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
max-height: 50vh;
|
max-height: 50vh;
|
||||||
}
|
}
|
||||||
|
@ -51,17 +56,37 @@ form label {
|
||||||
background: lightyellow;
|
background: lightyellow;
|
||||||
}
|
}
|
||||||
</style>
|
</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>
|
</head>
|
||||||
<body>
|
<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">
|
<form name="settings">
|
||||||
|
<label>
|
||||||
|
Serverless mode:
|
||||||
|
<input name="serverless" type="checkbox" value="true" />
|
||||||
|
</label>
|
||||||
<label>
|
<label>
|
||||||
Recieve remote video:
|
Recieve remote video:
|
||||||
<select name="client-video">
|
<select name="client-video">
|
||||||
<option value="none">none</option>
|
<option value="none">none</option>
|
||||||
|
<option value="true">any camera</option>
|
||||||
<option value="environment" selected>rear camera</option>
|
<option value="environment" selected>rear camera</option>
|
||||||
<option value="user">front camera</option>
|
<option value="user">front camera</option>
|
||||||
<option value="true" selected>any camera</option>
|
|
||||||
<option value="screen">screen share</option>
|
<option value="screen">screen share</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
@ -74,6 +99,8 @@ form label {
|
||||||
<select name="host-video">
|
<select name="host-video">
|
||||||
<option value="none" selected>none</option>
|
<option value="none" selected>none</option>
|
||||||
<option value="true">any camera</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>
|
<option value="screen">screen share</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
@ -81,6 +108,17 @@ form label {
|
||||||
Transmit audio:
|
Transmit audio:
|
||||||
<input name="host-audio" type="checkbox" value="true" />
|
<input name="host-audio" type="checkbox" value="true" />
|
||||||
</label>
|
</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>
|
</form>
|
||||||
<div id="status"></div>
|
<div id="status"></div>
|
||||||
<div id="videos">
|
<div id="videos">
|
||||||
|
@ -88,19 +126,42 @@ form label {
|
||||||
<button id="start" style="display:none;">Start Streaming</button>
|
<button id="start" style="display:none;">Start Streaming</button>
|
||||||
<video id="remoteView" width="100%" autoplay muted style="display:none;"></video>
|
<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="selfView" width="200" height="150" autoplay muted style="display:none;"></video>
|
||||||
|
<video id="qrscan" width="100%" autoplay muted style="display:none;"></video>
|
||||||
</div>
|
</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 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 body = document.querySelector("body");
|
||||||
const out = document.getElementById("status");
|
const out = document.getElementById("status");
|
||||||
const qrcode = document.getElementById("qrcode");
|
const qrcode = document.getElementById("qrcode");
|
||||||
const qrcodelink = document.getElementById("qrcodelink");
|
const qrcodelink = document.getElementById("qrcodelink");
|
||||||
|
const qrcodeObjContainer = document.getElementById("qrcodeObjContainer");
|
||||||
|
const qrcodeObj = document.getElementById("qrcodeObj");
|
||||||
const remoteView = document.getElementById("remoteView");
|
const remoteView = document.getElementById("remoteView");
|
||||||
const selfView = document.getElementById("selfView");
|
const selfView = document.getElementById("selfView");
|
||||||
|
const qrscan = document.getElementById("qrscan");
|
||||||
const start = document.getElementById("start");
|
const start = document.getElementById("start");
|
||||||
const unmute = document.getElementById("unmute");
|
const unmute = document.getElementById("unmute");
|
||||||
const form = document.forms["settings"];
|
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) {
|
function _log(str, tag) {
|
||||||
const logEntry = document.createElement(tag);
|
const logEntry = document.createElement(tag);
|
||||||
|
@ -134,18 +195,64 @@ form label {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const el of form.elements) {
|
for (const el of form.elements) {
|
||||||
el.addEventListener("change", _ => {
|
el.addEventListener("change", e => {
|
||||||
window.location.hash = JSON.stringify(readSettingsForm(false));
|
window.location.hash = JSON.stringify(readSettingsForm(false));
|
||||||
|
if (e.target.name == 'serverless') {
|
||||||
|
window.location.reload(false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRoomName() {
|
function isServerless() {
|
||||||
JSON.parse(document.getElementById('room-name').textContent);
|
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 roomName = window.location.hash;
|
||||||
let isHost = roomName === undefined || !roomName;
|
let isHost = roomName === undefined || !roomName;
|
||||||
if (roomName.startsWith("#{")) {
|
if (roomName && roomName.startsWith("#{")) {
|
||||||
roomName = undefined;
|
roomName = undefined;
|
||||||
isHost = true;
|
isHost = true;
|
||||||
|
|
||||||
|
@ -167,6 +274,25 @@ form label {
|
||||||
log("Failed to read settings from hash: " + error);
|
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) {
|
if (isHost) {
|
||||||
// From https://stackoverflow.com/a/1349426
|
// From https://stackoverflow.com/a/1349426
|
||||||
function makeid(length) {
|
function makeid(length) {
|
||||||
|
@ -179,17 +305,23 @@ form label {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isServerless()) {
|
||||||
roomName = makeid(8);
|
roomName = makeid(8);
|
||||||
qrcodelink.href = window.location.href.split('#')[0] + '#' + roomName;
|
displayQRUrl(withHash(roomName));
|
||||||
qrcode.src = window.location.href.split('#')[0] + roomName + '/qr';
|
}
|
||||||
qrcodelink.style.display = '';
|
|
||||||
} else {
|
} else {
|
||||||
|
if (!isServerless()) {
|
||||||
roomName = roomName.substring(1);
|
roomName = roomName.substring(1);
|
||||||
|
}
|
||||||
qrcodelink.style.display = 'none';
|
qrcodelink.style.display = 'none';
|
||||||
form.style.display = 'none';
|
form.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isServerless()) {
|
||||||
|
log("In serverless mode.");
|
||||||
|
} else {
|
||||||
log("Room: " + roomName);
|
log("Room: " + roomName);
|
||||||
|
}
|
||||||
|
|
||||||
var webSocket = undefined;
|
var webSocket = undefined;
|
||||||
|
|
||||||
|
@ -199,6 +331,10 @@ form label {
|
||||||
const method = dcReady ? "dataConnection" : "webSocket";
|
const method = dcReady ? "dataConnection" : "webSocket";
|
||||||
log("Sending message via " + method + "...");
|
log("Sending message via " + method + "...");
|
||||||
logPre(toSend);
|
logPre(toSend);
|
||||||
|
if (isServerless() && !dcReady) {
|
||||||
|
log("ERROR: Attempted to use webSocket in serverless mode.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
(dcReady ? dc : webSocket).send(toSend);
|
(dcReady ? dc : webSocket).send(toSend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +343,12 @@ form label {
|
||||||
|
|
||||||
function sendOffer() {
|
function sendOffer() {
|
||||||
if (pc.iceGatheringState == "complete") {
|
if (pc.iceGatheringState == "complete") {
|
||||||
sendJson({
|
const sendFunc = !isServerless() || dc && dc.readyState == "open"
|
||||||
|
? sendJson
|
||||||
|
: isHost
|
||||||
|
? displayQRUrlForObj
|
||||||
|
: displayQRCodeForObj;
|
||||||
|
sendFunc({
|
||||||
description: pc.localDescription
|
description: pc.localDescription
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -232,7 +373,10 @@ form label {
|
||||||
// ... but only once icegathering is complete.
|
// ... but only once icegathering is complete.
|
||||||
pc.onnegotiationneeded = async function () {
|
pc.onnegotiationneeded = async function () {
|
||||||
log("In pc.onnegotiationneeded...");
|
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();
|
sendOffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,11 +389,11 @@ form label {
|
||||||
|
|
||||||
if (stream.getVideoTracks().length > 0) {
|
if (stream.getVideoTracks().length > 0) {
|
||||||
remoteView.style.display = '';
|
remoteView.style.display = '';
|
||||||
out.style.display = 'none';
|
|
||||||
form.style.display = 'none';
|
|
||||||
videos.style.display = '';
|
videos.style.display = '';
|
||||||
|
if (!settings.debug) {
|
||||||
body.classList.add('justVideo');
|
body.classList.add('justVideo');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pc.ondatachannel = e => {
|
pc.ondatachannel = e => {
|
||||||
|
@ -258,6 +402,7 @@ form label {
|
||||||
receiveMessage({source: "dataChannel", data: e.data});
|
receiveMessage({source: "dataChannel", data: e.data});
|
||||||
}
|
}
|
||||||
log('Data channel initialized.');
|
log('Data channel initialized.');
|
||||||
|
qrcodeObjContainer.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isHost) {
|
if (isHost) {
|
||||||
|
@ -266,6 +411,8 @@ form label {
|
||||||
receiveMessage({source: "dataChannel", data: e.data});
|
receiveMessage({source: "dataChannel", data: e.data});
|
||||||
}
|
}
|
||||||
dc.onopen = e => {
|
dc.onopen = e => {
|
||||||
|
remoteOffer.disabled = true;
|
||||||
|
remoteOfferScan.disabled = true;
|
||||||
log("Data channel open, sending settings...")
|
log("Data channel open, sending settings...")
|
||||||
settings = readSettingsForm(true);
|
settings = readSettingsForm(true);
|
||||||
sendJson({settings});
|
sendJson({settings});
|
||||||
|
@ -306,7 +453,7 @@ form label {
|
||||||
: videoSettings == 'true'
|
: videoSettings == 'true'
|
||||||
? true
|
? true
|
||||||
: { advanced: [{facingMode: videoSettings}] };
|
: { advanced: [{facingMode: videoSettings}] };
|
||||||
log("Created videoConstraints.");
|
log("Created videoConstraints: " + JSON.stringify(videoConstraints));
|
||||||
if (!videoConstraints && !audioSettings) return;
|
if (!videoConstraints && !audioSettings) return;
|
||||||
|
|
||||||
const stream = videoSettings == 'screen'
|
const stream = videoSettings == 'screen'
|
||||||
|
@ -327,8 +474,13 @@ form label {
|
||||||
log("Added track.");
|
log("Added track.");
|
||||||
pc.addTrack(track, stream);
|
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) {
|
function startStreamingWithErorrHandling(fromButton) {
|
||||||
|
try {
|
||||||
startStreaming(fromButton)
|
startStreaming(fromButton)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
log("startStreaming() finished.");
|
log("startStreaming() finished.");
|
||||||
|
@ -336,6 +488,9 @@ form label {
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
log("startStreaming() errored: " + e.message);
|
log("startStreaming() errored: " + e.message);
|
||||||
});
|
});
|
||||||
|
} catch (e) {
|
||||||
|
log("Error in startStreaming(): " + e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
start.addEventListener("click", _ => {
|
start.addEventListener("click", _ => {
|
||||||
|
@ -354,12 +509,20 @@ form label {
|
||||||
settings = data.settings;
|
settings = data.settings;
|
||||||
startStreamingWithErorrHandling(false);
|
startStreamingWithErorrHandling(false);
|
||||||
} else if (data.description) {
|
} else if (data.description) {
|
||||||
|
try {
|
||||||
if (pc == undefined) pc = createRTCPeerConnection();
|
if (pc == undefined) pc = createRTCPeerConnection();
|
||||||
await pc.setRemoteDescription(data.description);
|
await pc.setRemoteDescription(data.description);
|
||||||
if (data.description.type == "offer") {
|
if (data.description.type == "offer") {
|
||||||
log("Got an offer...");
|
log("Got an offer...");
|
||||||
|
if (!settings || !('separateIce' in settings) || !settings.separateIce) {
|
||||||
await pc.setLocalDescription(await pc.createAnswer());
|
await pc.setLocalDescription(await pc.createAnswer());
|
||||||
sendOffer();
|
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;
|
return webSocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isServerless()) {
|
||||||
webSocket = createWebSocket();
|
webSocket = createWebSocket();
|
||||||
|
|
||||||
if (!isHost) {
|
if (!isHost) {
|
||||||
|
@ -396,8 +560,42 @@ form label {
|
||||||
// so here just notify the host to start the process.
|
// so here just notify the host to start the process.
|
||||||
webSocket.onopen = _ => sendJson({ready: true});
|
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.");
|
log("Finished <script> block.");
|
||||||
|
}
|
||||||
|
initialize();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -79,7 +79,7 @@ USE_TZ = True
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/3.0/howto/static-files/
|
# https://docs.djangoproject.com/en/3.0/howto/static-files/
|
||||||
|
|
||||||
STATIC_URL = '/static/'
|
STATIC_URL = '/camera/static/'
|
||||||
|
|
||||||
ASGI_APPLICATION = 'camera_site.routing.application'
|
ASGI_APPLICATION = 'camera_site.routing.application'
|
||||||
CHANNEL_LAYERS = {
|
CHANNEL_LAYERS = {
|
||||||
|
|
1
external/LZMA-JS
vendored
Submodule
1
external/LZMA-JS
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 8f98fe85a1ef78ab6e9d26ab85ce338f50095a23
|
1
external/base64.js
vendored
Submodule
1
external/base64.js
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 74d3cbf82481545bc26c104de2419f4ee30c7dd7
|
4
external/qr-code-generator/README
vendored
Normal file
4
external/qr-code-generator/README
vendored
Normal 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
902
external/qr-code-generator/qrcodegen.js
vendored
Normal 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
1
external/qr-scanner
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit ecd8a3425f5d222ded65a0c6a560a437e53c204c
|
6
external/qr-scanner-binaryData/README
vendored
Normal file
6
external/qr-scanner-binaryData/README
vendored
Normal 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
|
86
external/qr-scanner-binaryData/qr-scanner-worker.min.js
vendored
Normal file
86
external/qr-scanner-binaryData/qr-scanner-worker.min.js
vendored
Normal 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
|
|
@ -18,10 +18,11 @@ server {
|
||||||
|
|
||||||
server_name _;
|
server_name _;
|
||||||
|
|
||||||
location / {
|
location /camera/ {
|
||||||
try_files $uri $uri/ @proxy_to_app;
|
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_pass http://minimal-webrtc;
|
||||||
|
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
|
|
1
wwwroot/index.html
Symbolic link
1
wwwroot/index.html
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../camera/templates/index.html
|
1
wwwroot/static
Symbolic link
1
wwwroot/static
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../camera/static
|
Loading…
Reference in New Issue
Block a user