import { BarcodeDetector as BarcodeDetectorPonyfill, prepareZXingModule } from "barcode-detector/dist/cjs/ponyfill.js";
import { createUUID } from "../../utils/uuid";
import { getConfig } from "../config";
import scanBeepAudio from "../../../res/scan_beep.ogg";
const scanWeb = {
uuid: null,
finish: true,
stream: null,
videoEl: null
}
const DEFAULT_SCAN_BEEP_AUDIO = scanBeepAudio;
const ZXING_READER_WASM_PATH = "lib/reader.wasm";
const WEBSCAN_CLOSE_BUTTON_ID = "__webscan_close_button__";
const WEBSCAN_PICK_IMAGE_BUTTON_ID = "__webscan_pick_image_button__";
const WEBSCAN_CLOSE_ICON_SVG = "";
const WEBSCAN_PICK_IMAGE_ICON_SVG = "";
const WEBSCAN_OVERLAY_BUTTON_SIZE = 40;
const WEBSCAN_OVERLAY_BUTTON_EDGE = 8;
const WEBSCAN_OVERLAY_BUTTON_GAP = 10;
function getDefaultWebScanOverlayButtonStyle() {
return "width: " + WEBSCAN_OVERLAY_BUTTON_SIZE + "px; height: " + WEBSCAN_OVERLAY_BUTTON_SIZE + "px; padding: 0; border: 0; border-radius: 50%; background: rgba(0, 0, 0, 0.55); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 10000;";
}
const currentScriptSrc = typeof document !== "undefined"
&& document.currentScript
&& document.currentScript.src;
let barcodeDetectorPreparePromise = null;
let scanBeepAudioEl = null;
let scanBeepAudioSrc = null;
let scanBeepUnlockAudioEl = null;
let scanBeepUnlocked = false;
let scanBeepUnlocking = false;
let scanBeepGestureUnlockInstalled = false;
/** 极短静音 WAV,仅用于在用户手势内解锁自动播放,不播放真实提示音文件 */
const SILENT_BEEP_UNLOCK_AUDIO = "data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQAAAAA=";
function removeEl(id, uuid) {
try {
let el = document.getElementById(id);
if (uuid && el && el.uuid !== uuid) {
return;
}
document.body.removeChild(el);
} catch (error) {
}
}
function createEl(tagName, id, style, appendChild) {
let el = document.getElementById(id);
if (!el) {
el = document.createElement(tagName);
el.id = id;
el.style = style;
appendChild && document.body.appendChild(el);
}
return el;
}
function stopMediaStream(stream) {
try {
const tracks = stream && stream.getTracks && stream.getTracks();
if (tracks && tracks.length) {
for (let i = 0; i < tracks.length; i++) {
tracks[i].stop();
}
}
} catch (e) {
}
}
function stopActiveWebScan() {
scanWeb.uuid = null;
stopMediaStream(scanWeb.stream);
try {
if (scanWeb.videoEl) {
scanWeb.videoEl.pause && scanWeb.videoEl.pause();
scanWeb.videoEl.srcObject = null;
}
} catch (e) {
}
scanWeb.stream = null;
scanWeb.videoEl = null;
}
function transformPoint(point, width, height, mirrorHorizontal, mirrorVertical, cover) {
let x = point.x;
let y = point.y;
if (cover) {
x = cover.x + point.x * cover.scale;
y = cover.y + point.y * cover.scale;
}
return {
x: mirrorHorizontal ? width - x : x,
y: mirrorVertical ? height - y : y
};
}
function canvasDrawLine(context, width, height, begin, end, color, mirrorHorizontal, mirrorVertical, cover) {
const beginPoint = transformPoint(begin, width, height, mirrorHorizontal, mirrorVertical, cover);
const endPoint = transformPoint(end, width, height, mirrorHorizontal, mirrorVertical, cover);
context.beginPath();
context.moveTo(beginPoint.x, beginPoint.y);
context.lineTo(endPoint.x, endPoint.y);
context.lineWidth = 4;
context.strokeStyle = color;
context.stroke();
}
function isMobile() {
return typeof navigator !== 'undefined'
&& /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent || "");
}
function getBarcodeFormats(scanType) {
let formats = [];
if (!scanType) {
scanType = ["qrCode", "barCode"];
}
if (scanType.includes('qrCode')) {
formats.push('qr_code');
}
if (scanType.includes('barCode')) {
formats.push(
'ean_13',
'ean_8',
'code_128',
'code_39',
'codabar',
'upc_a',
'upc_e',
'itf',
'aztec',
'data_matrix',
'pdf417'
);
}
return formats;
}
function getBarcodeDetectorClass() {
if (typeof BarcodeDetector !== 'undefined') {
return BarcodeDetector;
}
return BarcodeDetectorPonyfill;
}
function getZXingReaderWasmUrl() {
if (currentScriptSrc) {
return new URL(ZXING_READER_WASM_PATH, currentScriptSrc).href;
}
return "./" + ZXING_READER_WASM_PATH;
}
function prepareBarcodeDetector() {
const BarcodeDetectorClass = getBarcodeDetectorClass();
if (typeof BarcodeDetector !== 'undefined' || !prepareZXingModule) {
return Promise.resolve(BarcodeDetectorClass);
}
if (!barcodeDetectorPreparePromise) {
barcodeDetectorPreparePromise = prepareZXingModule({
fireImmediately: true,
overrides: {
locateFile: path => {
if (path && path.indexOf(".wasm") !== -1) {
return getZXingReaderWasmUrl();
}
return path;
}
}
}).then(() => BarcodeDetectorClass);
}
return barcodeDetectorPreparePromise;
}
function createBarcodeDetector(scanType) {
return prepareBarcodeDetector().then(BarcodeDetectorClass => {
if (!BarcodeDetectorClass) {
throw new Error("BarcodeDetector is not supported");
}
const formats = getBarcodeFormats(scanType);
if (BarcodeDetectorClass.getSupportedFormats) {
return BarcodeDetectorClass.getSupportedFormats().then(supportedFormats => {
const supported = formats.filter(format => supportedFormats.indexOf(format) !== -1);
if (!supported.length) {
throw new Error("No supported barcode formats");
}
return new BarcodeDetectorClass({ formats: supported });
});
}
return new BarcodeDetectorClass({ formats });
});
}
function shouldMirrorWebVideo(stream) {
const webScanVideoMirror = getConfig("webScanVideoMirror");
if (typeof webScanVideoMirror === "boolean") {
return webScanVideoMirror;
}
try {
const track = stream && stream.getVideoTracks && stream.getVideoTracks()[0];
const settings = track && track.getSettings && track.getSettings();
if (settings && settings.facingMode === "environment") {
return false;
}
if (settings && settings.facingMode === "user") {
return true;
}
} catch (e) {
}
return !isMobile();
}
function shouldMirrorWebVideoVertical() {
return getConfig("webScanVideoMirrorVertical") === true;
}
function getDefaultWebScanCanvasSizePx() {
if (typeof window === "undefined") {
return { width: 300, height: 300 };
}
if (isMobile()) {
const side = Math.min(window.innerWidth || 300, window.innerHeight || 300);
return {
width: Math.max(1, Math.round(side)),
height: Math.max(1, Math.round(side))
};
}
return { width: 300, height: 300 };
}
function getCanvasDisplaySize(canvasEl, fallbackWidth, fallbackHeight) {
const rect = canvasEl.getBoundingClientRect();
const width = rect.width || parseFloat(canvasEl.style.width) || fallbackWidth;
const height = rect.height || parseFloat(canvasEl.style.height) || fallbackHeight;
return {
width: Math.max(1, Math.round(width)),
height: Math.max(1, Math.round(height))
};
}
function getWebScanCloseButtonPositionStyle(canvasEl) {
const rect = canvasEl.getBoundingClientRect();
const top = Math.max(0, Math.round(rect.top + WEBSCAN_OVERLAY_BUTTON_EDGE));
const left = Math.max(0, Math.round(rect.right - (WEBSCAN_OVERLAY_BUTTON_SIZE + WEBSCAN_OVERLAY_BUTTON_EDGE)));
return "position: fixed; top: " + top + "px; left: " + left + "px;";
}
function getWebScanPickImageButtonPositionStyle(canvasEl) {
const rect = canvasEl.getBoundingClientRect();
const top = Math.max(0, Math.round(rect.top + WEBSCAN_OVERLAY_BUTTON_EDGE + WEBSCAN_OVERLAY_BUTTON_SIZE + WEBSCAN_OVERLAY_BUTTON_GAP));
const left = Math.max(0, Math.round(rect.right - (WEBSCAN_OVERLAY_BUTTON_SIZE + WEBSCAN_OVERLAY_BUTTON_EDGE)));
return "position: fixed; top: " + top + "px; left: " + left + "px;";
}
function updateWebScanCloseButtonStyle(closeButtonEl, canvasEl) {
if (!closeButtonEl) {
return;
}
const customStyle = getConfig("webScanCloseButtonStyle") || "";
closeButtonEl.style.cssText = getDefaultWebScanOverlayButtonStyle()
+ getWebScanCloseButtonPositionStyle(canvasEl)
+ customStyle;
}
function updateWebScanPickImageButtonStyle(pickButtonEl, canvasEl) {
if (!pickButtonEl) {
return;
}
const customStyle = getConfig("webScanPickImageButtonStyle") || "";
pickButtonEl.style.cssText = getDefaultWebScanOverlayButtonStyle()
+ getWebScanPickImageButtonPositionStyle(canvasEl)
+ customStyle;
}
function getCoverDrawOptions(sourceWidth, sourceHeight, targetWidth, targetHeight) {
const scale = Math.max(targetWidth / sourceWidth, targetHeight / sourceHeight);
const width = sourceWidth * scale;
const height = sourceHeight * scale;
return {
scale,
width,
height,
x: (targetWidth - width) / 2,
y: (targetHeight - height) / 2
};
}
function drawBarcode(context, width, height, barcode, mirrorHorizontal, mirrorVertical, cover) {
const cornerPoints = barcode.cornerPoints;
if (cornerPoints && cornerPoints.length) {
for (let i = 0; i < cornerPoints.length; i++) {
canvasDrawLine(context, width, height, cornerPoints[i], cornerPoints[(i + 1) % cornerPoints.length], "#FF3B58", mirrorHorizontal, mirrorVertical, cover);
}
return;
}
if (barcode.boundingBox) {
const rect = barcode.boundingBox;
const points = [
{ x: rect.x, y: rect.y },
{ x: rect.x + rect.width, y: rect.y },
{ x: rect.x + rect.width, y: rect.y + rect.height },
{ x: rect.x, y: rect.y + rect.height }
];
for (let i = 0; i < points.length; i++) {
canvasDrawLine(context, width, height, points[i], points[(i + 1) % points.length], "#FF3B58", mirrorHorizontal, mirrorVertical, cover);
}
}
}
function isScanBeepEnabled() {
return getConfig("scanBeepEnabled") !== false;
}
function getScanBeepAudioSrc() {
return getConfig("scanBeepAudio") || DEFAULT_SCAN_BEEP_AUDIO;
}
function resetScanBeepPlayback(audio) {
try {
audio.pause();
audio.currentTime = 0;
} catch (e) {
}
}
function installScanBeepGestureUnlock() {
if (scanBeepGestureUnlockInstalled || typeof document === "undefined" || !isScanBeepEnabled()) {
return;
}
scanBeepGestureUnlockInstalled = true;
const onGesture = () => {
unlockScanBeep();
};
document.addEventListener("click", onGesture, true);
document.addEventListener("touchstart", onGesture, true);
document.addEventListener("keydown", onGesture, true);
}
export function playScanBeep() {
if (!isScanBeepEnabled()) {
return;
}
const audio = getScanBeepAudio();
if (!audio) {
return;
}
try {
audio.muted = false;
audio.volume = 1;
audio.currentTime = 0;
const playPromise = audio.play();
playPromise && playPromise.catch && playPromise.catch(err => {
if (err && err.name === "NotAllowedError") {
scanBeepUnlocked = false;
}
});
} catch (e) {
}
}
function getScanBeepUnlockAudio() {
if (typeof Audio === "undefined") {
return null;
}
if (!scanBeepUnlockAudioEl) {
scanBeepUnlockAudioEl = new Audio(SILENT_BEEP_UNLOCK_AUDIO);
scanBeepUnlockAudioEl.preload = "auto";
scanBeepUnlockAudioEl.muted = true;
scanBeepUnlockAudioEl.volume = 0;
scanBeepUnlockAudioEl.setAttribute("playsinline", "");
try {
scanBeepUnlockAudioEl.load && scanBeepUnlockAudioEl.load();
} catch (e) {
}
}
return scanBeepUnlockAudioEl;
}
function getScanBeepAudio() {
const audioSrc = getScanBeepAudioSrc();
if (!audioSrc || typeof Audio === 'undefined') {
return null;
}
if (!scanBeepAudioEl || scanBeepAudioSrc !== audioSrc) {
scanBeepAudioSrc = audioSrc;
scanBeepUnlocked = false;
scanBeepAudioEl = new Audio(audioSrc);
scanBeepAudioEl.preload = "auto";
scanBeepAudioEl.setAttribute("playsinline", "");
try {
scanBeepAudioEl.load && scanBeepAudioEl.load();
} catch (e) {
}
}
installScanBeepGestureUnlock();
return scanBeepAudioEl;
}
export function unlockScanBeep() {
if (!isScanBeepEnabled() || scanBeepUnlocked || scanBeepUnlocking) {
return;
}
getScanBeepAudio();
const audio = getScanBeepUnlockAudio();
if (!audio) {
return;
}
scanBeepUnlocking = true;
try {
audio.muted = true;
audio.volume = 0;
audio.currentTime = 0;
const playPromise = audio.play();
const finishUnlock = () => {
resetScanBeepPlayback(audio);
scanBeepUnlocking = false;
scanBeepUnlocked = true;
};
if (!playPromise || !playPromise.then) {
finishUnlock();
return;
}
playPromise.then(finishUnlock).catch(() => {
resetScanBeepPlayback(audio);
scanBeepUnlocking = false;
});
} catch (e) {
scanBeepUnlocking = false;
}
}
export function isSupportWebScan() {
return typeof navigator !== 'undefined'
&& navigator.mediaDevices
&& navigator.mediaDevices.getUserMedia
&& !!getBarcodeDetectorClass()
&& getConfig("webScanEnabled") !== false;
}
export function isSupportImageScan() {
return typeof document !== 'undefined'
&& typeof URL !== 'undefined'
&& !!URL.createObjectURL;
}
export function isWebScanImageFallbackEnabled() {
return isSupportImageScan() && getConfig("webScanImageFallbackOnVideoError") !== false;
}
function getWebScanVideoAccessTimeout() {
const timeout = getConfig("webScanVideoAccessTimeout");
return typeof timeout === "number" && timeout > 0 ? timeout : 10000;
}
function getWebScanVideoReadyTimeout() {
const timeout = getConfig("webScanVideoReadyTimeout");
return typeof timeout === "number" && timeout > 0 ? timeout : 8000;
}
function getUserMediaWithTimeout(constraints, timeoutMs) {
return new Promise((resolve, reject) => {
let settled = false;
const timer = setTimeout(() => {
if (settled) {
return;
}
settled = true;
reject(new Error("getUserMedia timeout"));
}, timeoutMs);
navigator.mediaDevices.getUserMedia(constraints).then(stream => {
if (settled) {
stopMediaStream(stream);
return;
}
settled = true;
clearTimeout(timer);
resolve(stream);
}).catch(err => {
if (settled) {
return;
}
settled = true;
clearTimeout(timer);
reject(err);
});
});
}
function runWebScanImageFallback(onResult, resolve, reject) {
stopActiveWebScan();
unlockScanBeep();
return createBarcodeDetector(getConfig("webScanType")).then(detector => {
return chooseImageFile({ preferCapture: isMobile() }).then(file => {
if (!file) {
return Promise.reject({ cancel: 1 });
}
return detectImageFile(detector, file);
}).then(code => {
if (code && code.rawValue) {
if (!onResult || !onResult(code.rawValue)) {
return Promise.reject({ cancel: 1 });
}
scanWeb.finish = true;
resolve({
result: code.rawValue
});
return;
}
return Promise.reject({
success: false,
error: "未识别到二维码或条形码",
imageFallbackUsed: true
});
});
}).catch(err => {
if (err && err.cancel) {
reject({ cancel: 1, imageFallbackUsed: true });
return;
}
if (err && err.result) {
reject(Object.assign({ imageFallbackUsed: true }, err));
return;
}
reject({
imageFallbackUsed: true,
error: err && err.error ? err.error : err
});
});
}
function tryWebScanImageFallback(onResult, resolve, reject, state) {
if (!isWebScanImageFallbackEnabled()) {
return false;
}
if (state && state.imageFallbackStarted) {
return false;
}
if (state) {
state.imageFallbackStarted = true;
}
runWebScanImageFallback(onResult, resolve, reject);
return true;
}
export function stopScanForWeb() {
return Promise.resolve().then(() => {
stopActiveWebScan();
})
}
function chooseImageFile(options) {
options = options || {};
return new Promise(resolve => {
const input = document.createElement("input");
input.type = "file";
input.accept = "image/*";
if (options.preferCapture) {
input.setAttribute("capture", options.captureMode || "environment");
}
input.style.display = "none";
let finished = false;
let finish = file => {
if (finished) {
return;
}
finished = true;
removeEl("__webscan_image_input__");
resolve(file);
};
input.id = "__webscan_image_input__";
input.onchange = () => {
unlockScanBeep();
finish(input.files && input.files[0]);
};
input.oncancel = () => {
finish(null);
};
document.body.appendChild(input);
input.click();
});
}
function detectImageFile(detector, file) {
if (!file) {
return Promise.resolve(null);
}
if (typeof createImageBitmap !== 'undefined') {
return createImageBitmap(file).then(image => {
return detector.detect(image).then(barcodes => {
image.close && image.close();
return barcodes && barcodes[0];
}).catch(err => {
image.close && image.close();
throw err;
});
});
}
return new Promise((resolve, reject) => {
const image = new Image();
const url = URL.createObjectURL(file);
image.onload = () => {
detector.detect(image).then(barcodes => {
URL.revokeObjectURL(url);
resolve(barcodes && barcodes[0]);
}).catch(err => {
URL.revokeObjectURL(url);
reject(err);
});
};
image.onerror = err => {
URL.revokeObjectURL(url);
reject(err);
};
image.src = url;
});
}
export function startScanForImage() {
return chooseImageFile().then(file => {
if (!file) {
return null;
}
return createBarcodeDetector(getConfig("webScanType")).then(detector => {
return detectImageFile(detector, file);
});
}).then(code => {
if (code && code.rawValue) {
return {
result: code.rawValue
};
}
return {
success: false,
error: "未识别到二维码或条形码"
};
});
}
export function startScanForWeb(onResult) {
let currentUuid = null;
const fallbackState = { imageFallbackStarted: false };
return new Promise((resolve, reject) => {
try {
stopActiveWebScan();
scanWeb.uuid = createUUID();
scanWeb.finish = false;
currentUuid = scanWeb.uuid;
let canvasStyle = getConfig("webScanCanvasStyle");
let canvasClass = getConfig("webScanCanvasClass");
let videoEl = createEl("video",
"__webscan_video__",
"display: none", false);
let canvasEnabled = getConfig("webScanCanvasEnabled") !== false;
let canvasDisplay = "";
let useDefaultCanvasLayout = !canvasStyle && !canvasClass;
let defaultCanvasPx = useDefaultCanvasLayout ? getDefaultWebScanCanvasSizePx() : { width: 300, height: 300 };
let canvasFallbackWidth = defaultCanvasPx.width;
let canvasFallbackHeight = defaultCanvasPx.height;
let canvasBaseStyle = canvasStyle || (!!canvasClass ? "" : "position: fixed; width: " + defaultCanvasPx.width + "px; height: " + defaultCanvasPx.height + "px; top: 0; left: 0; z-index: 9999;");
let canvasEl = createEl("canvas",
"__webscan_canvas__",
canvasBaseStyle + " display: none;", true);
canvasDisplay = canvasEl.style.display;
canvasEl.style.cssText = canvasBaseStyle;
canvasEl.className = canvasClass || "";
canvasDisplay = canvasEl.style.display;
let canvasDisplaySize = getCanvasDisplaySize(canvasEl, canvasFallbackWidth, canvasFallbackHeight);
canvasEl.style.display = "none";
let closeButtonEl = null;
let pickImageButtonEl = null;
if (canvasEnabled) {
closeButtonEl = createEl("button", WEBSCAN_CLOSE_BUTTON_ID, "display: none;", true);
closeButtonEl.type = "button";
closeButtonEl.className = getConfig("webScanCloseButtonClass") || "";
closeButtonEl.innerHTML = WEBSCAN_CLOSE_ICON_SVG;
closeButtonEl.setAttribute("aria-label", "close");
closeButtonEl.onclick = event => {
event.preventDefault && event.preventDefault();
event.stopPropagation && event.stopPropagation();
if (scanWeb.uuid === currentUuid) {
scanWeb.uuid = null;
}
};
closeButtonEl.uuid = scanWeb.uuid;
if (isSupportImageScan()) {
pickImageButtonEl = createEl("button", WEBSCAN_PICK_IMAGE_BUTTON_ID, "display: none;", true);
pickImageButtonEl.type = "button";
pickImageButtonEl.className = getConfig("webScanPickImageButtonClass") || "";
pickImageButtonEl.innerHTML = WEBSCAN_PICK_IMAGE_ICON_SVG;
pickImageButtonEl.setAttribute("aria-label", "pick image");
pickImageButtonEl.uuid = scanWeb.uuid;
}
}
let context = canvasEl.getContext("2d");
videoEl.width = 300;
videoEl.height = 300;
videoEl.uuid = scanWeb.uuid;
canvasEl.uuid = scanWeb.uuid;
createBarcodeDetector(getConfig("webScanType")).then(detector => {
return getUserMediaWithTimeout({
video: {
facingMode: "environment"
}
}, getWebScanVideoAccessTimeout()).then(stream => {
return {
detector,
stream
};
});
}).then(function (options) {
const detector = options.detector;
const stream = options.stream;
if (scanWeb.uuid !== currentUuid) {
stopMediaStream(stream);
reject({ cancel: 1 });
return;
}
scanWeb.stream = stream;
scanWeb.videoEl = videoEl;
const mirrorVideo = shouldMirrorWebVideo(stream);
const mirrorVideoVertical = shouldMirrorWebVideoVertical();
videoEl.srcObject = stream;
videoEl.setAttribute("playsinline", true); // iOS使用
canvasEl.style.display = "none";
let detecting = false;
let displayed = false;
let closed = false;
let videoReadyTimer = null;
const clearVideoReadyTimer = () => {
if (videoReadyTimer) {
clearTimeout(videoReadyTimer);
videoReadyTimer = null;
}
};
const scheduleVideoReadyTimeout = () => {
clearVideoReadyTimer();
videoReadyTimer = setTimeout(() => {
if (scanWeb.uuid !== currentUuid || closed || scanWeb.finish) {
return;
}
if (videoEl.readyState >= videoEl.HAVE_ENOUGH_DATA) {
return;
}
closed = true;
close();
tryWebScanImageFallback(onResult, resolve, reject, fallbackState);
}, getWebScanVideoReadyTimeout());
};
scheduleVideoReadyTimeout();
let close = () => {
if (closed) {
return;
}
closed = true;
clearVideoReadyTimer();
stopMediaStream(stream);
if (scanWeb.uuid === currentUuid || scanWeb.stream === stream) {
scanWeb.stream = null;
scanWeb.videoEl = null;
}
try {
videoEl.pause && videoEl.pause();
videoEl.srcObject = null;
} catch (_e) { }
if (closeButtonEl) {
closeButtonEl.style.display = "none";
}
if (pickImageButtonEl) {
pickImageButtonEl.style.display = "none";
}
};
const videoPlayPromise = videoEl.play && videoEl.play();
if (videoPlayPromise && videoPlayPromise.catch) {
videoPlayPromise.catch(() => {
if (scanWeb.uuid !== currentUuid || closed || scanWeb.finish) {
return;
}
close();
tryWebScanImageFallback(onResult, resolve, reject, fallbackState);
});
}
if (pickImageButtonEl) {
pickImageButtonEl.onclick = event => {
event.preventDefault && event.preventDefault();
event.stopPropagation && event.stopPropagation();
if (scanWeb.uuid !== currentUuid || closed) {
return;
}
unlockScanBeep();
chooseImageFile().then(file => {
if (!file || scanWeb.uuid !== currentUuid || closed) {
return;
}
detectImageFile(detector, file).then(code => {
if (!code || !code.rawValue || scanWeb.uuid !== currentUuid || closed) {
return;
}
if (!onResult || !onResult(code.rawValue)) {
return;
}
scanWeb.uuid = null;
scanWeb.finish = true;
close();
resolve({
result: code.rawValue
});
}).catch(() => {
});
});
};
}
let tick = () => {
try {
if (videoEl.readyState === videoEl.HAVE_ENOUGH_DATA && !detecting) {
clearVideoReadyTimer();
canvasEl.width = canvasDisplaySize.width;
canvasEl.height = canvasDisplaySize.height;
const cover = getCoverDrawOptions(videoEl.videoWidth, videoEl.videoHeight, canvasEl.width, canvasEl.height);
context.setTransform(
mirrorVideo ? -1 : 1,
0,
0,
mirrorVideoVertical ? -1 : 1,
mirrorVideo ? canvasEl.width : 0,
mirrorVideoVertical ? canvasEl.height : 0
);
context.drawImage(videoEl, cover.x, cover.y, cover.width, cover.height);
context.setTransform(1, 0, 0, 1, 0, 0);
if (canvasEnabled && !displayed) {
displayed = true;
canvasEl.style.display = canvasDisplay || "";
canvasDisplaySize = getCanvasDisplaySize(canvasEl, canvasFallbackWidth, canvasFallbackHeight);
updateWebScanCloseButtonStyle(closeButtonEl, canvasEl);
closeButtonEl && (closeButtonEl.style.display = "flex");
if (pickImageButtonEl) {
updateWebScanPickImageButtonStyle(pickImageButtonEl, canvasEl);
pickImageButtonEl.style.display = "flex";
}
}
detecting = true;
detector.detect(videoEl).then(barcodes => {
const code = barcodes && barcodes[0];
if (code && code.rawValue && scanWeb.uuid == currentUuid) {
if (!onResult || !onResult(code.rawValue)) {
return;
}
drawBarcode(context, canvasEl.width, canvasEl.height, code, mirrorVideo, mirrorVideoVertical, cover);
scanWeb.uuid = null;
scanWeb.finish = true;
close();
resolve({
result: code.rawValue
})
}
}).catch(() => {
}).finally(() => {
detecting = false;
});
}
} catch (e) {
detecting = false;
}
if (scanWeb.uuid == currentUuid) {
requestAnimationFrame(() => {
tick()
});
} else {
if (!scanWeb.finish) {
reject({ cancel: 1 });
scanWeb.finish = true;
}
close();
}
if (scanWeb.finish) {
close();
}
};
requestAnimationFrame(() => {
tick();
});
}).catch(err => {
if (!tryWebScanImageFallback(onResult, resolve, reject, fallbackState)) {
reject({ error: err });
}
});
} catch (e) {
reject({ error: e });
}
}).finally(() => {
removeEl("__webscan_video__", currentUuid);
removeEl("__webscan_canvas__", currentUuid);
removeEl(WEBSCAN_CLOSE_BUTTON_ID, currentUuid);
removeEl(WEBSCAN_PICK_IMAGE_BUTTON_ID, currentUuid);
})
}