diff --git a/README.md b/README.md index 73ab6b0..44a4a29 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ Supported options are: |---|---| | `onDecodeError` | Handler to be invoked on decoding errors. The default is `QrScanner._onDecodeError`. | | `preferredCamera` | Preference for the camera to be used. The preference can be either a device id as returned by `listCameras` or a facing mode specified as `'environment'` or `'user'`. The default is `'environment'`. Note that there is no guarantee that the preference can actually be fulfilled. | -| `maxScansPerSecond` | This option can be used to throttle the scans for less battery consumption. The default is 25. | +| `maxScansPerSecond` | This option can be used to throttle the scans for less battery consumption. The default is 25. [If supported by the browser](https://caniuse.com/mdn-api_htmlvideoelement_requestvideoframecallback), the scan rate is never higher than the camera's frame rate to avoid unnecessary duplicate scans on the same frame. | | `calculateScanRegion` | A method that determines a region to which scanning should be restricted as a performance improvement. This region can optionally also be scaled down before performing the scan as an additional performance improvement. The region is specified as `x`, `y`, `width` and `height`; the dimensions for the downscaled region as `downScaledWidth` and `downScaledHeight`. Note that the aspect ratio between `width` and `height` and `downScaledWidth` and `downScaledHeight` should remain the same. By default, the scan region is restricted to a centered square of two thirds of the video width or height, whichever is smaller, and scaled down to a 400x400 square. | | `highlightScanRegion` | Set this option to `true` for rendering an outline around the scan region on the video stream. This uses an absolutely positioned `div` that covers the scan region. This `div` can either be supplied as option `overlay`, see below, or automatically created and then accessed via `qrScanner.$overlay`. It can be freely styled via CSS, e.g. by setting an outline, border, background color, etc. See the [demo](https://nimiq.github.io/qr-scanner/demo/) for examples. | | `highlightCodeOutline` | Set this option to `true` for rendering an outline around detected QR codes. This uses an absolutely positioned `div` on which an SVG for rendering the outline will be placed. This `div` can either be supplied as option `overlay`, see below, or be accessed via `qrScanner.$overlay`. The SVG can be freely styled via CSS, e.g. by setting the fill color, stroke color, stroke width, etc. See the [demo](https://nimiq.github.io/qr-scanner/demo/) for examples. For more special needs, you can also use the `cornerPoints` directly, see below, for rendering an outline or the points yourself. | diff --git a/qr-scanner.legacy.min.js b/qr-scanner.legacy.min.js index 90dfd42..86fd38b 100644 --- a/qr-scanner.legacy.min.js +++ b/qr-scanner.legacy.min.js @@ -38,13 +38,13 @@ return n?{data:k.rawValue,cornerPoints:h._convertPoints(k.cornerPoints,l)}:k.raw this._calculateScanRegion(this.$video);this._updateOverlay()}_onVisibilityChange(){document.hidden?this.pause():this._active&&this.start()}_calculateScanRegion(a){let b=Math.round(2/3*Math.min(a.videoWidth,a.videoHeight));return{x:Math.round((a.videoWidth-b)/2),y:Math.round((a.videoHeight-b)/2),width:b,height:b,downScaledWidth:this._legacyCanvasSize,downScaledHeight:this._legacyCanvasSize}}_updateOverlay(){requestAnimationFrame(()=>{if(this.$overlay){var a=this.$video,b=a.videoWidth,d=a.videoHeight, e=a.offsetWidth,f=a.offsetHeight,g=a.offsetLeft,l=a.offsetTop,n=window.getComputedStyle(a),t=n.objectFit,m=b/d,r=e/f;switch(t){case "none":var k=b;var p=d;break;case "fill":k=e;p=f;break;default:("cover"===t?m>r:m{const q=parseFloat(u);return u.endsWith("%")?(y?f-p:e-k)*q/100:q});n=this._scanRegion.width||b;r=this._scanRegion.height||d;t=this._scanRegion.x||0;var v=this._scanRegion.y|| 0;m=this.$overlay.style;m.width=`${n/b*k}px`;m.height=`${r/d*p}px`;m.top=`${l+x+v/d*p}px`;d=/scaleX\(-1\)/.test(a.style.transform);m.left=`${g+(d?e-w-k:w)+(d?b-t-n:t)/b*k}px`;m.transform=a.style.transform}})}static _convertPoints(a,b){if(!b)return a;let d=b.x||0,e=b.y||0,f=b.width&&b.downScaledWidth?b.width/b.downScaledWidth:1;b=b.height&&b.downScaledHeight?b.height/b.downScaledHeight:1;for(let g of a)g.x=g.x*f+d,g.y=g.y*b+e;return a}_scanFrame(){!this._active||this.$video.paused||this.$video.ended|| -requestAnimationFrame(()=>{const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){if(!(1>=a.$video.readyState)){var b=Date.now()-a._lastScanTimestamp,d=1E3/a._maxScansPerSecond;bsetTimeout(f,d-b)));a._lastScanTimestamp=Date.now();try{var e=yield h.scanImage(a.$video,{scanRegion:a._scanRegion,qrEngine:a._qrEnginePromise,canvas:a.$canvas})}catch(f){if(!a._active)return;(f.message||f).includes("service unavailable")&&(a._qrEnginePromise=h.createQrEngine());a._onDecodeError(f)}e? -(a._onDecode?a._onDecode(e):a._legacyOnDecode&&a._legacyOnDecode(e.data),a.$codeOutlineHighlight&&(clearTimeout(a._codeOutlineHighlightRemovalTimeout),a._codeOutlineHighlightRemovalTimeout=void 0,a.$codeOutlineHighlight.setAttribute("viewBox",`${a._scanRegion.x||0} `+`${a._scanRegion.y||0} `+`${a._scanRegion.width||a.$video.videoWidth} `+`${a._scanRegion.height||a.$video.videoHeight}`),a.$codeOutlineHighlight.firstElementChild.setAttribute("points",e.cornerPoints.map(({x:f,y:g})=>`${f},${g}`).join(" ")), -a.$codeOutlineHighlight.style.display="")):a.$codeOutlineHighlight&&!a._codeOutlineHighlightRemovalTimeout&&(a._codeOutlineHighlightRemovalTimeout=setTimeout(()=>a.$codeOutlineHighlight.style.display="none",100))}a._scanFrame()})})}_onDecodeError(a){a!==h.NO_QR_CODE_FOUND&&console.log(a)}_getCameraStream(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){if(!navigator.mediaDevices)throw"Camera not found.";let b=/^(environment|user)$/.test(a._preferredCamera)?"facingMode":"deviceId", -d=[{width:{min:1024}},{width:{min:768}},{}],e=d.map(f=>Object.assign({},f,{[b]:{exact:a._preferredCamera}}));for(let f of[...e,...d])try{let g=yield navigator.mediaDevices.getUserMedia({video:f,audio:!1}),l=a._getFacingMode(g)||(f.facingMode?a._preferredCamera:"environment"===a._preferredCamera?"user":"environment");return{stream:g,facingMode:l}}catch(g){}throw"Camera not found.";})}_restartVideoStream(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){let b=a._paused;(yield a.pause(!0))&& -!b&&a._active&&(yield a.start())})}static _stopVideoStream(a){for(let b of a.getTracks())b.stop(),a.removeTrack(b)}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===a?-1:1)+")"}_getFacingMode(a){return(a=a.getVideoTracks()[0])?/rear|back|environment/i.test(a.label)?"environment":/front|user|face/i.test(a.label)?"user":null:null}static _drawToCanvas(a,b,d,e=!1){d=d||document.createElement("canvas");let f=b&&b.x?b.x:0,g=b&&b.y?b.y:0,l=b&&b.width?b.width:a.videoWidth||a.width,n=b&&b.height? -b.height:a.videoHeight||a.height;e||(e=b&&b.downScaledWidth?b.downScaledWidth:l,b=b&&b.downScaledHeight?b.downScaledHeight:n,d.width!==e&&(d.width=e),d.height!==b&&(d.height=b));b=d.getContext("2d",{alpha:!1});b.imageSmoothingEnabled=!1;b.drawImage(a,f,g,l,n,0,0,d.width,d.height);return[d,b]}static _loadImage(a){return c.asyncExecutePromiseGeneratorFunction(function*(){if(a instanceof Image)return yield h._awaitImageLoad(a),a;if(a instanceof HTMLVideoElement||a instanceof HTMLCanvasElement||a instanceof -SVGImageElement||"OffscreenCanvas"in window&&a instanceof OffscreenCanvas||"ImageBitmap"in window&&a instanceof ImageBitmap)return a;if(a instanceof File||a instanceof Blob||a instanceof URL||"string"===typeof a){let b=new Image;b.src=a instanceof File||a instanceof Blob?URL.createObjectURL(a):a.toString();try{return yield h._awaitImageLoad(b),b}finally{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src)}}else throw"Unsupported image type.";})}static _awaitImageLoad(a){return c.asyncExecutePromiseGeneratorFunction(function*(){a.complete&& +("requestVideoFrameCallback"in this.$video?this.$video.requestVideoFrameCallback.bind(this.$video):requestAnimationFrame)(()=>{const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){if(!(1>=a.$video.readyState)){var b=Date.now()-a._lastScanTimestamp,d=1E3/a._maxScansPerSecond;bsetTimeout(f,d-b)));a._lastScanTimestamp=Date.now();try{var e=yield h.scanImage(a.$video,{scanRegion:a._scanRegion,qrEngine:a._qrEnginePromise,canvas:a.$canvas})}catch(f){if(!a._active)return; +(f.message||f).includes("service unavailable")&&(a._qrEnginePromise=h.createQrEngine());a._onDecodeError(f)}e?(a._onDecode?a._onDecode(e):a._legacyOnDecode&&a._legacyOnDecode(e.data),a.$codeOutlineHighlight&&(clearTimeout(a._codeOutlineHighlightRemovalTimeout),a._codeOutlineHighlightRemovalTimeout=void 0,a.$codeOutlineHighlight.setAttribute("viewBox",`${a._scanRegion.x||0} `+`${a._scanRegion.y||0} `+`${a._scanRegion.width||a.$video.videoWidth} `+`${a._scanRegion.height||a.$video.videoHeight}`),a.$codeOutlineHighlight.firstElementChild.setAttribute("points", +e.cornerPoints.map(({x:f,y:g})=>`${f},${g}`).join(" ")),a.$codeOutlineHighlight.style.display="")):a.$codeOutlineHighlight&&!a._codeOutlineHighlightRemovalTimeout&&(a._codeOutlineHighlightRemovalTimeout=setTimeout(()=>a.$codeOutlineHighlight.style.display="none",100))}a._scanFrame()})})}_onDecodeError(a){a!==h.NO_QR_CODE_FOUND&&console.log(a)}_getCameraStream(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){if(!navigator.mediaDevices)throw"Camera not found.";let b=/^(environment|user)$/.test(a._preferredCamera)? +"facingMode":"deviceId",d=[{width:{min:1024}},{width:{min:768}},{}],e=d.map(f=>Object.assign({},f,{[b]:{exact:a._preferredCamera}}));for(let f of[...e,...d])try{let g=yield navigator.mediaDevices.getUserMedia({video:f,audio:!1}),l=a._getFacingMode(g)||(f.facingMode?a._preferredCamera:"environment"===a._preferredCamera?"user":"environment");return{stream:g,facingMode:l}}catch(g){}throw"Camera not found.";})}_restartVideoStream(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){let b= +a._paused;(yield a.pause(!0))&&!b&&a._active&&(yield a.start())})}static _stopVideoStream(a){for(let b of a.getTracks())b.stop(),a.removeTrack(b)}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===a?-1:1)+")"}_getFacingMode(a){return(a=a.getVideoTracks()[0])?/rear|back|environment/i.test(a.label)?"environment":/front|user|face/i.test(a.label)?"user":null:null}static _drawToCanvas(a,b,d,e=!1){d=d||document.createElement("canvas");let f=b&&b.x?b.x:0,g=b&&b.y?b.y:0,l=b&&b.width?b.width: +a.videoWidth||a.width,n=b&&b.height?b.height:a.videoHeight||a.height;e||(e=b&&b.downScaledWidth?b.downScaledWidth:l,b=b&&b.downScaledHeight?b.downScaledHeight:n,d.width!==e&&(d.width=e),d.height!==b&&(d.height=b));b=d.getContext("2d",{alpha:!1});b.imageSmoothingEnabled=!1;b.drawImage(a,f,g,l,n,0,0,d.width,d.height);return[d,b]}static _loadImage(a){return c.asyncExecutePromiseGeneratorFunction(function*(){if(a instanceof Image)return yield h._awaitImageLoad(a),a;if(a instanceof HTMLVideoElement||a instanceof +HTMLCanvasElement||a instanceof SVGImageElement||"OffscreenCanvas"in window&&a instanceof OffscreenCanvas||"ImageBitmap"in window&&a instanceof ImageBitmap)return a;if(a instanceof File||a instanceof Blob||a instanceof URL||"string"===typeof a){let b=new Image;b.src=a instanceof File||a instanceof Blob?URL.createObjectURL(a):a.toString();try{return yield h._awaitImageLoad(b),b}finally{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src)}}else throw"Unsupported image type.";})}static _awaitImageLoad(a){return c.asyncExecutePromiseGeneratorFunction(function*(){a.complete&& 0!==a.naturalWidth||(yield new Promise((b,d)=>{let e=f=>{a.removeEventListener("load",e);a.removeEventListener("error",e);f instanceof ErrorEvent?d("Image load error"):b()};a.addEventListener("load",e);a.addEventListener("error",e)}))})}static _postWorkerMessage(a,b,d){return c.asyncExecutePromiseGeneratorFunction(function*(){let e=yield a;e instanceof Worker&&e.postMessage({type:b,data:d})})}}h.DEFAULT_CANVAS_SIZE=400;h.NO_QR_CODE_FOUND="No QR code found";h.WORKER_PATH="qr-scanner-worker.min.js"; export default h //# sourceMappingURL=qr-scanner.legacy.min.js.map diff --git a/qr-scanner.min.js b/qr-scanner.min.js index fac4a1a..865b096 100644 --- a/qr-scanner.min.js +++ b/qr-scanner.min.js @@ -17,13 +17,13 @@ cornerPoints:f._convertPoints(g.cornerPoints,m)}:g.rawValue}catch(g){throw`Scann a)}static async createQrEngine(a=f.WORKER_PATH){return"BarcodeDetector"in window&&BarcodeDetector.getSupportedFormats&&(await BarcodeDetector.getSupportedFormats()).includes("qr_code")?new BarcodeDetector({formats:["qr_code"]}):new Worker(a)}_onPlay(){this._scanRegion=this._calculateScanRegion(this.$video);this._updateOverlay();this.$overlay&&(this.$overlay.style.display="");this._scanFrame()}_onLoadedMetaData(){this._scanRegion=this._calculateScanRegion(this.$video);this._updateOverlay()}_onVisibilityChange(){document.hidden? this.pause():this._active&&this.start()}_calculateScanRegion(a){let b=Math.round(2/3*Math.min(a.videoWidth,a.videoHeight));return{x:Math.round((a.videoWidth-b)/2),y:Math.round((a.videoHeight-b)/2),width:b,height:b,downScaledWidth:this._legacyCanvasSize,downScaledHeight:this._legacyCanvasSize}}_updateOverlay(){requestAnimationFrame(()=>{if(this.$overlay){var a=this.$video,b=a.videoWidth,c=a.videoHeight,d=a.offsetWidth,e=a.offsetHeight,h=a.offsetLeft,m=a.offsetTop,n=window.getComputedStyle(a),q=n.objectFit, k=b/c,g=d/e;switch(q){case "none":var l=b;var p=c;break;case "fill":l=d;p=e;break;default:("cover"===q?k>g:k{const x=parseFloat(w);return w.endsWith("%")?(r?e-p:d-l)*x/100:x});n=this._scanRegion.width||b;g=this._scanRegion.height||c;q=this._scanRegion.x||0;var t=this._scanRegion.y||0;k=this.$overlay.style;k.width=`${n/b*l}px`;k.height=`${g/c*p}px`;k.top=`${m+v+t/c*p}px`; -c=/scaleX\(-1\)/.test(a.style.transform);k.left=`${h+(c?d-u-l:u)+(c?b-q-n:q)/b*l}px`;k.transform=a.style.transform}})}static _convertPoints(a,b){if(!b)return a;let c=b.x||0,d=b.y||0,e=b.width&&b.downScaledWidth?b.width/b.downScaledWidth:1;b=b.height&&b.downScaledHeight?b.height/b.downScaledHeight:1;for(let h of a)h.x=h.x*e+c,h.y=h.y*b+d;return a}_scanFrame(){!this._active||this.$video.paused||this.$video.ended||requestAnimationFrame(async()=>{if(!(1>=this.$video.readyState)){var a=Date.now()-this._lastScanTimestamp, -b=1E3/this._maxScansPerSecond;asetTimeout(d,b-a));this._lastScanTimestamp=Date.now();try{var c=await f.scanImage(this.$video,{scanRegion:this._scanRegion,qrEngine:this._qrEnginePromise,canvas:this.$canvas})}catch(d){if(!this._active)return;(d.message||d).includes("service unavailable")&&(this._qrEnginePromise=f.createQrEngine());this._onDecodeError(d)}c?(this._onDecode?this._onDecode(c):this._legacyOnDecode&&this._legacyOnDecode(c.data),this.$codeOutlineHighlight&&(clearTimeout(this._codeOutlineHighlightRemovalTimeout), -this._codeOutlineHighlightRemovalTimeout=void 0,this.$codeOutlineHighlight.setAttribute("viewBox",`${this._scanRegion.x||0} `+`${this._scanRegion.y||0} `+`${this._scanRegion.width||this.$video.videoWidth} `+`${this._scanRegion.height||this.$video.videoHeight}`),this.$codeOutlineHighlight.firstElementChild.setAttribute("points",c.cornerPoints.map(({x:d,y:e})=>`${d},${e}`).join(" ")),this.$codeOutlineHighlight.style.display="")):this.$codeOutlineHighlight&&!this._codeOutlineHighlightRemovalTimeout&& -(this._codeOutlineHighlightRemovalTimeout=setTimeout(()=>this.$codeOutlineHighlight.style.display="none",100))}this._scanFrame()})}_onDecodeError(a){a!==f.NO_QR_CODE_FOUND&&console.log(a)}async _getCameraStream(){if(!navigator.mediaDevices)throw"Camera not found.";let a=/^(environment|user)$/.test(this._preferredCamera)?"facingMode":"deviceId",b=[{width:{min:1024}},{width:{min:768}},{}],c=b.map(d=>Object.assign({},d,{[a]:{exact:this._preferredCamera}}));for(let d of[...c,...b])try{let e=await navigator.mediaDevices.getUserMedia({video:d, -audio:!1}),h=this._getFacingMode(e)||(d.facingMode?this._preferredCamera:"environment"===this._preferredCamera?"user":"environment");return{stream:e,facingMode:h}}catch(e){}throw"Camera not found.";}async _restartVideoStream(){let a=this._paused;await this.pause(!0)&&!a&&this._active&&await this.start()}static _stopVideoStream(a){for(let b of a.getTracks())b.stop(),a.removeTrack(b)}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===a?-1:1)+")"}_getFacingMode(a){return(a=a.getVideoTracks()[0])? -/rear|back|environment/i.test(a.label)?"environment":/front|user|face/i.test(a.label)?"user":null:null}static _drawToCanvas(a,b,c,d=!1){c=c||document.createElement("canvas");let e=b&&b.x?b.x:0,h=b&&b.y?b.y:0,m=b&&b.width?b.width:a.videoWidth||a.width,n=b&&b.height?b.height:a.videoHeight||a.height;d||(d=b&&b.downScaledWidth?b.downScaledWidth:m,b=b&&b.downScaledHeight?b.downScaledHeight:n,c.width!==d&&(c.width=d),c.height!==b&&(c.height=b));b=c.getContext("2d",{alpha:!1});b.imageSmoothingEnabled=!1; -b.drawImage(a,e,h,m,n,0,0,c.width,c.height);return[c,b]}static async _loadImage(a){if(a instanceof Image)return await f._awaitImageLoad(a),a;if(a instanceof HTMLVideoElement||a instanceof HTMLCanvasElement||a instanceof SVGImageElement||"OffscreenCanvas"in window&&a instanceof OffscreenCanvas||"ImageBitmap"in window&&a instanceof ImageBitmap)return a;if(a instanceof File||a instanceof Blob||a instanceof URL||"string"===typeof a){let b=new Image;b.src=a instanceof File||a instanceof Blob?URL.createObjectURL(a): -a.toString();try{return await f._awaitImageLoad(b),b}finally{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src)}}else throw"Unsupported image type.";}static async _awaitImageLoad(a){a.complete&&0!==a.naturalWidth||await new Promise((b,c)=>{let d=e=>{a.removeEventListener("load",d);a.removeEventListener("error",d);e instanceof ErrorEvent?c("Image load error"):b()};a.addEventListener("load",d);a.addEventListener("error",d)})}static async _postWorkerMessage(a,b,c){a=await a;a instanceof -Worker&&a.postMessage({type:b,data:c})}}f.DEFAULT_CANVAS_SIZE=400;f.NO_QR_CODE_FOUND="No QR code found";f.WORKER_PATH="qr-scanner-worker.min.js";export default f +c=/scaleX\(-1\)/.test(a.style.transform);k.left=`${h+(c?d-u-l:u)+(c?b-q-n:q)/b*l}px`;k.transform=a.style.transform}})}static _convertPoints(a,b){if(!b)return a;let c=b.x||0,d=b.y||0,e=b.width&&b.downScaledWidth?b.width/b.downScaledWidth:1;b=b.height&&b.downScaledHeight?b.height/b.downScaledHeight:1;for(let h of a)h.x=h.x*e+c,h.y=h.y*b+d;return a}_scanFrame(){!this._active||this.$video.paused||this.$video.ended||("requestVideoFrameCallback"in this.$video?this.$video.requestVideoFrameCallback.bind(this.$video): +requestAnimationFrame)(async()=>{if(!(1>=this.$video.readyState)){var a=Date.now()-this._lastScanTimestamp,b=1E3/this._maxScansPerSecond;asetTimeout(d,b-a));this._lastScanTimestamp=Date.now();try{var c=await f.scanImage(this.$video,{scanRegion:this._scanRegion,qrEngine:this._qrEnginePromise,canvas:this.$canvas})}catch(d){if(!this._active)return;(d.message||d).includes("service unavailable")&&(this._qrEnginePromise=f.createQrEngine());this._onDecodeError(d)}c?(this._onDecode? +this._onDecode(c):this._legacyOnDecode&&this._legacyOnDecode(c.data),this.$codeOutlineHighlight&&(clearTimeout(this._codeOutlineHighlightRemovalTimeout),this._codeOutlineHighlightRemovalTimeout=void 0,this.$codeOutlineHighlight.setAttribute("viewBox",`${this._scanRegion.x||0} `+`${this._scanRegion.y||0} `+`${this._scanRegion.width||this.$video.videoWidth} `+`${this._scanRegion.height||this.$video.videoHeight}`),this.$codeOutlineHighlight.firstElementChild.setAttribute("points",c.cornerPoints.map(({x:d, +y:e})=>`${d},${e}`).join(" ")),this.$codeOutlineHighlight.style.display="")):this.$codeOutlineHighlight&&!this._codeOutlineHighlightRemovalTimeout&&(this._codeOutlineHighlightRemovalTimeout=setTimeout(()=>this.$codeOutlineHighlight.style.display="none",100))}this._scanFrame()})}_onDecodeError(a){a!==f.NO_QR_CODE_FOUND&&console.log(a)}async _getCameraStream(){if(!navigator.mediaDevices)throw"Camera not found.";let a=/^(environment|user)$/.test(this._preferredCamera)?"facingMode":"deviceId",b=[{width:{min:1024}}, +{width:{min:768}},{}],c=b.map(d=>Object.assign({},d,{[a]:{exact:this._preferredCamera}}));for(let d of[...c,...b])try{let e=await navigator.mediaDevices.getUserMedia({video:d,audio:!1}),h=this._getFacingMode(e)||(d.facingMode?this._preferredCamera:"environment"===this._preferredCamera?"user":"environment");return{stream:e,facingMode:h}}catch(e){}throw"Camera not found.";}async _restartVideoStream(){let a=this._paused;await this.pause(!0)&&!a&&this._active&&await this.start()}static _stopVideoStream(a){for(let b of a.getTracks())b.stop(), +a.removeTrack(b)}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===a?-1:1)+")"}_getFacingMode(a){return(a=a.getVideoTracks()[0])?/rear|back|environment/i.test(a.label)?"environment":/front|user|face/i.test(a.label)?"user":null:null}static _drawToCanvas(a,b,c,d=!1){c=c||document.createElement("canvas");let e=b&&b.x?b.x:0,h=b&&b.y?b.y:0,m=b&&b.width?b.width:a.videoWidth||a.width,n=b&&b.height?b.height:a.videoHeight||a.height;d||(d=b&&b.downScaledWidth?b.downScaledWidth:m,b=b&&b.downScaledHeight? +b.downScaledHeight:n,c.width!==d&&(c.width=d),c.height!==b&&(c.height=b));b=c.getContext("2d",{alpha:!1});b.imageSmoothingEnabled=!1;b.drawImage(a,e,h,m,n,0,0,c.width,c.height);return[c,b]}static async _loadImage(a){if(a instanceof Image)return await f._awaitImageLoad(a),a;if(a instanceof HTMLVideoElement||a instanceof HTMLCanvasElement||a instanceof SVGImageElement||"OffscreenCanvas"in window&&a instanceof OffscreenCanvas||"ImageBitmap"in window&&a instanceof ImageBitmap)return a;if(a instanceof +File||a instanceof Blob||a instanceof URL||"string"===typeof a){let b=new Image;b.src=a instanceof File||a instanceof Blob?URL.createObjectURL(a):a.toString();try{return await f._awaitImageLoad(b),b}finally{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src)}}else throw"Unsupported image type.";}static async _awaitImageLoad(a){a.complete&&0!==a.naturalWidth||await new Promise((b,c)=>{let d=e=>{a.removeEventListener("load",d);a.removeEventListener("error",d);e instanceof ErrorEvent? +c("Image load error"):b()};a.addEventListener("load",d);a.addEventListener("error",d)})}static async _postWorkerMessage(a,b,c){a=await a;a instanceof Worker&&a.postMessage({type:b,data:c})}}f.DEFAULT_CANVAS_SIZE=400;f.NO_QR_CODE_FOUND="No QR code found";f.WORKER_PATH="qr-scanner-worker.min.js";export default f //# sourceMappingURL=qr-scanner.min.js.map diff --git a/qr-scanner.min.js.map b/qr-scanner.min.js.map index f7bdd7a..6ab6bee 100644 --- a/qr-scanner.min.js.map +++ b/qr-scanner.min.js.map @@ -1 +1 @@ -{"version":3,"file":"qr-scanner.min.js","sources":["src/qr-scanner.ts"],"sourcesContent":["export default class QrScanner {\n static readonly DEFAULT_CANVAS_SIZE = 400;\n static readonly NO_QR_CODE_FOUND = 'No QR code found';\n static WORKER_PATH = 'qr-scanner-worker.min.js';\n\n static async hasCamera(): Promise {\n try {\n return !!(await QrScanner.listCameras(false)).length;\n } catch (e) {\n return false;\n }\n }\n\n static async listCameras(requestLabels = false): Promise> {\n if (!navigator.mediaDevices) return [];\n\n const enumerateCameras = async (): Promise> =>\n (await navigator.mediaDevices.enumerateDevices()).filter((device) => device.kind === 'videoinput');\n\n // Note that enumerateDevices can always be called and does not prompt the user for permission.\n // However, enumerateDevices only includes device labels if served via https and an active media stream exists\n // or permission to access the camera was given. Therefore, if we're not getting labels but labels are requested\n // ask for camera permission by opening a stream.\n let openedStream: MediaStream | undefined;\n try {\n if (requestLabels && (await enumerateCameras()).every((camera) => !camera.label)) {\n openedStream = await navigator.mediaDevices.getUserMedia({ audio: false, video: true });\n }\n } catch (e) {\n // Fail gracefully, especially if the device has no camera or on mobile when the camera is already in use\n // and some browsers disallow a second stream.\n }\n\n try {\n return (await enumerateCameras()).map((camera, i) => ({\n id: camera.deviceId,\n label: camera.label || (i === 0 ? 'Default Camera' : `Camera ${i + 1}`),\n }));\n } finally {\n // close the stream we just opened for getting camera access for listing the device labels\n if (openedStream) {\n console.warn('Call listCameras after successfully starting a QR scanner to avoid creating '\n + 'a temporary video stream');\n QrScanner._stopVideoStream(openedStream);\n }\n }\n }\n\n readonly $video: HTMLVideoElement;\n readonly $canvas: HTMLCanvasElement;\n readonly $overlay?: HTMLDivElement;\n private readonly $codeOutlineHighlight?: SVGSVGElement;\n private readonly _onDecode?: (result: QrScanner.ScanResult) => void;\n private readonly _legacyOnDecode?: (result: string) => void;\n private readonly _legacyCanvasSize: number = QrScanner.DEFAULT_CANVAS_SIZE;\n private _preferredCamera: QrScanner.FacingMode | QrScanner.DeviceId = 'environment';\n private readonly _maxScansPerSecond: number = 25;\n private _lastScanTimestamp: number = -1;\n private _scanRegion: QrScanner.ScanRegion;\n private _codeOutlineHighlightRemovalTimeout?: number;\n private _qrEnginePromise: Promise\n private _active: boolean = false;\n private _paused: boolean = false;\n private _flashOn: boolean = false;\n private _destroyed: boolean = false;\n\n constructor(\n video: HTMLVideoElement,\n onDecode: (result: QrScanner.ScanResult) => void,\n options: {\n onDecodeError?: (error: Error | string) => void,\n calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n maxScansPerSecond?: number;\n highlightScanRegion?: boolean,\n highlightCodeOutline?: boolean,\n overlay?: HTMLDivElement,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n },\n );\n /** @deprecated */\n constructor(\n video: HTMLVideoElement,\n onDecode: (result: string) => void,\n onDecodeError?: (error: Error | string) => void,\n calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n );\n /** @deprecated */\n constructor(\n video: HTMLVideoElement,\n onDecode: (result: string) => void,\n onDecodeError?: (error: Error | string) => void,\n canvasSize?: number,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n );\n /** @deprecated */\n constructor(video: HTMLVideoElement, onDecode: (result: string) => void, canvasSize?: number);\n constructor(\n video: HTMLVideoElement,\n onDecode: ((result: QrScanner.ScanResult) => void) | ((result: string) => void),\n canvasSizeOrOnDecodeErrorOrOptions?: number | ((error: Error | string) => void) | {\n onDecodeError?: (error: Error | string) => void,\n calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n maxScansPerSecond?: number;\n highlightScanRegion?: boolean,\n highlightCodeOutline?: boolean,\n overlay?: HTMLDivElement,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n },\n canvasSizeOrCalculateScanRegion?: number | ((video: HTMLVideoElement) => QrScanner.ScanRegion),\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n ) {\n this.$video = video;\n this.$canvas = document.createElement('canvas');\n\n if (canvasSizeOrOnDecodeErrorOrOptions && typeof canvasSizeOrOnDecodeErrorOrOptions === 'object') {\n // we got an options object using the new api\n this._onDecode = onDecode as QrScanner['_onDecode'];\n } else {\n if (canvasSizeOrOnDecodeErrorOrOptions || canvasSizeOrCalculateScanRegion || preferredCamera) {\n console.warn('You\\'re using a deprecated version of the QrScanner constructor which will be removed in '\n + 'the future');\n } else {\n // Only video and onDecode were specified and we can't distinguish between new or old api usage. For\n // backwards compatibility we have to assume the old api for now. The options object is marked as non-\n // optional in the parameter list above to make clear that ScanResult instead of string is only passed\n // if an options object was provided. However, in the future once legacy support is removed, the options\n // object should become optional.\n console.warn('Note that the type of the scan result passed to onDecode will change in the future. '\n + 'To already switch to the new api today, you can pass returnDetailedScanResult: true.');\n }\n this._legacyOnDecode = onDecode as QrScanner['_legacyOnDecode'];\n }\n\n const options = typeof canvasSizeOrOnDecodeErrorOrOptions === 'object'\n ? canvasSizeOrOnDecodeErrorOrOptions\n : {};\n this._onDecodeError = options.onDecodeError || (typeof canvasSizeOrOnDecodeErrorOrOptions === 'function'\n ? canvasSizeOrOnDecodeErrorOrOptions\n : this._onDecodeError);\n this._calculateScanRegion = options.calculateScanRegion || (typeof canvasSizeOrCalculateScanRegion==='function'\n ? canvasSizeOrCalculateScanRegion\n : this._calculateScanRegion);\n this._preferredCamera = options.preferredCamera || preferredCamera || this._preferredCamera;\n this._legacyCanvasSize = typeof canvasSizeOrOnDecodeErrorOrOptions === 'number'\n ? canvasSizeOrOnDecodeErrorOrOptions\n : typeof canvasSizeOrCalculateScanRegion === 'number'\n ? canvasSizeOrCalculateScanRegion\n : this._legacyCanvasSize;\n this._maxScansPerSecond = options.maxScansPerSecond || this._maxScansPerSecond;\n\n this._onPlay = this._onPlay.bind(this);\n this._onLoadedMetaData = this._onLoadedMetaData.bind(this);\n this._onVisibilityChange = this._onVisibilityChange.bind(this);\n this._updateOverlay = this._updateOverlay.bind(this);\n\n // @ts-ignore\n video.disablePictureInPicture = true;\n // Allow inline playback on iPhone instead of requiring full screen playback,\n // see https://webkit.org/blog/6784/new-video-policies-for-ios/\n // @ts-ignore\n video.playsInline = true;\n // Allow play() on iPhone without requiring a user gesture. Should not really be needed as camera stream\n // includes no audio, but just to be safe.\n video.muted = true;\n\n // Avoid Safari stopping the video stream on a hidden video.\n // See https://github.com/cozmo/jsQR/issues/185\n let shouldHideVideo = false;\n if (video.hidden) {\n video.hidden = false;\n shouldHideVideo = true;\n }\n if (!document.body.contains(video)) {\n document.body.appendChild(video);\n shouldHideVideo = true;\n }\n const videoContainer = video.parentElement!;\n\n if (options.highlightScanRegion || options.highlightCodeOutline) {\n const gotExternalOverlay = !!options.overlay;\n this.$overlay = options.overlay || document.createElement('div');\n const overlayStyle = this.$overlay.style;\n overlayStyle.position = 'absolute';\n overlayStyle.display = 'none';\n overlayStyle.pointerEvents = 'none';\n this.$overlay.classList.add('scan-region-highlight');\n if (!gotExternalOverlay && options.highlightScanRegion) {\n // default style; can be overwritten via css\n overlayStyle.outline = '#e9b213 solid 5px';\n videoContainer.insertBefore(this.$overlay, this.$video.nextSibling);\n }\n if (options.highlightCodeOutline) {\n this.$codeOutlineHighlight = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');\n this.$codeOutlineHighlight.appendChild(polygon);\n this.$overlay.appendChild(this.$codeOutlineHighlight);\n const codeOutlineHighlightStyle = this.$codeOutlineHighlight.style;\n codeOutlineHighlightStyle.width = '100%';\n codeOutlineHighlightStyle.height = '100%';\n // to support distorted videos, e.g. via object-fit: fill\n this.$codeOutlineHighlight.setAttribute('preserveAspectRatio', 'none');\n codeOutlineHighlightStyle.display = 'none';\n this.$codeOutlineHighlight.classList.add('code-outline-highlight');\n // default style; can be overwritten via css\n codeOutlineHighlightStyle.fill = 'none';\n codeOutlineHighlightStyle.stroke = '#e9b213';\n codeOutlineHighlightStyle.strokeWidth = '4';\n }\n }\n this._scanRegion = this._calculateScanRegion(video);\n\n requestAnimationFrame(() => {\n // Checking in requestAnimationFrame which should avoid a potential additional re-flow for getComputedStyle.\n const videoStyle = window.getComputedStyle(video);\n if (videoStyle.display === 'none') {\n video.style.setProperty('display', 'block', 'important');\n shouldHideVideo = true;\n }\n if (videoStyle.visibility !== 'visible') {\n video.style.setProperty('visibility', 'visible', 'important');\n shouldHideVideo = true;\n }\n if (shouldHideVideo) {\n // Hide the video in a way that doesn't cause Safari to stop the playback.\n console.warn('QrScanner has overwritten the video hiding style to avoid Safari stopping the playback.');\n video.style.opacity = '0';\n video.style.width = '0';\n video.style.height = '0';\n if (this.$overlay && this.$overlay.parentElement) {\n this.$overlay.parentElement.removeChild(this.$overlay);\n }\n // @ts-ignore\n delete this.$overlay!;\n // @ts-ignore\n delete this.$codeOutlineHighlight!;\n }\n\n if (this.$overlay) {\n this._updateOverlay();\n }\n });\n\n video.addEventListener('play', this._onPlay);\n video.addEventListener('loadedmetadata', this._onLoadedMetaData);\n document.addEventListener('visibilitychange', this._onVisibilityChange);\n window.addEventListener('resize', this._updateOverlay);\n\n this._qrEnginePromise = QrScanner.createQrEngine();\n }\n\n async hasFlash(): Promise {\n let stream: MediaStream | undefined;\n try {\n if (this.$video.srcObject) {\n if (!(this.$video.srcObject instanceof MediaStream)) return false; // srcObject is not a camera stream\n stream = this.$video.srcObject;\n } else {\n stream = (await this._getCameraStream()).stream;\n }\n return 'torch' in stream.getVideoTracks()[0].getSettings();\n } catch (e) {\n return false;\n } finally {\n // close the stream we just opened for detecting whether it supports flash\n if (stream && stream !== this.$video.srcObject) {\n console.warn('Call hasFlash after successfully starting the scanner to avoid creating '\n + 'a temporary video stream');\n QrScanner._stopVideoStream(stream);\n }\n }\n }\n\n isFlashOn(): boolean {\n return this._flashOn;\n }\n\n async toggleFlash(): Promise {\n if (this._flashOn) {\n await this.turnFlashOff();\n } else {\n await this.turnFlashOn();\n }\n }\n\n async turnFlashOn(): Promise {\n if (this._flashOn || this._destroyed) return;\n this._flashOn = true;\n if (!this._active || this._paused) return; // flash will be turned on later on .start()\n try {\n if (!await this.hasFlash()) throw 'No flash available';\n // Note that the video track is guaranteed to exist and to be a MediaStream due to the check in hasFlash\n await (this.$video.srcObject as MediaStream).getVideoTracks()[0].applyConstraints({\n // @ts-ignore: constraint 'torch' is unknown to ts\n advanced: [{ torch: true }],\n });\n } catch (e) {\n this._flashOn = false;\n throw e;\n }\n }\n\n async turnFlashOff(): Promise {\n if (!this._flashOn) return;\n // applyConstraints with torch: false does not work to turn the flashlight off, as a stream's torch stays\n // continuously on, see https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#torch. Therefore,\n // we have to stop the stream to turn the flashlight off.\n this._flashOn = false;\n await this._restartVideoStream();\n }\n\n destroy(): void {\n this.$video.removeEventListener('loadedmetadata', this._onLoadedMetaData);\n this.$video.removeEventListener('play', this._onPlay);\n document.removeEventListener('visibilitychange', this._onVisibilityChange);\n window.removeEventListener('resize', this._updateOverlay);\n\n this._destroyed = true;\n this._flashOn = false;\n this.stop(); // sets this._paused = true and this._active = false\n QrScanner._postWorkerMessage(this._qrEnginePromise, 'close');\n }\n\n async start(): Promise {\n if ((this._active && !this._paused) || this._destroyed) return;\n\n if (window.location.protocol !== 'https:') {\n // warn but try starting the camera anyways\n console.warn('The camera stream is only accessible if the page is transferred via https.');\n }\n\n this._active = true;\n if (document.hidden) return; // camera will be started as soon as tab is in foreground\n this._paused = false;\n if (this.$video.srcObject) {\n // camera stream already/still set\n await this.$video.play();\n return;\n }\n\n try {\n const { stream, facingMode } = await this._getCameraStream();\n if (!this._active || this._paused) {\n // was stopped in the meantime\n QrScanner._stopVideoStream(stream);\n return;\n }\n this._setVideoMirror(facingMode);\n this.$video.srcObject = stream;\n await this.$video.play();\n\n // Restart the flash if it was previously on\n if (this._flashOn) {\n this._flashOn = false; // force turnFlashOn to restart the flash\n this.turnFlashOn().catch(() => {});\n }\n } catch (e) {\n if (this._paused) return;\n this._active = false;\n throw e;\n }\n }\n\n stop(): void {\n this.pause();\n this._active = false;\n }\n\n async pause(stopStreamImmediately = false): Promise {\n this._paused = true;\n if (!this._active) return true;\n this.$video.pause();\n\n if (this.$overlay) {\n this.$overlay.style.display = 'none';\n }\n\n const stopStream = () => {\n if (this.$video.srcObject instanceof MediaStream) {\n // revoke srcObject only if it's a stream which was likely set by us\n QrScanner._stopVideoStream(this.$video.srcObject);\n this.$video.srcObject = null;\n }\n };\n\n if (stopStreamImmediately) {\n stopStream();\n return true;\n }\n\n await new Promise((resolve) => setTimeout(resolve, 300));\n if (!this._paused) return false;\n stopStream();\n return true;\n }\n\n async setCamera(facingModeOrDeviceId: QrScanner.FacingMode | QrScanner.DeviceId): Promise {\n if (facingModeOrDeviceId === this._preferredCamera) return;\n this._preferredCamera = facingModeOrDeviceId;\n // Restart the scanner with the new camera which will also update the video mirror and the scan region.\n await this._restartVideoStream();\n }\n\n static async scanImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n options: {\n scanRegion?: QrScanner.ScanRegion | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing?: boolean,\n alsoTryWithoutScanRegion?: boolean,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n },\n ): Promise;\n /** @deprecated */\n static async scanImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n scanRegion?: QrScanner.ScanRegion | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing?: boolean,\n alsoTryWithoutScanRegion?: boolean,\n ): Promise;\n static async scanImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n scanRegionOrOptions?: QrScanner.ScanRegion | {\n scanRegion?: QrScanner.ScanRegion | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing?: boolean,\n alsoTryWithoutScanRegion?: boolean,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n } | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing: boolean = false,\n alsoTryWithoutScanRegion: boolean = false,\n ): Promise {\n let scanRegion: QrScanner.ScanRegion | null | undefined;\n let returnDetailedScanResult = false;\n if (scanRegionOrOptions && (\n 'scanRegion' in scanRegionOrOptions\n || 'qrEngine' in scanRegionOrOptions\n || 'canvas' in scanRegionOrOptions\n || 'disallowCanvasResizing' in scanRegionOrOptions\n || 'alsoTryWithoutScanRegion' in scanRegionOrOptions\n || 'returnDetailedScanResult' in scanRegionOrOptions\n )) {\n // we got an options object using the new api\n scanRegion = scanRegionOrOptions.scanRegion;\n qrEngine = scanRegionOrOptions.qrEngine;\n canvas = scanRegionOrOptions.canvas;\n disallowCanvasResizing = scanRegionOrOptions.disallowCanvasResizing || false;\n alsoTryWithoutScanRegion = scanRegionOrOptions.alsoTryWithoutScanRegion || false;\n returnDetailedScanResult = true;\n } else if (scanRegionOrOptions || qrEngine || canvas || disallowCanvasResizing || alsoTryWithoutScanRegion) {\n console.warn('You\\'re using a deprecated api for scanImage which will be removed in the future.');\n } else {\n // Only imageOrFileOrBlobOrUrl was specified and we can't distinguish between new or old api usage. For\n // backwards compatibility we have to assume the old api for now. The options object is marked as non-\n // optional in the parameter list above to make clear that ScanResult instead of string is only returned if\n // an options object was provided. However, in the future once legacy support is removed, the options object\n // should become optional.\n console.warn('Note that the return type of scanImage will change in the future. To already switch to the '\n + 'new api today, you can pass returnDetailedScanResult: true.');\n }\n\n const gotExternalEngine = !!qrEngine;\n\n try {\n let image: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement;\n let canvasContext: CanvasRenderingContext2D;\n [qrEngine, image] = await Promise.all([\n qrEngine || QrScanner.createQrEngine(),\n QrScanner._loadImage(imageOrFileOrBlobOrUrl),\n ]);\n [canvas, canvasContext] = QrScanner._drawToCanvas(image, scanRegion, canvas, disallowCanvasResizing);\n\n if (qrEngine instanceof Worker) {\n const qrEngineWorker = qrEngine; // for ts to know that it's still a worker later in the event listeners\n if (!gotExternalEngine) {\n // Enable scanning of inverted color qr codes. Not using _postWorkerMessage as it's async\n qrEngineWorker.postMessage({ type: 'inversionMode', data: 'both' });\n }\n return await new Promise((resolve, reject) => {\n let timeout: number;\n let onMessage: (event: MessageEvent) => void;\n let onError: (error: ErrorEvent | string) => void;\n onMessage = (event: MessageEvent) => {\n if (event.data.type !== 'qrResult') {\n return;\n }\n qrEngineWorker.removeEventListener('message', onMessage);\n qrEngineWorker.removeEventListener('error', onError);\n clearTimeout(timeout);\n if (event.data.data !== null) {\n resolve(returnDetailedScanResult ? {\n data: event.data.data,\n cornerPoints: QrScanner._convertPoints(event.data.cornerPoints, scanRegion),\n } : event.data.data);\n } else {\n reject(QrScanner.NO_QR_CODE_FOUND);\n }\n };\n onError = (error: ErrorEvent | string) => {\n qrEngineWorker.removeEventListener('message', onMessage);\n qrEngineWorker.removeEventListener('error', onError);\n clearTimeout(timeout);\n const errorMessage = !error ? 'Unknown Error' : ((error as ErrorEvent).message || error);\n reject('Scanner error: ' + errorMessage);\n };\n qrEngineWorker.addEventListener('message', onMessage);\n qrEngineWorker.addEventListener('error', onError);\n timeout = setTimeout(() => onError('timeout'), 10000);\n const imageData = canvasContext.getImageData(0, 0, canvas!.width, canvas!.height);\n qrEngineWorker.postMessage({\n type: 'decode',\n data: imageData\n }, [imageData.data.buffer]);\n });\n } else {\n return await Promise.race([\n new Promise((resolve, reject) => window.setTimeout(\n () => reject('Scanner error: timeout'),\n 10000,\n )),\n (async (): Promise => {\n try {\n const [scanResult] = await qrEngine.detect(canvas!);\n if (!scanResult) throw QrScanner.NO_QR_CODE_FOUND;\n return returnDetailedScanResult ? {\n data: scanResult.rawValue,\n cornerPoints: QrScanner._convertPoints(scanResult.cornerPoints, scanRegion),\n } : scanResult.rawValue;\n } catch (e) {\n throw `Scanner error: ${(e as Error).message || e}`;\n }\n })(),\n ]);\n }\n } catch (e) {\n if (!scanRegion || !alsoTryWithoutScanRegion) throw e;\n const detailedScanResult = await QrScanner.scanImage(\n imageOrFileOrBlobOrUrl,\n { qrEngine, canvas, disallowCanvasResizing },\n );\n return returnDetailedScanResult ? detailedScanResult : detailedScanResult.data;\n } finally {\n if (!gotExternalEngine) {\n QrScanner._postWorkerMessage(qrEngine!, 'close');\n }\n }\n }\n\n setGrayscaleWeights(red: number, green: number, blue: number, useIntegerApproximation: boolean = true): void {\n // Note that for the native BarcodeDecoder or if the worker was destroyed, this is a no-op. However, the native\n // implementations work also well with colored qr codes.\n QrScanner._postWorkerMessage(\n this._qrEnginePromise,\n 'grayscaleWeights',\n { red, green, blue, useIntegerApproximation }\n );\n }\n\n setInversionMode(inversionMode: QrScanner.InversionMode): void {\n // Note that for the native BarcodeDecoder or if the worker was destroyed, this is a no-op. However, the native\n // implementations scan normal and inverted qr codes by default\n QrScanner._postWorkerMessage(this._qrEnginePromise, 'inversionMode', inversionMode);\n }\n\n static async createQrEngine(workerPath: string = QrScanner.WORKER_PATH): Promise {\n const hasNativeBarcodeDetector = 'BarcodeDetector' in window && BarcodeDetector.getSupportedFormats\n ? (await BarcodeDetector.getSupportedFormats()).includes('qr_code')\n : false;\n return hasNativeBarcodeDetector ? new BarcodeDetector({ formats: ['qr_code'] }) : new Worker(workerPath);\n }\n\n private _onPlay(): void {\n this._scanRegion = this._calculateScanRegion(this.$video);\n this._updateOverlay();\n if (this.$overlay) {\n this.$overlay.style.display = '';\n }\n this._scanFrame();\n }\n\n private _onLoadedMetaData(): void {\n this._scanRegion = this._calculateScanRegion(this.$video);\n this._updateOverlay();\n }\n\n private _onVisibilityChange(): void {\n if (document.hidden) {\n this.pause();\n } else if (this._active) {\n this.start();\n }\n }\n\n private _calculateScanRegion(video: HTMLVideoElement): QrScanner.ScanRegion {\n // Default scan region calculation. Note that this can be overwritten in the constructor.\n const smallestDimension = Math.min(video.videoWidth, video.videoHeight);\n const scanRegionSize = Math.round(2 / 3 * smallestDimension);\n return {\n x: Math.round((video.videoWidth - scanRegionSize) / 2),\n y: Math.round((video.videoHeight - scanRegionSize) / 2),\n width: scanRegionSize,\n height: scanRegionSize,\n downScaledWidth: this._legacyCanvasSize,\n downScaledHeight: this._legacyCanvasSize,\n };\n }\n\n private _updateOverlay(): void {\n requestAnimationFrame(() => {\n // Running in requestAnimationFrame which should avoid a potential additional re-flow for getComputedStyle\n // and offsetWidth, offsetHeight, offsetLeft, offsetTop.\n if (!this.$overlay) return;\n const video = this.$video;\n const videoWidth = video.videoWidth;\n const videoHeight = video.videoHeight;\n const elementWidth = video.offsetWidth;\n const elementHeight = video.offsetHeight;\n const elementX = video.offsetLeft;\n const elementY = video.offsetTop;\n\n const videoStyle = window.getComputedStyle(video);\n const videoObjectFit = videoStyle.objectFit;\n const videoAspectRatio = videoWidth / videoHeight;\n const elementAspectRatio = elementWidth / elementHeight;\n let videoScaledWidth: number;\n let videoScaledHeight: number;\n switch (videoObjectFit) {\n case 'none':\n videoScaledWidth = videoWidth;\n videoScaledHeight = videoHeight;\n break;\n case 'fill':\n videoScaledWidth = elementWidth;\n videoScaledHeight = elementHeight;\n break;\n default: // 'cover', 'contains', 'scale-down'\n if (videoObjectFit === 'cover'\n ? videoAspectRatio > elementAspectRatio\n : videoAspectRatio < elementAspectRatio) {\n // The scaled height is the element height\n // - for 'cover' if the video aspect ratio is wider than the element aspect ratio\n // (scaled height matches element height and scaled width overflows element width)\n // - for 'contains'/'scale-down' if element aspect ratio is wider than the video aspect ratio\n // (scaled height matched element height and element width overflows scaled width)\n videoScaledHeight = elementHeight;\n videoScaledWidth = videoScaledHeight * videoAspectRatio;\n } else {\n videoScaledWidth = elementWidth;\n videoScaledHeight = videoScaledWidth / videoAspectRatio;\n }\n if (videoObjectFit === 'scale-down') {\n // for 'scale-down' the dimensions are the minimum of 'contains' and 'none'\n videoScaledWidth = Math.min(videoScaledWidth, videoWidth);\n videoScaledHeight = Math.min(videoScaledHeight, videoHeight);\n }\n }\n\n // getComputedStyle is so nice to convert keywords (left, center, right, top, bottom) to percent and makes\n // sure to set the default of 50% if only one or no component was provided, therefore we can be sure that\n // both components are set. Additionally, it converts units other than px (e.g. rem) to px.\n const [videoX, videoY] = videoStyle.objectPosition.split(' ').map((length, i) => {\n const lengthValue = parseFloat(length);\n return length.endsWith('%')\n ? (!i ? elementWidth - videoScaledWidth : elementHeight - videoScaledHeight) * lengthValue / 100\n : lengthValue;\n });\n\n const regionWidth = this._scanRegion.width || videoWidth;\n const regionHeight = this._scanRegion.height || videoHeight;\n const regionX = this._scanRegion.x || 0;\n const regionY = this._scanRegion.y || 0;\n\n const overlayStyle = this.$overlay.style;\n overlayStyle.width = `${regionWidth / videoWidth * videoScaledWidth}px`;\n overlayStyle.height = `${regionHeight / videoHeight * videoScaledHeight}px`;\n overlayStyle.top = `${elementY + videoY + regionY / videoHeight * videoScaledHeight}px`;\n const isVideoMirrored = /scaleX\\(-1\\)/.test(video.style.transform!);\n overlayStyle.left = `${elementX\n + (isVideoMirrored ? elementWidth - videoX - videoScaledWidth : videoX)\n + (isVideoMirrored ? videoWidth - regionX - regionWidth : regionX) / videoWidth * videoScaledWidth}px`;\n // apply same mirror as on video\n overlayStyle.transform = video.style.transform;\n });\n }\n\n private static _convertPoints(\n points: QrScanner.Point[],\n scanRegion?: QrScanner.ScanRegion | null,\n ): QrScanner.Point[] {\n if (!scanRegion) return points;\n const offsetX = scanRegion.x || 0;\n const offsetY = scanRegion.y || 0;\n const scaleFactorX = scanRegion.width && scanRegion.downScaledWidth\n ? scanRegion.width / scanRegion.downScaledWidth\n : 1;\n const scaleFactorY = scanRegion.height && scanRegion.downScaledHeight\n ? scanRegion.height / scanRegion.downScaledHeight\n : 1;\n for (const point of points) {\n point.x = point.x * scaleFactorX + offsetX;\n point.y = point.y * scaleFactorY + offsetY;\n }\n return points;\n }\n\n private _scanFrame(): void {\n if (!this._active || this.$video.paused || this.$video.ended) return;\n // using requestAnimationFrame to avoid scanning if tab is in background\n requestAnimationFrame(async () => {\n if (this.$video.readyState <= 1) {\n // Skip scans until the video is ready as drawImage() only works correctly on a video with readyState\n // > 1, see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage#Notes.\n // This also avoids false positives for videos paused after a successful scan which remains visible on\n // the canvas until the video is started again and ready.\n this._scanFrame();\n return;\n }\n\n const timeSinceLastScan = Date.now() - this._lastScanTimestamp;\n const minimumTimeBetweenScans = 1000 / this._maxScansPerSecond;\n if (timeSinceLastScan < minimumTimeBetweenScans) {\n await new Promise((resolve) => setTimeout(resolve, minimumTimeBetweenScans - timeSinceLastScan));\n }\n this._lastScanTimestamp = Date.now();\n\n let result: QrScanner.ScanResult | undefined;\n try {\n result = await QrScanner.scanImage(this.$video, {\n scanRegion: this._scanRegion,\n qrEngine: this._qrEnginePromise,\n canvas: this.$canvas,\n });\n } catch (error) {\n if (!this._active) return;\n const errorMessage = (error as Error).message || error as string;\n if (errorMessage.includes('service unavailable')) {\n // When the native BarcodeDetector crashed, create a new one\n this._qrEnginePromise = QrScanner.createQrEngine();\n }\n this._onDecodeError(error as Error | string);\n }\n\n if (result) {\n if (this._onDecode) {\n this._onDecode(result);\n } else if (this._legacyOnDecode) {\n this._legacyOnDecode(result.data);\n }\n\n if (this.$codeOutlineHighlight) {\n clearTimeout(this._codeOutlineHighlightRemovalTimeout);\n this._codeOutlineHighlightRemovalTimeout = undefined;\n this.$codeOutlineHighlight.setAttribute(\n 'viewBox',\n `${this._scanRegion.x || 0} `\n + `${this._scanRegion.y || 0} `\n + `${this._scanRegion.width || this.$video.videoWidth} `\n + `${this._scanRegion.height || this.$video.videoHeight}`,\n );\n const polygon = this.$codeOutlineHighlight.firstElementChild!;\n polygon.setAttribute('points', result.cornerPoints.map(({x, y}) => `${x},${y}`).join(' '));\n this.$codeOutlineHighlight.style.display = '';\n }\n } else if (this.$codeOutlineHighlight && !this._codeOutlineHighlightRemovalTimeout) {\n // hide after timeout to make it flash less when on some frames the QR code is detected and on some not\n this._codeOutlineHighlightRemovalTimeout = setTimeout(\n () => this.$codeOutlineHighlight!.style.display = 'none',\n 100,\n );\n }\n\n this._scanFrame();\n });\n }\n\n private _onDecodeError(error: Error | string): void {\n // default error handler; can be overwritten in the constructor\n if (error === QrScanner.NO_QR_CODE_FOUND) return;\n console.log(error);\n }\n\n private async _getCameraStream(): Promise<{ stream: MediaStream, facingMode: QrScanner.FacingMode }> {\n if (!navigator.mediaDevices) throw 'Camera not found.';\n\n const preferenceType = /^(environment|user)$/.test(this._preferredCamera)\n ? 'facingMode'\n : 'deviceId';\n const constraintsWithoutCamera: Array = [{\n width: { min: 1024 }\n }, {\n width: { min: 768 }\n }, {}];\n const constraintsWithCamera = constraintsWithoutCamera.map((constraint) => Object.assign({}, constraint, {\n [preferenceType]: { exact: this._preferredCamera },\n }));\n\n for (const constraints of [...constraintsWithCamera, ...constraintsWithoutCamera]) {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ video: constraints, audio: false });\n // Try to determine the facing mode from the stream, otherwise use a guess or 'environment' as\n // default. Note that the guess is not always accurate as Safari returns cameras of different facing\n // mode, even for exact facingMode constraints.\n const facingMode = this._getFacingMode(stream)\n || (constraints.facingMode\n ? this._preferredCamera as QrScanner.FacingMode // a facing mode we were able to fulfill\n : (this._preferredCamera === 'environment'\n ? 'user' // switch as _preferredCamera was environment but we are not able to fulfill it\n : 'environment' // switch from unfulfilled user facingMode or default to environment\n )\n );\n return { stream, facingMode };\n } catch (e) {}\n }\n\n throw 'Camera not found.';\n }\n\n private async _restartVideoStream(): Promise {\n // Note that we always pause the stream and not only if !this._paused as even if this._paused === true, the\n // stream might still be running, as it's by default only stopped after a delay of 300ms.\n const wasPaused = this._paused;\n const paused = await this.pause(true);\n if (!paused || wasPaused || !this._active) return;\n await this.start();\n }\n\n private static _stopVideoStream(stream : MediaStream): void {\n for (const track of stream.getTracks()) {\n track.stop(); // note that this will also automatically turn the flashlight off\n stream.removeTrack(track);\n }\n }\n\n private _setVideoMirror(facingMode: QrScanner.FacingMode): void {\n // in user facing mode mirror the video to make it easier for the user to position the QR code\n const scaleFactor = facingMode === 'user'? -1 : 1;\n this.$video.style.transform = 'scaleX(' + scaleFactor + ')';\n }\n\n private _getFacingMode(videoStream: MediaStream): QrScanner.FacingMode | null {\n const videoTrack = videoStream.getVideoTracks()[0];\n if (!videoTrack) return null; // unknown\n // inspired by https://github.com/JodusNodus/react-qr-reader/blob/master/src/getDeviceId.js#L13\n return /rear|back|environment/i.test(videoTrack.label)\n ? 'environment'\n : /front|user|face/i.test(videoTrack.label)\n ? 'user'\n : null; // unknown\n }\n\n private static _drawToCanvas(\n image: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement,\n scanRegion?: QrScanner.ScanRegion | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing= false,\n ): [HTMLCanvasElement, CanvasRenderingContext2D] {\n canvas = canvas || document.createElement('canvas');\n const scanRegionX = scanRegion && scanRegion.x ? scanRegion.x : 0;\n const scanRegionY = scanRegion && scanRegion.y ? scanRegion.y : 0;\n const scanRegionWidth = scanRegion && scanRegion.width\n ? scanRegion.width\n : (image as HTMLVideoElement).videoWidth || image.width as number;\n const scanRegionHeight = scanRegion && scanRegion.height\n ? scanRegion.height\n : (image as HTMLVideoElement).videoHeight || image.height as number;\n\n if (!disallowCanvasResizing) {\n const canvasWidth = scanRegion && scanRegion.downScaledWidth\n ? scanRegion.downScaledWidth\n : scanRegionWidth;\n const canvasHeight = scanRegion && scanRegion.downScaledHeight\n ? scanRegion.downScaledHeight\n : scanRegionHeight;\n // Setting the canvas width or height clears the canvas, even if the values didn't change, therefore only\n // set them if they actually changed.\n if (canvas.width !== canvasWidth) {\n canvas.width = canvasWidth;\n }\n if (canvas.height !== canvasHeight) {\n canvas.height = canvasHeight;\n }\n }\n\n const context = canvas.getContext('2d', { alpha: false })!;\n context.imageSmoothingEnabled = false; // gives less blurry images\n context.drawImage(\n image,\n scanRegionX, scanRegionY, scanRegionWidth, scanRegionHeight,\n 0, 0, canvas.width, canvas.height,\n );\n return [canvas, context];\n }\n\n private static async _loadImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n ): Promise {\n if (imageOrFileOrBlobOrUrl instanceof Image) {\n await QrScanner._awaitImageLoad(imageOrFileOrBlobOrUrl);\n return imageOrFileOrBlobOrUrl;\n } else if (imageOrFileOrBlobOrUrl instanceof HTMLVideoElement\n || imageOrFileOrBlobOrUrl instanceof HTMLCanvasElement\n || imageOrFileOrBlobOrUrl instanceof SVGImageElement\n || 'OffscreenCanvas' in window && imageOrFileOrBlobOrUrl instanceof OffscreenCanvas\n || 'ImageBitmap' in window && imageOrFileOrBlobOrUrl instanceof ImageBitmap) {\n return imageOrFileOrBlobOrUrl;\n } else if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob\n || imageOrFileOrBlobOrUrl instanceof URL || typeof imageOrFileOrBlobOrUrl === 'string') {\n const image = new Image();\n if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob) {\n image.src = URL.createObjectURL(imageOrFileOrBlobOrUrl);\n } else {\n image.src = imageOrFileOrBlobOrUrl.toString();\n }\n try {\n await QrScanner._awaitImageLoad(image);\n return image;\n } finally {\n if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob) {\n URL.revokeObjectURL(image.src);\n }\n }\n } else {\n throw 'Unsupported image type.';\n }\n }\n\n private static async _awaitImageLoad(image: HTMLImageElement): Promise {\n if (image.complete && image.naturalWidth !== 0) return; // already loaded\n await new Promise((resolve, reject) => {\n const listener = (event: ErrorEvent | Event) => {\n image.removeEventListener('load', listener);\n image.removeEventListener('error', listener);\n if (event instanceof ErrorEvent) {\n reject('Image load error');\n } else {\n resolve();\n }\n };\n image.addEventListener('load', listener);\n image.addEventListener('error', listener);\n });\n }\n\n private static async _postWorkerMessage(\n qrEngineOrQrEnginePromise: Worker | BarcodeDetector | Promise,\n type: string,\n data?: any,\n ): Promise {\n const qrEngine = await qrEngineOrQrEnginePromise;\n if (!(qrEngine instanceof Worker)) return;\n qrEngine.postMessage({ type, data });\n }\n}\n\ndeclare namespace QrScanner {\n export interface ScanRegion {\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n downScaledWidth?: number;\n downScaledHeight?: number;\n }\n\n export type FacingMode = 'environment' | 'user';\n export type DeviceId = string;\n\n export interface Camera {\n id: DeviceId;\n label: string;\n }\n\n export type InversionMode = 'original' | 'invert' | 'both';\n\n export interface Point {\n x: number;\n y: number;\n }\n\n export interface ScanResult {\n data: string;\n // In clockwise order, starting at top left, but this might not be guaranteed in the future.\n cornerPoints: QrScanner.Point[];\n }\n}\n\n// simplified from https://wicg.github.io/shape-detection-api/#barcode-detection-api\ndeclare class BarcodeDetector {\n constructor(options?: { formats: string[] });\n static getSupportedFormats(): Promise;\n detect(image: ImageBitmapSource): Promise>;\n}\n"],"names":["QrScanner","constructor","video","onDecode","canvasSizeOrOnDecodeErrorOrOptions","canvasSizeOrCalculateScanRegion","preferredCamera","DEFAULT_CANVAS_SIZE","$video","$canvas","document","createElement","_onDecode","console","warn","_legacyOnDecode","_onDecodeError","options","onDecodeError","_calculateScanRegion","calculateScanRegion","_preferredCamera","_legacyCanvasSize","_maxScansPerSecond","maxScansPerSecond","_onPlay","bind","_onLoadedMetaData","_onVisibilityChange","_updateOverlay","disablePictureInPicture","playsInline","muted","shouldHideVideo","hidden","body","contains","appendChild","highlightScanRegion","highlightCodeOutline","$overlay","overlay","overlayStyle","position","display","pointerEvents","classList","add","gotExternalOverlay","outline","videoContainer","insertBefore","nextSibling","$codeOutlineHighlight","createElementNS","polygon","codeOutlineHighlightStyle","width","height","setAttribute","fill","stroke","strokeWidth","_scanRegion","requestAnimationFrame","videoStyle","style","setProperty","visibility","opacity","parentElement","removeChild","addEventListener","window","_qrEnginePromise","createQrEngine","hasCamera","length","listCameras","e","requestLabels","navigator","mediaDevices","filter","device","openedStream","every","enumerateCameras","camera","label","getUserMedia","audio","map","i","id","deviceId","_stopVideoStream","hasFlash","stream","srcObject","MediaStream","_getCameraStream","getVideoTracks","getSettings","isFlashOn","_flashOn","toggleFlash","turnFlashOff","turnFlashOn","_destroyed","_active","_paused","applyConstraints","advanced","torch","_restartVideoStream","destroy","removeEventListener","stop","_postWorkerMessage","start","location","protocol","play","facingMode","_setVideoMirror","catch","pause","stopStreamImmediately","stopStream","Promise","resolve","setTimeout","setCamera","facingModeOrDeviceId","scanImage","imageOrFileOrBlobOrUrl","scanRegionOrOptions","qrEngine","canvas","disallowCanvasResizing","alsoTryWithoutScanRegion","scanRegion","returnDetailedScanResult","image","canvasContext","all","_loadImage","_drawToCanvas","Worker","gotExternalEngine","qrEngineWorker","postMessage","type","data","reject","timeout","onMessage","onError","event","clearTimeout","cornerPoints","_convertPoints","NO_QR_CODE_FOUND","error","imageData","buffer","race","scanResult","rawValue","message","detailedScanResult","setGrayscaleWeights","red","green","blue","useIntegerApproximation","setInversionMode","inversionMode","workerPath","WORKER_PATH","includes","BarcodeDetector","getSupportedFormats","formats","_scanFrame","x","Math","round","videoWidth","scanRegionSize","y","videoHeight","downScaledWidth","downScaledHeight","videoObjectFit","videoScaledWidth","videoScaledHeight","elementWidth","elementHeight","videoAspectRatio","elementAspectRatio","min","videoY","lengthValue","parseFloat","endsWith","regionWidth","regionHeight","top","elementY","regionY","left","elementX","isVideoMirrored","videoX","regionX","transform","points","point","scaleFactorX","offsetX","scaleFactorY","offsetY","paused","ended","readyState","timeSinceLastScan","minimumTimeBetweenScans","_lastScanTimestamp","Date","now","result","_codeOutlineHighlightRemovalTimeout","undefined","join","log","constraint","preferenceType","exact","constraints","wasPaused","track","removeTrack","_getFacingMode","videoStream","test","videoTrack","scanRegionWidth","scanRegionHeight","canvasWidth","canvasHeight","alpha","context","imageSmoothingEnabled","drawImage","scanRegionX","scanRegionY","Image","_awaitImageLoad","HTMLVideoElement","HTMLCanvasElement","SVGImageElement","OffscreenCanvas","ImageBitmap","File","Blob","URL","src","createObjectURL","toString","revokeObjectURL","complete","naturalWidth","listener","ErrorEvent","qrEngineOrQrEnginePromise"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"qr-scanner.min.js","sources":["src/qr-scanner.ts"],"sourcesContent":["export default class QrScanner {\n static readonly DEFAULT_CANVAS_SIZE = 400;\n static readonly NO_QR_CODE_FOUND = 'No QR code found';\n static WORKER_PATH = 'qr-scanner-worker.min.js';\n\n static async hasCamera(): Promise {\n try {\n return !!(await QrScanner.listCameras(false)).length;\n } catch (e) {\n return false;\n }\n }\n\n static async listCameras(requestLabels = false): Promise> {\n if (!navigator.mediaDevices) return [];\n\n const enumerateCameras = async (): Promise> =>\n (await navigator.mediaDevices.enumerateDevices()).filter((device) => device.kind === 'videoinput');\n\n // Note that enumerateDevices can always be called and does not prompt the user for permission.\n // However, enumerateDevices only includes device labels if served via https and an active media stream exists\n // or permission to access the camera was given. Therefore, if we're not getting labels but labels are requested\n // ask for camera permission by opening a stream.\n let openedStream: MediaStream | undefined;\n try {\n if (requestLabels && (await enumerateCameras()).every((camera) => !camera.label)) {\n openedStream = await navigator.mediaDevices.getUserMedia({ audio: false, video: true });\n }\n } catch (e) {\n // Fail gracefully, especially if the device has no camera or on mobile when the camera is already in use\n // and some browsers disallow a second stream.\n }\n\n try {\n return (await enumerateCameras()).map((camera, i) => ({\n id: camera.deviceId,\n label: camera.label || (i === 0 ? 'Default Camera' : `Camera ${i + 1}`),\n }));\n } finally {\n // close the stream we just opened for getting camera access for listing the device labels\n if (openedStream) {\n console.warn('Call listCameras after successfully starting a QR scanner to avoid creating '\n + 'a temporary video stream');\n QrScanner._stopVideoStream(openedStream);\n }\n }\n }\n\n readonly $video: HTMLVideoElement;\n readonly $canvas: HTMLCanvasElement;\n readonly $overlay?: HTMLDivElement;\n private readonly $codeOutlineHighlight?: SVGSVGElement;\n private readonly _onDecode?: (result: QrScanner.ScanResult) => void;\n private readonly _legacyOnDecode?: (result: string) => void;\n private readonly _legacyCanvasSize: number = QrScanner.DEFAULT_CANVAS_SIZE;\n private _preferredCamera: QrScanner.FacingMode | QrScanner.DeviceId = 'environment';\n private readonly _maxScansPerSecond: number = 25;\n private _lastScanTimestamp: number = -1;\n private _scanRegion: QrScanner.ScanRegion;\n private _codeOutlineHighlightRemovalTimeout?: number;\n private _qrEnginePromise: Promise\n private _active: boolean = false;\n private _paused: boolean = false;\n private _flashOn: boolean = false;\n private _destroyed: boolean = false;\n\n constructor(\n video: HTMLVideoElement,\n onDecode: (result: QrScanner.ScanResult) => void,\n options: {\n onDecodeError?: (error: Error | string) => void,\n calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n maxScansPerSecond?: number;\n highlightScanRegion?: boolean,\n highlightCodeOutline?: boolean,\n overlay?: HTMLDivElement,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n },\n );\n /** @deprecated */\n constructor(\n video: HTMLVideoElement,\n onDecode: (result: string) => void,\n onDecodeError?: (error: Error | string) => void,\n calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n );\n /** @deprecated */\n constructor(\n video: HTMLVideoElement,\n onDecode: (result: string) => void,\n onDecodeError?: (error: Error | string) => void,\n canvasSize?: number,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n );\n /** @deprecated */\n constructor(video: HTMLVideoElement, onDecode: (result: string) => void, canvasSize?: number);\n constructor(\n video: HTMLVideoElement,\n onDecode: ((result: QrScanner.ScanResult) => void) | ((result: string) => void),\n canvasSizeOrOnDecodeErrorOrOptions?: number | ((error: Error | string) => void) | {\n onDecodeError?: (error: Error | string) => void,\n calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n maxScansPerSecond?: number;\n highlightScanRegion?: boolean,\n highlightCodeOutline?: boolean,\n overlay?: HTMLDivElement,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n },\n canvasSizeOrCalculateScanRegion?: number | ((video: HTMLVideoElement) => QrScanner.ScanRegion),\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n ) {\n this.$video = video;\n this.$canvas = document.createElement('canvas');\n\n if (canvasSizeOrOnDecodeErrorOrOptions && typeof canvasSizeOrOnDecodeErrorOrOptions === 'object') {\n // we got an options object using the new api\n this._onDecode = onDecode as QrScanner['_onDecode'];\n } else {\n if (canvasSizeOrOnDecodeErrorOrOptions || canvasSizeOrCalculateScanRegion || preferredCamera) {\n console.warn('You\\'re using a deprecated version of the QrScanner constructor which will be removed in '\n + 'the future');\n } else {\n // Only video and onDecode were specified and we can't distinguish between new or old api usage. For\n // backwards compatibility we have to assume the old api for now. The options object is marked as non-\n // optional in the parameter list above to make clear that ScanResult instead of string is only passed\n // if an options object was provided. However, in the future once legacy support is removed, the options\n // object should become optional.\n console.warn('Note that the type of the scan result passed to onDecode will change in the future. '\n + 'To already switch to the new api today, you can pass returnDetailedScanResult: true.');\n }\n this._legacyOnDecode = onDecode as QrScanner['_legacyOnDecode'];\n }\n\n const options = typeof canvasSizeOrOnDecodeErrorOrOptions === 'object'\n ? canvasSizeOrOnDecodeErrorOrOptions\n : {};\n this._onDecodeError = options.onDecodeError || (typeof canvasSizeOrOnDecodeErrorOrOptions === 'function'\n ? canvasSizeOrOnDecodeErrorOrOptions\n : this._onDecodeError);\n this._calculateScanRegion = options.calculateScanRegion || (typeof canvasSizeOrCalculateScanRegion==='function'\n ? canvasSizeOrCalculateScanRegion\n : this._calculateScanRegion);\n this._preferredCamera = options.preferredCamera || preferredCamera || this._preferredCamera;\n this._legacyCanvasSize = typeof canvasSizeOrOnDecodeErrorOrOptions === 'number'\n ? canvasSizeOrOnDecodeErrorOrOptions\n : typeof canvasSizeOrCalculateScanRegion === 'number'\n ? canvasSizeOrCalculateScanRegion\n : this._legacyCanvasSize;\n this._maxScansPerSecond = options.maxScansPerSecond || this._maxScansPerSecond;\n\n this._onPlay = this._onPlay.bind(this);\n this._onLoadedMetaData = this._onLoadedMetaData.bind(this);\n this._onVisibilityChange = this._onVisibilityChange.bind(this);\n this._updateOverlay = this._updateOverlay.bind(this);\n\n // @ts-ignore\n video.disablePictureInPicture = true;\n // Allow inline playback on iPhone instead of requiring full screen playback,\n // see https://webkit.org/blog/6784/new-video-policies-for-ios/\n // @ts-ignore\n video.playsInline = true;\n // Allow play() on iPhone without requiring a user gesture. Should not really be needed as camera stream\n // includes no audio, but just to be safe.\n video.muted = true;\n\n // Avoid Safari stopping the video stream on a hidden video.\n // See https://github.com/cozmo/jsQR/issues/185\n let shouldHideVideo = false;\n if (video.hidden) {\n video.hidden = false;\n shouldHideVideo = true;\n }\n if (!document.body.contains(video)) {\n document.body.appendChild(video);\n shouldHideVideo = true;\n }\n const videoContainer = video.parentElement!;\n\n if (options.highlightScanRegion || options.highlightCodeOutline) {\n const gotExternalOverlay = !!options.overlay;\n this.$overlay = options.overlay || document.createElement('div');\n const overlayStyle = this.$overlay.style;\n overlayStyle.position = 'absolute';\n overlayStyle.display = 'none';\n overlayStyle.pointerEvents = 'none';\n this.$overlay.classList.add('scan-region-highlight');\n if (!gotExternalOverlay && options.highlightScanRegion) {\n // default style; can be overwritten via css\n overlayStyle.outline = '#e9b213 solid 5px';\n videoContainer.insertBefore(this.$overlay, this.$video.nextSibling);\n }\n if (options.highlightCodeOutline) {\n this.$codeOutlineHighlight = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');\n this.$codeOutlineHighlight.appendChild(polygon);\n this.$overlay.appendChild(this.$codeOutlineHighlight);\n const codeOutlineHighlightStyle = this.$codeOutlineHighlight.style;\n codeOutlineHighlightStyle.width = '100%';\n codeOutlineHighlightStyle.height = '100%';\n // to support distorted videos, e.g. via object-fit: fill\n this.$codeOutlineHighlight.setAttribute('preserveAspectRatio', 'none');\n codeOutlineHighlightStyle.display = 'none';\n this.$codeOutlineHighlight.classList.add('code-outline-highlight');\n // default style; can be overwritten via css\n codeOutlineHighlightStyle.fill = 'none';\n codeOutlineHighlightStyle.stroke = '#e9b213';\n codeOutlineHighlightStyle.strokeWidth = '4';\n }\n }\n this._scanRegion = this._calculateScanRegion(video);\n\n requestAnimationFrame(() => {\n // Checking in requestAnimationFrame which should avoid a potential additional re-flow for getComputedStyle.\n const videoStyle = window.getComputedStyle(video);\n if (videoStyle.display === 'none') {\n video.style.setProperty('display', 'block', 'important');\n shouldHideVideo = true;\n }\n if (videoStyle.visibility !== 'visible') {\n video.style.setProperty('visibility', 'visible', 'important');\n shouldHideVideo = true;\n }\n if (shouldHideVideo) {\n // Hide the video in a way that doesn't cause Safari to stop the playback.\n console.warn('QrScanner has overwritten the video hiding style to avoid Safari stopping the playback.');\n video.style.opacity = '0';\n video.style.width = '0';\n video.style.height = '0';\n if (this.$overlay && this.$overlay.parentElement) {\n this.$overlay.parentElement.removeChild(this.$overlay);\n }\n // @ts-ignore\n delete this.$overlay!;\n // @ts-ignore\n delete this.$codeOutlineHighlight!;\n }\n\n if (this.$overlay) {\n this._updateOverlay();\n }\n });\n\n video.addEventListener('play', this._onPlay);\n video.addEventListener('loadedmetadata', this._onLoadedMetaData);\n document.addEventListener('visibilitychange', this._onVisibilityChange);\n window.addEventListener('resize', this._updateOverlay);\n\n this._qrEnginePromise = QrScanner.createQrEngine();\n }\n\n async hasFlash(): Promise {\n let stream: MediaStream | undefined;\n try {\n if (this.$video.srcObject) {\n if (!(this.$video.srcObject instanceof MediaStream)) return false; // srcObject is not a camera stream\n stream = this.$video.srcObject;\n } else {\n stream = (await this._getCameraStream()).stream;\n }\n return 'torch' in stream.getVideoTracks()[0].getSettings();\n } catch (e) {\n return false;\n } finally {\n // close the stream we just opened for detecting whether it supports flash\n if (stream && stream !== this.$video.srcObject) {\n console.warn('Call hasFlash after successfully starting the scanner to avoid creating '\n + 'a temporary video stream');\n QrScanner._stopVideoStream(stream);\n }\n }\n }\n\n isFlashOn(): boolean {\n return this._flashOn;\n }\n\n async toggleFlash(): Promise {\n if (this._flashOn) {\n await this.turnFlashOff();\n } else {\n await this.turnFlashOn();\n }\n }\n\n async turnFlashOn(): Promise {\n if (this._flashOn || this._destroyed) return;\n this._flashOn = true;\n if (!this._active || this._paused) return; // flash will be turned on later on .start()\n try {\n if (!await this.hasFlash()) throw 'No flash available';\n // Note that the video track is guaranteed to exist and to be a MediaStream due to the check in hasFlash\n await (this.$video.srcObject as MediaStream).getVideoTracks()[0].applyConstraints({\n // @ts-ignore: constraint 'torch' is unknown to ts\n advanced: [{ torch: true }],\n });\n } catch (e) {\n this._flashOn = false;\n throw e;\n }\n }\n\n async turnFlashOff(): Promise {\n if (!this._flashOn) return;\n // applyConstraints with torch: false does not work to turn the flashlight off, as a stream's torch stays\n // continuously on, see https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#torch. Therefore,\n // we have to stop the stream to turn the flashlight off.\n this._flashOn = false;\n await this._restartVideoStream();\n }\n\n destroy(): void {\n this.$video.removeEventListener('loadedmetadata', this._onLoadedMetaData);\n this.$video.removeEventListener('play', this._onPlay);\n document.removeEventListener('visibilitychange', this._onVisibilityChange);\n window.removeEventListener('resize', this._updateOverlay);\n\n this._destroyed = true;\n this._flashOn = false;\n this.stop(); // sets this._paused = true and this._active = false\n QrScanner._postWorkerMessage(this._qrEnginePromise, 'close');\n }\n\n async start(): Promise {\n if ((this._active && !this._paused) || this._destroyed) return;\n\n if (window.location.protocol !== 'https:') {\n // warn but try starting the camera anyways\n console.warn('The camera stream is only accessible if the page is transferred via https.');\n }\n\n this._active = true;\n if (document.hidden) return; // camera will be started as soon as tab is in foreground\n this._paused = false;\n if (this.$video.srcObject) {\n // camera stream already/still set\n await this.$video.play();\n return;\n }\n\n try {\n const { stream, facingMode } = await this._getCameraStream();\n if (!this._active || this._paused) {\n // was stopped in the meantime\n QrScanner._stopVideoStream(stream);\n return;\n }\n this._setVideoMirror(facingMode);\n this.$video.srcObject = stream;\n await this.$video.play();\n\n // Restart the flash if it was previously on\n if (this._flashOn) {\n this._flashOn = false; // force turnFlashOn to restart the flash\n this.turnFlashOn().catch(() => {});\n }\n } catch (e) {\n if (this._paused) return;\n this._active = false;\n throw e;\n }\n }\n\n stop(): void {\n this.pause();\n this._active = false;\n }\n\n async pause(stopStreamImmediately = false): Promise {\n this._paused = true;\n if (!this._active) return true;\n this.$video.pause();\n\n if (this.$overlay) {\n this.$overlay.style.display = 'none';\n }\n\n const stopStream = () => {\n if (this.$video.srcObject instanceof MediaStream) {\n // revoke srcObject only if it's a stream which was likely set by us\n QrScanner._stopVideoStream(this.$video.srcObject);\n this.$video.srcObject = null;\n }\n };\n\n if (stopStreamImmediately) {\n stopStream();\n return true;\n }\n\n await new Promise((resolve) => setTimeout(resolve, 300));\n if (!this._paused) return false;\n stopStream();\n return true;\n }\n\n async setCamera(facingModeOrDeviceId: QrScanner.FacingMode | QrScanner.DeviceId): Promise {\n if (facingModeOrDeviceId === this._preferredCamera) return;\n this._preferredCamera = facingModeOrDeviceId;\n // Restart the scanner with the new camera which will also update the video mirror and the scan region.\n await this._restartVideoStream();\n }\n\n static async scanImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n options: {\n scanRegion?: QrScanner.ScanRegion | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing?: boolean,\n alsoTryWithoutScanRegion?: boolean,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n },\n ): Promise;\n /** @deprecated */\n static async scanImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n scanRegion?: QrScanner.ScanRegion | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing?: boolean,\n alsoTryWithoutScanRegion?: boolean,\n ): Promise;\n static async scanImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n scanRegionOrOptions?: QrScanner.ScanRegion | {\n scanRegion?: QrScanner.ScanRegion | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing?: boolean,\n alsoTryWithoutScanRegion?: boolean,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n } | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing: boolean = false,\n alsoTryWithoutScanRegion: boolean = false,\n ): Promise {\n let scanRegion: QrScanner.ScanRegion | null | undefined;\n let returnDetailedScanResult = false;\n if (scanRegionOrOptions && (\n 'scanRegion' in scanRegionOrOptions\n || 'qrEngine' in scanRegionOrOptions\n || 'canvas' in scanRegionOrOptions\n || 'disallowCanvasResizing' in scanRegionOrOptions\n || 'alsoTryWithoutScanRegion' in scanRegionOrOptions\n || 'returnDetailedScanResult' in scanRegionOrOptions\n )) {\n // we got an options object using the new api\n scanRegion = scanRegionOrOptions.scanRegion;\n qrEngine = scanRegionOrOptions.qrEngine;\n canvas = scanRegionOrOptions.canvas;\n disallowCanvasResizing = scanRegionOrOptions.disallowCanvasResizing || false;\n alsoTryWithoutScanRegion = scanRegionOrOptions.alsoTryWithoutScanRegion || false;\n returnDetailedScanResult = true;\n } else if (scanRegionOrOptions || qrEngine || canvas || disallowCanvasResizing || alsoTryWithoutScanRegion) {\n console.warn('You\\'re using a deprecated api for scanImage which will be removed in the future.');\n } else {\n // Only imageOrFileOrBlobOrUrl was specified and we can't distinguish between new or old api usage. For\n // backwards compatibility we have to assume the old api for now. The options object is marked as non-\n // optional in the parameter list above to make clear that ScanResult instead of string is only returned if\n // an options object was provided. However, in the future once legacy support is removed, the options object\n // should become optional.\n console.warn('Note that the return type of scanImage will change in the future. To already switch to the '\n + 'new api today, you can pass returnDetailedScanResult: true.');\n }\n\n const gotExternalEngine = !!qrEngine;\n\n try {\n let image: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement;\n let canvasContext: CanvasRenderingContext2D;\n [qrEngine, image] = await Promise.all([\n qrEngine || QrScanner.createQrEngine(),\n QrScanner._loadImage(imageOrFileOrBlobOrUrl),\n ]);\n [canvas, canvasContext] = QrScanner._drawToCanvas(image, scanRegion, canvas, disallowCanvasResizing);\n\n if (qrEngine instanceof Worker) {\n const qrEngineWorker = qrEngine; // for ts to know that it's still a worker later in the event listeners\n if (!gotExternalEngine) {\n // Enable scanning of inverted color qr codes. Not using _postWorkerMessage as it's async\n qrEngineWorker.postMessage({ type: 'inversionMode', data: 'both' });\n }\n return await new Promise((resolve, reject) => {\n let timeout: number;\n let onMessage: (event: MessageEvent) => void;\n let onError: (error: ErrorEvent | string) => void;\n onMessage = (event: MessageEvent) => {\n if (event.data.type !== 'qrResult') {\n return;\n }\n qrEngineWorker.removeEventListener('message', onMessage);\n qrEngineWorker.removeEventListener('error', onError);\n clearTimeout(timeout);\n if (event.data.data !== null) {\n resolve(returnDetailedScanResult ? {\n data: event.data.data,\n cornerPoints: QrScanner._convertPoints(event.data.cornerPoints, scanRegion),\n } : event.data.data);\n } else {\n reject(QrScanner.NO_QR_CODE_FOUND);\n }\n };\n onError = (error: ErrorEvent | string) => {\n qrEngineWorker.removeEventListener('message', onMessage);\n qrEngineWorker.removeEventListener('error', onError);\n clearTimeout(timeout);\n const errorMessage = !error ? 'Unknown Error' : ((error as ErrorEvent).message || error);\n reject('Scanner error: ' + errorMessage);\n };\n qrEngineWorker.addEventListener('message', onMessage);\n qrEngineWorker.addEventListener('error', onError);\n timeout = setTimeout(() => onError('timeout'), 10000);\n const imageData = canvasContext.getImageData(0, 0, canvas!.width, canvas!.height);\n qrEngineWorker.postMessage({\n type: 'decode',\n data: imageData\n }, [imageData.data.buffer]);\n });\n } else {\n return await Promise.race([\n new Promise((resolve, reject) => window.setTimeout(\n () => reject('Scanner error: timeout'),\n 10000,\n )),\n (async (): Promise => {\n try {\n const [scanResult] = await qrEngine.detect(canvas!);\n if (!scanResult) throw QrScanner.NO_QR_CODE_FOUND;\n return returnDetailedScanResult ? {\n data: scanResult.rawValue,\n cornerPoints: QrScanner._convertPoints(scanResult.cornerPoints, scanRegion),\n } : scanResult.rawValue;\n } catch (e) {\n throw `Scanner error: ${(e as Error).message || e}`;\n }\n })(),\n ]);\n }\n } catch (e) {\n if (!scanRegion || !alsoTryWithoutScanRegion) throw e;\n const detailedScanResult = await QrScanner.scanImage(\n imageOrFileOrBlobOrUrl,\n { qrEngine, canvas, disallowCanvasResizing },\n );\n return returnDetailedScanResult ? detailedScanResult : detailedScanResult.data;\n } finally {\n if (!gotExternalEngine) {\n QrScanner._postWorkerMessage(qrEngine!, 'close');\n }\n }\n }\n\n setGrayscaleWeights(red: number, green: number, blue: number, useIntegerApproximation: boolean = true): void {\n // Note that for the native BarcodeDecoder or if the worker was destroyed, this is a no-op. However, the native\n // implementations work also well with colored qr codes.\n QrScanner._postWorkerMessage(\n this._qrEnginePromise,\n 'grayscaleWeights',\n { red, green, blue, useIntegerApproximation }\n );\n }\n\n setInversionMode(inversionMode: QrScanner.InversionMode): void {\n // Note that for the native BarcodeDecoder or if the worker was destroyed, this is a no-op. However, the native\n // implementations scan normal and inverted qr codes by default\n QrScanner._postWorkerMessage(this._qrEnginePromise, 'inversionMode', inversionMode);\n }\n\n static async createQrEngine(workerPath: string = QrScanner.WORKER_PATH): Promise {\n const hasNativeBarcodeDetector = 'BarcodeDetector' in window && BarcodeDetector.getSupportedFormats\n ? (await BarcodeDetector.getSupportedFormats()).includes('qr_code')\n : false;\n return hasNativeBarcodeDetector ? new BarcodeDetector({ formats: ['qr_code'] }) : new Worker(workerPath);\n }\n\n private _onPlay(): void {\n this._scanRegion = this._calculateScanRegion(this.$video);\n this._updateOverlay();\n if (this.$overlay) {\n this.$overlay.style.display = '';\n }\n this._scanFrame();\n }\n\n private _onLoadedMetaData(): void {\n this._scanRegion = this._calculateScanRegion(this.$video);\n this._updateOverlay();\n }\n\n private _onVisibilityChange(): void {\n if (document.hidden) {\n this.pause();\n } else if (this._active) {\n this.start();\n }\n }\n\n private _calculateScanRegion(video: HTMLVideoElement): QrScanner.ScanRegion {\n // Default scan region calculation. Note that this can be overwritten in the constructor.\n const smallestDimension = Math.min(video.videoWidth, video.videoHeight);\n const scanRegionSize = Math.round(2 / 3 * smallestDimension);\n return {\n x: Math.round((video.videoWidth - scanRegionSize) / 2),\n y: Math.round((video.videoHeight - scanRegionSize) / 2),\n width: scanRegionSize,\n height: scanRegionSize,\n downScaledWidth: this._legacyCanvasSize,\n downScaledHeight: this._legacyCanvasSize,\n };\n }\n\n private _updateOverlay(): void {\n requestAnimationFrame(() => {\n // Running in requestAnimationFrame which should avoid a potential additional re-flow for getComputedStyle\n // and offsetWidth, offsetHeight, offsetLeft, offsetTop.\n if (!this.$overlay) return;\n const video = this.$video;\n const videoWidth = video.videoWidth;\n const videoHeight = video.videoHeight;\n const elementWidth = video.offsetWidth;\n const elementHeight = video.offsetHeight;\n const elementX = video.offsetLeft;\n const elementY = video.offsetTop;\n\n const videoStyle = window.getComputedStyle(video);\n const videoObjectFit = videoStyle.objectFit;\n const videoAspectRatio = videoWidth / videoHeight;\n const elementAspectRatio = elementWidth / elementHeight;\n let videoScaledWidth: number;\n let videoScaledHeight: number;\n switch (videoObjectFit) {\n case 'none':\n videoScaledWidth = videoWidth;\n videoScaledHeight = videoHeight;\n break;\n case 'fill':\n videoScaledWidth = elementWidth;\n videoScaledHeight = elementHeight;\n break;\n default: // 'cover', 'contains', 'scale-down'\n if (videoObjectFit === 'cover'\n ? videoAspectRatio > elementAspectRatio\n : videoAspectRatio < elementAspectRatio) {\n // The scaled height is the element height\n // - for 'cover' if the video aspect ratio is wider than the element aspect ratio\n // (scaled height matches element height and scaled width overflows element width)\n // - for 'contains'/'scale-down' if element aspect ratio is wider than the video aspect ratio\n // (scaled height matched element height and element width overflows scaled width)\n videoScaledHeight = elementHeight;\n videoScaledWidth = videoScaledHeight * videoAspectRatio;\n } else {\n videoScaledWidth = elementWidth;\n videoScaledHeight = videoScaledWidth / videoAspectRatio;\n }\n if (videoObjectFit === 'scale-down') {\n // for 'scale-down' the dimensions are the minimum of 'contains' and 'none'\n videoScaledWidth = Math.min(videoScaledWidth, videoWidth);\n videoScaledHeight = Math.min(videoScaledHeight, videoHeight);\n }\n }\n\n // getComputedStyle is so nice to convert keywords (left, center, right, top, bottom) to percent and makes\n // sure to set the default of 50% if only one or no component was provided, therefore we can be sure that\n // both components are set. Additionally, it converts units other than px (e.g. rem) to px.\n const [videoX, videoY] = videoStyle.objectPosition.split(' ').map((length, i) => {\n const lengthValue = parseFloat(length);\n return length.endsWith('%')\n ? (!i ? elementWidth - videoScaledWidth : elementHeight - videoScaledHeight) * lengthValue / 100\n : lengthValue;\n });\n\n const regionWidth = this._scanRegion.width || videoWidth;\n const regionHeight = this._scanRegion.height || videoHeight;\n const regionX = this._scanRegion.x || 0;\n const regionY = this._scanRegion.y || 0;\n\n const overlayStyle = this.$overlay.style;\n overlayStyle.width = `${regionWidth / videoWidth * videoScaledWidth}px`;\n overlayStyle.height = `${regionHeight / videoHeight * videoScaledHeight}px`;\n overlayStyle.top = `${elementY + videoY + regionY / videoHeight * videoScaledHeight}px`;\n const isVideoMirrored = /scaleX\\(-1\\)/.test(video.style.transform!);\n overlayStyle.left = `${elementX\n + (isVideoMirrored ? elementWidth - videoX - videoScaledWidth : videoX)\n + (isVideoMirrored ? videoWidth - regionX - regionWidth : regionX) / videoWidth * videoScaledWidth}px`;\n // apply same mirror as on video\n overlayStyle.transform = video.style.transform;\n });\n }\n\n private static _convertPoints(\n points: QrScanner.Point[],\n scanRegion?: QrScanner.ScanRegion | null,\n ): QrScanner.Point[] {\n if (!scanRegion) return points;\n const offsetX = scanRegion.x || 0;\n const offsetY = scanRegion.y || 0;\n const scaleFactorX = scanRegion.width && scanRegion.downScaledWidth\n ? scanRegion.width / scanRegion.downScaledWidth\n : 1;\n const scaleFactorY = scanRegion.height && scanRegion.downScaledHeight\n ? scanRegion.height / scanRegion.downScaledHeight\n : 1;\n for (const point of points) {\n point.x = point.x * scaleFactorX + offsetX;\n point.y = point.y * scaleFactorY + offsetY;\n }\n return points;\n }\n\n private _scanFrame(): void {\n if (!this._active || this.$video.paused || this.$video.ended) return;\n // If requestVideoFrameCallback is available use that to avoid unnecessary scans on the same frame as the\n // camera's framerate can be lower than the screen refresh rate and this._maxScansPerSecond, especially in dark\n // settings where the exposure time is longer. Both, requestVideoFrameCallback and requestAnimationFrame are not\n // being fired if the tab is in the background, which is what we want.\n const requestFrame = 'requestVideoFrameCallback' in this.$video\n // @ts-ignore\n ? this.$video.requestVideoFrameCallback.bind(this.$video)\n : requestAnimationFrame;\n requestFrame(async () => {\n if (this.$video.readyState <= 1) {\n // Skip scans until the video is ready as drawImage() only works correctly on a video with readyState\n // > 1, see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage#Notes.\n // This also avoids false positives for videos paused after a successful scan which remains visible on\n // the canvas until the video is started again and ready.\n this._scanFrame();\n return;\n }\n\n const timeSinceLastScan = Date.now() - this._lastScanTimestamp;\n const minimumTimeBetweenScans = 1000 / this._maxScansPerSecond;\n if (timeSinceLastScan < minimumTimeBetweenScans) {\n await new Promise((resolve) => setTimeout(resolve, minimumTimeBetweenScans - timeSinceLastScan));\n }\n // console.log('Scan rate:', Math.round(1000 / (Date.now() - this._lastScanTimestamp)));\n this._lastScanTimestamp = Date.now();\n\n let result: QrScanner.ScanResult | undefined;\n try {\n result = await QrScanner.scanImage(this.$video, {\n scanRegion: this._scanRegion,\n qrEngine: this._qrEnginePromise,\n canvas: this.$canvas,\n });\n } catch (error) {\n if (!this._active) return;\n const errorMessage = (error as Error).message || error as string;\n if (errorMessage.includes('service unavailable')) {\n // When the native BarcodeDetector crashed, create a new one\n this._qrEnginePromise = QrScanner.createQrEngine();\n }\n this._onDecodeError(error as Error | string);\n }\n\n if (result) {\n if (this._onDecode) {\n this._onDecode(result);\n } else if (this._legacyOnDecode) {\n this._legacyOnDecode(result.data);\n }\n\n if (this.$codeOutlineHighlight) {\n clearTimeout(this._codeOutlineHighlightRemovalTimeout);\n this._codeOutlineHighlightRemovalTimeout = undefined;\n this.$codeOutlineHighlight.setAttribute(\n 'viewBox',\n `${this._scanRegion.x || 0} `\n + `${this._scanRegion.y || 0} `\n + `${this._scanRegion.width || this.$video.videoWidth} `\n + `${this._scanRegion.height || this.$video.videoHeight}`,\n );\n const polygon = this.$codeOutlineHighlight.firstElementChild!;\n polygon.setAttribute('points', result.cornerPoints.map(({x, y}) => `${x},${y}`).join(' '));\n this.$codeOutlineHighlight.style.display = '';\n }\n } else if (this.$codeOutlineHighlight && !this._codeOutlineHighlightRemovalTimeout) {\n // hide after timeout to make it flash less when on some frames the QR code is detected and on some not\n this._codeOutlineHighlightRemovalTimeout = setTimeout(\n () => this.$codeOutlineHighlight!.style.display = 'none',\n 100,\n );\n }\n\n this._scanFrame();\n });\n }\n\n private _onDecodeError(error: Error | string): void {\n // default error handler; can be overwritten in the constructor\n if (error === QrScanner.NO_QR_CODE_FOUND) return;\n console.log(error);\n }\n\n private async _getCameraStream(): Promise<{ stream: MediaStream, facingMode: QrScanner.FacingMode }> {\n if (!navigator.mediaDevices) throw 'Camera not found.';\n\n const preferenceType = /^(environment|user)$/.test(this._preferredCamera)\n ? 'facingMode'\n : 'deviceId';\n const constraintsWithoutCamera: Array = [{\n width: { min: 1024 }\n }, {\n width: { min: 768 }\n }, {}];\n const constraintsWithCamera = constraintsWithoutCamera.map((constraint) => Object.assign({}, constraint, {\n [preferenceType]: { exact: this._preferredCamera },\n }));\n\n for (const constraints of [...constraintsWithCamera, ...constraintsWithoutCamera]) {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ video: constraints, audio: false });\n // Try to determine the facing mode from the stream, otherwise use a guess or 'environment' as\n // default. Note that the guess is not always accurate as Safari returns cameras of different facing\n // mode, even for exact facingMode constraints.\n const facingMode = this._getFacingMode(stream)\n || (constraints.facingMode\n ? this._preferredCamera as QrScanner.FacingMode // a facing mode we were able to fulfill\n : (this._preferredCamera === 'environment'\n ? 'user' // switch as _preferredCamera was environment but we are not able to fulfill it\n : 'environment' // switch from unfulfilled user facingMode or default to environment\n )\n );\n return { stream, facingMode };\n } catch (e) {}\n }\n\n throw 'Camera not found.';\n }\n\n private async _restartVideoStream(): Promise {\n // Note that we always pause the stream and not only if !this._paused as even if this._paused === true, the\n // stream might still be running, as it's by default only stopped after a delay of 300ms.\n const wasPaused = this._paused;\n const paused = await this.pause(true);\n if (!paused || wasPaused || !this._active) return;\n await this.start();\n }\n\n private static _stopVideoStream(stream : MediaStream): void {\n for (const track of stream.getTracks()) {\n track.stop(); // note that this will also automatically turn the flashlight off\n stream.removeTrack(track);\n }\n }\n\n private _setVideoMirror(facingMode: QrScanner.FacingMode): void {\n // in user facing mode mirror the video to make it easier for the user to position the QR code\n const scaleFactor = facingMode === 'user'? -1 : 1;\n this.$video.style.transform = 'scaleX(' + scaleFactor + ')';\n }\n\n private _getFacingMode(videoStream: MediaStream): QrScanner.FacingMode | null {\n const videoTrack = videoStream.getVideoTracks()[0];\n if (!videoTrack) return null; // unknown\n // inspired by https://github.com/JodusNodus/react-qr-reader/blob/master/src/getDeviceId.js#L13\n return /rear|back|environment/i.test(videoTrack.label)\n ? 'environment'\n : /front|user|face/i.test(videoTrack.label)\n ? 'user'\n : null; // unknown\n }\n\n private static _drawToCanvas(\n image: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement,\n scanRegion?: QrScanner.ScanRegion | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing= false,\n ): [HTMLCanvasElement, CanvasRenderingContext2D] {\n canvas = canvas || document.createElement('canvas');\n const scanRegionX = scanRegion && scanRegion.x ? scanRegion.x : 0;\n const scanRegionY = scanRegion && scanRegion.y ? scanRegion.y : 0;\n const scanRegionWidth = scanRegion && scanRegion.width\n ? scanRegion.width\n : (image as HTMLVideoElement).videoWidth || image.width as number;\n const scanRegionHeight = scanRegion && scanRegion.height\n ? scanRegion.height\n : (image as HTMLVideoElement).videoHeight || image.height as number;\n\n if (!disallowCanvasResizing) {\n const canvasWidth = scanRegion && scanRegion.downScaledWidth\n ? scanRegion.downScaledWidth\n : scanRegionWidth;\n const canvasHeight = scanRegion && scanRegion.downScaledHeight\n ? scanRegion.downScaledHeight\n : scanRegionHeight;\n // Setting the canvas width or height clears the canvas, even if the values didn't change, therefore only\n // set them if they actually changed.\n if (canvas.width !== canvasWidth) {\n canvas.width = canvasWidth;\n }\n if (canvas.height !== canvasHeight) {\n canvas.height = canvasHeight;\n }\n }\n\n const context = canvas.getContext('2d', { alpha: false })!;\n context.imageSmoothingEnabled = false; // gives less blurry images\n context.drawImage(\n image,\n scanRegionX, scanRegionY, scanRegionWidth, scanRegionHeight,\n 0, 0, canvas.width, canvas.height,\n );\n return [canvas, context];\n }\n\n private static async _loadImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n ): Promise {\n if (imageOrFileOrBlobOrUrl instanceof Image) {\n await QrScanner._awaitImageLoad(imageOrFileOrBlobOrUrl);\n return imageOrFileOrBlobOrUrl;\n } else if (imageOrFileOrBlobOrUrl instanceof HTMLVideoElement\n || imageOrFileOrBlobOrUrl instanceof HTMLCanvasElement\n || imageOrFileOrBlobOrUrl instanceof SVGImageElement\n || 'OffscreenCanvas' in window && imageOrFileOrBlobOrUrl instanceof OffscreenCanvas\n || 'ImageBitmap' in window && imageOrFileOrBlobOrUrl instanceof ImageBitmap) {\n return imageOrFileOrBlobOrUrl;\n } else if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob\n || imageOrFileOrBlobOrUrl instanceof URL || typeof imageOrFileOrBlobOrUrl === 'string') {\n const image = new Image();\n if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob) {\n image.src = URL.createObjectURL(imageOrFileOrBlobOrUrl);\n } else {\n image.src = imageOrFileOrBlobOrUrl.toString();\n }\n try {\n await QrScanner._awaitImageLoad(image);\n return image;\n } finally {\n if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob) {\n URL.revokeObjectURL(image.src);\n }\n }\n } else {\n throw 'Unsupported image type.';\n }\n }\n\n private static async _awaitImageLoad(image: HTMLImageElement): Promise {\n if (image.complete && image.naturalWidth !== 0) return; // already loaded\n await new Promise((resolve, reject) => {\n const listener = (event: ErrorEvent | Event) => {\n image.removeEventListener('load', listener);\n image.removeEventListener('error', listener);\n if (event instanceof ErrorEvent) {\n reject('Image load error');\n } else {\n resolve();\n }\n };\n image.addEventListener('load', listener);\n image.addEventListener('error', listener);\n });\n }\n\n private static async _postWorkerMessage(\n qrEngineOrQrEnginePromise: Worker | BarcodeDetector | Promise,\n type: string,\n data?: any,\n ): Promise {\n const qrEngine = await qrEngineOrQrEnginePromise;\n if (!(qrEngine instanceof Worker)) return;\n qrEngine.postMessage({ type, data });\n }\n}\n\ndeclare namespace QrScanner {\n export interface ScanRegion {\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n downScaledWidth?: number;\n downScaledHeight?: number;\n }\n\n export type FacingMode = 'environment' | 'user';\n export type DeviceId = string;\n\n export interface Camera {\n id: DeviceId;\n label: string;\n }\n\n export type InversionMode = 'original' | 'invert' | 'both';\n\n export interface Point {\n x: number;\n y: number;\n }\n\n export interface ScanResult {\n data: string;\n // In clockwise order, starting at top left, but this might not be guaranteed in the future.\n cornerPoints: QrScanner.Point[];\n }\n}\n\n// simplified from https://wicg.github.io/shape-detection-api/#barcode-detection-api\ndeclare class BarcodeDetector {\n constructor(options?: { formats: string[] });\n static getSupportedFormats(): Promise;\n detect(image: ImageBitmapSource): Promise>;\n}\n"],"names":["QrScanner","constructor","video","onDecode","canvasSizeOrOnDecodeErrorOrOptions","canvasSizeOrCalculateScanRegion","preferredCamera","DEFAULT_CANVAS_SIZE","$video","$canvas","document","createElement","_onDecode","console","warn","_legacyOnDecode","_onDecodeError","options","onDecodeError","_calculateScanRegion","calculateScanRegion","_preferredCamera","_legacyCanvasSize","_maxScansPerSecond","maxScansPerSecond","_onPlay","bind","_onLoadedMetaData","_onVisibilityChange","_updateOverlay","disablePictureInPicture","playsInline","muted","shouldHideVideo","hidden","body","contains","appendChild","highlightScanRegion","highlightCodeOutline","$overlay","overlay","overlayStyle","position","display","pointerEvents","classList","add","gotExternalOverlay","outline","videoContainer","insertBefore","nextSibling","$codeOutlineHighlight","createElementNS","polygon","codeOutlineHighlightStyle","width","height","setAttribute","fill","stroke","strokeWidth","_scanRegion","requestAnimationFrame","videoStyle","style","setProperty","visibility","opacity","parentElement","removeChild","addEventListener","window","_qrEnginePromise","createQrEngine","hasCamera","length","listCameras","e","requestLabels","navigator","mediaDevices","filter","device","openedStream","every","enumerateCameras","camera","label","getUserMedia","audio","map","i","id","deviceId","_stopVideoStream","hasFlash","stream","srcObject","MediaStream","_getCameraStream","getVideoTracks","getSettings","isFlashOn","_flashOn","toggleFlash","turnFlashOff","turnFlashOn","_destroyed","_active","_paused","applyConstraints","advanced","torch","_restartVideoStream","destroy","removeEventListener","stop","_postWorkerMessage","start","location","protocol","play","facingMode","_setVideoMirror","catch","pause","stopStreamImmediately","stopStream","Promise","resolve","setTimeout","setCamera","facingModeOrDeviceId","scanImage","imageOrFileOrBlobOrUrl","scanRegionOrOptions","qrEngine","canvas","disallowCanvasResizing","alsoTryWithoutScanRegion","scanRegion","returnDetailedScanResult","image","canvasContext","all","_loadImage","_drawToCanvas","Worker","gotExternalEngine","qrEngineWorker","postMessage","type","data","reject","timeout","onMessage","onError","event","clearTimeout","cornerPoints","_convertPoints","NO_QR_CODE_FOUND","error","imageData","buffer","race","scanResult","rawValue","message","detailedScanResult","setGrayscaleWeights","red","green","blue","useIntegerApproximation","setInversionMode","inversionMode","workerPath","WORKER_PATH","includes","BarcodeDetector","getSupportedFormats","formats","_scanFrame","x","Math","round","videoWidth","scanRegionSize","y","videoHeight","downScaledWidth","downScaledHeight","videoObjectFit","videoScaledWidth","videoScaledHeight","elementWidth","elementHeight","videoAspectRatio","elementAspectRatio","min","videoY","lengthValue","parseFloat","endsWith","regionWidth","regionHeight","top","elementY","regionY","left","elementX","isVideoMirrored","videoX","regionX","transform","points","point","scaleFactorX","offsetX","scaleFactorY","offsetY","paused","ended","requestVideoFrameCallback","readyState","timeSinceLastScan","minimumTimeBetweenScans","_lastScanTimestamp","Date","now","result","_codeOutlineHighlightRemovalTimeout","undefined","join","log","constraint","preferenceType","exact","constraints","wasPaused","track","removeTrack","_getFacingMode","videoStream","test","videoTrack","scanRegionWidth","scanRegionHeight","canvasWidth","canvasHeight","alpha","context","imageSmoothingEnabled","drawImage","scanRegionX","scanRegionY","Image","_awaitImageLoad","HTMLVideoElement","HTMLCanvasElement","SVGImageElement","OffscreenCanvas","ImageBitmap","File","Blob","URL","src","createObjectURL","toString","revokeObjectURL","complete","naturalWidth","listener","ErrorEvent","qrEngineOrQrEnginePromise"],"mappings":"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"} \ No newline at end of file diff --git a/qr-scanner.umd.min.js b/qr-scanner.umd.min.js index f748fa2..6580402 100644 --- a/qr-scanner.umd.min.js +++ b/qr-scanner.umd.min.js @@ -18,13 +18,13 @@ t);u=setTimeout(()=>t("timeout"),1E4);let w=k.getImageData(0,0,d.width,d.height) new BarcodeDetector({formats:["qr_code"]}):new Worker(a)}_onPlay(){this._scanRegion=this._calculateScanRegion(this.$video);this._updateOverlay();this.$overlay&&(this.$overlay.style.display="");this._scanFrame()}_onLoadedMetaData(){this._scanRegion=this._calculateScanRegion(this.$video);this._updateOverlay()}_onVisibilityChange(){document.hidden?this.pause():this._active&&this.start()}_calculateScanRegion(a){let b=Math.round(2/3*Math.min(a.videoWidth,a.videoHeight));return{x:Math.round((a.videoWidth- b)/2),y:Math.round((a.videoHeight-b)/2),width:b,height:b,downScaledWidth:this._legacyCanvasSize,downScaledHeight:this._legacyCanvasSize}}_updateOverlay(){requestAnimationFrame(()=>{if(this.$overlay){var a=this.$video,b=a.videoWidth,c=a.videoHeight,d=a.offsetWidth,f=a.offsetHeight,h=a.offsetLeft,m=a.offsetTop,n=window.getComputedStyle(a),q=n.objectFit,k=b/c,g=d/f;switch(q){case "none":var l=b;var p=c;break;case "fill":l=d;p=f;break;default:("cover"===q?k>g:k{const x=parseFloat(w);return w.endsWith("%")?(r?f-p:d-l)*x/100:x});n=this._scanRegion.width||b;g=this._scanRegion.height||c;q=this._scanRegion.x||0;var t=this._scanRegion.y||0;k=this.$overlay.style;k.width=`${n/b*l}px`;k.height=`${g/c*p}px`;k.top=`${m+v+t/c*p}px`;c=/scaleX\(-1\)/.test(a.style.transform);k.left=`${h+(c?d-u-l:u)+(c?b-q-n:q)/b*l}px`;k.transform=a.style.transform}})}static _convertPoints(a,b){if(!b)return a; -let c=b.x||0,d=b.y||0,f=b.width&&b.downScaledWidth?b.width/b.downScaledWidth:1;b=b.height&&b.downScaledHeight?b.height/b.downScaledHeight:1;for(let h of a)h.x=h.x*f+c,h.y=h.y*b+d;return a}_scanFrame(){!this._active||this.$video.paused||this.$video.ended||requestAnimationFrame(async()=>{if(!(1>=this.$video.readyState)){var a=Date.now()-this._lastScanTimestamp,b=1E3/this._maxScansPerSecond;asetTimeout(d,b-a));this._lastScanTimestamp=Date.now();try{var c=await e.scanImage(this.$video, -{scanRegion:this._scanRegion,qrEngine:this._qrEnginePromise,canvas:this.$canvas})}catch(d){if(!this._active)return;(d.message||d).includes("service unavailable")&&(this._qrEnginePromise=e.createQrEngine());this._onDecodeError(d)}c?(this._onDecode?this._onDecode(c):this._legacyOnDecode&&this._legacyOnDecode(c.data),this.$codeOutlineHighlight&&(clearTimeout(this._codeOutlineHighlightRemovalTimeout),this._codeOutlineHighlightRemovalTimeout=void 0,this.$codeOutlineHighlight.setAttribute("viewBox",`${this._scanRegion.x|| -0} `+`${this._scanRegion.y||0} `+`${this._scanRegion.width||this.$video.videoWidth} `+`${this._scanRegion.height||this.$video.videoHeight}`),this.$codeOutlineHighlight.firstElementChild.setAttribute("points",c.cornerPoints.map(({x:d,y:f})=>`${d},${f}`).join(" ")),this.$codeOutlineHighlight.style.display="")):this.$codeOutlineHighlight&&!this._codeOutlineHighlightRemovalTimeout&&(this._codeOutlineHighlightRemovalTimeout=setTimeout(()=>this.$codeOutlineHighlight.style.display="none",100))}this._scanFrame()})}_onDecodeError(a){a!== -e.NO_QR_CODE_FOUND&&console.log(a)}async _getCameraStream(){if(!navigator.mediaDevices)throw"Camera not found.";let a=/^(environment|user)$/.test(this._preferredCamera)?"facingMode":"deviceId",b=[{width:{min:1024}},{width:{min:768}},{}],c=b.map(d=>Object.assign({},d,{[a]:{exact:this._preferredCamera}}));for(let d of[...c,...b])try{let f=await navigator.mediaDevices.getUserMedia({video:d,audio:!1}),h=this._getFacingMode(f)||(d.facingMode?this._preferredCamera:"environment"===this._preferredCamera? -"user":"environment");return{stream:f,facingMode:h}}catch(f){}throw"Camera not found.";}async _restartVideoStream(){let a=this._paused;await this.pause(!0)&&!a&&this._active&&await this.start()}static _stopVideoStream(a){for(let b of a.getTracks())b.stop(),a.removeTrack(b)}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===a?-1:1)+")"}_getFacingMode(a){return(a=a.getVideoTracks()[0])?/rear|back|environment/i.test(a.label)?"environment":/front|user|face/i.test(a.label)?"user":null: -null}static _drawToCanvas(a,b,c,d=!1){c=c||document.createElement("canvas");let f=b&&b.x?b.x:0,h=b&&b.y?b.y:0,m=b&&b.width?b.width:a.videoWidth||a.width,n=b&&b.height?b.height:a.videoHeight||a.height;d||(d=b&&b.downScaledWidth?b.downScaledWidth:m,b=b&&b.downScaledHeight?b.downScaledHeight:n,c.width!==d&&(c.width=d),c.height!==b&&(c.height=b));b=c.getContext("2d",{alpha:!1});b.imageSmoothingEnabled=!1;b.drawImage(a,f,h,m,n,0,0,c.width,c.height);return[c,b]}static async _loadImage(a){if(a instanceof -Image)return await e._awaitImageLoad(a),a;if(a instanceof HTMLVideoElement||a instanceof HTMLCanvasElement||a instanceof SVGImageElement||"OffscreenCanvas"in window&&a instanceof OffscreenCanvas||"ImageBitmap"in window&&a instanceof ImageBitmap)return a;if(a instanceof File||a instanceof Blob||a instanceof URL||"string"===typeof a){let b=new Image;b.src=a instanceof File||a instanceof Blob?URL.createObjectURL(a):a.toString();try{return await e._awaitImageLoad(b),b}finally{(a instanceof File||a instanceof -Blob)&&URL.revokeObjectURL(b.src)}}else throw"Unsupported image type.";}static async _awaitImageLoad(a){a.complete&&0!==a.naturalWidth||await new Promise((b,c)=>{let d=f=>{a.removeEventListener("load",d);a.removeEventListener("error",d);f instanceof ErrorEvent?c("Image load error"):b()};a.addEventListener("load",d);a.addEventListener("error",d)})}static async _postWorkerMessage(a,b,c){a=await a;a instanceof Worker&&a.postMessage({type:b,data:c})}}e.DEFAULT_CANVAS_SIZE=400;e.NO_QR_CODE_FOUND="No QR code found"; -e.WORKER_PATH="qr-scanner-worker.min.js";return e}) +let c=b.x||0,d=b.y||0,f=b.width&&b.downScaledWidth?b.width/b.downScaledWidth:1;b=b.height&&b.downScaledHeight?b.height/b.downScaledHeight:1;for(let h of a)h.x=h.x*f+c,h.y=h.y*b+d;return a}_scanFrame(){!this._active||this.$video.paused||this.$video.ended||("requestVideoFrameCallback"in this.$video?this.$video.requestVideoFrameCallback.bind(this.$video):requestAnimationFrame)(async()=>{if(!(1>=this.$video.readyState)){var a=Date.now()-this._lastScanTimestamp,b=1E3/this._maxScansPerSecond;a +setTimeout(d,b-a));this._lastScanTimestamp=Date.now();try{var c=await e.scanImage(this.$video,{scanRegion:this._scanRegion,qrEngine:this._qrEnginePromise,canvas:this.$canvas})}catch(d){if(!this._active)return;(d.message||d).includes("service unavailable")&&(this._qrEnginePromise=e.createQrEngine());this._onDecodeError(d)}c?(this._onDecode?this._onDecode(c):this._legacyOnDecode&&this._legacyOnDecode(c.data),this.$codeOutlineHighlight&&(clearTimeout(this._codeOutlineHighlightRemovalTimeout),this._codeOutlineHighlightRemovalTimeout= +void 0,this.$codeOutlineHighlight.setAttribute("viewBox",`${this._scanRegion.x||0} `+`${this._scanRegion.y||0} `+`${this._scanRegion.width||this.$video.videoWidth} `+`${this._scanRegion.height||this.$video.videoHeight}`),this.$codeOutlineHighlight.firstElementChild.setAttribute("points",c.cornerPoints.map(({x:d,y:f})=>`${d},${f}`).join(" ")),this.$codeOutlineHighlight.style.display="")):this.$codeOutlineHighlight&&!this._codeOutlineHighlightRemovalTimeout&&(this._codeOutlineHighlightRemovalTimeout= +setTimeout(()=>this.$codeOutlineHighlight.style.display="none",100))}this._scanFrame()})}_onDecodeError(a){a!==e.NO_QR_CODE_FOUND&&console.log(a)}async _getCameraStream(){if(!navigator.mediaDevices)throw"Camera not found.";let a=/^(environment|user)$/.test(this._preferredCamera)?"facingMode":"deviceId",b=[{width:{min:1024}},{width:{min:768}},{}],c=b.map(d=>Object.assign({},d,{[a]:{exact:this._preferredCamera}}));for(let d of[...c,...b])try{let f=await navigator.mediaDevices.getUserMedia({video:d, +audio:!1}),h=this._getFacingMode(f)||(d.facingMode?this._preferredCamera:"environment"===this._preferredCamera?"user":"environment");return{stream:f,facingMode:h}}catch(f){}throw"Camera not found.";}async _restartVideoStream(){let a=this._paused;await this.pause(!0)&&!a&&this._active&&await this.start()}static _stopVideoStream(a){for(let b of a.getTracks())b.stop(),a.removeTrack(b)}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===a?-1:1)+")"}_getFacingMode(a){return(a=a.getVideoTracks()[0])? +/rear|back|environment/i.test(a.label)?"environment":/front|user|face/i.test(a.label)?"user":null:null}static _drawToCanvas(a,b,c,d=!1){c=c||document.createElement("canvas");let f=b&&b.x?b.x:0,h=b&&b.y?b.y:0,m=b&&b.width?b.width:a.videoWidth||a.width,n=b&&b.height?b.height:a.videoHeight||a.height;d||(d=b&&b.downScaledWidth?b.downScaledWidth:m,b=b&&b.downScaledHeight?b.downScaledHeight:n,c.width!==d&&(c.width=d),c.height!==b&&(c.height=b));b=c.getContext("2d",{alpha:!1});b.imageSmoothingEnabled=!1; +b.drawImage(a,f,h,m,n,0,0,c.width,c.height);return[c,b]}static async _loadImage(a){if(a instanceof Image)return await e._awaitImageLoad(a),a;if(a instanceof HTMLVideoElement||a instanceof HTMLCanvasElement||a instanceof SVGImageElement||"OffscreenCanvas"in window&&a instanceof OffscreenCanvas||"ImageBitmap"in window&&a instanceof ImageBitmap)return a;if(a instanceof File||a instanceof Blob||a instanceof URL||"string"===typeof a){let b=new Image;b.src=a instanceof File||a instanceof Blob?URL.createObjectURL(a): +a.toString();try{return await e._awaitImageLoad(b),b}finally{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src)}}else throw"Unsupported image type.";}static async _awaitImageLoad(a){a.complete&&0!==a.naturalWidth||await new Promise((b,c)=>{let d=f=>{a.removeEventListener("load",d);a.removeEventListener("error",d);f instanceof ErrorEvent?c("Image load error"):b()};a.addEventListener("load",d);a.addEventListener("error",d)})}static async _postWorkerMessage(a,b,c){a=await a;a instanceof +Worker&&a.postMessage({type:b,data:c})}}e.DEFAULT_CANVAS_SIZE=400;e.NO_QR_CODE_FOUND="No QR code found";e.WORKER_PATH="qr-scanner-worker.min.js";return e}) //# sourceMappingURL=qr-scanner.umd.min.js.map diff --git a/qr-scanner.umd.min.js.map b/qr-scanner.umd.min.js.map index a9055fe..08dd400 100644 --- a/qr-scanner.umd.min.js.map +++ b/qr-scanner.umd.min.js.map @@ -1 +1 @@ -{"version":3,"file":"qr-scanner.umd.min.js","sources":["src/qr-scanner.ts"],"sourcesContent":["export default class QrScanner {\n static readonly DEFAULT_CANVAS_SIZE = 400;\n static readonly NO_QR_CODE_FOUND = 'No QR code found';\n static WORKER_PATH = 'qr-scanner-worker.min.js';\n\n static async hasCamera(): Promise {\n try {\n return !!(await QrScanner.listCameras(false)).length;\n } catch (e) {\n return false;\n }\n }\n\n static async listCameras(requestLabels = false): Promise> {\n if (!navigator.mediaDevices) return [];\n\n const enumerateCameras = async (): Promise> =>\n (await navigator.mediaDevices.enumerateDevices()).filter((device) => device.kind === 'videoinput');\n\n // Note that enumerateDevices can always be called and does not prompt the user for permission.\n // However, enumerateDevices only includes device labels if served via https and an active media stream exists\n // or permission to access the camera was given. Therefore, if we're not getting labels but labels are requested\n // ask for camera permission by opening a stream.\n let openedStream: MediaStream | undefined;\n try {\n if (requestLabels && (await enumerateCameras()).every((camera) => !camera.label)) {\n openedStream = await navigator.mediaDevices.getUserMedia({ audio: false, video: true });\n }\n } catch (e) {\n // Fail gracefully, especially if the device has no camera or on mobile when the camera is already in use\n // and some browsers disallow a second stream.\n }\n\n try {\n return (await enumerateCameras()).map((camera, i) => ({\n id: camera.deviceId,\n label: camera.label || (i === 0 ? 'Default Camera' : `Camera ${i + 1}`),\n }));\n } finally {\n // close the stream we just opened for getting camera access for listing the device labels\n if (openedStream) {\n console.warn('Call listCameras after successfully starting a QR scanner to avoid creating '\n + 'a temporary video stream');\n QrScanner._stopVideoStream(openedStream);\n }\n }\n }\n\n readonly $video: HTMLVideoElement;\n readonly $canvas: HTMLCanvasElement;\n readonly $overlay?: HTMLDivElement;\n private readonly $codeOutlineHighlight?: SVGSVGElement;\n private readonly _onDecode?: (result: QrScanner.ScanResult) => void;\n private readonly _legacyOnDecode?: (result: string) => void;\n private readonly _legacyCanvasSize: number = QrScanner.DEFAULT_CANVAS_SIZE;\n private _preferredCamera: QrScanner.FacingMode | QrScanner.DeviceId = 'environment';\n private readonly _maxScansPerSecond: number = 25;\n private _lastScanTimestamp: number = -1;\n private _scanRegion: QrScanner.ScanRegion;\n private _codeOutlineHighlightRemovalTimeout?: number;\n private _qrEnginePromise: Promise\n private _active: boolean = false;\n private _paused: boolean = false;\n private _flashOn: boolean = false;\n private _destroyed: boolean = false;\n\n constructor(\n video: HTMLVideoElement,\n onDecode: (result: QrScanner.ScanResult) => void,\n options: {\n onDecodeError?: (error: Error | string) => void,\n calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n maxScansPerSecond?: number;\n highlightScanRegion?: boolean,\n highlightCodeOutline?: boolean,\n overlay?: HTMLDivElement,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n },\n );\n /** @deprecated */\n constructor(\n video: HTMLVideoElement,\n onDecode: (result: string) => void,\n onDecodeError?: (error: Error | string) => void,\n calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n );\n /** @deprecated */\n constructor(\n video: HTMLVideoElement,\n onDecode: (result: string) => void,\n onDecodeError?: (error: Error | string) => void,\n canvasSize?: number,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n );\n /** @deprecated */\n constructor(video: HTMLVideoElement, onDecode: (result: string) => void, canvasSize?: number);\n constructor(\n video: HTMLVideoElement,\n onDecode: ((result: QrScanner.ScanResult) => void) | ((result: string) => void),\n canvasSizeOrOnDecodeErrorOrOptions?: number | ((error: Error | string) => void) | {\n onDecodeError?: (error: Error | string) => void,\n calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n maxScansPerSecond?: number;\n highlightScanRegion?: boolean,\n highlightCodeOutline?: boolean,\n overlay?: HTMLDivElement,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n },\n canvasSizeOrCalculateScanRegion?: number | ((video: HTMLVideoElement) => QrScanner.ScanRegion),\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n ) {\n this.$video = video;\n this.$canvas = document.createElement('canvas');\n\n if (canvasSizeOrOnDecodeErrorOrOptions && typeof canvasSizeOrOnDecodeErrorOrOptions === 'object') {\n // we got an options object using the new api\n this._onDecode = onDecode as QrScanner['_onDecode'];\n } else {\n if (canvasSizeOrOnDecodeErrorOrOptions || canvasSizeOrCalculateScanRegion || preferredCamera) {\n console.warn('You\\'re using a deprecated version of the QrScanner constructor which will be removed in '\n + 'the future');\n } else {\n // Only video and onDecode were specified and we can't distinguish between new or old api usage. For\n // backwards compatibility we have to assume the old api for now. The options object is marked as non-\n // optional in the parameter list above to make clear that ScanResult instead of string is only passed\n // if an options object was provided. However, in the future once legacy support is removed, the options\n // object should become optional.\n console.warn('Note that the type of the scan result passed to onDecode will change in the future. '\n + 'To already switch to the new api today, you can pass returnDetailedScanResult: true.');\n }\n this._legacyOnDecode = onDecode as QrScanner['_legacyOnDecode'];\n }\n\n const options = typeof canvasSizeOrOnDecodeErrorOrOptions === 'object'\n ? canvasSizeOrOnDecodeErrorOrOptions\n : {};\n this._onDecodeError = options.onDecodeError || (typeof canvasSizeOrOnDecodeErrorOrOptions === 'function'\n ? canvasSizeOrOnDecodeErrorOrOptions\n : this._onDecodeError);\n this._calculateScanRegion = options.calculateScanRegion || (typeof canvasSizeOrCalculateScanRegion==='function'\n ? canvasSizeOrCalculateScanRegion\n : this._calculateScanRegion);\n this._preferredCamera = options.preferredCamera || preferredCamera || this._preferredCamera;\n this._legacyCanvasSize = typeof canvasSizeOrOnDecodeErrorOrOptions === 'number'\n ? canvasSizeOrOnDecodeErrorOrOptions\n : typeof canvasSizeOrCalculateScanRegion === 'number'\n ? canvasSizeOrCalculateScanRegion\n : this._legacyCanvasSize;\n this._maxScansPerSecond = options.maxScansPerSecond || this._maxScansPerSecond;\n\n this._onPlay = this._onPlay.bind(this);\n this._onLoadedMetaData = this._onLoadedMetaData.bind(this);\n this._onVisibilityChange = this._onVisibilityChange.bind(this);\n this._updateOverlay = this._updateOverlay.bind(this);\n\n // @ts-ignore\n video.disablePictureInPicture = true;\n // Allow inline playback on iPhone instead of requiring full screen playback,\n // see https://webkit.org/blog/6784/new-video-policies-for-ios/\n // @ts-ignore\n video.playsInline = true;\n // Allow play() on iPhone without requiring a user gesture. Should not really be needed as camera stream\n // includes no audio, but just to be safe.\n video.muted = true;\n\n // Avoid Safari stopping the video stream on a hidden video.\n // See https://github.com/cozmo/jsQR/issues/185\n let shouldHideVideo = false;\n if (video.hidden) {\n video.hidden = false;\n shouldHideVideo = true;\n }\n if (!document.body.contains(video)) {\n document.body.appendChild(video);\n shouldHideVideo = true;\n }\n const videoContainer = video.parentElement!;\n\n if (options.highlightScanRegion || options.highlightCodeOutline) {\n const gotExternalOverlay = !!options.overlay;\n this.$overlay = options.overlay || document.createElement('div');\n const overlayStyle = this.$overlay.style;\n overlayStyle.position = 'absolute';\n overlayStyle.display = 'none';\n overlayStyle.pointerEvents = 'none';\n this.$overlay.classList.add('scan-region-highlight');\n if (!gotExternalOverlay && options.highlightScanRegion) {\n // default style; can be overwritten via css\n overlayStyle.outline = '#e9b213 solid 5px';\n videoContainer.insertBefore(this.$overlay, this.$video.nextSibling);\n }\n if (options.highlightCodeOutline) {\n this.$codeOutlineHighlight = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');\n this.$codeOutlineHighlight.appendChild(polygon);\n this.$overlay.appendChild(this.$codeOutlineHighlight);\n const codeOutlineHighlightStyle = this.$codeOutlineHighlight.style;\n codeOutlineHighlightStyle.width = '100%';\n codeOutlineHighlightStyle.height = '100%';\n // to support distorted videos, e.g. via object-fit: fill\n this.$codeOutlineHighlight.setAttribute('preserveAspectRatio', 'none');\n codeOutlineHighlightStyle.display = 'none';\n this.$codeOutlineHighlight.classList.add('code-outline-highlight');\n // default style; can be overwritten via css\n codeOutlineHighlightStyle.fill = 'none';\n codeOutlineHighlightStyle.stroke = '#e9b213';\n codeOutlineHighlightStyle.strokeWidth = '4';\n }\n }\n this._scanRegion = this._calculateScanRegion(video);\n\n requestAnimationFrame(() => {\n // Checking in requestAnimationFrame which should avoid a potential additional re-flow for getComputedStyle.\n const videoStyle = window.getComputedStyle(video);\n if (videoStyle.display === 'none') {\n video.style.setProperty('display', 'block', 'important');\n shouldHideVideo = true;\n }\n if (videoStyle.visibility !== 'visible') {\n video.style.setProperty('visibility', 'visible', 'important');\n shouldHideVideo = true;\n }\n if (shouldHideVideo) {\n // Hide the video in a way that doesn't cause Safari to stop the playback.\n console.warn('QrScanner has overwritten the video hiding style to avoid Safari stopping the playback.');\n video.style.opacity = '0';\n video.style.width = '0';\n video.style.height = '0';\n if (this.$overlay && this.$overlay.parentElement) {\n this.$overlay.parentElement.removeChild(this.$overlay);\n }\n // @ts-ignore\n delete this.$overlay!;\n // @ts-ignore\n delete this.$codeOutlineHighlight!;\n }\n\n if (this.$overlay) {\n this._updateOverlay();\n }\n });\n\n video.addEventListener('play', this._onPlay);\n video.addEventListener('loadedmetadata', this._onLoadedMetaData);\n document.addEventListener('visibilitychange', this._onVisibilityChange);\n window.addEventListener('resize', this._updateOverlay);\n\n this._qrEnginePromise = QrScanner.createQrEngine();\n }\n\n async hasFlash(): Promise {\n let stream: MediaStream | undefined;\n try {\n if (this.$video.srcObject) {\n if (!(this.$video.srcObject instanceof MediaStream)) return false; // srcObject is not a camera stream\n stream = this.$video.srcObject;\n } else {\n stream = (await this._getCameraStream()).stream;\n }\n return 'torch' in stream.getVideoTracks()[0].getSettings();\n } catch (e) {\n return false;\n } finally {\n // close the stream we just opened for detecting whether it supports flash\n if (stream && stream !== this.$video.srcObject) {\n console.warn('Call hasFlash after successfully starting the scanner to avoid creating '\n + 'a temporary video stream');\n QrScanner._stopVideoStream(stream);\n }\n }\n }\n\n isFlashOn(): boolean {\n return this._flashOn;\n }\n\n async toggleFlash(): Promise {\n if (this._flashOn) {\n await this.turnFlashOff();\n } else {\n await this.turnFlashOn();\n }\n }\n\n async turnFlashOn(): Promise {\n if (this._flashOn || this._destroyed) return;\n this._flashOn = true;\n if (!this._active || this._paused) return; // flash will be turned on later on .start()\n try {\n if (!await this.hasFlash()) throw 'No flash available';\n // Note that the video track is guaranteed to exist and to be a MediaStream due to the check in hasFlash\n await (this.$video.srcObject as MediaStream).getVideoTracks()[0].applyConstraints({\n // @ts-ignore: constraint 'torch' is unknown to ts\n advanced: [{ torch: true }],\n });\n } catch (e) {\n this._flashOn = false;\n throw e;\n }\n }\n\n async turnFlashOff(): Promise {\n if (!this._flashOn) return;\n // applyConstraints with torch: false does not work to turn the flashlight off, as a stream's torch stays\n // continuously on, see https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#torch. Therefore,\n // we have to stop the stream to turn the flashlight off.\n this._flashOn = false;\n await this._restartVideoStream();\n }\n\n destroy(): void {\n this.$video.removeEventListener('loadedmetadata', this._onLoadedMetaData);\n this.$video.removeEventListener('play', this._onPlay);\n document.removeEventListener('visibilitychange', this._onVisibilityChange);\n window.removeEventListener('resize', this._updateOverlay);\n\n this._destroyed = true;\n this._flashOn = false;\n this.stop(); // sets this._paused = true and this._active = false\n QrScanner._postWorkerMessage(this._qrEnginePromise, 'close');\n }\n\n async start(): Promise {\n if ((this._active && !this._paused) || this._destroyed) return;\n\n if (window.location.protocol !== 'https:') {\n // warn but try starting the camera anyways\n console.warn('The camera stream is only accessible if the page is transferred via https.');\n }\n\n this._active = true;\n if (document.hidden) return; // camera will be started as soon as tab is in foreground\n this._paused = false;\n if (this.$video.srcObject) {\n // camera stream already/still set\n await this.$video.play();\n return;\n }\n\n try {\n const { stream, facingMode } = await this._getCameraStream();\n if (!this._active || this._paused) {\n // was stopped in the meantime\n QrScanner._stopVideoStream(stream);\n return;\n }\n this._setVideoMirror(facingMode);\n this.$video.srcObject = stream;\n await this.$video.play();\n\n // Restart the flash if it was previously on\n if (this._flashOn) {\n this._flashOn = false; // force turnFlashOn to restart the flash\n this.turnFlashOn().catch(() => {});\n }\n } catch (e) {\n if (this._paused) return;\n this._active = false;\n throw e;\n }\n }\n\n stop(): void {\n this.pause();\n this._active = false;\n }\n\n async pause(stopStreamImmediately = false): Promise {\n this._paused = true;\n if (!this._active) return true;\n this.$video.pause();\n\n if (this.$overlay) {\n this.$overlay.style.display = 'none';\n }\n\n const stopStream = () => {\n if (this.$video.srcObject instanceof MediaStream) {\n // revoke srcObject only if it's a stream which was likely set by us\n QrScanner._stopVideoStream(this.$video.srcObject);\n this.$video.srcObject = null;\n }\n };\n\n if (stopStreamImmediately) {\n stopStream();\n return true;\n }\n\n await new Promise((resolve) => setTimeout(resolve, 300));\n if (!this._paused) return false;\n stopStream();\n return true;\n }\n\n async setCamera(facingModeOrDeviceId: QrScanner.FacingMode | QrScanner.DeviceId): Promise {\n if (facingModeOrDeviceId === this._preferredCamera) return;\n this._preferredCamera = facingModeOrDeviceId;\n // Restart the scanner with the new camera which will also update the video mirror and the scan region.\n await this._restartVideoStream();\n }\n\n static async scanImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n options: {\n scanRegion?: QrScanner.ScanRegion | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing?: boolean,\n alsoTryWithoutScanRegion?: boolean,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n },\n ): Promise;\n /** @deprecated */\n static async scanImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n scanRegion?: QrScanner.ScanRegion | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing?: boolean,\n alsoTryWithoutScanRegion?: boolean,\n ): Promise;\n static async scanImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n scanRegionOrOptions?: QrScanner.ScanRegion | {\n scanRegion?: QrScanner.ScanRegion | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing?: boolean,\n alsoTryWithoutScanRegion?: boolean,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n } | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing: boolean = false,\n alsoTryWithoutScanRegion: boolean = false,\n ): Promise {\n let scanRegion: QrScanner.ScanRegion | null | undefined;\n let returnDetailedScanResult = false;\n if (scanRegionOrOptions && (\n 'scanRegion' in scanRegionOrOptions\n || 'qrEngine' in scanRegionOrOptions\n || 'canvas' in scanRegionOrOptions\n || 'disallowCanvasResizing' in scanRegionOrOptions\n || 'alsoTryWithoutScanRegion' in scanRegionOrOptions\n || 'returnDetailedScanResult' in scanRegionOrOptions\n )) {\n // we got an options object using the new api\n scanRegion = scanRegionOrOptions.scanRegion;\n qrEngine = scanRegionOrOptions.qrEngine;\n canvas = scanRegionOrOptions.canvas;\n disallowCanvasResizing = scanRegionOrOptions.disallowCanvasResizing || false;\n alsoTryWithoutScanRegion = scanRegionOrOptions.alsoTryWithoutScanRegion || false;\n returnDetailedScanResult = true;\n } else if (scanRegionOrOptions || qrEngine || canvas || disallowCanvasResizing || alsoTryWithoutScanRegion) {\n console.warn('You\\'re using a deprecated api for scanImage which will be removed in the future.');\n } else {\n // Only imageOrFileOrBlobOrUrl was specified and we can't distinguish between new or old api usage. For\n // backwards compatibility we have to assume the old api for now. The options object is marked as non-\n // optional in the parameter list above to make clear that ScanResult instead of string is only returned if\n // an options object was provided. However, in the future once legacy support is removed, the options object\n // should become optional.\n console.warn('Note that the return type of scanImage will change in the future. To already switch to the '\n + 'new api today, you can pass returnDetailedScanResult: true.');\n }\n\n const gotExternalEngine = !!qrEngine;\n\n try {\n let image: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement;\n let canvasContext: CanvasRenderingContext2D;\n [qrEngine, image] = await Promise.all([\n qrEngine || QrScanner.createQrEngine(),\n QrScanner._loadImage(imageOrFileOrBlobOrUrl),\n ]);\n [canvas, canvasContext] = QrScanner._drawToCanvas(image, scanRegion, canvas, disallowCanvasResizing);\n\n if (qrEngine instanceof Worker) {\n const qrEngineWorker = qrEngine; // for ts to know that it's still a worker later in the event listeners\n if (!gotExternalEngine) {\n // Enable scanning of inverted color qr codes. Not using _postWorkerMessage as it's async\n qrEngineWorker.postMessage({ type: 'inversionMode', data: 'both' });\n }\n return await new Promise((resolve, reject) => {\n let timeout: number;\n let onMessage: (event: MessageEvent) => void;\n let onError: (error: ErrorEvent | string) => void;\n onMessage = (event: MessageEvent) => {\n if (event.data.type !== 'qrResult') {\n return;\n }\n qrEngineWorker.removeEventListener('message', onMessage);\n qrEngineWorker.removeEventListener('error', onError);\n clearTimeout(timeout);\n if (event.data.data !== null) {\n resolve(returnDetailedScanResult ? {\n data: event.data.data,\n cornerPoints: QrScanner._convertPoints(event.data.cornerPoints, scanRegion),\n } : event.data.data);\n } else {\n reject(QrScanner.NO_QR_CODE_FOUND);\n }\n };\n onError = (error: ErrorEvent | string) => {\n qrEngineWorker.removeEventListener('message', onMessage);\n qrEngineWorker.removeEventListener('error', onError);\n clearTimeout(timeout);\n const errorMessage = !error ? 'Unknown Error' : ((error as ErrorEvent).message || error);\n reject('Scanner error: ' + errorMessage);\n };\n qrEngineWorker.addEventListener('message', onMessage);\n qrEngineWorker.addEventListener('error', onError);\n timeout = setTimeout(() => onError('timeout'), 10000);\n const imageData = canvasContext.getImageData(0, 0, canvas!.width, canvas!.height);\n qrEngineWorker.postMessage({\n type: 'decode',\n data: imageData\n }, [imageData.data.buffer]);\n });\n } else {\n return await Promise.race([\n new Promise((resolve, reject) => window.setTimeout(\n () => reject('Scanner error: timeout'),\n 10000,\n )),\n (async (): Promise => {\n try {\n const [scanResult] = await qrEngine.detect(canvas!);\n if (!scanResult) throw QrScanner.NO_QR_CODE_FOUND;\n return returnDetailedScanResult ? {\n data: scanResult.rawValue,\n cornerPoints: QrScanner._convertPoints(scanResult.cornerPoints, scanRegion),\n } : scanResult.rawValue;\n } catch (e) {\n throw `Scanner error: ${(e as Error).message || e}`;\n }\n })(),\n ]);\n }\n } catch (e) {\n if (!scanRegion || !alsoTryWithoutScanRegion) throw e;\n const detailedScanResult = await QrScanner.scanImage(\n imageOrFileOrBlobOrUrl,\n { qrEngine, canvas, disallowCanvasResizing },\n );\n return returnDetailedScanResult ? detailedScanResult : detailedScanResult.data;\n } finally {\n if (!gotExternalEngine) {\n QrScanner._postWorkerMessage(qrEngine!, 'close');\n }\n }\n }\n\n setGrayscaleWeights(red: number, green: number, blue: number, useIntegerApproximation: boolean = true): void {\n // Note that for the native BarcodeDecoder or if the worker was destroyed, this is a no-op. However, the native\n // implementations work also well with colored qr codes.\n QrScanner._postWorkerMessage(\n this._qrEnginePromise,\n 'grayscaleWeights',\n { red, green, blue, useIntegerApproximation }\n );\n }\n\n setInversionMode(inversionMode: QrScanner.InversionMode): void {\n // Note that for the native BarcodeDecoder or if the worker was destroyed, this is a no-op. However, the native\n // implementations scan normal and inverted qr codes by default\n QrScanner._postWorkerMessage(this._qrEnginePromise, 'inversionMode', inversionMode);\n }\n\n static async createQrEngine(workerPath: string = QrScanner.WORKER_PATH): Promise {\n const hasNativeBarcodeDetector = 'BarcodeDetector' in window && BarcodeDetector.getSupportedFormats\n ? (await BarcodeDetector.getSupportedFormats()).includes('qr_code')\n : false;\n return hasNativeBarcodeDetector ? new BarcodeDetector({ formats: ['qr_code'] }) : new Worker(workerPath);\n }\n\n private _onPlay(): void {\n this._scanRegion = this._calculateScanRegion(this.$video);\n this._updateOverlay();\n if (this.$overlay) {\n this.$overlay.style.display = '';\n }\n this._scanFrame();\n }\n\n private _onLoadedMetaData(): void {\n this._scanRegion = this._calculateScanRegion(this.$video);\n this._updateOverlay();\n }\n\n private _onVisibilityChange(): void {\n if (document.hidden) {\n this.pause();\n } else if (this._active) {\n this.start();\n }\n }\n\n private _calculateScanRegion(video: HTMLVideoElement): QrScanner.ScanRegion {\n // Default scan region calculation. Note that this can be overwritten in the constructor.\n const smallestDimension = Math.min(video.videoWidth, video.videoHeight);\n const scanRegionSize = Math.round(2 / 3 * smallestDimension);\n return {\n x: Math.round((video.videoWidth - scanRegionSize) / 2),\n y: Math.round((video.videoHeight - scanRegionSize) / 2),\n width: scanRegionSize,\n height: scanRegionSize,\n downScaledWidth: this._legacyCanvasSize,\n downScaledHeight: this._legacyCanvasSize,\n };\n }\n\n private _updateOverlay(): void {\n requestAnimationFrame(() => {\n // Running in requestAnimationFrame which should avoid a potential additional re-flow for getComputedStyle\n // and offsetWidth, offsetHeight, offsetLeft, offsetTop.\n if (!this.$overlay) return;\n const video = this.$video;\n const videoWidth = video.videoWidth;\n const videoHeight = video.videoHeight;\n const elementWidth = video.offsetWidth;\n const elementHeight = video.offsetHeight;\n const elementX = video.offsetLeft;\n const elementY = video.offsetTop;\n\n const videoStyle = window.getComputedStyle(video);\n const videoObjectFit = videoStyle.objectFit;\n const videoAspectRatio = videoWidth / videoHeight;\n const elementAspectRatio = elementWidth / elementHeight;\n let videoScaledWidth: number;\n let videoScaledHeight: number;\n switch (videoObjectFit) {\n case 'none':\n videoScaledWidth = videoWidth;\n videoScaledHeight = videoHeight;\n break;\n case 'fill':\n videoScaledWidth = elementWidth;\n videoScaledHeight = elementHeight;\n break;\n default: // 'cover', 'contains', 'scale-down'\n if (videoObjectFit === 'cover'\n ? videoAspectRatio > elementAspectRatio\n : videoAspectRatio < elementAspectRatio) {\n // The scaled height is the element height\n // - for 'cover' if the video aspect ratio is wider than the element aspect ratio\n // (scaled height matches element height and scaled width overflows element width)\n // - for 'contains'/'scale-down' if element aspect ratio is wider than the video aspect ratio\n // (scaled height matched element height and element width overflows scaled width)\n videoScaledHeight = elementHeight;\n videoScaledWidth = videoScaledHeight * videoAspectRatio;\n } else {\n videoScaledWidth = elementWidth;\n videoScaledHeight = videoScaledWidth / videoAspectRatio;\n }\n if (videoObjectFit === 'scale-down') {\n // for 'scale-down' the dimensions are the minimum of 'contains' and 'none'\n videoScaledWidth = Math.min(videoScaledWidth, videoWidth);\n videoScaledHeight = Math.min(videoScaledHeight, videoHeight);\n }\n }\n\n // getComputedStyle is so nice to convert keywords (left, center, right, top, bottom) to percent and makes\n // sure to set the default of 50% if only one or no component was provided, therefore we can be sure that\n // both components are set. Additionally, it converts units other than px (e.g. rem) to px.\n const [videoX, videoY] = videoStyle.objectPosition.split(' ').map((length, i) => {\n const lengthValue = parseFloat(length);\n return length.endsWith('%')\n ? (!i ? elementWidth - videoScaledWidth : elementHeight - videoScaledHeight) * lengthValue / 100\n : lengthValue;\n });\n\n const regionWidth = this._scanRegion.width || videoWidth;\n const regionHeight = this._scanRegion.height || videoHeight;\n const regionX = this._scanRegion.x || 0;\n const regionY = this._scanRegion.y || 0;\n\n const overlayStyle = this.$overlay.style;\n overlayStyle.width = `${regionWidth / videoWidth * videoScaledWidth}px`;\n overlayStyle.height = `${regionHeight / videoHeight * videoScaledHeight}px`;\n overlayStyle.top = `${elementY + videoY + regionY / videoHeight * videoScaledHeight}px`;\n const isVideoMirrored = /scaleX\\(-1\\)/.test(video.style.transform!);\n overlayStyle.left = `${elementX\n + (isVideoMirrored ? elementWidth - videoX - videoScaledWidth : videoX)\n + (isVideoMirrored ? videoWidth - regionX - regionWidth : regionX) / videoWidth * videoScaledWidth}px`;\n // apply same mirror as on video\n overlayStyle.transform = video.style.transform;\n });\n }\n\n private static _convertPoints(\n points: QrScanner.Point[],\n scanRegion?: QrScanner.ScanRegion | null,\n ): QrScanner.Point[] {\n if (!scanRegion) return points;\n const offsetX = scanRegion.x || 0;\n const offsetY = scanRegion.y || 0;\n const scaleFactorX = scanRegion.width && scanRegion.downScaledWidth\n ? scanRegion.width / scanRegion.downScaledWidth\n : 1;\n const scaleFactorY = scanRegion.height && scanRegion.downScaledHeight\n ? scanRegion.height / scanRegion.downScaledHeight\n : 1;\n for (const point of points) {\n point.x = point.x * scaleFactorX + offsetX;\n point.y = point.y * scaleFactorY + offsetY;\n }\n return points;\n }\n\n private _scanFrame(): void {\n if (!this._active || this.$video.paused || this.$video.ended) return;\n // using requestAnimationFrame to avoid scanning if tab is in background\n requestAnimationFrame(async () => {\n if (this.$video.readyState <= 1) {\n // Skip scans until the video is ready as drawImage() only works correctly on a video with readyState\n // > 1, see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage#Notes.\n // This also avoids false positives for videos paused after a successful scan which remains visible on\n // the canvas until the video is started again and ready.\n this._scanFrame();\n return;\n }\n\n const timeSinceLastScan = Date.now() - this._lastScanTimestamp;\n const minimumTimeBetweenScans = 1000 / this._maxScansPerSecond;\n if (timeSinceLastScan < minimumTimeBetweenScans) {\n await new Promise((resolve) => setTimeout(resolve, minimumTimeBetweenScans - timeSinceLastScan));\n }\n this._lastScanTimestamp = Date.now();\n\n let result: QrScanner.ScanResult | undefined;\n try {\n result = await QrScanner.scanImage(this.$video, {\n scanRegion: this._scanRegion,\n qrEngine: this._qrEnginePromise,\n canvas: this.$canvas,\n });\n } catch (error) {\n if (!this._active) return;\n const errorMessage = (error as Error).message || error as string;\n if (errorMessage.includes('service unavailable')) {\n // When the native BarcodeDetector crashed, create a new one\n this._qrEnginePromise = QrScanner.createQrEngine();\n }\n this._onDecodeError(error as Error | string);\n }\n\n if (result) {\n if (this._onDecode) {\n this._onDecode(result);\n } else if (this._legacyOnDecode) {\n this._legacyOnDecode(result.data);\n }\n\n if (this.$codeOutlineHighlight) {\n clearTimeout(this._codeOutlineHighlightRemovalTimeout);\n this._codeOutlineHighlightRemovalTimeout = undefined;\n this.$codeOutlineHighlight.setAttribute(\n 'viewBox',\n `${this._scanRegion.x || 0} `\n + `${this._scanRegion.y || 0} `\n + `${this._scanRegion.width || this.$video.videoWidth} `\n + `${this._scanRegion.height || this.$video.videoHeight}`,\n );\n const polygon = this.$codeOutlineHighlight.firstElementChild!;\n polygon.setAttribute('points', result.cornerPoints.map(({x, y}) => `${x},${y}`).join(' '));\n this.$codeOutlineHighlight.style.display = '';\n }\n } else if (this.$codeOutlineHighlight && !this._codeOutlineHighlightRemovalTimeout) {\n // hide after timeout to make it flash less when on some frames the QR code is detected and on some not\n this._codeOutlineHighlightRemovalTimeout = setTimeout(\n () => this.$codeOutlineHighlight!.style.display = 'none',\n 100,\n );\n }\n\n this._scanFrame();\n });\n }\n\n private _onDecodeError(error: Error | string): void {\n // default error handler; can be overwritten in the constructor\n if (error === QrScanner.NO_QR_CODE_FOUND) return;\n console.log(error);\n }\n\n private async _getCameraStream(): Promise<{ stream: MediaStream, facingMode: QrScanner.FacingMode }> {\n if (!navigator.mediaDevices) throw 'Camera not found.';\n\n const preferenceType = /^(environment|user)$/.test(this._preferredCamera)\n ? 'facingMode'\n : 'deviceId';\n const constraintsWithoutCamera: Array = [{\n width: { min: 1024 }\n }, {\n width: { min: 768 }\n }, {}];\n const constraintsWithCamera = constraintsWithoutCamera.map((constraint) => Object.assign({}, constraint, {\n [preferenceType]: { exact: this._preferredCamera },\n }));\n\n for (const constraints of [...constraintsWithCamera, ...constraintsWithoutCamera]) {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ video: constraints, audio: false });\n // Try to determine the facing mode from the stream, otherwise use a guess or 'environment' as\n // default. Note that the guess is not always accurate as Safari returns cameras of different facing\n // mode, even for exact facingMode constraints.\n const facingMode = this._getFacingMode(stream)\n || (constraints.facingMode\n ? this._preferredCamera as QrScanner.FacingMode // a facing mode we were able to fulfill\n : (this._preferredCamera === 'environment'\n ? 'user' // switch as _preferredCamera was environment but we are not able to fulfill it\n : 'environment' // switch from unfulfilled user facingMode or default to environment\n )\n );\n return { stream, facingMode };\n } catch (e) {}\n }\n\n throw 'Camera not found.';\n }\n\n private async _restartVideoStream(): Promise {\n // Note that we always pause the stream and not only if !this._paused as even if this._paused === true, the\n // stream might still be running, as it's by default only stopped after a delay of 300ms.\n const wasPaused = this._paused;\n const paused = await this.pause(true);\n if (!paused || wasPaused || !this._active) return;\n await this.start();\n }\n\n private static _stopVideoStream(stream : MediaStream): void {\n for (const track of stream.getTracks()) {\n track.stop(); // note that this will also automatically turn the flashlight off\n stream.removeTrack(track);\n }\n }\n\n private _setVideoMirror(facingMode: QrScanner.FacingMode): void {\n // in user facing mode mirror the video to make it easier for the user to position the QR code\n const scaleFactor = facingMode === 'user'? -1 : 1;\n this.$video.style.transform = 'scaleX(' + scaleFactor + ')';\n }\n\n private _getFacingMode(videoStream: MediaStream): QrScanner.FacingMode | null {\n const videoTrack = videoStream.getVideoTracks()[0];\n if (!videoTrack) return null; // unknown\n // inspired by https://github.com/JodusNodus/react-qr-reader/blob/master/src/getDeviceId.js#L13\n return /rear|back|environment/i.test(videoTrack.label)\n ? 'environment'\n : /front|user|face/i.test(videoTrack.label)\n ? 'user'\n : null; // unknown\n }\n\n private static _drawToCanvas(\n image: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement,\n scanRegion?: QrScanner.ScanRegion | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing= false,\n ): [HTMLCanvasElement, CanvasRenderingContext2D] {\n canvas = canvas || document.createElement('canvas');\n const scanRegionX = scanRegion && scanRegion.x ? scanRegion.x : 0;\n const scanRegionY = scanRegion && scanRegion.y ? scanRegion.y : 0;\n const scanRegionWidth = scanRegion && scanRegion.width\n ? scanRegion.width\n : (image as HTMLVideoElement).videoWidth || image.width as number;\n const scanRegionHeight = scanRegion && scanRegion.height\n ? scanRegion.height\n : (image as HTMLVideoElement).videoHeight || image.height as number;\n\n if (!disallowCanvasResizing) {\n const canvasWidth = scanRegion && scanRegion.downScaledWidth\n ? scanRegion.downScaledWidth\n : scanRegionWidth;\n const canvasHeight = scanRegion && scanRegion.downScaledHeight\n ? scanRegion.downScaledHeight\n : scanRegionHeight;\n // Setting the canvas width or height clears the canvas, even if the values didn't change, therefore only\n // set them if they actually changed.\n if (canvas.width !== canvasWidth) {\n canvas.width = canvasWidth;\n }\n if (canvas.height !== canvasHeight) {\n canvas.height = canvasHeight;\n }\n }\n\n const context = canvas.getContext('2d', { alpha: false })!;\n context.imageSmoothingEnabled = false; // gives less blurry images\n context.drawImage(\n image,\n scanRegionX, scanRegionY, scanRegionWidth, scanRegionHeight,\n 0, 0, canvas.width, canvas.height,\n );\n return [canvas, context];\n }\n\n private static async _loadImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n ): Promise {\n if (imageOrFileOrBlobOrUrl instanceof Image) {\n await QrScanner._awaitImageLoad(imageOrFileOrBlobOrUrl);\n return imageOrFileOrBlobOrUrl;\n } else if (imageOrFileOrBlobOrUrl instanceof HTMLVideoElement\n || imageOrFileOrBlobOrUrl instanceof HTMLCanvasElement\n || imageOrFileOrBlobOrUrl instanceof SVGImageElement\n || 'OffscreenCanvas' in window && imageOrFileOrBlobOrUrl instanceof OffscreenCanvas\n || 'ImageBitmap' in window && imageOrFileOrBlobOrUrl instanceof ImageBitmap) {\n return imageOrFileOrBlobOrUrl;\n } else if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob\n || imageOrFileOrBlobOrUrl instanceof URL || typeof imageOrFileOrBlobOrUrl === 'string') {\n const image = new Image();\n if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob) {\n image.src = URL.createObjectURL(imageOrFileOrBlobOrUrl);\n } else {\n image.src = imageOrFileOrBlobOrUrl.toString();\n }\n try {\n await QrScanner._awaitImageLoad(image);\n return image;\n } finally {\n if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob) {\n URL.revokeObjectURL(image.src);\n }\n }\n } else {\n throw 'Unsupported image type.';\n }\n }\n\n private static async _awaitImageLoad(image: HTMLImageElement): Promise {\n if (image.complete && image.naturalWidth !== 0) return; // already loaded\n await new Promise((resolve, reject) => {\n const listener = (event: ErrorEvent | Event) => {\n image.removeEventListener('load', listener);\n image.removeEventListener('error', listener);\n if (event instanceof ErrorEvent) {\n reject('Image load error');\n } else {\n resolve();\n }\n };\n image.addEventListener('load', listener);\n image.addEventListener('error', listener);\n });\n }\n\n private static async _postWorkerMessage(\n qrEngineOrQrEnginePromise: Worker | BarcodeDetector | Promise,\n type: string,\n data?: any,\n ): Promise {\n const qrEngine = await qrEngineOrQrEnginePromise;\n if (!(qrEngine instanceof Worker)) return;\n qrEngine.postMessage({ type, data });\n }\n}\n\ndeclare namespace QrScanner {\n export interface ScanRegion {\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n downScaledWidth?: number;\n downScaledHeight?: number;\n }\n\n export type FacingMode = 'environment' | 'user';\n export type DeviceId = string;\n\n export interface Camera {\n id: DeviceId;\n label: string;\n }\n\n export type InversionMode = 'original' | 'invert' | 'both';\n\n export interface Point {\n x: number;\n y: number;\n }\n\n export interface ScanResult {\n data: string;\n // In clockwise order, starting at top left, but this might not be guaranteed in the future.\n cornerPoints: QrScanner.Point[];\n }\n}\n\n// simplified from https://wicg.github.io/shape-detection-api/#barcode-detection-api\ndeclare class BarcodeDetector {\n constructor(options?: { formats: string[] });\n static getSupportedFormats(): Promise;\n detect(image: ImageBitmapSource): Promise>;\n}\n"],"names":["QrScanner","constructor","video","onDecode","canvasSizeOrOnDecodeErrorOrOptions","canvasSizeOrCalculateScanRegion","preferredCamera","DEFAULT_CANVAS_SIZE","$video","$canvas","document","createElement","_onDecode","console","warn","_legacyOnDecode","_onDecodeError","options","onDecodeError","_calculateScanRegion","calculateScanRegion","_preferredCamera","_legacyCanvasSize","_maxScansPerSecond","maxScansPerSecond","_onPlay","bind","_onLoadedMetaData","_onVisibilityChange","_updateOverlay","disablePictureInPicture","playsInline","muted","shouldHideVideo","hidden","body","contains","appendChild","highlightScanRegion","highlightCodeOutline","$overlay","overlay","overlayStyle","position","display","pointerEvents","classList","add","gotExternalOverlay","outline","videoContainer","insertBefore","nextSibling","$codeOutlineHighlight","createElementNS","polygon","codeOutlineHighlightStyle","width","height","setAttribute","fill","stroke","strokeWidth","_scanRegion","requestAnimationFrame","videoStyle","style","setProperty","visibility","opacity","parentElement","removeChild","addEventListener","window","_qrEnginePromise","createQrEngine","hasCamera","length","listCameras","e","requestLabels","navigator","mediaDevices","filter","device","openedStream","every","enumerateCameras","camera","label","getUserMedia","audio","map","i","id","deviceId","_stopVideoStream","hasFlash","stream","srcObject","MediaStream","_getCameraStream","getVideoTracks","getSettings","isFlashOn","_flashOn","toggleFlash","turnFlashOff","turnFlashOn","_destroyed","_active","_paused","applyConstraints","advanced","torch","_restartVideoStream","destroy","removeEventListener","stop","_postWorkerMessage","start","location","protocol","play","facingMode","_setVideoMirror","catch","pause","stopStreamImmediately","stopStream","Promise","resolve","setTimeout","setCamera","facingModeOrDeviceId","scanImage","imageOrFileOrBlobOrUrl","scanRegionOrOptions","qrEngine","canvas","disallowCanvasResizing","alsoTryWithoutScanRegion","scanRegion","returnDetailedScanResult","image","canvasContext","all","_loadImage","_drawToCanvas","Worker","gotExternalEngine","qrEngineWorker","postMessage","type","data","reject","timeout","onMessage","onError","event","clearTimeout","cornerPoints","_convertPoints","NO_QR_CODE_FOUND","error","imageData","buffer","race","scanResult","rawValue","message","detailedScanResult","setGrayscaleWeights","red","green","blue","useIntegerApproximation","setInversionMode","inversionMode","workerPath","WORKER_PATH","includes","BarcodeDetector","getSupportedFormats","formats","_scanFrame","x","Math","round","videoWidth","scanRegionSize","y","videoHeight","downScaledWidth","downScaledHeight","videoObjectFit","videoScaledWidth","videoScaledHeight","elementWidth","elementHeight","videoAspectRatio","elementAspectRatio","min","videoY","lengthValue","parseFloat","endsWith","regionWidth","regionHeight","top","elementY","regionY","left","elementX","isVideoMirrored","videoX","regionX","transform","points","point","scaleFactorX","offsetX","scaleFactorY","offsetY","paused","ended","readyState","timeSinceLastScan","minimumTimeBetweenScans","_lastScanTimestamp","Date","now","result","_codeOutlineHighlightRemovalTimeout","undefined","join","log","constraint","preferenceType","exact","constraints","wasPaused","track","removeTrack","_getFacingMode","videoStream","test","videoTrack","scanRegionWidth","scanRegionHeight","canvasWidth","canvasHeight","alpha","context","imageSmoothingEnabled","drawImage","scanRegionX","scanRegionY","Image","_awaitImageLoad","HTMLVideoElement","HTMLCanvasElement","SVGImageElement","OffscreenCanvas","ImageBitmap","File","Blob","URL","src","createObjectURL","toString","revokeObjectURL","complete","naturalWidth","listener","ErrorEvent","qrEngineOrQrEnginePromise"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"qr-scanner.umd.min.js","sources":["src/qr-scanner.ts"],"sourcesContent":["export default class QrScanner {\n static readonly DEFAULT_CANVAS_SIZE = 400;\n static readonly NO_QR_CODE_FOUND = 'No QR code found';\n static WORKER_PATH = 'qr-scanner-worker.min.js';\n\n static async hasCamera(): Promise {\n try {\n return !!(await QrScanner.listCameras(false)).length;\n } catch (e) {\n return false;\n }\n }\n\n static async listCameras(requestLabels = false): Promise> {\n if (!navigator.mediaDevices) return [];\n\n const enumerateCameras = async (): Promise> =>\n (await navigator.mediaDevices.enumerateDevices()).filter((device) => device.kind === 'videoinput');\n\n // Note that enumerateDevices can always be called and does not prompt the user for permission.\n // However, enumerateDevices only includes device labels if served via https and an active media stream exists\n // or permission to access the camera was given. Therefore, if we're not getting labels but labels are requested\n // ask for camera permission by opening a stream.\n let openedStream: MediaStream | undefined;\n try {\n if (requestLabels && (await enumerateCameras()).every((camera) => !camera.label)) {\n openedStream = await navigator.mediaDevices.getUserMedia({ audio: false, video: true });\n }\n } catch (e) {\n // Fail gracefully, especially if the device has no camera or on mobile when the camera is already in use\n // and some browsers disallow a second stream.\n }\n\n try {\n return (await enumerateCameras()).map((camera, i) => ({\n id: camera.deviceId,\n label: camera.label || (i === 0 ? 'Default Camera' : `Camera ${i + 1}`),\n }));\n } finally {\n // close the stream we just opened for getting camera access for listing the device labels\n if (openedStream) {\n console.warn('Call listCameras after successfully starting a QR scanner to avoid creating '\n + 'a temporary video stream');\n QrScanner._stopVideoStream(openedStream);\n }\n }\n }\n\n readonly $video: HTMLVideoElement;\n readonly $canvas: HTMLCanvasElement;\n readonly $overlay?: HTMLDivElement;\n private readonly $codeOutlineHighlight?: SVGSVGElement;\n private readonly _onDecode?: (result: QrScanner.ScanResult) => void;\n private readonly _legacyOnDecode?: (result: string) => void;\n private readonly _legacyCanvasSize: number = QrScanner.DEFAULT_CANVAS_SIZE;\n private _preferredCamera: QrScanner.FacingMode | QrScanner.DeviceId = 'environment';\n private readonly _maxScansPerSecond: number = 25;\n private _lastScanTimestamp: number = -1;\n private _scanRegion: QrScanner.ScanRegion;\n private _codeOutlineHighlightRemovalTimeout?: number;\n private _qrEnginePromise: Promise\n private _active: boolean = false;\n private _paused: boolean = false;\n private _flashOn: boolean = false;\n private _destroyed: boolean = false;\n\n constructor(\n video: HTMLVideoElement,\n onDecode: (result: QrScanner.ScanResult) => void,\n options: {\n onDecodeError?: (error: Error | string) => void,\n calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n maxScansPerSecond?: number;\n highlightScanRegion?: boolean,\n highlightCodeOutline?: boolean,\n overlay?: HTMLDivElement,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n },\n );\n /** @deprecated */\n constructor(\n video: HTMLVideoElement,\n onDecode: (result: string) => void,\n onDecodeError?: (error: Error | string) => void,\n calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n );\n /** @deprecated */\n constructor(\n video: HTMLVideoElement,\n onDecode: (result: string) => void,\n onDecodeError?: (error: Error | string) => void,\n canvasSize?: number,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n );\n /** @deprecated */\n constructor(video: HTMLVideoElement, onDecode: (result: string) => void, canvasSize?: number);\n constructor(\n video: HTMLVideoElement,\n onDecode: ((result: QrScanner.ScanResult) => void) | ((result: string) => void),\n canvasSizeOrOnDecodeErrorOrOptions?: number | ((error: Error | string) => void) | {\n onDecodeError?: (error: Error | string) => void,\n calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n maxScansPerSecond?: number;\n highlightScanRegion?: boolean,\n highlightCodeOutline?: boolean,\n overlay?: HTMLDivElement,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n },\n canvasSizeOrCalculateScanRegion?: number | ((video: HTMLVideoElement) => QrScanner.ScanRegion),\n preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,\n ) {\n this.$video = video;\n this.$canvas = document.createElement('canvas');\n\n if (canvasSizeOrOnDecodeErrorOrOptions && typeof canvasSizeOrOnDecodeErrorOrOptions === 'object') {\n // we got an options object using the new api\n this._onDecode = onDecode as QrScanner['_onDecode'];\n } else {\n if (canvasSizeOrOnDecodeErrorOrOptions || canvasSizeOrCalculateScanRegion || preferredCamera) {\n console.warn('You\\'re using a deprecated version of the QrScanner constructor which will be removed in '\n + 'the future');\n } else {\n // Only video and onDecode were specified and we can't distinguish between new or old api usage. For\n // backwards compatibility we have to assume the old api for now. The options object is marked as non-\n // optional in the parameter list above to make clear that ScanResult instead of string is only passed\n // if an options object was provided. However, in the future once legacy support is removed, the options\n // object should become optional.\n console.warn('Note that the type of the scan result passed to onDecode will change in the future. '\n + 'To already switch to the new api today, you can pass returnDetailedScanResult: true.');\n }\n this._legacyOnDecode = onDecode as QrScanner['_legacyOnDecode'];\n }\n\n const options = typeof canvasSizeOrOnDecodeErrorOrOptions === 'object'\n ? canvasSizeOrOnDecodeErrorOrOptions\n : {};\n this._onDecodeError = options.onDecodeError || (typeof canvasSizeOrOnDecodeErrorOrOptions === 'function'\n ? canvasSizeOrOnDecodeErrorOrOptions\n : this._onDecodeError);\n this._calculateScanRegion = options.calculateScanRegion || (typeof canvasSizeOrCalculateScanRegion==='function'\n ? canvasSizeOrCalculateScanRegion\n : this._calculateScanRegion);\n this._preferredCamera = options.preferredCamera || preferredCamera || this._preferredCamera;\n this._legacyCanvasSize = typeof canvasSizeOrOnDecodeErrorOrOptions === 'number'\n ? canvasSizeOrOnDecodeErrorOrOptions\n : typeof canvasSizeOrCalculateScanRegion === 'number'\n ? canvasSizeOrCalculateScanRegion\n : this._legacyCanvasSize;\n this._maxScansPerSecond = options.maxScansPerSecond || this._maxScansPerSecond;\n\n this._onPlay = this._onPlay.bind(this);\n this._onLoadedMetaData = this._onLoadedMetaData.bind(this);\n this._onVisibilityChange = this._onVisibilityChange.bind(this);\n this._updateOverlay = this._updateOverlay.bind(this);\n\n // @ts-ignore\n video.disablePictureInPicture = true;\n // Allow inline playback on iPhone instead of requiring full screen playback,\n // see https://webkit.org/blog/6784/new-video-policies-for-ios/\n // @ts-ignore\n video.playsInline = true;\n // Allow play() on iPhone without requiring a user gesture. Should not really be needed as camera stream\n // includes no audio, but just to be safe.\n video.muted = true;\n\n // Avoid Safari stopping the video stream on a hidden video.\n // See https://github.com/cozmo/jsQR/issues/185\n let shouldHideVideo = false;\n if (video.hidden) {\n video.hidden = false;\n shouldHideVideo = true;\n }\n if (!document.body.contains(video)) {\n document.body.appendChild(video);\n shouldHideVideo = true;\n }\n const videoContainer = video.parentElement!;\n\n if (options.highlightScanRegion || options.highlightCodeOutline) {\n const gotExternalOverlay = !!options.overlay;\n this.$overlay = options.overlay || document.createElement('div');\n const overlayStyle = this.$overlay.style;\n overlayStyle.position = 'absolute';\n overlayStyle.display = 'none';\n overlayStyle.pointerEvents = 'none';\n this.$overlay.classList.add('scan-region-highlight');\n if (!gotExternalOverlay && options.highlightScanRegion) {\n // default style; can be overwritten via css\n overlayStyle.outline = '#e9b213 solid 5px';\n videoContainer.insertBefore(this.$overlay, this.$video.nextSibling);\n }\n if (options.highlightCodeOutline) {\n this.$codeOutlineHighlight = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n const polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');\n this.$codeOutlineHighlight.appendChild(polygon);\n this.$overlay.appendChild(this.$codeOutlineHighlight);\n const codeOutlineHighlightStyle = this.$codeOutlineHighlight.style;\n codeOutlineHighlightStyle.width = '100%';\n codeOutlineHighlightStyle.height = '100%';\n // to support distorted videos, e.g. via object-fit: fill\n this.$codeOutlineHighlight.setAttribute('preserveAspectRatio', 'none');\n codeOutlineHighlightStyle.display = 'none';\n this.$codeOutlineHighlight.classList.add('code-outline-highlight');\n // default style; can be overwritten via css\n codeOutlineHighlightStyle.fill = 'none';\n codeOutlineHighlightStyle.stroke = '#e9b213';\n codeOutlineHighlightStyle.strokeWidth = '4';\n }\n }\n this._scanRegion = this._calculateScanRegion(video);\n\n requestAnimationFrame(() => {\n // Checking in requestAnimationFrame which should avoid a potential additional re-flow for getComputedStyle.\n const videoStyle = window.getComputedStyle(video);\n if (videoStyle.display === 'none') {\n video.style.setProperty('display', 'block', 'important');\n shouldHideVideo = true;\n }\n if (videoStyle.visibility !== 'visible') {\n video.style.setProperty('visibility', 'visible', 'important');\n shouldHideVideo = true;\n }\n if (shouldHideVideo) {\n // Hide the video in a way that doesn't cause Safari to stop the playback.\n console.warn('QrScanner has overwritten the video hiding style to avoid Safari stopping the playback.');\n video.style.opacity = '0';\n video.style.width = '0';\n video.style.height = '0';\n if (this.$overlay && this.$overlay.parentElement) {\n this.$overlay.parentElement.removeChild(this.$overlay);\n }\n // @ts-ignore\n delete this.$overlay!;\n // @ts-ignore\n delete this.$codeOutlineHighlight!;\n }\n\n if (this.$overlay) {\n this._updateOverlay();\n }\n });\n\n video.addEventListener('play', this._onPlay);\n video.addEventListener('loadedmetadata', this._onLoadedMetaData);\n document.addEventListener('visibilitychange', this._onVisibilityChange);\n window.addEventListener('resize', this._updateOverlay);\n\n this._qrEnginePromise = QrScanner.createQrEngine();\n }\n\n async hasFlash(): Promise {\n let stream: MediaStream | undefined;\n try {\n if (this.$video.srcObject) {\n if (!(this.$video.srcObject instanceof MediaStream)) return false; // srcObject is not a camera stream\n stream = this.$video.srcObject;\n } else {\n stream = (await this._getCameraStream()).stream;\n }\n return 'torch' in stream.getVideoTracks()[0].getSettings();\n } catch (e) {\n return false;\n } finally {\n // close the stream we just opened for detecting whether it supports flash\n if (stream && stream !== this.$video.srcObject) {\n console.warn('Call hasFlash after successfully starting the scanner to avoid creating '\n + 'a temporary video stream');\n QrScanner._stopVideoStream(stream);\n }\n }\n }\n\n isFlashOn(): boolean {\n return this._flashOn;\n }\n\n async toggleFlash(): Promise {\n if (this._flashOn) {\n await this.turnFlashOff();\n } else {\n await this.turnFlashOn();\n }\n }\n\n async turnFlashOn(): Promise {\n if (this._flashOn || this._destroyed) return;\n this._flashOn = true;\n if (!this._active || this._paused) return; // flash will be turned on later on .start()\n try {\n if (!await this.hasFlash()) throw 'No flash available';\n // Note that the video track is guaranteed to exist and to be a MediaStream due to the check in hasFlash\n await (this.$video.srcObject as MediaStream).getVideoTracks()[0].applyConstraints({\n // @ts-ignore: constraint 'torch' is unknown to ts\n advanced: [{ torch: true }],\n });\n } catch (e) {\n this._flashOn = false;\n throw e;\n }\n }\n\n async turnFlashOff(): Promise {\n if (!this._flashOn) return;\n // applyConstraints with torch: false does not work to turn the flashlight off, as a stream's torch stays\n // continuously on, see https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#torch. Therefore,\n // we have to stop the stream to turn the flashlight off.\n this._flashOn = false;\n await this._restartVideoStream();\n }\n\n destroy(): void {\n this.$video.removeEventListener('loadedmetadata', this._onLoadedMetaData);\n this.$video.removeEventListener('play', this._onPlay);\n document.removeEventListener('visibilitychange', this._onVisibilityChange);\n window.removeEventListener('resize', this._updateOverlay);\n\n this._destroyed = true;\n this._flashOn = false;\n this.stop(); // sets this._paused = true and this._active = false\n QrScanner._postWorkerMessage(this._qrEnginePromise, 'close');\n }\n\n async start(): Promise {\n if ((this._active && !this._paused) || this._destroyed) return;\n\n if (window.location.protocol !== 'https:') {\n // warn but try starting the camera anyways\n console.warn('The camera stream is only accessible if the page is transferred via https.');\n }\n\n this._active = true;\n if (document.hidden) return; // camera will be started as soon as tab is in foreground\n this._paused = false;\n if (this.$video.srcObject) {\n // camera stream already/still set\n await this.$video.play();\n return;\n }\n\n try {\n const { stream, facingMode } = await this._getCameraStream();\n if (!this._active || this._paused) {\n // was stopped in the meantime\n QrScanner._stopVideoStream(stream);\n return;\n }\n this._setVideoMirror(facingMode);\n this.$video.srcObject = stream;\n await this.$video.play();\n\n // Restart the flash if it was previously on\n if (this._flashOn) {\n this._flashOn = false; // force turnFlashOn to restart the flash\n this.turnFlashOn().catch(() => {});\n }\n } catch (e) {\n if (this._paused) return;\n this._active = false;\n throw e;\n }\n }\n\n stop(): void {\n this.pause();\n this._active = false;\n }\n\n async pause(stopStreamImmediately = false): Promise {\n this._paused = true;\n if (!this._active) return true;\n this.$video.pause();\n\n if (this.$overlay) {\n this.$overlay.style.display = 'none';\n }\n\n const stopStream = () => {\n if (this.$video.srcObject instanceof MediaStream) {\n // revoke srcObject only if it's a stream which was likely set by us\n QrScanner._stopVideoStream(this.$video.srcObject);\n this.$video.srcObject = null;\n }\n };\n\n if (stopStreamImmediately) {\n stopStream();\n return true;\n }\n\n await new Promise((resolve) => setTimeout(resolve, 300));\n if (!this._paused) return false;\n stopStream();\n return true;\n }\n\n async setCamera(facingModeOrDeviceId: QrScanner.FacingMode | QrScanner.DeviceId): Promise {\n if (facingModeOrDeviceId === this._preferredCamera) return;\n this._preferredCamera = facingModeOrDeviceId;\n // Restart the scanner with the new camera which will also update the video mirror and the scan region.\n await this._restartVideoStream();\n }\n\n static async scanImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n options: {\n scanRegion?: QrScanner.ScanRegion | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing?: boolean,\n alsoTryWithoutScanRegion?: boolean,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n },\n ): Promise;\n /** @deprecated */\n static async scanImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n scanRegion?: QrScanner.ScanRegion | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing?: boolean,\n alsoTryWithoutScanRegion?: boolean,\n ): Promise;\n static async scanImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n scanRegionOrOptions?: QrScanner.ScanRegion | {\n scanRegion?: QrScanner.ScanRegion | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing?: boolean,\n alsoTryWithoutScanRegion?: boolean,\n /** just a temporary flag until we switch entirely to the new api */\n returnDetailedScanResult?: true,\n } | null,\n qrEngine?: Worker | BarcodeDetector | Promise | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing: boolean = false,\n alsoTryWithoutScanRegion: boolean = false,\n ): Promise {\n let scanRegion: QrScanner.ScanRegion | null | undefined;\n let returnDetailedScanResult = false;\n if (scanRegionOrOptions && (\n 'scanRegion' in scanRegionOrOptions\n || 'qrEngine' in scanRegionOrOptions\n || 'canvas' in scanRegionOrOptions\n || 'disallowCanvasResizing' in scanRegionOrOptions\n || 'alsoTryWithoutScanRegion' in scanRegionOrOptions\n || 'returnDetailedScanResult' in scanRegionOrOptions\n )) {\n // we got an options object using the new api\n scanRegion = scanRegionOrOptions.scanRegion;\n qrEngine = scanRegionOrOptions.qrEngine;\n canvas = scanRegionOrOptions.canvas;\n disallowCanvasResizing = scanRegionOrOptions.disallowCanvasResizing || false;\n alsoTryWithoutScanRegion = scanRegionOrOptions.alsoTryWithoutScanRegion || false;\n returnDetailedScanResult = true;\n } else if (scanRegionOrOptions || qrEngine || canvas || disallowCanvasResizing || alsoTryWithoutScanRegion) {\n console.warn('You\\'re using a deprecated api for scanImage which will be removed in the future.');\n } else {\n // Only imageOrFileOrBlobOrUrl was specified and we can't distinguish between new or old api usage. For\n // backwards compatibility we have to assume the old api for now. The options object is marked as non-\n // optional in the parameter list above to make clear that ScanResult instead of string is only returned if\n // an options object was provided. However, in the future once legacy support is removed, the options object\n // should become optional.\n console.warn('Note that the return type of scanImage will change in the future. To already switch to the '\n + 'new api today, you can pass returnDetailedScanResult: true.');\n }\n\n const gotExternalEngine = !!qrEngine;\n\n try {\n let image: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement;\n let canvasContext: CanvasRenderingContext2D;\n [qrEngine, image] = await Promise.all([\n qrEngine || QrScanner.createQrEngine(),\n QrScanner._loadImage(imageOrFileOrBlobOrUrl),\n ]);\n [canvas, canvasContext] = QrScanner._drawToCanvas(image, scanRegion, canvas, disallowCanvasResizing);\n\n if (qrEngine instanceof Worker) {\n const qrEngineWorker = qrEngine; // for ts to know that it's still a worker later in the event listeners\n if (!gotExternalEngine) {\n // Enable scanning of inverted color qr codes. Not using _postWorkerMessage as it's async\n qrEngineWorker.postMessage({ type: 'inversionMode', data: 'both' });\n }\n return await new Promise((resolve, reject) => {\n let timeout: number;\n let onMessage: (event: MessageEvent) => void;\n let onError: (error: ErrorEvent | string) => void;\n onMessage = (event: MessageEvent) => {\n if (event.data.type !== 'qrResult') {\n return;\n }\n qrEngineWorker.removeEventListener('message', onMessage);\n qrEngineWorker.removeEventListener('error', onError);\n clearTimeout(timeout);\n if (event.data.data !== null) {\n resolve(returnDetailedScanResult ? {\n data: event.data.data,\n cornerPoints: QrScanner._convertPoints(event.data.cornerPoints, scanRegion),\n } : event.data.data);\n } else {\n reject(QrScanner.NO_QR_CODE_FOUND);\n }\n };\n onError = (error: ErrorEvent | string) => {\n qrEngineWorker.removeEventListener('message', onMessage);\n qrEngineWorker.removeEventListener('error', onError);\n clearTimeout(timeout);\n const errorMessage = !error ? 'Unknown Error' : ((error as ErrorEvent).message || error);\n reject('Scanner error: ' + errorMessage);\n };\n qrEngineWorker.addEventListener('message', onMessage);\n qrEngineWorker.addEventListener('error', onError);\n timeout = setTimeout(() => onError('timeout'), 10000);\n const imageData = canvasContext.getImageData(0, 0, canvas!.width, canvas!.height);\n qrEngineWorker.postMessage({\n type: 'decode',\n data: imageData\n }, [imageData.data.buffer]);\n });\n } else {\n return await Promise.race([\n new Promise((resolve, reject) => window.setTimeout(\n () => reject('Scanner error: timeout'),\n 10000,\n )),\n (async (): Promise => {\n try {\n const [scanResult] = await qrEngine.detect(canvas!);\n if (!scanResult) throw QrScanner.NO_QR_CODE_FOUND;\n return returnDetailedScanResult ? {\n data: scanResult.rawValue,\n cornerPoints: QrScanner._convertPoints(scanResult.cornerPoints, scanRegion),\n } : scanResult.rawValue;\n } catch (e) {\n throw `Scanner error: ${(e as Error).message || e}`;\n }\n })(),\n ]);\n }\n } catch (e) {\n if (!scanRegion || !alsoTryWithoutScanRegion) throw e;\n const detailedScanResult = await QrScanner.scanImage(\n imageOrFileOrBlobOrUrl,\n { qrEngine, canvas, disallowCanvasResizing },\n );\n return returnDetailedScanResult ? detailedScanResult : detailedScanResult.data;\n } finally {\n if (!gotExternalEngine) {\n QrScanner._postWorkerMessage(qrEngine!, 'close');\n }\n }\n }\n\n setGrayscaleWeights(red: number, green: number, blue: number, useIntegerApproximation: boolean = true): void {\n // Note that for the native BarcodeDecoder or if the worker was destroyed, this is a no-op. However, the native\n // implementations work also well with colored qr codes.\n QrScanner._postWorkerMessage(\n this._qrEnginePromise,\n 'grayscaleWeights',\n { red, green, blue, useIntegerApproximation }\n );\n }\n\n setInversionMode(inversionMode: QrScanner.InversionMode): void {\n // Note that for the native BarcodeDecoder or if the worker was destroyed, this is a no-op. However, the native\n // implementations scan normal and inverted qr codes by default\n QrScanner._postWorkerMessage(this._qrEnginePromise, 'inversionMode', inversionMode);\n }\n\n static async createQrEngine(workerPath: string = QrScanner.WORKER_PATH): Promise {\n const hasNativeBarcodeDetector = 'BarcodeDetector' in window && BarcodeDetector.getSupportedFormats\n ? (await BarcodeDetector.getSupportedFormats()).includes('qr_code')\n : false;\n return hasNativeBarcodeDetector ? new BarcodeDetector({ formats: ['qr_code'] }) : new Worker(workerPath);\n }\n\n private _onPlay(): void {\n this._scanRegion = this._calculateScanRegion(this.$video);\n this._updateOverlay();\n if (this.$overlay) {\n this.$overlay.style.display = '';\n }\n this._scanFrame();\n }\n\n private _onLoadedMetaData(): void {\n this._scanRegion = this._calculateScanRegion(this.$video);\n this._updateOverlay();\n }\n\n private _onVisibilityChange(): void {\n if (document.hidden) {\n this.pause();\n } else if (this._active) {\n this.start();\n }\n }\n\n private _calculateScanRegion(video: HTMLVideoElement): QrScanner.ScanRegion {\n // Default scan region calculation. Note that this can be overwritten in the constructor.\n const smallestDimension = Math.min(video.videoWidth, video.videoHeight);\n const scanRegionSize = Math.round(2 / 3 * smallestDimension);\n return {\n x: Math.round((video.videoWidth - scanRegionSize) / 2),\n y: Math.round((video.videoHeight - scanRegionSize) / 2),\n width: scanRegionSize,\n height: scanRegionSize,\n downScaledWidth: this._legacyCanvasSize,\n downScaledHeight: this._legacyCanvasSize,\n };\n }\n\n private _updateOverlay(): void {\n requestAnimationFrame(() => {\n // Running in requestAnimationFrame which should avoid a potential additional re-flow for getComputedStyle\n // and offsetWidth, offsetHeight, offsetLeft, offsetTop.\n if (!this.$overlay) return;\n const video = this.$video;\n const videoWidth = video.videoWidth;\n const videoHeight = video.videoHeight;\n const elementWidth = video.offsetWidth;\n const elementHeight = video.offsetHeight;\n const elementX = video.offsetLeft;\n const elementY = video.offsetTop;\n\n const videoStyle = window.getComputedStyle(video);\n const videoObjectFit = videoStyle.objectFit;\n const videoAspectRatio = videoWidth / videoHeight;\n const elementAspectRatio = elementWidth / elementHeight;\n let videoScaledWidth: number;\n let videoScaledHeight: number;\n switch (videoObjectFit) {\n case 'none':\n videoScaledWidth = videoWidth;\n videoScaledHeight = videoHeight;\n break;\n case 'fill':\n videoScaledWidth = elementWidth;\n videoScaledHeight = elementHeight;\n break;\n default: // 'cover', 'contains', 'scale-down'\n if (videoObjectFit === 'cover'\n ? videoAspectRatio > elementAspectRatio\n : videoAspectRatio < elementAspectRatio) {\n // The scaled height is the element height\n // - for 'cover' if the video aspect ratio is wider than the element aspect ratio\n // (scaled height matches element height and scaled width overflows element width)\n // - for 'contains'/'scale-down' if element aspect ratio is wider than the video aspect ratio\n // (scaled height matched element height and element width overflows scaled width)\n videoScaledHeight = elementHeight;\n videoScaledWidth = videoScaledHeight * videoAspectRatio;\n } else {\n videoScaledWidth = elementWidth;\n videoScaledHeight = videoScaledWidth / videoAspectRatio;\n }\n if (videoObjectFit === 'scale-down') {\n // for 'scale-down' the dimensions are the minimum of 'contains' and 'none'\n videoScaledWidth = Math.min(videoScaledWidth, videoWidth);\n videoScaledHeight = Math.min(videoScaledHeight, videoHeight);\n }\n }\n\n // getComputedStyle is so nice to convert keywords (left, center, right, top, bottom) to percent and makes\n // sure to set the default of 50% if only one or no component was provided, therefore we can be sure that\n // both components are set. Additionally, it converts units other than px (e.g. rem) to px.\n const [videoX, videoY] = videoStyle.objectPosition.split(' ').map((length, i) => {\n const lengthValue = parseFloat(length);\n return length.endsWith('%')\n ? (!i ? elementWidth - videoScaledWidth : elementHeight - videoScaledHeight) * lengthValue / 100\n : lengthValue;\n });\n\n const regionWidth = this._scanRegion.width || videoWidth;\n const regionHeight = this._scanRegion.height || videoHeight;\n const regionX = this._scanRegion.x || 0;\n const regionY = this._scanRegion.y || 0;\n\n const overlayStyle = this.$overlay.style;\n overlayStyle.width = `${regionWidth / videoWidth * videoScaledWidth}px`;\n overlayStyle.height = `${regionHeight / videoHeight * videoScaledHeight}px`;\n overlayStyle.top = `${elementY + videoY + regionY / videoHeight * videoScaledHeight}px`;\n const isVideoMirrored = /scaleX\\(-1\\)/.test(video.style.transform!);\n overlayStyle.left = `${elementX\n + (isVideoMirrored ? elementWidth - videoX - videoScaledWidth : videoX)\n + (isVideoMirrored ? videoWidth - regionX - regionWidth : regionX) / videoWidth * videoScaledWidth}px`;\n // apply same mirror as on video\n overlayStyle.transform = video.style.transform;\n });\n }\n\n private static _convertPoints(\n points: QrScanner.Point[],\n scanRegion?: QrScanner.ScanRegion | null,\n ): QrScanner.Point[] {\n if (!scanRegion) return points;\n const offsetX = scanRegion.x || 0;\n const offsetY = scanRegion.y || 0;\n const scaleFactorX = scanRegion.width && scanRegion.downScaledWidth\n ? scanRegion.width / scanRegion.downScaledWidth\n : 1;\n const scaleFactorY = scanRegion.height && scanRegion.downScaledHeight\n ? scanRegion.height / scanRegion.downScaledHeight\n : 1;\n for (const point of points) {\n point.x = point.x * scaleFactorX + offsetX;\n point.y = point.y * scaleFactorY + offsetY;\n }\n return points;\n }\n\n private _scanFrame(): void {\n if (!this._active || this.$video.paused || this.$video.ended) return;\n // If requestVideoFrameCallback is available use that to avoid unnecessary scans on the same frame as the\n // camera's framerate can be lower than the screen refresh rate and this._maxScansPerSecond, especially in dark\n // settings where the exposure time is longer. Both, requestVideoFrameCallback and requestAnimationFrame are not\n // being fired if the tab is in the background, which is what we want.\n const requestFrame = 'requestVideoFrameCallback' in this.$video\n // @ts-ignore\n ? this.$video.requestVideoFrameCallback.bind(this.$video)\n : requestAnimationFrame;\n requestFrame(async () => {\n if (this.$video.readyState <= 1) {\n // Skip scans until the video is ready as drawImage() only works correctly on a video with readyState\n // > 1, see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage#Notes.\n // This also avoids false positives for videos paused after a successful scan which remains visible on\n // the canvas until the video is started again and ready.\n this._scanFrame();\n return;\n }\n\n const timeSinceLastScan = Date.now() - this._lastScanTimestamp;\n const minimumTimeBetweenScans = 1000 / this._maxScansPerSecond;\n if (timeSinceLastScan < minimumTimeBetweenScans) {\n await new Promise((resolve) => setTimeout(resolve, minimumTimeBetweenScans - timeSinceLastScan));\n }\n // console.log('Scan rate:', Math.round(1000 / (Date.now() - this._lastScanTimestamp)));\n this._lastScanTimestamp = Date.now();\n\n let result: QrScanner.ScanResult | undefined;\n try {\n result = await QrScanner.scanImage(this.$video, {\n scanRegion: this._scanRegion,\n qrEngine: this._qrEnginePromise,\n canvas: this.$canvas,\n });\n } catch (error) {\n if (!this._active) return;\n const errorMessage = (error as Error).message || error as string;\n if (errorMessage.includes('service unavailable')) {\n // When the native BarcodeDetector crashed, create a new one\n this._qrEnginePromise = QrScanner.createQrEngine();\n }\n this._onDecodeError(error as Error | string);\n }\n\n if (result) {\n if (this._onDecode) {\n this._onDecode(result);\n } else if (this._legacyOnDecode) {\n this._legacyOnDecode(result.data);\n }\n\n if (this.$codeOutlineHighlight) {\n clearTimeout(this._codeOutlineHighlightRemovalTimeout);\n this._codeOutlineHighlightRemovalTimeout = undefined;\n this.$codeOutlineHighlight.setAttribute(\n 'viewBox',\n `${this._scanRegion.x || 0} `\n + `${this._scanRegion.y || 0} `\n + `${this._scanRegion.width || this.$video.videoWidth} `\n + `${this._scanRegion.height || this.$video.videoHeight}`,\n );\n const polygon = this.$codeOutlineHighlight.firstElementChild!;\n polygon.setAttribute('points', result.cornerPoints.map(({x, y}) => `${x},${y}`).join(' '));\n this.$codeOutlineHighlight.style.display = '';\n }\n } else if (this.$codeOutlineHighlight && !this._codeOutlineHighlightRemovalTimeout) {\n // hide after timeout to make it flash less when on some frames the QR code is detected and on some not\n this._codeOutlineHighlightRemovalTimeout = setTimeout(\n () => this.$codeOutlineHighlight!.style.display = 'none',\n 100,\n );\n }\n\n this._scanFrame();\n });\n }\n\n private _onDecodeError(error: Error | string): void {\n // default error handler; can be overwritten in the constructor\n if (error === QrScanner.NO_QR_CODE_FOUND) return;\n console.log(error);\n }\n\n private async _getCameraStream(): Promise<{ stream: MediaStream, facingMode: QrScanner.FacingMode }> {\n if (!navigator.mediaDevices) throw 'Camera not found.';\n\n const preferenceType = /^(environment|user)$/.test(this._preferredCamera)\n ? 'facingMode'\n : 'deviceId';\n const constraintsWithoutCamera: Array = [{\n width: { min: 1024 }\n }, {\n width: { min: 768 }\n }, {}];\n const constraintsWithCamera = constraintsWithoutCamera.map((constraint) => Object.assign({}, constraint, {\n [preferenceType]: { exact: this._preferredCamera },\n }));\n\n for (const constraints of [...constraintsWithCamera, ...constraintsWithoutCamera]) {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ video: constraints, audio: false });\n // Try to determine the facing mode from the stream, otherwise use a guess or 'environment' as\n // default. Note that the guess is not always accurate as Safari returns cameras of different facing\n // mode, even for exact facingMode constraints.\n const facingMode = this._getFacingMode(stream)\n || (constraints.facingMode\n ? this._preferredCamera as QrScanner.FacingMode // a facing mode we were able to fulfill\n : (this._preferredCamera === 'environment'\n ? 'user' // switch as _preferredCamera was environment but we are not able to fulfill it\n : 'environment' // switch from unfulfilled user facingMode or default to environment\n )\n );\n return { stream, facingMode };\n } catch (e) {}\n }\n\n throw 'Camera not found.';\n }\n\n private async _restartVideoStream(): Promise {\n // Note that we always pause the stream and not only if !this._paused as even if this._paused === true, the\n // stream might still be running, as it's by default only stopped after a delay of 300ms.\n const wasPaused = this._paused;\n const paused = await this.pause(true);\n if (!paused || wasPaused || !this._active) return;\n await this.start();\n }\n\n private static _stopVideoStream(stream : MediaStream): void {\n for (const track of stream.getTracks()) {\n track.stop(); // note that this will also automatically turn the flashlight off\n stream.removeTrack(track);\n }\n }\n\n private _setVideoMirror(facingMode: QrScanner.FacingMode): void {\n // in user facing mode mirror the video to make it easier for the user to position the QR code\n const scaleFactor = facingMode === 'user'? -1 : 1;\n this.$video.style.transform = 'scaleX(' + scaleFactor + ')';\n }\n\n private _getFacingMode(videoStream: MediaStream): QrScanner.FacingMode | null {\n const videoTrack = videoStream.getVideoTracks()[0];\n if (!videoTrack) return null; // unknown\n // inspired by https://github.com/JodusNodus/react-qr-reader/blob/master/src/getDeviceId.js#L13\n return /rear|back|environment/i.test(videoTrack.label)\n ? 'environment'\n : /front|user|face/i.test(videoTrack.label)\n ? 'user'\n : null; // unknown\n }\n\n private static _drawToCanvas(\n image: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement,\n scanRegion?: QrScanner.ScanRegion | null,\n canvas?: HTMLCanvasElement | null,\n disallowCanvasResizing= false,\n ): [HTMLCanvasElement, CanvasRenderingContext2D] {\n canvas = canvas || document.createElement('canvas');\n const scanRegionX = scanRegion && scanRegion.x ? scanRegion.x : 0;\n const scanRegionY = scanRegion && scanRegion.y ? scanRegion.y : 0;\n const scanRegionWidth = scanRegion && scanRegion.width\n ? scanRegion.width\n : (image as HTMLVideoElement).videoWidth || image.width as number;\n const scanRegionHeight = scanRegion && scanRegion.height\n ? scanRegion.height\n : (image as HTMLVideoElement).videoHeight || image.height as number;\n\n if (!disallowCanvasResizing) {\n const canvasWidth = scanRegion && scanRegion.downScaledWidth\n ? scanRegion.downScaledWidth\n : scanRegionWidth;\n const canvasHeight = scanRegion && scanRegion.downScaledHeight\n ? scanRegion.downScaledHeight\n : scanRegionHeight;\n // Setting the canvas width or height clears the canvas, even if the values didn't change, therefore only\n // set them if they actually changed.\n if (canvas.width !== canvasWidth) {\n canvas.width = canvasWidth;\n }\n if (canvas.height !== canvasHeight) {\n canvas.height = canvasHeight;\n }\n }\n\n const context = canvas.getContext('2d', { alpha: false })!;\n context.imageSmoothingEnabled = false; // gives less blurry images\n context.drawImage(\n image,\n scanRegionX, scanRegionY, scanRegionWidth, scanRegionHeight,\n 0, 0, canvas.width, canvas.height,\n );\n return [canvas, context];\n }\n\n private static async _loadImage(\n imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap\n | SVGImageElement | File | Blob | URL | String,\n ): Promise {\n if (imageOrFileOrBlobOrUrl instanceof Image) {\n await QrScanner._awaitImageLoad(imageOrFileOrBlobOrUrl);\n return imageOrFileOrBlobOrUrl;\n } else if (imageOrFileOrBlobOrUrl instanceof HTMLVideoElement\n || imageOrFileOrBlobOrUrl instanceof HTMLCanvasElement\n || imageOrFileOrBlobOrUrl instanceof SVGImageElement\n || 'OffscreenCanvas' in window && imageOrFileOrBlobOrUrl instanceof OffscreenCanvas\n || 'ImageBitmap' in window && imageOrFileOrBlobOrUrl instanceof ImageBitmap) {\n return imageOrFileOrBlobOrUrl;\n } else if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob\n || imageOrFileOrBlobOrUrl instanceof URL || typeof imageOrFileOrBlobOrUrl === 'string') {\n const image = new Image();\n if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob) {\n image.src = URL.createObjectURL(imageOrFileOrBlobOrUrl);\n } else {\n image.src = imageOrFileOrBlobOrUrl.toString();\n }\n try {\n await QrScanner._awaitImageLoad(image);\n return image;\n } finally {\n if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob) {\n URL.revokeObjectURL(image.src);\n }\n }\n } else {\n throw 'Unsupported image type.';\n }\n }\n\n private static async _awaitImageLoad(image: HTMLImageElement): Promise {\n if (image.complete && image.naturalWidth !== 0) return; // already loaded\n await new Promise((resolve, reject) => {\n const listener = (event: ErrorEvent | Event) => {\n image.removeEventListener('load', listener);\n image.removeEventListener('error', listener);\n if (event instanceof ErrorEvent) {\n reject('Image load error');\n } else {\n resolve();\n }\n };\n image.addEventListener('load', listener);\n image.addEventListener('error', listener);\n });\n }\n\n private static async _postWorkerMessage(\n qrEngineOrQrEnginePromise: Worker | BarcodeDetector | Promise,\n type: string,\n data?: any,\n ): Promise {\n const qrEngine = await qrEngineOrQrEnginePromise;\n if (!(qrEngine instanceof Worker)) return;\n qrEngine.postMessage({ type, data });\n }\n}\n\ndeclare namespace QrScanner {\n export interface ScanRegion {\n x?: number;\n y?: number;\n width?: number;\n height?: number;\n downScaledWidth?: number;\n downScaledHeight?: number;\n }\n\n export type FacingMode = 'environment' | 'user';\n export type DeviceId = string;\n\n export interface Camera {\n id: DeviceId;\n label: string;\n }\n\n export type InversionMode = 'original' | 'invert' | 'both';\n\n export interface Point {\n x: number;\n y: number;\n }\n\n export interface ScanResult {\n data: string;\n // In clockwise order, starting at top left, but this might not be guaranteed in the future.\n cornerPoints: QrScanner.Point[];\n }\n}\n\n// simplified from https://wicg.github.io/shape-detection-api/#barcode-detection-api\ndeclare class BarcodeDetector {\n constructor(options?: { formats: string[] });\n static getSupportedFormats(): Promise;\n detect(image: ImageBitmapSource): Promise>;\n}\n"],"names":["QrScanner","constructor","video","onDecode","canvasSizeOrOnDecodeErrorOrOptions","canvasSizeOrCalculateScanRegion","preferredCamera","DEFAULT_CANVAS_SIZE","$video","$canvas","document","createElement","_onDecode","console","warn","_legacyOnDecode","_onDecodeError","options","onDecodeError","_calculateScanRegion","calculateScanRegion","_preferredCamera","_legacyCanvasSize","_maxScansPerSecond","maxScansPerSecond","_onPlay","bind","_onLoadedMetaData","_onVisibilityChange","_updateOverlay","disablePictureInPicture","playsInline","muted","shouldHideVideo","hidden","body","contains","appendChild","highlightScanRegion","highlightCodeOutline","$overlay","overlay","overlayStyle","position","display","pointerEvents","classList","add","gotExternalOverlay","outline","videoContainer","insertBefore","nextSibling","$codeOutlineHighlight","createElementNS","polygon","codeOutlineHighlightStyle","width","height","setAttribute","fill","stroke","strokeWidth","_scanRegion","requestAnimationFrame","videoStyle","style","setProperty","visibility","opacity","parentElement","removeChild","addEventListener","window","_qrEnginePromise","createQrEngine","hasCamera","length","listCameras","e","requestLabels","navigator","mediaDevices","filter","device","openedStream","every","enumerateCameras","camera","label","getUserMedia","audio","map","i","id","deviceId","_stopVideoStream","hasFlash","stream","srcObject","MediaStream","_getCameraStream","getVideoTracks","getSettings","isFlashOn","_flashOn","toggleFlash","turnFlashOff","turnFlashOn","_destroyed","_active","_paused","applyConstraints","advanced","torch","_restartVideoStream","destroy","removeEventListener","stop","_postWorkerMessage","start","location","protocol","play","facingMode","_setVideoMirror","catch","pause","stopStreamImmediately","stopStream","Promise","resolve","setTimeout","setCamera","facingModeOrDeviceId","scanImage","imageOrFileOrBlobOrUrl","scanRegionOrOptions","qrEngine","canvas","disallowCanvasResizing","alsoTryWithoutScanRegion","scanRegion","returnDetailedScanResult","image","canvasContext","all","_loadImage","_drawToCanvas","Worker","gotExternalEngine","qrEngineWorker","postMessage","type","data","reject","timeout","onMessage","onError","event","clearTimeout","cornerPoints","_convertPoints","NO_QR_CODE_FOUND","error","imageData","buffer","race","scanResult","rawValue","message","detailedScanResult","setGrayscaleWeights","red","green","blue","useIntegerApproximation","setInversionMode","inversionMode","workerPath","WORKER_PATH","includes","BarcodeDetector","getSupportedFormats","formats","_scanFrame","x","Math","round","videoWidth","scanRegionSize","y","videoHeight","downScaledWidth","downScaledHeight","videoObjectFit","videoScaledWidth","videoScaledHeight","elementWidth","elementHeight","videoAspectRatio","elementAspectRatio","min","videoY","lengthValue","parseFloat","endsWith","regionWidth","regionHeight","top","elementY","regionY","left","elementX","isVideoMirrored","videoX","regionX","transform","points","point","scaleFactorX","offsetX","scaleFactorY","offsetY","paused","ended","requestVideoFrameCallback","readyState","timeSinceLastScan","minimumTimeBetweenScans","_lastScanTimestamp","Date","now","result","_codeOutlineHighlightRemovalTimeout","undefined","join","log","constraint","preferenceType","exact","constraints","wasPaused","track","removeTrack","_getFacingMode","videoStream","test","videoTrack","scanRegionWidth","scanRegionHeight","canvasWidth","canvasHeight","alpha","context","imageSmoothingEnabled","drawImage","scanRegionX","scanRegionY","Image","_awaitImageLoad","HTMLVideoElement","HTMLCanvasElement","SVGImageElement","OffscreenCanvas","ImageBitmap","File","Blob","URL","src","createObjectURL","toString","revokeObjectURL","complete","naturalWidth","listener","ErrorEvent","qrEngineOrQrEnginePromise"],"mappings":"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"} \ No newline at end of file diff --git a/src/qr-scanner.ts b/src/qr-scanner.ts index 6062eb2..ecb19f1 100644 --- a/src/qr-scanner.ts +++ b/src/qr-scanner.ts @@ -721,8 +721,15 @@ export default class QrScanner { private _scanFrame(): void { if (!this._active || this.$video.paused || this.$video.ended) return; - // using requestAnimationFrame to avoid scanning if tab is in background - requestAnimationFrame(async () => { + // If requestVideoFrameCallback is available use that to avoid unnecessary scans on the same frame as the + // camera's framerate can be lower than the screen refresh rate and this._maxScansPerSecond, especially in dark + // settings where the exposure time is longer. Both, requestVideoFrameCallback and requestAnimationFrame are not + // being fired if the tab is in the background, which is what we want. + const requestFrame = 'requestVideoFrameCallback' in this.$video + // @ts-ignore + ? this.$video.requestVideoFrameCallback.bind(this.$video) + : requestAnimationFrame; + requestFrame(async () => { if (this.$video.readyState <= 1) { // Skip scans until the video is ready as drawImage() only works correctly on a video with readyState // > 1, see https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage#Notes. @@ -737,6 +744,7 @@ export default class QrScanner { if (timeSinceLastScan < minimumTimeBetweenScans) { await new Promise((resolve) => setTimeout(resolve, minimumTimeBetweenScans - timeSinceLastScan)); } + // console.log('Scan rate:', Math.round(1000 / (Date.now() - this._lastScanTimestamp))); this._lastScanTimestamp = Date.now(); let result: QrScanner.ScanResult | undefined;