Switch constructor and scanImage to using options objects

This commit is contained in:
Daniel
2022-01-25 10:21:26 +01:00
parent b769540c3b
commit 954d3e2930
10 changed files with 179 additions and 94 deletions
+18 -11
View File
@@ -142,13 +142,17 @@ Create a `<video>` element where the web cam video stream should get rendered:
```js
const qrScanner = new QrScanner(videoElem, result => console.log('decoded qr code:', result));
```
As an optional third parameter an error handler to be invoked on decoding errors can be specified. The default is `QrScanner._onDecodeError`.
As an optional fourth parameter a method can be provided 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.
As an optional third parameter an options object can be provided.
Supported options are:
As an optional fifth parameter a preference for the camera to use can be specified. 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.
| Option | Description |
|---|---|
| `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. |
| `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. |
To use the default value for an optional parameter, omit it or use `undefined`.
To use the default value for an option, omit it or supply `undefined`.
#### 3. Start scanning
```js
@@ -184,15 +188,18 @@ Supported image sources are:
[Data URIs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs),
URLs pointing to an image (if they are on the same origin or [CORS enabled](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image))
As an optional second parameter a region defined by `x`, `y`, `width` and `height` can be specified to which the search for a QR code should be restricted. As a performance improvement this region can be scaled down before performing the scan by providing a `downScaledWidth` and `downScaledHeight`. By default, the region spans the whole image and is not scaled down.
As an optional second parameter an options object can be provided.
Supported options are:
As an optional third parameter a manually created QR scanner engine instance to be reused can be specified. This improves performance if you're scanning a lot of images. An engine can be manually created via `QrScanner.createQrEngine(QrScanner.WORKER_PATH)` (async). By default, no engine is reused for single image scanning.
| Option | Description |
|---|---|
| `scanRegion` | A region defined by `x`, `y`, `width` and `height` to which the search for a QR code should be restricted. As a performance improvement this region can be scaled down before performing the scan by providing a `downScaledWidth` and `downScaledHeight`. Note that the aspect ratio between `width` and `height` and `downScaledWidth` and `downScaledHeight` should remain the same. By default, the region spans the whole image and is not scaled down. |
| `qrEngine` | A manually created QR scanner engine instance to be reused. This improves performance if you're scanning a lot of images. An engine can be manually created via `QrScanner.createQrEngine(QrScanner.WORKER_PATH)`. By default, no engine is reused for single image scanning. |
| `canvas` | A manually created canvas to be reused. This improves performance if you're scanning a lot of images. A canvas can be manually created via a `<canvas>` tag in your markup or `document.createElement('canvas')`. By default, no canvas is reused for single image scanning. |
| `disallowCanvasResizing` | Request a provided canvas for reuse to not be resized, irrespective of the source image or source region dimensions. Note that the canvas and source region should have the same aspect ratio to avoid that the image to scan gets distorted which could make detecting QR codes impossible. By default, the canvas size is adapted to the scan region dimensions or down scaled scan region for single image scanning. |
| `alsoTryWithoutScanRegion` | Request a second scan on the entire image if a `scanRegion` was provided and no QR code was found within that region. By default, no second scan is attempted. |
As an optional fourth parameter a manually created canvas to be reused can be specified. This improves performance if you're scanning a lot of images. A canvas can be manually created via a `<canvas>` tag in your markup or `document.createElement('canvas')`. By default, no canvas is reused for single image scanning.
As an optional fifth parameter you can request a provided canvas for reuse to not be resized, irrespective of the source image or source region dimensions. Note that the canvas and source region should have the same aspect ratio to avoid that the image to scan gets distorted which could make detecting QR codes impossible. By default, the canvas size is adapted to the source region dimensions for single image scanning.
To use the default value for an optional parameter, omit it or use `undefined`.
To use the default value for an option, omit it or supply `undefined`.
### Checking for Camera availability
+5 -3
View File
@@ -93,9 +93,11 @@
// ####### Web Cam Scanning #######
const scanner = new QrScanner(video, result => setResult(camQrResult, result), error => {
camQrResult.textContent = error;
camQrResult.style.color = 'inherit';
const scanner = new QrScanner(video, result => setResult(camQrResult, result), {
onDecodeError: error => {
camQrResult.textContent = error;
camQrResult.style.color = 'inherit';
},
});
const updateFlashAvailability = () => {
+17 -16
View File
@@ -17,25 +17,26 @@ c.generator.Engine_.prototype.yieldAllStep_=function(a,b,d){try{var e=a.call(thi
c.generator.Engine_.prototype.nextStep_=function(){for(;this.context_.nextAddress;)try{var a=this.program_(this.context_);if(a)return this.context_.stop_(),{value:a.value,done:!1}}catch(b){this.context_.yieldResult=void 0,this.context_.throw_(b)}this.context_.stop_();if(this.context_.abruptCompletion_){a=this.context_.abruptCompletion_;this.context_.abruptCompletion_=null;if(a.isException)throw a.exception;return{value:a.return,done:!0}}return{value:void 0,done:!0}};
c.generator.Generator_=function(a){this.next=function(b){return a.next_(b)};this.throw=function(b){return a.throw_(b)};this.return=function(b){return a.return_(b)};this[Symbol.iterator]=function(){return this}};c.generator.createGenerator=function(a,b){b=new c.generator.Generator_(new c.generator.Engine_(b));c.setPrototypeOf&&a.prototype&&c.setPrototypeOf(b,a.prototype);return b};
c.asyncExecutePromiseGenerator=function(a){function b(e){return a.next(e)}function d(e){return a.throw(e)}return new Promise(function(e,f){function h(k){k.done?e(k.value):Promise.resolve(k.value).then(b,d).then(h,f)}h(a.next())})};c.asyncExecutePromiseGeneratorFunction=function(a){return c.asyncExecutePromiseGenerator(a())};c.asyncExecutePromiseGeneratorProgram=function(a){return c.asyncExecutePromiseGenerator(new c.generator.Generator_(new c.generator.Engine_(a)))};
class g{constructor(a,b,d,e,f="environment"){this._legacyCanvasSize=g.DEFAULT_CANVAS_SIZE;this._destroyed=this._flashOn=this._paused=this._active=!1;d=d||this._onDecodeError;e=e||this._calculateScanRegion;this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;this._preferredCamera=f;"number"===typeof d?(this._legacyCanvasSize=d,console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future")):this._onDecodeError=d;"number"===
typeof e?(this._legacyCanvasSize=e,console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future")):this._calculateScanRegion=e;this._scanRegion=this._calculateScanRegion(a);this._onPlay=this._onPlay.bind(this);this._onLoadedMetaData=this._onLoadedMetaData.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);a.disablePictureInPicture=!0;a.playsInline=!0;a.muted=!0;let h=!1;a.hidden&&(a.hidden=!1,h=!0);document.body.contains(a)||
(document.body.appendChild(a),h=!0);requestAnimationFrame(()=>{let k=window.getComputedStyle(a);"none"===k.display&&(a.style.setProperty("display","block","important"),h=!0);"visible"!==k.visibility&&(a.style.setProperty("visibility","visible","important"),h=!0);h&&(console.warn("QrScanner has overwritten the video hiding style to avoid Safari stopping the playback."),a.style.opacity="0",a.style.width="0",a.style.height="0")});a.addEventListener("play",this._onPlay);a.addEventListener("loadedmetadata",
this._onLoadedMetaData);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrEnginePromise=g.createQrEngine()}static hasCamera(){return c.asyncExecutePromiseGeneratorFunction(function*(){try{return!!(yield g.listCameras(!1)).length}catch(a){return!1}})}static listCameras(a=!1){return c.asyncExecutePromiseGeneratorFunction(function*(){if(!navigator.mediaDevices)return[];let b=()=>c.asyncExecutePromiseGeneratorFunction(function*(){return(yield navigator.mediaDevices.enumerateDevices()).filter(e=>
class g{constructor(a,b,d,e,f){this._preferredCamera="environment";this._legacyCanvasSize=g.DEFAULT_CANVAS_SIZE;this._destroyed=this._flashOn=this._paused=this._active=!1;this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;(d&&"object"!==typeof d||e||f)&&console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future");b="object"===typeof d?d:{};this._onDecodeError=b.onDecodeError||("function"===typeof d?d:this._onDecodeError);
this._calculateScanRegion=b.calculateScanRegion||("function"===typeof e?e:this._calculateScanRegion);this._preferredCamera=b.preferredCamera||f||this._preferredCamera;this._legacyCanvasSize="number"===typeof d?d:"number"===typeof e?e:this._legacyCanvasSize;this._scanRegion=this._calculateScanRegion(a);this._onPlay=this._onPlay.bind(this);this._onLoadedMetaData=this._onLoadedMetaData.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);a.disablePictureInPicture=!0;a.playsInline=
!0;a.muted=!0;let h=!1;a.hidden&&(a.hidden=!1,h=!0);document.body.contains(a)||(document.body.appendChild(a),h=!0);requestAnimationFrame(()=>{let k=window.getComputedStyle(a);"none"===k.display&&(a.style.setProperty("display","block","important"),h=!0);"visible"!==k.visibility&&(a.style.setProperty("visibility","visible","important"),h=!0);h&&(console.warn("QrScanner has overwritten the video hiding style to avoid Safari stopping the playback."),a.style.opacity="0",a.style.width="0",a.style.height=
"0")});a.addEventListener("play",this._onPlay);a.addEventListener("loadedmetadata",this._onLoadedMetaData);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrEnginePromise=g.createQrEngine()}static hasCamera(){return c.asyncExecutePromiseGeneratorFunction(function*(){try{return!!(yield g.listCameras(!1)).length}catch(a){return!1}})}static listCameras(a=!1){return c.asyncExecutePromiseGeneratorFunction(function*(){if(!navigator.mediaDevices)return[];let b=()=>c.asyncExecutePromiseGeneratorFunction(function*(){return(yield navigator.mediaDevices.enumerateDevices()).filter(e=>
"videoinput"===e.kind)}),d;try{a&&(yield b()).every(e=>!e.label)&&(d=yield navigator.mediaDevices.getUserMedia({audio:!1,video:!0}))}catch(e){}try{return(yield b()).map((e,f)=>({id:e.deviceId,label:e.label||(0===f?"Default Camera":`Camera ${f+1}`)}))}finally{d&&(console.warn("Call listCameras after successfully starting a QR scanner to avoid creating a temporary video stream"),g._stopVideoStream(d))}})}hasFlash(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){let b;try{if(a.$video.srcObject){if(!(a.$video.srcObject instanceof
MediaStream))return!1;b=a.$video.srcObject}else b=(yield a._getCameraStream()).stream;return"torch"in b.getVideoTracks()[0].getSettings()}catch(d){return!1}finally{b&&b!==a.$video.srcObject&&(console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream"),g._stopVideoStream(b))}})}isFlashOn(){return this._flashOn}toggleFlash(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){a._flashOn?yield a.turnFlashOff():yield a.turnFlashOn()})}turnFlashOn(){const a=
this;return c.asyncExecutePromiseGeneratorFunction(function*(){if(!a._flashOn&&!a._destroyed&&(a._flashOn=!0,a._active&&!a._paused))try{if(!(yield a.hasFlash()))throw"No flash available";yield a.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:!0}]})}catch(b){throw a._flashOn=!1,b;}})}turnFlashOff(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){a._flashOn&&(a._flashOn=!1,yield a._restartVideoStream())})}destroy(){this.$video.removeEventListener("loadedmetadata",
this._onLoadedMetaData);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",this._onVisibilityChange);this._destroyed=!0;this._flashOn=!1;this.stop();g._postWorkerMessage(this._qrEnginePromise,"close")}start(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){if(!(a._active&&!a._paused||a._destroyed||("https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https."),
a._active=!0,document.hidden)))if(a._paused=!1,a.$video.srcObject)yield a.$video.play();else try{let {stream:b,facingMode:d}=yield a._getCameraStream();!a._active||a._paused?g._stopVideoStream(b):(a.$video.srcObject=b,yield a.$video.play(),a._setVideoMirror(d),a._flashOn&&(a._flashOn=!1,a.turnFlashOn().catch(()=>{})))}catch(b){if(!a._paused)throw a._active=!1,b;}})}stop(){this.pause();this._active=!1}pause(a=!1){const b=this;return c.asyncExecutePromiseGeneratorFunction(function*(){b._paused=!0;if(!b._active)return!0;
b.$video.pause();let d=()=>{b.$video.srcObject instanceof MediaStream&&(g._stopVideoStream(b.$video.srcObject),b.$video.srcObject=null)};if(a)return d(),!0;yield new Promise(e=>setTimeout(e,300));if(!b._paused)return!1;d();return!0})}setCamera(a){const b=this;return c.asyncExecutePromiseGeneratorFunction(function*(){a!==b._preferredCamera&&(b._preferredCamera=a,yield b._restartVideoStream())})}static scanImage(a,b,d,e,f=!1,h=!1){return c.asyncExecutePromiseGeneratorFunction(function*(){let k=!!d;
try{let m,u;[d,m]=yield Promise.all([d||g.createQrEngine(),g._loadImage(a)]);[e,u]=g._drawToCanvas(m,b,e,f);if(d instanceof Worker){let l=d;k||l.postMessage({type:"inversionMode",data:"both"});return yield new Promise((r,v)=>{let t,q,p;q=n=>{"qrResult"===n.data.type&&(l.removeEventListener("message",q),l.removeEventListener("error",p),clearTimeout(t),null!==n.data.data?r(n.data.data):v(g.NO_QR_CODE_FOUND))};p=n=>{l.removeEventListener("message",q);l.removeEventListener("error",p);clearTimeout(t);
v("Scanner error: "+(n?n.message||n:"Unknown Error"))};l.addEventListener("message",q);l.addEventListener("error",p);t=setTimeout(()=>p("timeout"),1E4);let w=u.getImageData(0,0,e.width,e.height);l.postMessage({type:"decode",data:w},[w.data.buffer])})}return yield Promise.race([new Promise((l,r)=>window.setTimeout(()=>r("Scanner error: timeout"),1E4)),(()=>c.asyncExecutePromiseGeneratorFunction(function*(){try{let [l]=yield d.detect(e);if(!l)throw g.NO_QR_CODE_FOUND;return l.rawValue}catch(l){throw`Scanner error: ${l.message||
l}`;}}))()])}catch(m){if(!b||!h)throw m;return yield g.scanImage(a,null,d,e,f)}finally{k||g._postWorkerMessage(d,"close")}})}setGrayscaleWeights(a,b,d,e=!0){g._postWorkerMessage(this._qrEnginePromise,"grayscaleWeights",{red:a,green:b,blue:d,useIntegerApproximation:e})}setInversionMode(a){g._postWorkerMessage(this._qrEnginePromise,"inversionMode",a)}static createQrEngine(a=g.WORKER_PATH){return c.asyncExecutePromiseGeneratorFunction(function*(){return"BarcodeDetector"in window&&BarcodeDetector.getSupportedFormats&&
(yield BarcodeDetector.getSupportedFormats()).includes("qr_code")?new BarcodeDetector({formats:["qr_code"]}):new Worker(a)})}_onPlay(){this._scanRegion=this._calculateScanRegion(this.$video);this._scanFrame()}_onLoadedMetaData(){this._scanRegion=this._calculateScanRegion(this.$video)}_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}}_scanFrame(){!this._active||this.$video.paused||this.$video.ended||requestAnimationFrame(()=>{const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){if(!(1>=a.$video.readyState)){try{var b=yield g.scanImage(a.$video,a._scanRegion,a._qrEnginePromise,a.$canvas)}catch(d){if(!a._active)return;(d.message||d).includes("service unavailable")&&(a._qrEnginePromise=g.createQrEngine());a._onDecodeError(d)}b&&
a._onDecode(b)}a._scanFrame()})})}_onDecodeError(a){a!==g.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 h=yield navigator.mediaDevices.getUserMedia({video:f,
audio:!1}),k=a._getFacingMode(h)||(f.facingMode?a._preferredCamera:"environment"===a._preferredCamera?"user":"environment");return{stream:h,facingMode:k}}catch(h){}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,h=b&&b.y?b.y:0,k=b&&b.width?b.width:a.videoWidth||a.width,m=b&&b.height?b.height:a.videoHeight||a.height;e||(e=b&&b.downScaledWidth?b.downScaledWidth:k,b=b&&b.downScaledHeight?b.downScaledHeight:m,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,h,k,m,0,0,d.width,d.height);return[d,b]}static _loadImage(a){return c.asyncExecutePromiseGeneratorFunction(function*(){if(a instanceof Image)return yield g._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 g._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})})}}g.DEFAULT_CANVAS_SIZE=400;g.NO_QR_CODE_FOUND="No QR code found";g.WORKER_PATH="qr-scanner-worker.min.js";export default g
b.$video.pause();let d=()=>{b.$video.srcObject instanceof MediaStream&&(g._stopVideoStream(b.$video.srcObject),b.$video.srcObject=null)};if(a)return d(),!0;yield new Promise(e=>setTimeout(e,300));if(!b._paused)return!1;d();return!0})}setCamera(a){const b=this;return c.asyncExecutePromiseGeneratorFunction(function*(){a!==b._preferredCamera&&(b._preferredCamera=a,yield b._restartVideoStream())})}static scanImage(a,b,d,e,f=!1,h=!1){return c.asyncExecutePromiseGeneratorFunction(function*(){let k;b&&("scanRegion"in
b||"qrEngine"in b||"canvas"in b||"disallowCanvasResizing"in b||"alsoTryWithoutScanRegion"in b)?(k=b.scanRegion,d=b.qrEngine,e=b.canvas,f=b.disallowCanvasResizing||!1,h=b.alsoTryWithoutScanRegion||!1):(b||d||e||f||h)&&console.warn("You're using a deprecated api for scanImage which will be removed in the future.");let n=!!d;try{let q,v;[d,q]=yield Promise.all([d||g.createQrEngine(),g._loadImage(a)]);[e,v]=g._drawToCanvas(q,k,e,f);if(d instanceof Worker){let l=d;n||l.postMessage({type:"inversionMode",
data:"both"});return yield new Promise((t,w)=>{let u,r,p;r=m=>{"qrResult"===m.data.type&&(l.removeEventListener("message",r),l.removeEventListener("error",p),clearTimeout(u),null!==m.data.data?t(m.data.data):w(g.NO_QR_CODE_FOUND))};p=m=>{l.removeEventListener("message",r);l.removeEventListener("error",p);clearTimeout(u);w("Scanner error: "+(m?m.message||m:"Unknown Error"))};l.addEventListener("message",r);l.addEventListener("error",p);u=setTimeout(()=>p("timeout"),1E4);let x=v.getImageData(0,0,e.width,
e.height);l.postMessage({type:"decode",data:x},[x.data.buffer])})}return yield Promise.race([new Promise((l,t)=>window.setTimeout(()=>t("Scanner error: timeout"),1E4)),(()=>c.asyncExecutePromiseGeneratorFunction(function*(){try{let [l]=yield d.detect(e);if(!l)throw g.NO_QR_CODE_FOUND;return l.rawValue}catch(l){throw`Scanner error: ${l.message||l}`;}}))()])}catch(q){if(!k||!h)throw q;return yield g.scanImage(a,{qrEngine:d,canvas:e,disallowCanvasResizing:f})}finally{n||g._postWorkerMessage(d,"close")}})}setGrayscaleWeights(a,
b,d,e=!0){g._postWorkerMessage(this._qrEnginePromise,"grayscaleWeights",{red:a,green:b,blue:d,useIntegerApproximation:e})}setInversionMode(a){g._postWorkerMessage(this._qrEnginePromise,"inversionMode",a)}static createQrEngine(a=g.WORKER_PATH){return c.asyncExecutePromiseGeneratorFunction(function*(){return"BarcodeDetector"in window&&BarcodeDetector.getSupportedFormats&&(yield BarcodeDetector.getSupportedFormats()).includes("qr_code")?new BarcodeDetector({formats:["qr_code"]}):new Worker(a)})}_onPlay(){this._scanRegion=
this._calculateScanRegion(this.$video);this._scanFrame()}_onLoadedMetaData(){this._scanRegion=this._calculateScanRegion(this.$video)}_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}}_scanFrame(){!this._active||this.$video.paused||
this.$video.ended||requestAnimationFrame(()=>{const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){if(!(1>=a.$video.readyState)){try{var b=yield g.scanImage(a.$video,{scanRegion:a._scanRegion,qrEngine:a._qrEnginePromise,canvas:a.$canvas})}catch(d){if(!a._active)return;(d.message||d).includes("service unavailable")&&(a._qrEnginePromise=g.createQrEngine());a._onDecodeError(d)}b&&a._onDecode(b)}a._scanFrame()})})}_onDecodeError(a){a!==g.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 h=yield navigator.mediaDevices.getUserMedia({video:f,audio:!1}),k=a._getFacingMode(h)||(f.facingMode?a._preferredCamera:"environment"===a._preferredCamera?"user":"environment");
return{stream:h,facingMode:k}}catch(h){}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,h=b&&b.y?b.y:0,k=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:k,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,h,k,n,0,0,d.width,d.height);return[d,b]}static _loadImage(a){return c.asyncExecutePromiseGeneratorFunction(function*(){if(a instanceof
Image)return yield g._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 g._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})})}}g.DEFAULT_CANVAS_SIZE=400;g.NO_QR_CODE_FOUND="No QR code found";g.WORKER_PATH="qr-scanner-worker.min.js";export default g
//# sourceMappingURL=qr-scanner.legacy.min.js.map
File diff suppressed because one or more lines are too long
+15 -14
View File
@@ -1,17 +1,18 @@
class e{constructor(a,b,c,d,f="environment"){this._legacyCanvasSize=e.DEFAULT_CANVAS_SIZE;this._destroyed=this._flashOn=this._paused=this._active=!1;c=c||this._onDecodeError;d=d||this._calculateScanRegion;this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;this._preferredCamera=f;"number"===typeof c?(this._legacyCanvasSize=c,console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future")):this._onDecodeError=
c;"number"===typeof d?(this._legacyCanvasSize=d,console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future")):this._calculateScanRegion=d;this._scanRegion=this._calculateScanRegion(a);this._onPlay=this._onPlay.bind(this);this._onLoadedMetaData=this._onLoadedMetaData.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);a.disablePictureInPicture=!0;a.playsInline=!0;a.muted=!0;let h=!1;a.hidden&&(a.hidden=!1,h=!0);document.body.contains(a)||
(document.body.appendChild(a),h=!0);requestAnimationFrame(()=>{let k=window.getComputedStyle(a);"none"===k.display&&(a.style.setProperty("display","block","important"),h=!0);"visible"!==k.visibility&&(a.style.setProperty("visibility","visible","important"),h=!0);h&&(console.warn("QrScanner has overwritten the video hiding style to avoid Safari stopping the playback."),a.style.opacity="0",a.style.width="0",a.style.height="0")});a.addEventListener("play",this._onPlay);a.addEventListener("loadedmetadata",
this._onLoadedMetaData);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrEnginePromise=e.createQrEngine()}static async hasCamera(){try{return!!(await e.listCameras(!1)).length}catch(a){return!1}}static async listCameras(a=!1){if(!navigator.mediaDevices)return[];let b=async()=>(await navigator.mediaDevices.enumerateDevices()).filter(d=>"videoinput"===d.kind),c;try{a&&(await b()).every(d=>!d.label)&&(c=await navigator.mediaDevices.getUserMedia({audio:!1,video:!0}))}catch(d){}try{return(await b()).map((d,
f)=>({id:d.deviceId,label:d.label||(0===f?"Default Camera":`Camera ${f+1}`)}))}finally{c&&(console.warn("Call listCameras after successfully starting a QR scanner to avoid creating a temporary video stream"),e._stopVideoStream(c))}}async hasFlash(){let a;try{if(this.$video.srcObject){if(!(this.$video.srcObject instanceof MediaStream))return!1;a=this.$video.srcObject}else a=(await this._getCameraStream()).stream;return"torch"in a.getVideoTracks()[0].getSettings()}catch(b){return!1}finally{a&&a!==this.$video.srcObject&&
(console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream"),e._stopVideoStream(a))}}isFlashOn(){return this._flashOn}async toggleFlash(){this._flashOn?await this.turnFlashOff():await this.turnFlashOn()}async turnFlashOn(){if(!this._flashOn&&!this._destroyed&&(this._flashOn=!0,this._active&&!this._paused))try{if(!await this.hasFlash())throw"No flash available";await this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:!0}]})}catch(a){throw this._flashOn=
!1,a;}}async turnFlashOff(){this._flashOn&&(this._flashOn=!1,await this._restartVideoStream())}destroy(){this.$video.removeEventListener("loadedmetadata",this._onLoadedMetaData);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",this._onVisibilityChange);this._destroyed=!0;this._flashOn=!1;this.stop();e._postWorkerMessage(this._qrEnginePromise,"close")}async start(){if(!(this._active&&!this._paused||this._destroyed||("https:"!==window.location.protocol&&
console.warn("The camera stream is only accessible if the page is transferred via https."),this._active=!0,document.hidden)))if(this._paused=!1,this.$video.srcObject)await this.$video.play();else try{let {stream:a,facingMode:b}=await this._getCameraStream();!this._active||this._paused?e._stopVideoStream(a):(this.$video.srcObject=a,await this.$video.play(),this._setVideoMirror(b),this._flashOn&&(this._flashOn=!1,this.turnFlashOn().catch(()=>{})))}catch(a){if(!this._paused)throw this._active=!1,a;}}stop(){this.pause();
this._active=!1}async pause(a=!1){this._paused=!0;if(!this._active)return!0;this.$video.pause();let b=()=>{this.$video.srcObject instanceof MediaStream&&(e._stopVideoStream(this.$video.srcObject),this.$video.srcObject=null)};if(a)return b(),!0;await new Promise(c=>setTimeout(c,300));if(!this._paused)return!1;b();return!0}async setCamera(a){a!==this._preferredCamera&&(this._preferredCamera=a,await this._restartVideoStream())}static async scanImage(a,b,c,d,f=!1,h=!1){let k=!!c;try{let l,t;[c,l]=await Promise.all([c||
e.createQrEngine(),e._loadImage(a)]);[d,t]=e._drawToCanvas(l,b,d,f);if(c instanceof Worker){let g=c;k||g.postMessage({type:"inversionMode",data:"both"});return await new Promise((q,u)=>{let r,p,n;p=m=>{"qrResult"===m.data.type&&(g.removeEventListener("message",p),g.removeEventListener("error",n),clearTimeout(r),null!==m.data.data?q(m.data.data):u(e.NO_QR_CODE_FOUND))};n=m=>{g.removeEventListener("message",p);g.removeEventListener("error",n);clearTimeout(r);u("Scanner error: "+(m?m.message||m:"Unknown Error"))};
g.addEventListener("message",p);g.addEventListener("error",n);r=setTimeout(()=>n("timeout"),1E4);let v=t.getImageData(0,0,d.width,d.height);g.postMessage({type:"decode",data:v},[v.data.buffer])})}return await Promise.race([new Promise((g,q)=>window.setTimeout(()=>q("Scanner error: timeout"),1E4)),(async()=>{try{let [g]=await c.detect(d);if(!g)throw e.NO_QR_CODE_FOUND;return g.rawValue}catch(g){throw`Scanner error: ${g.message||g}`;}})()])}catch(l){if(!b||!h)throw l;return await e.scanImage(a,null,
c,d,f)}finally{k||e._postWorkerMessage(c,"close")}}setGrayscaleWeights(a,b,c,d=!0){e._postWorkerMessage(this._qrEnginePromise,"grayscaleWeights",{red:a,green:b,blue:c,useIntegerApproximation:d})}setInversionMode(a){e._postWorkerMessage(this._qrEnginePromise,"inversionMode",a)}static async createQrEngine(a=e.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._scanFrame()}_onLoadedMetaData(){this._scanRegion=this._calculateScanRegion(this.$video)}_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}}_scanFrame(){!this._active||
this.$video.paused||this.$video.ended||requestAnimationFrame(async()=>{if(!(1>=this.$video.readyState)){try{var a=await e.scanImage(this.$video,this._scanRegion,this._qrEnginePromise,this.$canvas)}catch(b){if(!this._active)return;(b.message||b).includes("service unavailable")&&(this._qrEnginePromise=e.createQrEngine());this._onDecodeError(b)}a&&this._onDecode(a)}this._scanFrame()})}_onDecodeError(a){a!==e.NO_QR_CODE_FOUND&&console.log(a)}async _getCameraStream(){if(!navigator.mediaDevices)throw"Camera not found.";
class e{constructor(a,b,c,d,f){this._preferredCamera="environment";this._legacyCanvasSize=e.DEFAULT_CANVAS_SIZE;this._destroyed=this._flashOn=this._paused=this._active=!1;this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;(c&&"object"!==typeof c||d||f)&&console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future");b="object"===typeof c?c:{};this._onDecodeError=b.onDecodeError||("function"===typeof c?c:this._onDecodeError);
this._calculateScanRegion=b.calculateScanRegion||("function"===typeof d?d:this._calculateScanRegion);this._preferredCamera=b.preferredCamera||f||this._preferredCamera;this._legacyCanvasSize="number"===typeof c?c:"number"===typeof d?d:this._legacyCanvasSize;this._scanRegion=this._calculateScanRegion(a);this._onPlay=this._onPlay.bind(this);this._onLoadedMetaData=this._onLoadedMetaData.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);a.disablePictureInPicture=!0;a.playsInline=
!0;a.muted=!0;let h=!1;a.hidden&&(a.hidden=!1,h=!0);document.body.contains(a)||(document.body.appendChild(a),h=!0);requestAnimationFrame(()=>{let k=window.getComputedStyle(a);"none"===k.display&&(a.style.setProperty("display","block","important"),h=!0);"visible"!==k.visibility&&(a.style.setProperty("visibility","visible","important"),h=!0);h&&(console.warn("QrScanner has overwritten the video hiding style to avoid Safari stopping the playback."),a.style.opacity="0",a.style.width="0",a.style.height=
"0")});a.addEventListener("play",this._onPlay);a.addEventListener("loadedmetadata",this._onLoadedMetaData);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrEnginePromise=e.createQrEngine()}static async hasCamera(){try{return!!(await e.listCameras(!1)).length}catch(a){return!1}}static async listCameras(a=!1){if(!navigator.mediaDevices)return[];let b=async()=>(await navigator.mediaDevices.enumerateDevices()).filter(d=>"videoinput"===d.kind),c;try{a&&(await b()).every(d=>
!d.label)&&(c=await navigator.mediaDevices.getUserMedia({audio:!1,video:!0}))}catch(d){}try{return(await b()).map((d,f)=>({id:d.deviceId,label:d.label||(0===f?"Default Camera":`Camera ${f+1}`)}))}finally{c&&(console.warn("Call listCameras after successfully starting a QR scanner to avoid creating a temporary video stream"),e._stopVideoStream(c))}}async hasFlash(){let a;try{if(this.$video.srcObject){if(!(this.$video.srcObject instanceof MediaStream))return!1;a=this.$video.srcObject}else a=(await this._getCameraStream()).stream;
return"torch"in a.getVideoTracks()[0].getSettings()}catch(b){return!1}finally{a&&a!==this.$video.srcObject&&(console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream"),e._stopVideoStream(a))}}isFlashOn(){return this._flashOn}async toggleFlash(){this._flashOn?await this.turnFlashOff():await this.turnFlashOn()}async turnFlashOn(){if(!this._flashOn&&!this._destroyed&&(this._flashOn=!0,this._active&&!this._paused))try{if(!await this.hasFlash())throw"No flash available";
await this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:!0}]})}catch(a){throw this._flashOn=!1,a;}}async turnFlashOff(){this._flashOn&&(this._flashOn=!1,await this._restartVideoStream())}destroy(){this.$video.removeEventListener("loadedmetadata",this._onLoadedMetaData);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",this._onVisibilityChange);this._destroyed=!0;this._flashOn=!1;this.stop();e._postWorkerMessage(this._qrEnginePromise,
"close")}async start(){if(!(this._active&&!this._paused||this._destroyed||("https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https."),this._active=!0,document.hidden)))if(this._paused=!1,this.$video.srcObject)await this.$video.play();else try{let {stream:a,facingMode:b}=await this._getCameraStream();!this._active||this._paused?e._stopVideoStream(a):(this.$video.srcObject=a,await this.$video.play(),this._setVideoMirror(b),this._flashOn&&
(this._flashOn=!1,this.turnFlashOn().catch(()=>{})))}catch(a){if(!this._paused)throw this._active=!1,a;}}stop(){this.pause();this._active=!1}async pause(a=!1){this._paused=!0;if(!this._active)return!0;this.$video.pause();let b=()=>{this.$video.srcObject instanceof MediaStream&&(e._stopVideoStream(this.$video.srcObject),this.$video.srcObject=null)};if(a)return b(),!0;await new Promise(c=>setTimeout(c,300));if(!this._paused)return!1;b();return!0}async setCamera(a){a!==this._preferredCamera&&(this._preferredCamera=
a,await this._restartVideoStream())}static async scanImage(a,b,c,d,f=!1,h=!1){let k;b&&("scanRegion"in b||"qrEngine"in b||"canvas"in b||"disallowCanvasResizing"in b||"alsoTryWithoutScanRegion"in b)?(k=b.scanRegion,c=b.qrEngine,d=b.canvas,f=b.disallowCanvasResizing||!1,h=b.alsoTryWithoutScanRegion||!1):(b||c||d||f||h)&&console.warn("You're using a deprecated api for scanImage which will be removed in the future.");b=!!c;try{let l,t;[c,l]=await Promise.all([c||e.createQrEngine(),e._loadImage(a)]);[d,
t]=e._drawToCanvas(l,k,d,f);if(c instanceof Worker){let g=c;b||g.postMessage({type:"inversionMode",data:"both"});return await new Promise((q,u)=>{let r,p,n;p=m=>{"qrResult"===m.data.type&&(g.removeEventListener("message",p),g.removeEventListener("error",n),clearTimeout(r),null!==m.data.data?q(m.data.data):u(e.NO_QR_CODE_FOUND))};n=m=>{g.removeEventListener("message",p);g.removeEventListener("error",n);clearTimeout(r);u("Scanner error: "+(m?m.message||m:"Unknown Error"))};g.addEventListener("message",
p);g.addEventListener("error",n);r=setTimeout(()=>n("timeout"),1E4);let v=t.getImageData(0,0,d.width,d.height);g.postMessage({type:"decode",data:v},[v.data.buffer])})}return await Promise.race([new Promise((g,q)=>window.setTimeout(()=>q("Scanner error: timeout"),1E4)),(async()=>{try{let [g]=await c.detect(d);if(!g)throw e.NO_QR_CODE_FOUND;return g.rawValue}catch(g){throw`Scanner error: ${g.message||g}`;}})()])}catch(l){if(!k||!h)throw l;return await e.scanImage(a,{qrEngine:c,canvas:d,disallowCanvasResizing:f})}finally{b||
e._postWorkerMessage(c,"close")}}setGrayscaleWeights(a,b,c,d=!0){e._postWorkerMessage(this._qrEnginePromise,"grayscaleWeights",{red:a,green:b,blue:c,useIntegerApproximation:d})}setInversionMode(a){e._postWorkerMessage(this._qrEnginePromise,"inversionMode",a)}static async createQrEngine(a=e.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._scanFrame()}_onLoadedMetaData(){this._scanRegion=this._calculateScanRegion(this.$video)}_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}}_scanFrame(){!this._active||this.$video.paused||
this.$video.ended||requestAnimationFrame(async()=>{if(!(1>=this.$video.readyState)){try{var a=await e.scanImage(this.$video,{scanRegion:this._scanRegion,qrEngine:this._qrEnginePromise,canvas:this.$canvas})}catch(b){if(!this._active)return;(b.message||b).includes("service unavailable")&&(this._qrEnginePromise=e.createQrEngine());this._onDecodeError(b)}a&&this._onDecode(a)}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,k=b&&b.width?
b.width:a.videoWidth||a.width,l=b&&b.height?b.height:a.videoHeight||a.height;d||(d=b&&b.downScaledWidth?b.downScaledWidth:k,b=b&&b.downScaledHeight?b.downScaledHeight:l,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,k,l,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||
File diff suppressed because one or more lines are too long
+21 -20
View File
@@ -1,21 +1,22 @@
'use strict';(function(e,a){"object"===typeof exports&&"undefined"!==typeof module?module.exports=a():"function"===typeof define&&define.amd?define(a):(e="undefined"!==typeof globalThis?globalThis:e||self,e.QrScanner=a())})(this,function(){class e{constructor(a,b,c,d,f="environment"){this._legacyCanvasSize=e.DEFAULT_CANVAS_SIZE;this._destroyed=this._flashOn=this._paused=this._active=!1;c=c||this._onDecodeError;d=d||this._calculateScanRegion;this.$video=a;this.$canvas=document.createElement("canvas");
this._onDecode=b;this._preferredCamera=f;"number"===typeof c?(this._legacyCanvasSize=c,console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future")):this._onDecodeError=c;"number"===typeof d?(this._legacyCanvasSize=d,console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future")):this._calculateScanRegion=d;this._scanRegion=this._calculateScanRegion(a);this._onPlay=this._onPlay.bind(this);this._onLoadedMetaData=
this._onLoadedMetaData.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);a.disablePictureInPicture=!0;a.playsInline=!0;a.muted=!0;let h=!1;a.hidden&&(a.hidden=!1,h=!0);document.body.contains(a)||(document.body.appendChild(a),h=!0);requestAnimationFrame(()=>{let k=window.getComputedStyle(a);"none"===k.display&&(a.style.setProperty("display","block","important"),h=!0);"visible"!==k.visibility&&(a.style.setProperty("visibility","visible","important"),h=!0);h&&(console.warn("QrScanner has overwritten the video hiding style to avoid Safari stopping the playback."),
a.style.opacity="0",a.style.width="0",a.style.height="0")});a.addEventListener("play",this._onPlay);a.addEventListener("loadedmetadata",this._onLoadedMetaData);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrEnginePromise=e.createQrEngine()}static async hasCamera(){try{return!!(await e.listCameras(!1)).length}catch(a){return!1}}static async listCameras(a=!1){if(!navigator.mediaDevices)return[];let b=async()=>(await navigator.mediaDevices.enumerateDevices()).filter(d=>
"videoinput"===d.kind),c;try{a&&(await b()).every(d=>!d.label)&&(c=await navigator.mediaDevices.getUserMedia({audio:!1,video:!0}))}catch(d){}try{return(await b()).map((d,f)=>({id:d.deviceId,label:d.label||(0===f?"Default Camera":`Camera ${f+1}`)}))}finally{c&&(console.warn("Call listCameras after successfully starting a QR scanner to avoid creating a temporary video stream"),e._stopVideoStream(c))}}async hasFlash(){let a;try{if(this.$video.srcObject){if(!(this.$video.srcObject instanceof MediaStream))return!1;
a=this.$video.srcObject}else a=(await this._getCameraStream()).stream;return"torch"in a.getVideoTracks()[0].getSettings()}catch(b){return!1}finally{a&&a!==this.$video.srcObject&&(console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream"),e._stopVideoStream(a))}}isFlashOn(){return this._flashOn}async toggleFlash(){this._flashOn?await this.turnFlashOff():await this.turnFlashOn()}async turnFlashOn(){if(!this._flashOn&&!this._destroyed&&(this._flashOn=
!0,this._active&&!this._paused))try{if(!await this.hasFlash())throw"No flash available";await this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:!0}]})}catch(a){throw this._flashOn=!1,a;}}async turnFlashOff(){this._flashOn&&(this._flashOn=!1,await this._restartVideoStream())}destroy(){this.$video.removeEventListener("loadedmetadata",this._onLoadedMetaData);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",this._onVisibilityChange);
this._destroyed=!0;this._flashOn=!1;this.stop();e._postWorkerMessage(this._qrEnginePromise,"close")}async start(){if(!(this._active&&!this._paused||this._destroyed||("https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https."),this._active=!0,document.hidden)))if(this._paused=!1,this.$video.srcObject)await this.$video.play();else try{let {stream:a,facingMode:b}=await this._getCameraStream();!this._active||this._paused?e._stopVideoStream(a):
(this.$video.srcObject=a,await this.$video.play(),this._setVideoMirror(b),this._flashOn&&(this._flashOn=!1,this.turnFlashOn().catch(()=>{})))}catch(a){if(!this._paused)throw this._active=!1,a;}}stop(){this.pause();this._active=!1}async pause(a=!1){this._paused=!0;if(!this._active)return!0;this.$video.pause();let b=()=>{this.$video.srcObject instanceof MediaStream&&(e._stopVideoStream(this.$video.srcObject),this.$video.srcObject=null)};if(a)return b(),!0;await new Promise(c=>setTimeout(c,300));if(!this._paused)return!1;
b();return!0}async setCamera(a){a!==this._preferredCamera&&(this._preferredCamera=a,await this._restartVideoStream())}static async scanImage(a,b,c,d,f=!1,h=!1){let k=!!c;try{let l,t;[c,l]=await Promise.all([c||e.createQrEngine(),e._loadImage(a)]);[d,t]=e._drawToCanvas(l,b,d,f);if(c instanceof Worker){let g=c;k||g.postMessage({type:"inversionMode",data:"both"});return await new Promise((q,u)=>{let r,p,n;p=m=>{"qrResult"===m.data.type&&(g.removeEventListener("message",p),g.removeEventListener("error",
n),clearTimeout(r),null!==m.data.data?q(m.data.data):u(e.NO_QR_CODE_FOUND))};n=m=>{g.removeEventListener("message",p);g.removeEventListener("error",n);clearTimeout(r);u("Scanner error: "+(m?m.message||m:"Unknown Error"))};g.addEventListener("message",p);g.addEventListener("error",n);r=setTimeout(()=>n("timeout"),1E4);let v=t.getImageData(0,0,d.width,d.height);g.postMessage({type:"decode",data:v},[v.data.buffer])})}return await Promise.race([new Promise((g,q)=>window.setTimeout(()=>q("Scanner error: timeout"),
1E4)),(async()=>{try{let [g]=await c.detect(d);if(!g)throw e.NO_QR_CODE_FOUND;return g.rawValue}catch(g){throw`Scanner error: ${g.message||g}`;}})()])}catch(l){if(!b||!h)throw l;return await e.scanImage(a,null,c,d,f)}finally{k||e._postWorkerMessage(c,"close")}}setGrayscaleWeights(a,b,c,d=!0){e._postWorkerMessage(this._qrEnginePromise,"grayscaleWeights",{red:a,green:b,blue:c,useIntegerApproximation:d})}setInversionMode(a){e._postWorkerMessage(this._qrEnginePromise,"inversionMode",a)}static async createQrEngine(a=
e.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._scanFrame()}_onLoadedMetaData(){this._scanRegion=this._calculateScanRegion(this.$video)}_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}}_scanFrame(){!this._active||this.$video.paused||this.$video.ended||requestAnimationFrame(async()=>{if(!(1>=this.$video.readyState)){try{var a=await e.scanImage(this.$video,this._scanRegion,this._qrEnginePromise,this.$canvas)}catch(b){if(!this._active)return;(b.message||b).includes("service unavailable")&&(this._qrEnginePromise=
e.createQrEngine());this._onDecodeError(b)}a&&this._onDecode(a)}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,k=b&&b.width?b.width:a.videoWidth||a.width,l=b&&b.height?b.height:a.videoHeight||a.height;d||(d=b&&b.downScaledWidth?b.downScaledWidth:k,b=b&&b.downScaledHeight?b.downScaledHeight:l,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,k,l,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})
'use strict';(function(e,a){"object"===typeof exports&&"undefined"!==typeof module?module.exports=a():"function"===typeof define&&define.amd?define(a):(e="undefined"!==typeof globalThis?globalThis:e||self,e.QrScanner=a())})(this,function(){class e{constructor(a,b,c,d,f){this._preferredCamera="environment";this._legacyCanvasSize=e.DEFAULT_CANVAS_SIZE;this._destroyed=this._flashOn=this._paused=this._active=!1;this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;(c&&"object"!==
typeof c||d||f)&&console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future");b="object"===typeof c?c:{};this._onDecodeError=b.onDecodeError||("function"===typeof c?c:this._onDecodeError);this._calculateScanRegion=b.calculateScanRegion||("function"===typeof d?d:this._calculateScanRegion);this._preferredCamera=b.preferredCamera||f||this._preferredCamera;this._legacyCanvasSize="number"===typeof c?c:"number"===typeof d?d:this._legacyCanvasSize;this._scanRegion=
this._calculateScanRegion(a);this._onPlay=this._onPlay.bind(this);this._onLoadedMetaData=this._onLoadedMetaData.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);a.disablePictureInPicture=!0;a.playsInline=!0;a.muted=!0;let h=!1;a.hidden&&(a.hidden=!1,h=!0);document.body.contains(a)||(document.body.appendChild(a),h=!0);requestAnimationFrame(()=>{let k=window.getComputedStyle(a);"none"===k.display&&(a.style.setProperty("display","block","important"),h=!0);"visible"!==k.visibility&&
(a.style.setProperty("visibility","visible","important"),h=!0);h&&(console.warn("QrScanner has overwritten the video hiding style to avoid Safari stopping the playback."),a.style.opacity="0",a.style.width="0",a.style.height="0")});a.addEventListener("play",this._onPlay);a.addEventListener("loadedmetadata",this._onLoadedMetaData);document.addEventListener("visibilitychange",this._onVisibilityChange);this._qrEnginePromise=e.createQrEngine()}static async hasCamera(){try{return!!(await e.listCameras(!1)).length}catch(a){return!1}}static async listCameras(a=
!1){if(!navigator.mediaDevices)return[];let b=async()=>(await navigator.mediaDevices.enumerateDevices()).filter(d=>"videoinput"===d.kind),c;try{a&&(await b()).every(d=>!d.label)&&(c=await navigator.mediaDevices.getUserMedia({audio:!1,video:!0}))}catch(d){}try{return(await b()).map((d,f)=>({id:d.deviceId,label:d.label||(0===f?"Default Camera":`Camera ${f+1}`)}))}finally{c&&(console.warn("Call listCameras after successfully starting a QR scanner to avoid creating a temporary video stream"),e._stopVideoStream(c))}}async hasFlash(){let a;
try{if(this.$video.srcObject){if(!(this.$video.srcObject instanceof MediaStream))return!1;a=this.$video.srcObject}else a=(await this._getCameraStream()).stream;return"torch"in a.getVideoTracks()[0].getSettings()}catch(b){return!1}finally{a&&a!==this.$video.srcObject&&(console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream"),e._stopVideoStream(a))}}isFlashOn(){return this._flashOn}async toggleFlash(){this._flashOn?await this.turnFlashOff():await this.turnFlashOn()}async turnFlashOn(){if(!this._flashOn&&
!this._destroyed&&(this._flashOn=!0,this._active&&!this._paused))try{if(!await this.hasFlash())throw"No flash available";await this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:!0}]})}catch(a){throw this._flashOn=!1,a;}}async turnFlashOff(){this._flashOn&&(this._flashOn=!1,await this._restartVideoStream())}destroy(){this.$video.removeEventListener("loadedmetadata",this._onLoadedMetaData);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",
this._onVisibilityChange);this._destroyed=!0;this._flashOn=!1;this.stop();e._postWorkerMessage(this._qrEnginePromise,"close")}async start(){if(!(this._active&&!this._paused||this._destroyed||("https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https."),this._active=!0,document.hidden)))if(this._paused=!1,this.$video.srcObject)await this.$video.play();else try{let {stream:a,facingMode:b}=await this._getCameraStream();!this._active||
this._paused?e._stopVideoStream(a):(this.$video.srcObject=a,await this.$video.play(),this._setVideoMirror(b),this._flashOn&&(this._flashOn=!1,this.turnFlashOn().catch(()=>{})))}catch(a){if(!this._paused)throw this._active=!1,a;}}stop(){this.pause();this._active=!1}async pause(a=!1){this._paused=!0;if(!this._active)return!0;this.$video.pause();let b=()=>{this.$video.srcObject instanceof MediaStream&&(e._stopVideoStream(this.$video.srcObject),this.$video.srcObject=null)};if(a)return b(),!0;await new Promise(c=>
setTimeout(c,300));if(!this._paused)return!1;b();return!0}async setCamera(a){a!==this._preferredCamera&&(this._preferredCamera=a,await this._restartVideoStream())}static async scanImage(a,b,c,d,f=!1,h=!1){let k;b&&("scanRegion"in b||"qrEngine"in b||"canvas"in b||"disallowCanvasResizing"in b||"alsoTryWithoutScanRegion"in b)?(k=b.scanRegion,c=b.qrEngine,d=b.canvas,f=b.disallowCanvasResizing||!1,h=b.alsoTryWithoutScanRegion||!1):(b||c||d||f||h)&&console.warn("You're using a deprecated api for scanImage which will be removed in the future.");
b=!!c;try{let l,t;[c,l]=await Promise.all([c||e.createQrEngine(),e._loadImage(a)]);[d,t]=e._drawToCanvas(l,k,d,f);if(c instanceof Worker){let g=c;b||g.postMessage({type:"inversionMode",data:"both"});return await new Promise((q,u)=>{let r,p,n;p=m=>{"qrResult"===m.data.type&&(g.removeEventListener("message",p),g.removeEventListener("error",n),clearTimeout(r),null!==m.data.data?q(m.data.data):u(e.NO_QR_CODE_FOUND))};n=m=>{g.removeEventListener("message",p);g.removeEventListener("error",n);clearTimeout(r);
u("Scanner error: "+(m?m.message||m:"Unknown Error"))};g.addEventListener("message",p);g.addEventListener("error",n);r=setTimeout(()=>n("timeout"),1E4);let v=t.getImageData(0,0,d.width,d.height);g.postMessage({type:"decode",data:v},[v.data.buffer])})}return await Promise.race([new Promise((g,q)=>window.setTimeout(()=>q("Scanner error: timeout"),1E4)),(async()=>{try{let [g]=await c.detect(d);if(!g)throw e.NO_QR_CODE_FOUND;return g.rawValue}catch(g){throw`Scanner error: ${g.message||g}`;}})()])}catch(l){if(!k||
!h)throw l;return await e.scanImage(a,{qrEngine:c,canvas:d,disallowCanvasResizing:f})}finally{b||e._postWorkerMessage(c,"close")}}setGrayscaleWeights(a,b,c,d=!0){e._postWorkerMessage(this._qrEnginePromise,"grayscaleWeights",{red:a,green:b,blue:c,useIntegerApproximation:d})}setInversionMode(a){e._postWorkerMessage(this._qrEnginePromise,"inversionMode",a)}static async createQrEngine(a=e.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._scanFrame()}_onLoadedMetaData(){this._scanRegion=this._calculateScanRegion(this.$video)}_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}}_scanFrame(){!this._active||this.$video.paused||this.$video.ended||requestAnimationFrame(async()=>{if(!(1>=this.$video.readyState)){try{var a=await e.scanImage(this.$video,{scanRegion:this._scanRegion,qrEngine:this._qrEnginePromise,canvas:this.$canvas})}catch(b){if(!this._active)return;(b.message||b).includes("service unavailable")&&(this._qrEnginePromise=e.createQrEngine());this._onDecodeError(b)}a&&this._onDecode(a)}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,k=b&&b.width?b.width:a.videoWidth||a.width,l=b&&b.height?b.height:a.videoHeight||a.height;d||(d=b&&b.downScaledWidth?b.downScaledWidth:k,b=b&&b.downScaledHeight?b.downScaledHeight:l,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,k,l,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
File diff suppressed because one or more lines are too long
+86 -27
View File
@@ -49,7 +49,7 @@ export default class QrScanner {
$video: HTMLVideoElement;
$canvas: HTMLCanvasElement;
private readonly _onDecode: (result: string) => void;
private _preferredCamera: QrScanner.FacingMode | QrScanner.DeviceId;
private _preferredCamera: QrScanner.FacingMode | QrScanner.DeviceId = 'environment';
private _scanRegion: QrScanner.ScanRegion;
private _legacyCanvasSize: number = QrScanner.DEFAULT_CANVAS_SIZE;
private _qrEnginePromise: Promise<Worker | BarcodeDetector>
@@ -58,6 +58,16 @@ export default class QrScanner {
private _flashOn: boolean = false;
private _destroyed: boolean = false;
constructor(
video: HTMLVideoElement,
onDecode: (result: string) => void,
options?: {
onDecodeError?: (error: Error | string) => void,
calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,
preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,
},
);
/** @deprecated */
constructor(
video: HTMLVideoElement,
onDecode: (result: string) => void,
@@ -78,35 +88,40 @@ export default class QrScanner {
constructor(
video: HTMLVideoElement,
onDecode: (result: string) => void,
canvasSizeOrOnDecodeError?: number | ((error: Error | string) => void),
canvasSizeOrOnDecodeErrorOrOptions?: number | ((error: Error | string) => void) | {
onDecodeError?: (error: Error | string) => void,
calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion,
preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,
},
canvasSizeOrCalculateScanRegion?: number | ((video: HTMLVideoElement) => QrScanner.ScanRegion),
preferredCamera: QrScanner.FacingMode | QrScanner.DeviceId = 'environment',
preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId,
) {
canvasSizeOrOnDecodeError = canvasSizeOrOnDecodeError || this._onDecodeError;
canvasSizeOrCalculateScanRegion = canvasSizeOrCalculateScanRegion || this._calculateScanRegion;
this.$video = video;
this.$canvas = document.createElement('canvas');
this._onDecode = onDecode;
this._preferredCamera = preferredCamera;
if (typeof canvasSizeOrOnDecodeError === 'number') {
// legacy function signature where the third argument is the canvas size
this._legacyCanvasSize = canvasSizeOrOnDecodeError;
if ((canvasSizeOrOnDecodeErrorOrOptions && typeof canvasSizeOrOnDecodeErrorOrOptions !== 'object')
|| (canvasSizeOrCalculateScanRegion || preferredCamera)) {
// New constructor signature has the optional options object as last argument not followed by anything else.
console.warn('You\'re using a deprecated version of the QrScanner constructor which will be removed in '
+ 'the future');
} else {
this._onDecodeError = canvasSizeOrOnDecodeError;
}
if (typeof canvasSizeOrCalculateScanRegion === 'number') {
// legacy function signature where the fourth argument is the canvas size
this._legacyCanvasSize = canvasSizeOrCalculateScanRegion;
console.warn('You\'re using a deprecated version of the QrScanner constructor which will be removed in '
+ 'the future');
} else {
this._calculateScanRegion = canvasSizeOrCalculateScanRegion;
}
const options = typeof canvasSizeOrOnDecodeErrorOrOptions === 'object'
? canvasSizeOrOnDecodeErrorOrOptions
: {};
this._onDecodeError = options.onDecodeError || (typeof canvasSizeOrOnDecodeErrorOrOptions === 'function'
? canvasSizeOrOnDecodeErrorOrOptions
: this._onDecodeError);
this._calculateScanRegion = options.calculateScanRegion || (typeof canvasSizeOrCalculateScanRegion==='function'
? canvasSizeOrCalculateScanRegion
: this._calculateScanRegion);
this._preferredCamera = options.preferredCamera || preferredCamera || this._preferredCamera;
this._legacyCanvasSize = typeof canvasSizeOrOnDecodeErrorOrOptions === 'number'
? canvasSizeOrOnDecodeErrorOrOptions
: typeof canvasSizeOrCalculateScanRegion === 'number'
? canvasSizeOrCalculateScanRegion
: this._legacyCanvasSize;
this._scanRegion = this._calculateScanRegion(video);
@@ -309,15 +324,60 @@ export default class QrScanner {
await this._restartVideoStream();
}
static async scanImage(
imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap
| SVGImageElement | File | Blob | URL | String,
options?: {
scanRegion?: QrScanner.ScanRegion | null,
qrEngine?: Worker | BarcodeDetector | Promise<Worker | BarcodeDetector> | null,
canvas?: HTMLCanvasElement | null,
disallowCanvasResizing?: boolean,
alsoTryWithoutScanRegion?: boolean,
},
): Promise<string>;
/** @deprecated */
static async scanImage(
imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap
| SVGImageElement | File | Blob | URL | String,
scanRegion?: QrScanner.ScanRegion | null,
qrEngine?: Worker | BarcodeDetector | Promise<Worker | BarcodeDetector> | null,
canvas?: HTMLCanvasElement | null,
disallowCanvasResizing?: boolean,
alsoTryWithoutScanRegion?: boolean,
): Promise<string>;
static async scanImage(
imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap
| SVGImageElement | File | Blob | URL | String,
scanRegionOrOptions?: QrScanner.ScanRegion | {
scanRegion?: QrScanner.ScanRegion | null,
qrEngine?: Worker | BarcodeDetector | Promise<Worker | BarcodeDetector> | null,
canvas?: HTMLCanvasElement | null,
disallowCanvasResizing?: boolean,
alsoTryWithoutScanRegion?: boolean,
} | null,
qrEngine?: Worker | BarcodeDetector | Promise<Worker | BarcodeDetector> | null,
canvas?: HTMLCanvasElement | null,
disallowCanvasResizing: boolean = false,
alsoTryWithoutScanRegion: boolean = false,
): Promise<string> {
let scanRegion: QrScanner.ScanRegion | null | undefined;
if (scanRegionOrOptions && (
'scanRegion' in scanRegionOrOptions
|| 'qrEngine' in scanRegionOrOptions
|| 'canvas' in scanRegionOrOptions
|| 'disallowCanvasResizing' in scanRegionOrOptions
|| 'alsoTryWithoutScanRegion' in scanRegionOrOptions
)) {
// we got an options object
scanRegion = scanRegionOrOptions.scanRegion;
qrEngine = scanRegionOrOptions.qrEngine;
canvas = scanRegionOrOptions.canvas;
disallowCanvasResizing = scanRegionOrOptions.disallowCanvasResizing || false;
alsoTryWithoutScanRegion = scanRegionOrOptions.alsoTryWithoutScanRegion || false;
} else if (scanRegionOrOptions || qrEngine || canvas || disallowCanvasResizing || alsoTryWithoutScanRegion) {
console.warn('You\'re using a deprecated api for scanImage which will be removed in the future.');
}
const gotExternalEngine = !!qrEngine;
try {
@@ -388,7 +448,7 @@ export default class QrScanner {
}
} catch (e) {
if (!scanRegion || !alsoTryWithoutScanRegion) throw e;
return await QrScanner.scanImage(imageOrFileOrBlobOrUrl, null, qrEngine, canvas, disallowCanvasResizing);
return await QrScanner.scanImage(imageOrFileOrBlobOrUrl, { qrEngine, canvas, disallowCanvasResizing });
} finally {
if (!gotExternalEngine) {
QrScanner._postWorkerMessage(qrEngine!, 'close');
@@ -465,12 +525,11 @@ export default class QrScanner {
let result: string | undefined;
try {
result = await QrScanner.scanImage(
this.$video,
this._scanRegion,
this._qrEnginePromise,
this.$canvas,
);
result = await QrScanner.scanImage(this.$video, {
scanRegion: this._scanRegion,
qrEngine: this._qrEnginePromise,
canvas: this.$canvas,
});
} catch (error) {
if (!this._active) return;
const errorMessage = (error as Error).message || error as string;
+14
View File
@@ -16,6 +16,12 @@ export default class QrScanner {
private _paused;
private _flashOn;
private _destroyed;
constructor(video: HTMLVideoElement, onDecode: (result: string) => void, options?: {
onDecodeError?: (error: Error | string) => void;
calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion;
preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId;
});
/** @deprecated */
constructor(video: HTMLVideoElement, onDecode: (result: string) => void, onDecodeError?: (error: Error | string) => void, calculateScanRegion?: (video: HTMLVideoElement) => QrScanner.ScanRegion, preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId);
/** @deprecated */
constructor(video: HTMLVideoElement, onDecode: (result: string) => void, onDecodeError?: (error: Error | string) => void, canvasSize?: number, preferredCamera?: QrScanner.FacingMode | QrScanner.DeviceId);
@@ -31,6 +37,14 @@ export default class QrScanner {
stop(): void;
pause(stopStreamImmediately?: boolean): Promise<boolean>;
setCamera(facingModeOrDeviceId: QrScanner.FacingMode | QrScanner.DeviceId): Promise<void>;
static scanImage(imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | SVGImageElement | File | Blob | URL | String, options?: {
scanRegion?: QrScanner.ScanRegion | null;
qrEngine?: Worker | BarcodeDetector | Promise<Worker | BarcodeDetector> | null;
canvas?: HTMLCanvasElement | null;
disallowCanvasResizing?: boolean;
alsoTryWithoutScanRegion?: boolean;
}): Promise<string>;
/** @deprecated */
static scanImage(imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap | SVGImageElement | File | Blob | URL | String, scanRegion?: QrScanner.ScanRegion | null, qrEngine?: Worker | BarcodeDetector | Promise<Worker | BarcodeDetector> | null, canvas?: HTMLCanvasElement | null, disallowCanvasResizing?: boolean, alsoTryWithoutScanRegion?: boolean): Promise<string>;
setGrayscaleWeights(red: number, green: number, blue: number, useIntegerApproximation?: boolean): void;
setInversionMode(inversionMode: QrScanner.InversionMode): void;