@nddeps/barcode-scanner 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,35 +1,43 @@
1
1
  import { type ScanArea } from './utils';
2
- declare function createBarcodeScanner(video: HTMLVideoElement, { calcScanArea, debug, }?: {
3
- calcScanArea?: (video: HTMLVideoElement) => ScanArea;
2
+ type DecodeFailureHandler = (this: State) => Promise<void> | void;
3
+ type DecodeSuccessHandler = (this: State, data: string, area: ScanArea) => Promise<void> | void;
4
+ type LifecycleHook = (this: State) => void;
5
+ type State = {
6
+ decodeFrameTs: number;
7
+ isDecodeFrameProcessed: boolean;
8
+ isDestroyed: boolean;
9
+ isVideoActive: boolean;
10
+ isVideoPaused: boolean;
11
+ scanArea: ScanArea;
12
+ scanRate: number;
13
+ video: HTMLVideoElement;
14
+ };
15
+ declare function createBarcodeScanner(video: HTMLVideoElement, { debug, getScanArea, handleDecodeFailure, handleDecodeSuccess, lifecycle, scanRate, }?: {
4
16
  debug?: boolean;
5
- }): {
6
- destroy: () => Promise<void>;
7
- pause: () => void;
8
- start: ({ facingMode }: {
9
- facingMode?: "environment" | "user";
10
- } | undefined, onDecodeSuccess: (data: string, area: ScanArea) => void, onDecodeFailure?: () => void) => Promise<void>;
11
- state: {
12
- calcScanArea: (video: HTMLVideoElement) => ScanArea;
13
- canvas: HTMLCanvasElement;
14
- canvasContext: CanvasRenderingContext2D;
15
- debug: boolean;
16
- decodeFrameRequestTimestamp: number;
17
- facingMode: "environment" | "user";
18
- isDecodeFrameProcessed: boolean;
19
- isDestroyed: boolean;
20
- isReady: boolean;
21
- isVideoActive: boolean;
22
- isVideoPaused: boolean;
23
- onDecodeFailure: () => void;
24
- onDecodeSuccess: (data: string, area: ScanArea) => void;
25
- requestFrame: (callback: () => void) => number;
26
- resumeOnVisibilityChange: boolean;
27
- scanArea: ScanArea;
28
- scanRate: number;
29
- video: HTMLVideoElement;
30
- worker: null | Worker;
17
+ getScanArea?: (video: HTMLVideoElement) => ScanArea;
18
+ handleDecodeFailure?: DecodeFailureHandler;
19
+ handleDecodeSuccess?: DecodeSuccessHandler;
20
+ lifecycle?: {
21
+ onBeforeCreate?: LifecycleHook;
22
+ onBeforeDecode?: LifecycleHook;
23
+ onBeforeStart?: LifecycleHook;
24
+ onBeforeStop?: LifecycleHook;
25
+ onCreate?: LifecycleHook;
26
+ onDecode?: LifecycleHook;
27
+ onStart?: LifecycleHook;
28
+ onStop?: LifecycleHook;
31
29
  };
32
- stop: () => void;
33
- watch: <T>(source: () => T, callback: (newValue: T) => void) => () => void;
34
- };
30
+ scanRate?: number;
31
+ }): Promise<{
32
+ destroy: () => void;
33
+ pause: () => Promise<void>;
34
+ start: ({ facingMode, ...rest }?: {
35
+ facingMode?: "environment" | "user";
36
+ handleDecodeFailure?: DecodeFailureHandler;
37
+ handleDecodeSuccess?: DecodeSuccessHandler;
38
+ }) => Promise<void>;
39
+ state: State;
40
+ stop: () => Promise<void>;
41
+ }>;
42
+ export type { DecodeFailureHandler, DecodeSuccessHandler, LifecycleHook, State };
35
43
  export { createBarcodeScanner };
@@ -0,0 +1,6 @@
1
+ import type { DetectedBarcode } from 'barcode-detector/ponyfill';
2
+ declare function createWorker(): {
3
+ decode: (imageData: ImageData) => Promise<DetectedBarcode | null>;
4
+ worker: Worker;
5
+ };
6
+ export { createWorker };
package/dist/index.js CHANGED
@@ -1,289 +1,289 @@
1
- function createWatchable(e) {
2
- let r = new EventTarget(), i = new Proxy(e, {
3
- get(e, r, i) {
4
- return Reflect.get(e, r, i);
5
- },
6
- set(e, i, a, o) {
7
- let s = Reflect.set(e, i, a, o);
8
- return r.dispatchEvent(new CustomEvent("change")), s;
9
- }
10
- });
11
- function a(e, i) {
12
- let a = e();
13
- function o() {
14
- let r = e();
15
- JSON.stringify(r) !== JSON.stringify(a) && (a = r, i(r));
16
- }
17
- return r.addEventListener("change", o), () => r.removeEventListener("change", o);
18
- }
19
- return {
20
- state: i,
21
- watch: a
22
- };
1
+ function x(t) {
2
+ const r = new EventTarget(), a = new Proxy(t, {
3
+ get(i, o, c) {
4
+ return Reflect.get(i, o, c);
5
+ },
6
+ set(i, o, c, s) {
7
+ const d = Reflect.set(i, o, c, s);
8
+ return r.dispatchEvent(new CustomEvent("change")), d;
9
+ }
10
+ });
11
+ function n(i, o) {
12
+ let c = i();
13
+ function s() {
14
+ const d = i();
15
+ JSON.stringify(d) !== JSON.stringify(c) && (c = d, o(d));
16
+ }
17
+ return r.addEventListener("change", s), () => r.removeEventListener("change", s);
18
+ }
19
+ return {
20
+ state: a,
21
+ watch: n
22
+ };
23
23
  }
24
- async function hasCameraAccess() {
25
- try {
26
- return (await navigator.permissions.query({ name: "camera" })).state === "granted";
27
- } catch {
28
- return (await navigator.mediaDevices.enumerateDevices()).filter((e) => e.deviceId && e.kind === "videoinput").length > 0;
29
- }
24
+ const O = 1e3 * 32, F = 1e3 * 16;
25
+ function T() {
26
+ const t = new Worker(new URL(
27
+ /* @vite-ignore */
28
+ "" + new URL("worker.js", import.meta.url).href,
29
+ import.meta.url
30
+ ), { type: "module" }), r = new Promise((n, i) => {
31
+ const o = setTimeout(() => {
32
+ i(new Error("Worker load timeout"));
33
+ }, O);
34
+ t.addEventListener(
35
+ "message",
36
+ ({ data: { payload: c, type: s } }) => {
37
+ s === "init" && (clearTimeout(o), c.status === "success" ? n(!0) : i(new Error("Worker failed to load")));
38
+ },
39
+ {
40
+ once: !0
41
+ }
42
+ );
43
+ });
44
+ async function a(n) {
45
+ await r;
46
+ const i = `${performance.now()}-${Math.random().toString(36).slice(2)}`;
47
+ return new Promise((o, c) => {
48
+ const s = setTimeout(() => {
49
+ t.removeEventListener("message", d), c(new Error("Decode timeout"));
50
+ }, F), d = ({ data: { payload: w, type: g } }) => {
51
+ g !== "decode" || w.uuid !== i || (clearTimeout(s), t.removeEventListener("message", d), o(w.data));
52
+ };
53
+ t.addEventListener("message", d), t.postMessage({ payload: { data: n, uuid: i }, type: "decode" });
54
+ });
55
+ }
56
+ return {
57
+ decode: a,
58
+ worker: t
59
+ };
30
60
  }
31
- async function getCameraAccess() {
32
- if (await hasCameraAccess()) return !0;
33
- try {
34
- let e = (await navigator.mediaDevices.getUserMedia({ video: !0 })).getTracks();
35
- for (let r of e) r.stop();
36
- return !0;
37
- } catch {
38
- return !1;
39
- }
61
+ async function V() {
62
+ try {
63
+ return (await navigator.permissions.query({ name: "camera" })).state === "granted";
64
+ } catch {
65
+ return (await navigator.mediaDevices.enumerateDevices()).filter((a) => a.deviceId && a.kind === "videoinput").length > 0;
66
+ }
40
67
  }
41
- function getScanArea(e) {
42
- let r = Math.round(2 / 3 * Math.min(e.videoWidth, e.videoHeight));
43
- return {
44
- height: r,
45
- width: r,
46
- x: Math.round((e.videoWidth - r) / 2),
47
- y: Math.round((e.videoHeight - r) / 2)
48
- };
68
+ async function H() {
69
+ if (await V())
70
+ return !0;
71
+ try {
72
+ const r = (await navigator.mediaDevices.getUserMedia({ video: !0 })).getTracks();
73
+ for (const a of r)
74
+ a.stop();
75
+ return !0;
76
+ } catch {
77
+ return !1;
78
+ }
49
79
  }
50
- function getVideoRenderOffset(e, r) {
51
- let [i, a] = window.getComputedStyle(e).objectPosition.split(" ").map((i, a) => i.endsWith("%") ? (a === 0 ? e.offsetWidth - r.width : e.offsetHeight - r.height) * parseFloat(i) / 100 : parseFloat(i));
52
- return {
53
- x: i,
54
- y: a
55
- };
80
+ function k(t) {
81
+ const r = Math.round(0.6666666666666666 * Math.min(t.videoWidth, t.videoHeight));
82
+ return {
83
+ height: r,
84
+ width: r,
85
+ x: Math.round((t.videoWidth - r) / 2),
86
+ y: Math.round((t.videoHeight - r) / 2)
87
+ };
56
88
  }
57
- function getVideoRenderSize(e) {
58
- let r = window.getComputedStyle(e), i = {
59
- height: e.offsetHeight,
60
- width: e.offsetWidth
61
- }, a = i.width / i.height, o = {
62
- height: e.videoHeight,
63
- width: e.videoWidth
64
- }, s = o.width / o.height;
65
- switch (r.objectFit) {
66
- case "contain": return {
67
- height: s < a ? e.offsetHeight : e.offsetWidth / s,
68
- width: s < a ? e.offsetHeight * s : e.offsetWidth
69
- };
70
- case "cover": return {
71
- height: s > a ? e.offsetHeight : e.offsetWidth / s,
72
- width: s > a ? e.offsetHeight * s : e.offsetWidth
73
- };
74
- case "fill": return i;
75
- case "none": return o;
76
- case "scale-down": return {
77
- height: Math.min(s < a ? e.offsetHeight : e.offsetWidth / s, e.videoHeight),
78
- width: Math.min(s < a ? e.offsetHeight * s : e.offsetWidth, e.videoWidth)
79
- };
80
- default: return o;
81
- }
89
+ function A(t, r) {
90
+ const a = window.getComputedStyle(t), [n, i] = a.objectPosition.split(" ").map(
91
+ (o, c) => o.endsWith("%") ? (c === 0 ? t.offsetWidth - r.width : t.offsetHeight - r.height) * parseFloat(o) / 100 : parseFloat(o)
92
+ );
93
+ return {
94
+ x: n,
95
+ y: i
96
+ };
82
97
  }
83
- function isBarcodeDetectorAvailable(e) {
84
- return "BarcodeDetector" in e;
98
+ function W(t) {
99
+ const r = window.getComputedStyle(t), a = { height: t.offsetHeight, width: t.offsetWidth }, n = a.width / a.height, i = { height: t.videoHeight, width: t.videoWidth }, o = i.width / i.height;
100
+ switch (r.objectFit) {
101
+ case "contain":
102
+ return {
103
+ height: o < n ? t.offsetHeight : t.offsetWidth / o,
104
+ width: o < n ? t.offsetHeight * o : t.offsetWidth
105
+ };
106
+ case "cover":
107
+ return {
108
+ height: o > n ? t.offsetHeight : t.offsetWidth / o,
109
+ width: o > n ? t.offsetHeight * o : t.offsetWidth
110
+ };
111
+ case "fill":
112
+ return a;
113
+ case "none":
114
+ return i;
115
+ case "scale-down":
116
+ return {
117
+ height: Math.min(
118
+ o < n ? t.offsetHeight : t.offsetWidth / o,
119
+ t.videoHeight
120
+ ),
121
+ width: Math.min(
122
+ o < n ? t.offsetHeight * o : t.offsetWidth,
123
+ t.videoWidth
124
+ )
125
+ };
126
+ default:
127
+ return i;
128
+ }
85
129
  }
86
- function translateAreaToVideoRender(e, r) {
87
- let i = /scaleX\(-1\)/.test(e.style.transform), a = getVideoRenderSize(e), c = getVideoRenderOffset(e, a), l = i ? e.videoWidth - r.x - r.width : r.x, u = r.y;
88
- return {
89
- height: r.height / e.videoHeight * a.height,
90
- width: r.width / e.videoWidth * a.width,
91
- x: l / e.videoWidth * a.width + c.x,
92
- y: u / e.videoHeight * a.height + c.y
93
- };
130
+ function L(t) {
131
+ return "BarcodeDetector" in t;
94
132
  }
95
- function translateAreaToVideoSource(e, r) {
96
- let i = /scaleX\(-1\)/.test(e.style.transform), a = getVideoRenderSize(e), c = getVideoRenderOffset(e, a), l = i ? a.width - (r.x - c.x) - r.width : r.x - c.x, u = r.y - c.y, d = e.videoHeight / a.height, f = e.videoWidth / a.width;
97
- return {
98
- height: r.height * d,
99
- width: r.width * f,
100
- x: l * f,
101
- y: u * d
102
- };
133
+ function j(t, r) {
134
+ const a = /scaleX\(-1\)/.test(t.style.transform), n = W(t), i = A(t, n), o = a ? t.videoWidth - r.x - r.width : r.x, c = r.y;
135
+ return {
136
+ height: r.height / t.videoHeight * n.height,
137
+ width: r.width / t.videoWidth * n.width,
138
+ x: o / t.videoWidth * n.width + i.x,
139
+ y: c / t.videoHeight * n.height + i.y
140
+ };
103
141
  }
104
- function wait(e) {
105
- return new Promise((r) => setTimeout(r, e));
142
+ function C(t, r) {
143
+ const a = /scaleX\(-1\)/.test(t.style.transform), n = W(t), i = A(t, n), o = a ? n.width - (r.x - i.x) - r.width : r.x - i.x, c = r.y - i.y, s = t.videoHeight / n.height, d = t.videoWidth / n.width;
144
+ return {
145
+ height: r.height * s,
146
+ width: r.width * d,
147
+ x: o * d,
148
+ y: c * s
149
+ };
106
150
  }
107
- var Dt = [
108
- ["Aztec", "M"],
109
- ["Codabar", "L"],
110
- ["Code39", "L"],
111
- ["Code93", "L"],
112
- ["Code128", "L"],
113
- ["DataBar", "L"],
114
- ["DataBarExpanded", "L"],
115
- ["DataMatrix", "M"],
116
- ["EAN-8", "L"],
117
- ["EAN-13", "L"],
118
- ["ITF", "L"],
119
- ["MaxiCode", "M"],
120
- ["PDF417", "M"],
121
- ["QRCode", "M"],
122
- ["UPC-A", "L"],
123
- ["UPC-E", "L"],
124
- ["MicroQRCode", "M"],
125
- ["rMQRCode", "M"],
126
- ["DXFilmEdge", "L"],
127
- ["DataBarLimited", "L"]
128
- ], Mt = Dt.map(([e]) => e);
129
- Mt.filter((e, r) => Dt[r][1] === "L"), Mt.filter((e, r) => Dt[r][1] === "M");
130
- var It = {
131
- formats: [],
132
- tryHarder: !0,
133
- tryRotate: !0,
134
- tryInvert: !0,
135
- tryDownscale: !0,
136
- tryDenoise: !1,
137
- binarizer: "LocalAverage",
138
- isPure: !1,
139
- downscaleFactor: 3,
140
- downscaleThreshold: 500,
141
- minLineCount: 2,
142
- maxNumberOfSymbols: 255,
143
- tryCode39ExtendedMode: !0,
144
- returnErrors: !1,
145
- eanAddOnSymbol: "Ignore",
146
- textMode: "HRI",
147
- characterSet: "Unknown"
148
- };
149
- ({ ...It }), [...It.formats], [...[
150
- ["aztec", "Aztec"],
151
- ["code_128", "Code128"],
152
- ["code_39", "Code39"],
153
- ["code_93", "Code93"],
154
- ["codabar", "Codabar"],
155
- ["databar", "DataBar"],
156
- ["databar_expanded", "DataBarExpanded"],
157
- ["databar_limited", "DataBarLimited"],
158
- ["data_matrix", "DataMatrix"],
159
- ["dx_film_edge", "DXFilmEdge"],
160
- ["ean_13", "EAN-13"],
161
- ["ean_8", "EAN-8"],
162
- ["itf", "ITF"],
163
- ["maxi_code", "MaxiCode"],
164
- ["micro_qr_code", "MicroQRCode"],
165
- ["pdf417", "PDF417"],
166
- ["qr_code", "QRCode"],
167
- ["rm_qr_code", "rMQRCode"],
168
- ["upc_a", "UPC-A"],
169
- ["upc_e", "UPC-E"],
170
- ["linear_codes", "Linear-Codes"],
171
- ["matrix_codes", "Matrix-Codes"],
172
- ["any", "Any"]
173
- ], ["unknown"]].map((e) => e[0]);
174
- var instance = { value: null };
175
- async function decode(e) {
176
- let r = instance.value;
177
- if (!r) throw Error("Worker not installed");
178
- let i = `${performance.now()}-${Math.random().toString(36).slice(2)}`;
179
- return new Promise((a, o) => {
180
- let s = 0, c = ({ data: { payload: e, type: o } }) => {
181
- o !== "decode" || e.uuid !== i || (clearTimeout(s), r.removeEventListener("message", c), a(e.data));
182
- };
183
- s = setTimeout(() => {
184
- r.removeEventListener("message", c), o(null);
185
- }, 1e3), r.addEventListener("message", c), r.postMessage({
186
- payload: {
187
- data: e,
188
- uuid: i
189
- },
190
- type: "decode"
191
- });
192
- });
193
- }
194
- function install() {
195
- if (instance.value) return Promise.resolve(instance.value);
196
- let e = new Worker(new URL(
197
- /* @vite-ignore */
198
- "" + new URL("worker.js", import.meta.url).href,
199
- "" + import.meta.url
200
- ), { type: "module" });
201
- return new Promise((r, i) => {
202
- e.addEventListener("message", ({ data: { type: a } }) => {
203
- a === "init" ? r(e) : i(/* @__PURE__ */ Error("Worker load failed"));
204
- }, { once: !0 }), instance.value = e;
205
- });
151
+ function U(t) {
152
+ return new Promise((r) => setTimeout(r, t));
206
153
  }
207
- function createBarcodeScanner(r, { calcScanArea: o, debug: s = !1 } = {}) {
208
- if (!(r instanceof HTMLVideoElement)) throw Error("video is not a HTMLVideoElement");
209
- let c = document.createElement("canvas"), l = c.getContext("2d", { willReadFrequently: !0 });
210
- if (!l) throw Error("Failed to get canvas context");
211
- let { state: u, watch: d } = createWatchable({
212
- calcScanArea: o ?? getScanArea,
213
- canvas: c,
214
- canvasContext: l,
215
- debug: s,
216
- decodeFrameRequestTimestamp: performance.now(),
217
- facingMode: "environment",
218
- isDecodeFrameProcessed: !1,
219
- isDestroyed: !1,
220
- isReady: !1,
221
- isVideoActive: !1,
222
- isVideoPaused: !1,
223
- onDecodeFailure: () => {},
224
- onDecodeSuccess: () => {},
225
- requestFrame: r.requestVideoFrameCallback ? r.requestVideoFrameCallback.bind(r) : requestAnimationFrame,
226
- resumeOnVisibilityChange: !1,
227
- scanArea: {
228
- height: 0,
229
- width: 0,
230
- x: 0,
231
- y: 0
232
- },
233
- scanRate: 24,
234
- video: r,
235
- worker: null
236
- });
237
- install().then((e) => u.worker = e).then(() => Promise.resolve()).then(() => u.isReady = !0), u.video.autoplay = !0, u.video.disablePictureInPicture = !0, u.video.hidden = !1, u.video.muted = !0, u.video.playsInline = !0, document.addEventListener("visibilitychange", f);
238
- function f() {
239
- s && console.log("barcode-scanner:handleVisibilityChange", document.visibilityState, u), document.visibilityState === "hidden" ? u.isVideoActive && u.isVideoPaused === !1 && (u.resumeOnVisibilityChange = !0, h()) : u.resumeOnVisibilityChange && (u.resumeOnVisibilityChange = !1, g({ facingMode: u.facingMode }, u.onDecodeSuccess, u.onDecodeFailure));
240
- }
241
- function p(e, r) {
242
- s && console.log("barcode-scanner:handleDecode", u, performance.now()), !(u.isDestroyed || u.isVideoActive === !1 || u.isVideoPaused) && u.requestFrame(() => {
243
- if (u.isReady === !1 || performance.now() - u.decodeFrameRequestTimestamp < 1e3 / u.scanRate || u.isDecodeFrameProcessed || u.video.readyState <= 1) {
244
- u.decodeFrameRequestTimestamp = performance.now(), p(e, r);
245
- return;
246
- }
247
- u.isDecodeFrameProcessed = !0, u.scanArea = u.calcScanArea(u.video), u.canvas.height = u.scanArea.height, u.canvas.width = u.scanArea.width, u.canvasContext.clearRect(0, 0, u.canvas.width, u.canvas.height), u.canvasContext.drawImage(u.video, u.scanArea.x, u.scanArea.y, u.scanArea.width, u.scanArea.height, 0, 0, u.canvas.width, u.canvas.height);
248
- let i = u.canvasContext.getImageData(0, 0, u.canvas.width, u.canvas.height);
249
- u.debug && window.dispatchEvent(new CustomEvent("barcode-scanner:decode-frame", { detail: { imageData: i } })), decode(i).then((i) => {
250
- if (i) {
251
- let r = i.cornerPoints.map((e) => e.x), a = i.cornerPoints.map((e) => e.y);
252
- e(i.rawValue, {
253
- height: Math.max(...a) - Math.min(...a),
254
- width: Math.max(...r) - Math.min(...r),
255
- x: Math.min(...r) + u.scanArea.x,
256
- y: Math.min(...a) + u.scanArea.y
257
- });
258
- } else r();
259
- }).catch(() => {
260
- console.error("Failed to decode barcode");
261
- }).finally(() => {
262
- u.isDecodeFrameProcessed = !1, u.decodeFrameRequestTimestamp = performance.now(), p(e, r);
263
- });
264
- });
265
- }
266
- async function m() {
267
- u.isDestroyed || (_(), document.removeEventListener("visibilitychange", f), u.worker?.terminate(), u.worker = null, u.isDestroyed = !0, u.isReady = !1);
268
- }
269
- function h() {
270
- s && console.log("barcode-scanner:pause", u), u.canvas.height = u.video.videoHeight, u.canvas.width = u.video.videoWidth, u.canvasContext.clearRect(0, 0, u.canvas.width, u.canvas.height), u.canvasContext.drawImage(u.video, 0, 0, u.canvas.width, u.canvas.height, 0, 0, u.canvas.width, u.canvas.height), u.video.poster = u.canvas.toDataURL(), u.video.srcObject instanceof MediaStream && u.video.srcObject.getTracks().forEach((e) => e.stop()), u.video.srcObject = null, u.isVideoPaused = !0;
271
- }
272
- async function g({ facingMode: e = "environment" } = {}, r, a = () => {}) {
273
- if (s && console.log("barcode-scanner:start:before", u), !await getCameraAccess()) throw Error("No camera access");
274
- u.video.srcObject instanceof MediaStream && u.isVideoActive && u.isVideoPaused === !1 || (u.video.srcObject instanceof MediaStream || (u.video.srcObject = await navigator.mediaDevices.getUserMedia({ video: { facingMode: e } })), await u.video.play(), u.facingMode = e, u.isVideoActive = !0, u.isVideoPaused = !1, u.onDecodeFailure = a, u.onDecodeSuccess = r, u.scanArea = u.calcScanArea(u.video), u.video.style.transform = e === "user" ? "scaleX(-1)" : "none", s && console.log("barcode-scanner:start:after", u), p(r, a));
275
- }
276
- function _() {
277
- u.video.srcObject instanceof MediaStream && u.video.srcObject.getTracks().forEach((e) => e.stop()), u.video.poster = "", u.video.srcObject = null, u.isVideoActive = !1, u.isVideoPaused = !1;
278
- }
279
- return {
280
- destroy: m,
281
- pause: h,
282
- start: g,
283
- state: u,
284
- stop: _,
285
- watch: d
286
- };
154
+ async function B(t, {
155
+ debug: r,
156
+ getScanArea: a = k,
157
+ handleDecodeFailure: n = () => {
158
+ },
159
+ handleDecodeSuccess: i = () => {
160
+ },
161
+ lifecycle: o = {},
162
+ scanRate: c = 24
163
+ } = {}) {
164
+ if (!(t instanceof HTMLVideoElement))
165
+ throw new Error("video is not a HTMLVideoElement");
166
+ if (!(i instanceof Function))
167
+ throw new Error("handleDecodeSuccess is not a function");
168
+ if (!(n instanceof Function))
169
+ throw new Error("handleDecodeFailure is not a function");
170
+ const s = document.createElement("canvas"), d = s.getContext("2d", { willReadFrequently: !0 });
171
+ if (!d)
172
+ throw new Error("canvas context is not supported");
173
+ const { decode: w, worker: g } = T(), { state: e } = x({
174
+ decodeFrameTs: performance.now(),
175
+ isDecodeFrameProcessed: !1,
176
+ isDestroyed: !1,
177
+ isVideoActive: !1,
178
+ isVideoPaused: !1,
179
+ scanArea: a(t),
180
+ scanRate: c,
181
+ video: t
182
+ }), v = t.requestVideoFrameCallback?.bind(t) ?? requestAnimationFrame;
183
+ e.video.autoplay = !0, e.video.disablePictureInPicture = !0, e.video.hidden = !1, e.video.muted = !0, e.video.playsInline = !0, o.onCreate && o.onCreate.call(e);
184
+ function b(h, u) {
185
+ v(m);
186
+ async function m() {
187
+ if (e.isDestroyed || e.isVideoActive === !1)
188
+ return;
189
+ if (
190
+ // Skip if the time since the last request frame is less than the scan rate
191
+ performance.now() - e.decodeFrameTs < 1e3 / e.scanRate || // Skip if the frame is already processed
192
+ e.isDecodeFrameProcessed || // Skip if the video is not ready
193
+ e.video.readyState <= 1
194
+ ) {
195
+ v(m);
196
+ return;
197
+ }
198
+ e.isDecodeFrameProcessed = !0, e.scanArea = a(e.video), o.onBeforeDecode && o.onBeforeDecode.call(e), s.height = e.scanArea.height, s.width = e.scanArea.width, d.clearRect(0, 0, s.width, s.height), d.drawImage(
199
+ e.video,
200
+ e.scanArea.x,
201
+ e.scanArea.y,
202
+ e.scanArea.width,
203
+ e.scanArea.height,
204
+ 0,
205
+ 0,
206
+ s.width,
207
+ s.height
208
+ );
209
+ const l = d.getImageData(0, 0, s.width, s.height);
210
+ r && window.dispatchEvent(
211
+ new CustomEvent("barcode-scanner:decode-frame", {
212
+ detail: {
213
+ imageData: l
214
+ }
215
+ })
216
+ );
217
+ try {
218
+ const f = await w(l);
219
+ if (f) {
220
+ const p = f.cornerPoints.map((E) => E.x), y = f.cornerPoints.map((E) => E.y), P = {
221
+ height: Math.max(...y) - Math.min(...y),
222
+ width: Math.max(...p) - Math.min(...p),
223
+ x: Math.min(...p) + e.scanArea.x,
224
+ y: Math.min(...y) + e.scanArea.y
225
+ };
226
+ await Promise.resolve(h.call(e, f.rawValue, P));
227
+ } else
228
+ await Promise.resolve(u.call(e));
229
+ } catch (f) {
230
+ console.warn("Failed to decode barcode"), f && console.error(f);
231
+ } finally {
232
+ o.onDecode && o.onDecode.call(e), e.decodeFrameTs = performance.now(), e.isDecodeFrameProcessed = !1, v(m);
233
+ }
234
+ }
235
+ }
236
+ function R() {
237
+ e.isDestroyed || (D(), g.terminate(), e.isDestroyed = !0);
238
+ }
239
+ async function S() {
240
+ if (e.isVideoActive === !1 || e.isVideoPaused || e.isDestroyed)
241
+ return;
242
+ e.video.pause(), s.height = e.video.videoHeight, s.width = e.video.videoWidth, d.drawImage(e.video, 0, 0, s.width, s.height, 0, 0, s.width, s.height);
243
+ const h = await new Promise((u, m) => {
244
+ s.toBlob(
245
+ (l) => {
246
+ l ? u(l) : m(new Error("Failed to convert canvas to blob"));
247
+ },
248
+ "image/jpeg",
249
+ 0.9
250
+ );
251
+ });
252
+ e.video.poster.startsWith("blob:") && URL.revokeObjectURL(e.video.poster), e.video.poster = URL.createObjectURL(h), e.video.srcObject instanceof MediaStream && e.video.srcObject.getTracks().forEach((u) => u.stop()), e.isVideoPaused = !0, e.video.srcObject = null;
253
+ }
254
+ async function M({
255
+ facingMode: h = "environment",
256
+ ...u
257
+ } = {}) {
258
+ if (o.onBeforeStart && o.onBeforeStart.call(e), !await H())
259
+ throw new Error("No camera access");
260
+ e.video.srcObject instanceof MediaStream || (e.video.srcObject = await navigator.mediaDevices.getUserMedia({
261
+ video: {
262
+ facingMode: h
263
+ }
264
+ }), await e.video.play(), e.isVideoActive = !0, e.isVideoPaused = !1, e.scanArea = a(e.video), e.video.style.transform = h === "user" ? "scaleX(-1)" : "none", o.onStart && o.onStart.call(e), b(u.handleDecodeSuccess ?? i, u.handleDecodeFailure ?? n));
265
+ }
266
+ async function D() {
267
+ e.isVideoActive === !1 || e.isDestroyed || (o.onBeforeStop && o.onBeforeStop.call(e), e.video.srcObject instanceof MediaStream && e.video.srcObject.getTracks().forEach((h) => h.stop()), e.video.poster.startsWith("blob:") && URL.revokeObjectURL(e.video.poster), e.isVideoActive = !1, e.isVideoPaused = !1, e.video.poster = "", e.video.srcObject = null, o.onStop && o.onStop.call(e));
268
+ }
269
+ return {
270
+ destroy: R,
271
+ pause: S,
272
+ start: M,
273
+ state: e,
274
+ stop: D
275
+ };
287
276
  }
288
- var lib_default = createBarcodeScanner;
289
- export { createBarcodeScanner, lib_default as default, getCameraAccess, getScanArea, getVideoRenderOffset, getVideoRenderSize, hasCameraAccess, isBarcodeDetectorAvailable, translateAreaToVideoRender, translateAreaToVideoSource, wait };
277
+ export {
278
+ B as createBarcodeScanner,
279
+ B as default,
280
+ H as getCameraAccess,
281
+ k as getScanArea,
282
+ A as getVideoRenderOffset,
283
+ W as getVideoRenderSize,
284
+ V as hasCameraAccess,
285
+ L as isBarcodeDetectorAvailable,
286
+ j as translateAreaToVideoRender,
287
+ C as translateAreaToVideoSource,
288
+ U as wait
289
+ };