Fix turning flash off

applyConstraints with torch: false does not work to turn the
flashlight off, as a stream's torch stays continuously on, see
https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#torch.
Therefore, we have to stop the stream to turn the flashlight off.
This commit is contained in:
Daniel
2021-08-16 21:20:32 +02:00
parent e2edfe1e88
commit fe31236d51
5 changed files with 80 additions and 65 deletions
+19 -19
View File
@@ -1,20 +1,20 @@
class e{static hasCamera(){return e.listCameras(!1).then(a=>!!a.length).catch(()=>!1)}static listCameras(a=!1){if(!navigator.mediaDevices)return Promise.resolve([]);let b=null;return(a?navigator.mediaDevices.getUserMedia({audio:!1,video:!0}).then(a=>b=a).catch(()=>{}):Promise.resolve()).then(()=>navigator.mediaDevices.enumerateDevices()).then(a=>a.filter(a=>"videoinput"===a.kind).map((a,b)=>({id:a.deviceId,label:a.label||(0===b?"Default Camera":`Camera ${b+1}`)}))).finally(()=>{if(b)for(let a of b.getTracks())a.stop(),
b.removeTrack(a)})}constructor(a,b,c=this._onDecodeError,d=this._calculateScanRegion,f="environment"){this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;this._legacyCanvasSize=e.DEFAULT_CANVAS_SIZE;this._preferredCamera=f;this._flashOn=this._paused=this._active=!1;"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 g=!1;a.hidden&&(a.hidden=!1,g=!0);document.body.contains(a)||(document.body.appendChild(a),g=
!0);requestAnimationFrame(()=>{let b=window.getComputedStyle(a);"none"===b.display&&(a.style.setProperty("display","block","important"),g=!0);"visible"!==b.visibility&&(a.style.setProperty("visibility","visible","important"),g=!0);g&&(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()}hasFlash(){if(!("ImageCapture"in window))return Promise.resolve(!1);let a=this.$video.srcObject?this.$video.srcObject.getVideoTracks()[0]:null;return a?(new ImageCapture(a)).getPhotoCapabilities().then(a=>a.fillLightMode.includes("flash")).catch(a=>{console.warn(a);return!1}):Promise.reject("Camera not started or not available")}isFlashOn(){return this._flashOn}toggleFlash(){return this._setFlash(!this._flashOn)}turnFlashOff(){return this._setFlash(!1)}turnFlashOn(){return this._setFlash(!0)}destroy(){this.$video.removeEventListener("loadedmetadata",
this._onLoadedMetaData);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",this._onVisibilityChange);this.stop();e._postWorkerMessage(this._qrEnginePromise,"close")}start(){if(this._active&&!this._paused)return Promise.resolve();"https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https.");this._active=!0;if(document.hidden)return Promise.resolve();this._paused=!1;if(this.$video.srcObject)return this.$video.play(),
Promise.resolve();let a="environment"===this._preferredCamera||"user"===this._preferredCamera?this._preferredCamera:null;return this._getCameraStream(this._preferredCamera,!0).catch(()=>{a="environment"===a?"user":"environment";return this._getCameraStream()}).then(b=>{a=this._getFacingMode(b)||a||"environment";this.$video.srcObject=b;this.$video.play();this._setVideoMirror(a);this._flashOn&&this.turnFlashOn().catch(()=>{})}).catch(a=>{this._active=!1;throw a;})}stop(){this.pause();this._active=!1}pause(a=
!1){this._paused=!0;if(!this._active)return Promise.resolve(!0);this.$video.pause();let b=()=>{const a=this.$video.srcObject?this.$video.srcObject.getTracks():[];for(const b of a)b.stop(),this.$video.srcObject.removeTrack(b);this.$video.srcObject=null};return a?(b(),Promise.resolve(!0)):(new Promise(a=>setTimeout(a,300))).then(()=>{if(!this._paused)return!1;b();return!0})}setCamera(a){if(a===this._preferredCamera)return Promise.resolve();this._preferredCamera=a;let b=this._paused;return this.pause(!0).then(a=>
{if(a&&!b&&this._active)return this.start()})}static scanImage(a,b=null,c=null,d=null,f=!1,g=!1){let h=c instanceof Worker,k=Promise.all([c||e.createQrEngine(),e._loadImage(a)]).then(([a,g])=>{c=a;let k;[d,k]=this._drawToCanvas(g,b,d,f);return c instanceof Worker?(h||c.postMessage({type:"inversionMode",data:"both"}),new Promise((a,b)=>{let f,g,h;g=d=>{"qrResult"===d.data.type&&(c.removeEventListener("message",g),c.removeEventListener("error",h),clearTimeout(f),null!==d.data.data?a(d.data.data):b(e.NO_QR_CODE_FOUND))};
h=a=>{c.removeEventListener("message",g);c.removeEventListener("error",h);clearTimeout(f);b("Scanner error: "+(a?a.message||a:"Unknown Error"))};c.addEventListener("message",g);c.addEventListener("error",h);f=setTimeout(()=>h("timeout"),1E4);let l=k.getImageData(0,0,d.width,d.height);c.postMessage({type:"decode",data:l},[l.data.buffer])})):new Promise((a,b)=>{let f=setTimeout(()=>b("Scanner error: timeout"),1E4);c.detect(d).then(c=>{c.length?a(c[0].rawValue):b(e.NO_QR_CODE_FOUND)}).catch(a=>b("Scanner error: "+
(a.message||a))).finally(()=>clearTimeout(f))})});b&&g&&(k=k.catch(()=>e.scanImage(a,null,c,d,f)));return k=k.finally(()=>{h||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 createQrEngine(a=e.WORKER_PATH){return("BarcodeDetector"in window?BarcodeDetector.getSupportedFormats():Promise.resolve([])).then(b=>
-1!==b.indexOf("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(){if(!this._active||this.$video.paused||this.$video.ended)return!1;requestAnimationFrame(()=>{1>=this.$video.readyState?this._scanFrame():this._qrEnginePromise.then(a=>e.scanImage(this.$video,this._scanRegion,a,this.$canvas)).then(this._onDecode,a=>{this._active&&(-1!==(a.message||a).indexOf("service unavailable")&&(this._qrEnginePromise=e.createQrEngine()),this._onDecodeError(a))}).then(()=>this._scanFrame())})}_onDecodeError(a){a!==e.NO_QR_CODE_FOUND&&
console.log(a)}_getCameraStream(a,b=!1){let c=[{width:{min:1024}},{width:{min:768}},{}];if(a){let d="environment"===a||"user"===a?"facingMode":"deviceId";b&&(a={exact:a});c.forEach(b=>b[d]=a)}return this._getMatchingCameraStream(c)}_getMatchingCameraStream(a){return navigator.mediaDevices&&0!==a.length?navigator.mediaDevices.getUserMedia({video:a.shift()}).catch(()=>this._getMatchingCameraStream(a)):Promise.reject("Camera not found.")}_setFlash(a){this._flashOn=a;return!a||this._active&&!this._paused?
this.hasFlash().then(b=>b?this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:a}]}):Promise.reject("No flash available")).catch(b=>{this._flashOn=!a;throw b;}):Promise.resolve()}_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=null,c=null,d=!1){c=c||document.createElement("canvas");
let f=b&&b.x?b.x:0,g=b&&b.y?b.y:0,h=b&&b.width?b.width:a.width||a.videoWidth,k=b&&b.height?b.height:a.height||a.videoHeight;d||(d=b&&b.downScaledWidth?b.downScaledWidth:h,b=b&&b.downScaledHeight?b.downScaledHeight:k,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,g,h,k,0,0,c.width,c.height);return[c,b]}static _loadImage(a){if(a instanceof HTMLCanvasElement||a instanceof HTMLVideoElement||window.ImageBitmap&&a instanceof
window.ImageBitmap||window.OffscreenCanvas&&a instanceof window.OffscreenCanvas)return Promise.resolve(a);if(a instanceof Image)return e._awaitImageLoad(a).then(()=>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;return e._awaitImageLoad(b).then(()=>{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src);return b})}return Promise.reject("Unsupported image type.")}static _awaitImageLoad(a){return new Promise((b,
c)=>{if(a.complete&&0!==a.naturalWidth)b();else{let d,f;d=()=>{a.removeEventListener("load",d);a.removeEventListener("error",f);b()};f=()=>{a.removeEventListener("load",d);a.removeEventListener("error",f);c("Image load error")};a.addEventListener("load",d);a.addEventListener("error",f)}})}static _postWorkerMessage(a,b,c){return Promise.resolve(a).then(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";
export default e
class f{static hasCamera(){return f.listCameras(!1).then(a=>!!a.length).catch(()=>!1)}static listCameras(a=!1){if(!navigator.mediaDevices)return Promise.resolve([]);let b=null;return(a?navigator.mediaDevices.getUserMedia({audio:!1,video:!0}).then(a=>b=a).catch(()=>{}):Promise.resolve()).then(()=>navigator.mediaDevices.enumerateDevices()).then(a=>a.filter(a=>"videoinput"===a.kind).map((a,b)=>({id:a.deviceId,label:a.label||(0===b?"Default Camera":`Camera ${b+1}`)}))).finally(()=>{if(b)for(let a of b.getTracks())a.stop(),
b.removeTrack(a)})}constructor(a,b,c=this._onDecodeError,d=this._calculateScanRegion,g="environment"){this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;this._legacyCanvasSize=f.DEFAULT_CANVAS_SIZE;this._preferredCamera=g;this._flashOn=this._paused=this._active=!1;"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 b=window.getComputedStyle(a);"none"===b.display&&(a.style.setProperty("display","block","important"),h=!0);"visible"!==b.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=f.createQrEngine()}hasFlash(){if(!("ImageCapture"in window))return Promise.resolve(!1);let a=this.$video.srcObject?this.$video.srcObject.getVideoTracks()[0]:null;return a?(new ImageCapture(a)).getPhotoCapabilities().then(a=>a.fillLightMode.includes("flash")).catch(a=>{console.warn(a);return!1}):Promise.reject("Camera not started or not available")}isFlashOn(){return this._flashOn}toggleFlash(){return this._flashOn?this.turnFlashOff():this.turnFlashOn()}turnFlashOn(){if(this._flashOn)return Promise.resolve();
this._flashOn=!0;return!this._active||this._paused?Promise.resolve():this.hasFlash().then(a=>a?this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:!0}]}):Promise.reject("No flash available")).catch(()=>{this._flashOn=!1;throw e;})}turnFlashOff(){if(this._flashOn)return this._flashOn=!1,this._restartVideoStream()}destroy(){this.$video.removeEventListener("loadedmetadata",this._onLoadedMetaData);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",
this._onVisibilityChange);this.stop();f._postWorkerMessage(this._qrEnginePromise,"close")}start(){if(this._active&&!this._paused)return Promise.resolve();"https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https.");this._active=!0;if(document.hidden)return Promise.resolve();this._paused=!1;if(this.$video.srcObject)return this.$video.play(),Promise.resolve();let a="environment"===this._preferredCamera||"user"===this._preferredCamera?
this._preferredCamera:null;return this._getCameraStream(this._preferredCamera,!0).catch(()=>{a="environment"===a?"user":"environment";return this._getCameraStream()}).then(b=>{a=this._getFacingMode(b)||a||"environment";this.$video.srcObject=b;this.$video.play();this._setVideoMirror(a);this._flashOn&&(this._flashOn=!1,this.turnFlashOn().catch(()=>{}))}).catch(a=>{this._active=!1;throw a;})}stop(){this.pause();this._active=!1}pause(a=!1){this._paused=!0;if(!this._active)return Promise.resolve(!0);this.$video.pause();
let b=()=>{const a=this.$video.srcObject?this.$video.srcObject.getTracks():[];for(const b of a)b.stop(),this.$video.srcObject.removeTrack(b);this.$video.srcObject=null};return a?(b(),Promise.resolve(!0)):(new Promise(a=>setTimeout(a,300))).then(()=>{if(!this._paused)return!1;b();return!0})}setCamera(a){if(a===this._preferredCamera)return Promise.resolve();this._preferredCamera=a;return this._restartVideoStream()}static scanImage(a,b=null,c=null,d=null,g=!1,h=!1){let k=c instanceof Worker,l=Promise.all([c||
f.createQrEngine(),f._loadImage(a)]).then(([a,h])=>{c=a;let l;[d,l]=this._drawToCanvas(h,b,d,g);return c instanceof Worker?(k||c.postMessage({type:"inversionMode",data:"both"}),new Promise((a,b)=>{let g,h,k;h=d=>{"qrResult"===d.data.type&&(c.removeEventListener("message",h),c.removeEventListener("error",k),clearTimeout(g),null!==d.data.data?a(d.data.data):b(f.NO_QR_CODE_FOUND))};k=a=>{c.removeEventListener("message",h);c.removeEventListener("error",k);clearTimeout(g);b("Scanner error: "+(a?a.message||
a:"Unknown Error"))};c.addEventListener("message",h);c.addEventListener("error",k);g=setTimeout(()=>k("timeout"),1E4);let m=l.getImageData(0,0,d.width,d.height);c.postMessage({type:"decode",data:m},[m.data.buffer])})):new Promise((a,b)=>{let g=setTimeout(()=>b("Scanner error: timeout"),1E4);c.detect(d).then(c=>{c.length?a(c[0].rawValue):b(f.NO_QR_CODE_FOUND)}).catch(a=>b("Scanner error: "+(a.message||a))).finally(()=>clearTimeout(g))})});b&&h&&(l=l.catch(()=>f.scanImage(a,null,c,d,g)));return l=l.finally(()=>
{k||f._postWorkerMessage(c,"close")})}setGrayscaleWeights(a,b,c,d=!0){f._postWorkerMessage(this._qrEnginePromise,"grayscaleWeights",{red:a,green:b,blue:c,useIntegerApproximation:d})}setInversionMode(a){f._postWorkerMessage(this._qrEnginePromise,"inversionMode",a)}static createQrEngine(a=f.WORKER_PATH){return("BarcodeDetector"in window?BarcodeDetector.getSupportedFormats():Promise.resolve([])).then(b=>-1!==b.indexOf("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(){if(!this._active||this.$video.paused||
this.$video.ended)return!1;requestAnimationFrame(()=>{1>=this.$video.readyState?this._scanFrame():this._qrEnginePromise.then(a=>f.scanImage(this.$video,this._scanRegion,a,this.$canvas)).then(this._onDecode,a=>{this._active&&(-1!==(a.message||a).indexOf("service unavailable")&&(this._qrEnginePromise=f.createQrEngine()),this._onDecodeError(a))}).then(()=>this._scanFrame())})}_onDecodeError(a){a!==f.NO_QR_CODE_FOUND&&console.log(a)}_getCameraStream(a,b=!1){let c=[{width:{min:1024}},{width:{min:768}},
{}];if(a){let d="environment"===a||"user"===a?"facingMode":"deviceId";b&&(a={exact:a});c.forEach(b=>b[d]=a)}return this._getMatchingCameraStream(c)}_getMatchingCameraStream(a){return navigator.mediaDevices&&0!==a.length?navigator.mediaDevices.getUserMedia({video:a.shift()}).catch(()=>this._getMatchingCameraStream(a)):Promise.reject("Camera not found.")}_restartVideoStream(){let a=this._paused;return this.pause(!0).then(b=>{if(b&&!a&&this._active)return this.start()})}_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=null,c=null,d=!1){c=c||document.createElement("canvas");let g=b&&b.x?b.x:0,h=b&&b.y?b.y:0,k=b&&b.width?b.width:a.width||a.videoWidth,l=b&&b.height?b.height:a.height||a.videoHeight;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,g,h,k,l,0,0,c.width,c.height);return[c,b]}static _loadImage(a){if(a instanceof HTMLCanvasElement||a instanceof HTMLVideoElement||window.ImageBitmap&&a instanceof window.ImageBitmap||window.OffscreenCanvas&&a instanceof window.OffscreenCanvas)return Promise.resolve(a);if(a instanceof Image)return f._awaitImageLoad(a).then(()=>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;return f._awaitImageLoad(b).then(()=>{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src);return b})}return Promise.reject("Unsupported image type.")}static _awaitImageLoad(a){return new Promise((b,c)=>{if(a.complete&&0!==a.naturalWidth)b();else{let d,g;d=()=>{a.removeEventListener("load",d);a.removeEventListener("error",g);b()};g=()=>{a.removeEventListener("load",d);a.removeEventListener("error",
g);c("Image load error")};a.addEventListener("load",d);a.addEventListener("error",g)}})}static _postWorkerMessage(a,b,c){return Promise.resolve(a).then(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
File diff suppressed because one or more lines are too long
+18 -17
View File
@@ -1,20 +1,21 @@
'use strict';(function(d,a){"object"===typeof exports&&"undefined"!==typeof module?module.exports=a():"function"===typeof define&&define.amd?define(a):(d=d||self,d.QrScanner=a())})(this,function(){class d{static hasCamera(){return d.listCameras(!1).then(a=>!!a.length).catch(()=>!1)}static listCameras(a=!1){if(!navigator.mediaDevices)return Promise.resolve([]);let b=null;return(a?navigator.mediaDevices.getUserMedia({audio:!1,video:!0}).then(a=>b=a).catch(()=>{}):Promise.resolve()).then(()=>navigator.mediaDevices.enumerateDevices()).then(a=>
a.filter(a=>"videoinput"===a.kind).map((a,b)=>({id:a.deviceId,label:a.label||(0===b?"Default Camera":`Camera ${b+1}`)}))).finally(()=>{if(b)for(let a of b.getTracks())a.stop(),b.removeTrack(a)})}constructor(a,b,c=this._onDecodeError,e=this._calculateScanRegion,h="environment"){this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;this._legacyCanvasSize=d.DEFAULT_CANVAS_SIZE;this._preferredCamera=h;this._flashOn=this._paused=this._active=!1;"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 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 f=!1;a.hidden&&(a.hidden=!1,f=!0);document.body.contains(a)||(document.body.appendChild(a),f=!0);requestAnimationFrame(()=>{let b=window.getComputedStyle(a);"none"===b.display&&(a.style.setProperty("display","block","important"),f=!0);"visible"!==b.visibility&&(a.style.setProperty("visibility","visible","important"),f=!0);f&&(console.warn("QrScanner has overwritten the video hiding style to avoid Safari stopping the playback."),a.style.opacity=
a.filter(a=>"videoinput"===a.kind).map((a,b)=>({id:a.deviceId,label:a.label||(0===b?"Default Camera":`Camera ${b+1}`)}))).finally(()=>{if(b)for(let a of b.getTracks())a.stop(),b.removeTrack(a)})}constructor(a,b,c=this._onDecodeError,f=this._calculateScanRegion,k="environment"){this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;this._legacyCanvasSize=d.DEFAULT_CANVAS_SIZE;this._preferredCamera=k;this._flashOn=this._paused=this._active=!1;"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 f?(this._legacyCanvasSize=f,console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future")):this._calculateScanRegion=f;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 g=!1;a.hidden&&(a.hidden=!1,g=!0);document.body.contains(a)||(document.body.appendChild(a),g=!0);requestAnimationFrame(()=>{let b=window.getComputedStyle(a);"none"===b.display&&(a.style.setProperty("display","block","important"),g=!0);"visible"!==b.visibility&&(a.style.setProperty("visibility","visible","important"),g=!0);g&&(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=d.createQrEngine()}hasFlash(){if(!("ImageCapture"in window))return Promise.resolve(!1);let a=this.$video.srcObject?this.$video.srcObject.getVideoTracks()[0]:null;return a?(new ImageCapture(a)).getPhotoCapabilities().then(a=>a.fillLightMode.includes("flash")).catch(a=>{console.warn(a);
return!1}):Promise.reject("Camera not started or not available")}isFlashOn(){return this._flashOn}toggleFlash(){return this._setFlash(!this._flashOn)}turnFlashOff(){return this._setFlash(!1)}turnFlashOn(){return this._setFlash(!0)}destroy(){this.$video.removeEventListener("loadedmetadata",this._onLoadedMetaData);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",this._onVisibilityChange);this.stop();d._postWorkerMessage(this._qrEnginePromise,"close")}start(){if(this._active&&
!this._paused)return Promise.resolve();"https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https.");this._active=!0;if(document.hidden)return Promise.resolve();this._paused=!1;if(this.$video.srcObject)return this.$video.play(),Promise.resolve();let a="environment"===this._preferredCamera||"user"===this._preferredCamera?this._preferredCamera:null;return this._getCameraStream(this._preferredCamera,!0).catch(()=>{a="environment"===a?
"user":"environment";return this._getCameraStream()}).then(b=>{a=this._getFacingMode(b)||a||"environment";this.$video.srcObject=b;this.$video.play();this._setVideoMirror(a);this._flashOn&&this.turnFlashOn().catch(()=>{})}).catch(a=>{this._active=!1;throw a;})}stop(){this.pause();this._active=!1}pause(a=!1){this._paused=!0;if(!this._active)return Promise.resolve(!0);this.$video.pause();let b=()=>{const a=this.$video.srcObject?this.$video.srcObject.getTracks():[];for(const b of a)b.stop(),this.$video.srcObject.removeTrack(b);
this.$video.srcObject=null};return a?(b(),Promise.resolve(!0)):(new Promise(a=>setTimeout(a,300))).then(()=>{if(!this._paused)return!1;b();return!0})}setCamera(a){if(a===this._preferredCamera)return Promise.resolve();this._preferredCamera=a;let b=this._paused;return this.pause(!0).then(a=>{if(a&&!b&&this._active)return this.start()})}static scanImage(a,b=null,c=null,e=null,h=!1,f=!1){let g=c instanceof Worker,k=Promise.all([c||d.createQrEngine(),d._loadImage(a)]).then(([a,f])=>{c=a;let k;[e,k]=this._drawToCanvas(f,
b,e,h);return c instanceof Worker?(g||c.postMessage({type:"inversionMode",data:"both"}),new Promise((a,b)=>{let h,f,g;f=e=>{"qrResult"===e.data.type&&(c.removeEventListener("message",f),c.removeEventListener("error",g),clearTimeout(h),null!==e.data.data?a(e.data.data):b(d.NO_QR_CODE_FOUND))};g=a=>{c.removeEventListener("message",f);c.removeEventListener("error",g);clearTimeout(h);b("Scanner error: "+(a?a.message||a:"Unknown Error"))};c.addEventListener("message",f);c.addEventListener("error",g);h=
setTimeout(()=>g("timeout"),1E4);let l=k.getImageData(0,0,e.width,e.height);c.postMessage({type:"decode",data:l},[l.data.buffer])})):new Promise((a,b)=>{let h=setTimeout(()=>b("Scanner error: timeout"),1E4);c.detect(e).then(c=>{c.length?a(c[0].rawValue):b(d.NO_QR_CODE_FOUND)}).catch(a=>b("Scanner error: "+(a.message||a))).finally(()=>clearTimeout(h))})});b&&f&&(k=k.catch(()=>d.scanImage(a,null,c,e,h)));return k=k.finally(()=>{g||d._postWorkerMessage(c,"close")})}setGrayscaleWeights(a,b,c,e=!0){d._postWorkerMessage(this._qrEnginePromise,
"grayscaleWeights",{red:a,green:b,blue:c,useIntegerApproximation:e})}setInversionMode(a){d._postWorkerMessage(this._qrEnginePromise,"inversionMode",a)}static createQrEngine(a=d.WORKER_PATH){return("BarcodeDetector"in window?BarcodeDetector.getSupportedFormats():Promise.resolve([])).then(b=>-1!==b.indexOf("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(){if(!this._active||this.$video.paused||this.$video.ended)return!1;requestAnimationFrame(()=>{1>=this.$video.readyState?this._scanFrame():this._qrEnginePromise.then(a=>d.scanImage(this.$video,this._scanRegion,
a,this.$canvas)).then(this._onDecode,a=>{this._active&&(-1!==(a.message||a).indexOf("service unavailable")&&(this._qrEnginePromise=d.createQrEngine()),this._onDecodeError(a))}).then(()=>this._scanFrame())})}_onDecodeError(a){a!==d.NO_QR_CODE_FOUND&&console.log(a)}_getCameraStream(a,b=!1){let c=[{width:{min:1024}},{width:{min:768}},{}];if(a){let e="environment"===a||"user"===a?"facingMode":"deviceId";b&&(a={exact:a});c.forEach(b=>b[e]=a)}return this._getMatchingCameraStream(c)}_getMatchingCameraStream(a){return navigator.mediaDevices&&
0!==a.length?navigator.mediaDevices.getUserMedia({video:a.shift()}).catch(()=>this._getMatchingCameraStream(a)):Promise.reject("Camera not found.")}_setFlash(a){this._flashOn=a;return!a||this._active&&!this._paused?this.hasFlash().then(b=>b?this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:a}]}):Promise.reject("No flash available")).catch(b=>{this._flashOn=!a;throw b;}):Promise.resolve()}_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=null,c=null,e=!1){c=c||document.createElement("canvas");let d=b&&b.x?b.x:0,f=b&&b.y?b.y:0,g=b&&b.width?b.width:a.width||a.videoWidth,k=b&&b.height?b.height:a.height||a.videoHeight;e||(e=b&&b.downScaledWidth?b.downScaledWidth:g,b=b&&b.downScaledHeight?b.downScaledHeight:k,c.width!==e&&(c.width=e),c.height!==b&&(c.height=b));b=c.getContext("2d",{alpha:!1});
b.imageSmoothingEnabled=!1;b.drawImage(a,d,f,g,k,0,0,c.width,c.height);return[c,b]}static _loadImage(a){if(a instanceof HTMLCanvasElement||a instanceof HTMLVideoElement||window.ImageBitmap&&a instanceof window.ImageBitmap||window.OffscreenCanvas&&a instanceof window.OffscreenCanvas)return Promise.resolve(a);if(a instanceof Image)return d._awaitImageLoad(a).then(()=>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;return d._awaitImageLoad(b).then(()=>{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src);return b})}return Promise.reject("Unsupported image type.")}static _awaitImageLoad(a){return new Promise((b,c)=>{if(a.complete&&0!==a.naturalWidth)b();else{let e,d;e=()=>{a.removeEventListener("load",e);a.removeEventListener("error",d);b()};d=()=>{a.removeEventListener("load",e);a.removeEventListener("error",d);c("Image load error")};a.addEventListener("load",e);a.addEventListener("error",
d)}})}static _postWorkerMessage(a,b,c){return Promise.resolve(a).then(a=>{a instanceof Worker&&a.postMessage({type:b,data:c})})}}d.DEFAULT_CANVAS_SIZE=400;d.NO_QR_CODE_FOUND="No QR code found";d.WORKER_PATH="qr-scanner-worker.min.js";return d})
return!1}):Promise.reject("Camera not started or not available")}isFlashOn(){return this._flashOn}toggleFlash(){return this._flashOn?this.turnFlashOff():this.turnFlashOn()}turnFlashOn(){if(this._flashOn)return Promise.resolve();this._flashOn=!0;return!this._active||this._paused?Promise.resolve():this.hasFlash().then(a=>a?this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:!0}]}):Promise.reject("No flash available")).catch(()=>{this._flashOn=!1;throw e;})}turnFlashOff(){if(this._flashOn)return this._flashOn=
!1,this._restartVideoStream()}destroy(){this.$video.removeEventListener("loadedmetadata",this._onLoadedMetaData);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",this._onVisibilityChange);this.stop();d._postWorkerMessage(this._qrEnginePromise,"close")}start(){if(this._active&&!this._paused)return Promise.resolve();"https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https.");this._active=
!0;if(document.hidden)return Promise.resolve();this._paused=!1;if(this.$video.srcObject)return this.$video.play(),Promise.resolve();let a="environment"===this._preferredCamera||"user"===this._preferredCamera?this._preferredCamera:null;return this._getCameraStream(this._preferredCamera,!0).catch(()=>{a="environment"===a?"user":"environment";return this._getCameraStream()}).then(b=>{a=this._getFacingMode(b)||a||"environment";this.$video.srcObject=b;this.$video.play();this._setVideoMirror(a);this._flashOn&&
(this._flashOn=!1,this.turnFlashOn().catch(()=>{}))}).catch(a=>{this._active=!1;throw a;})}stop(){this.pause();this._active=!1}pause(a=!1){this._paused=!0;if(!this._active)return Promise.resolve(!0);this.$video.pause();let b=()=>{const a=this.$video.srcObject?this.$video.srcObject.getTracks():[];for(const b of a)b.stop(),this.$video.srcObject.removeTrack(b);this.$video.srcObject=null};return a?(b(),Promise.resolve(!0)):(new Promise(a=>setTimeout(a,300))).then(()=>{if(!this._paused)return!1;b();return!0})}setCamera(a){if(a===
this._preferredCamera)return Promise.resolve();this._preferredCamera=a;return this._restartVideoStream()}static scanImage(a,b=null,c=null,f=null,k=!1,g=!1){let h=c instanceof Worker,l=Promise.all([c||d.createQrEngine(),d._loadImage(a)]).then(([a,g])=>{c=a;let l;[f,l]=this._drawToCanvas(g,b,f,k);return c instanceof Worker?(h||c.postMessage({type:"inversionMode",data:"both"}),new Promise((a,b)=>{let k,g,h;g=f=>{"qrResult"===f.data.type&&(c.removeEventListener("message",g),c.removeEventListener("error",
h),clearTimeout(k),null!==f.data.data?a(f.data.data):b(d.NO_QR_CODE_FOUND))};h=a=>{c.removeEventListener("message",g);c.removeEventListener("error",h);clearTimeout(k);b("Scanner error: "+(a?a.message||a:"Unknown Error"))};c.addEventListener("message",g);c.addEventListener("error",h);k=setTimeout(()=>h("timeout"),1E4);let m=l.getImageData(0,0,f.width,f.height);c.postMessage({type:"decode",data:m},[m.data.buffer])})):new Promise((a,b)=>{let k=setTimeout(()=>b("Scanner error: timeout"),1E4);c.detect(f).then(c=>
{c.length?a(c[0].rawValue):b(d.NO_QR_CODE_FOUND)}).catch(a=>b("Scanner error: "+(a.message||a))).finally(()=>clearTimeout(k))})});b&&g&&(l=l.catch(()=>d.scanImage(a,null,c,f,k)));return l=l.finally(()=>{h||d._postWorkerMessage(c,"close")})}setGrayscaleWeights(a,b,c,f=!0){d._postWorkerMessage(this._qrEnginePromise,"grayscaleWeights",{red:a,green:b,blue:c,useIntegerApproximation:f})}setInversionMode(a){d._postWorkerMessage(this._qrEnginePromise,"inversionMode",a)}static createQrEngine(a=d.WORKER_PATH){return("BarcodeDetector"in
window?BarcodeDetector.getSupportedFormats():Promise.resolve([])).then(b=>-1!==b.indexOf("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(){if(!this._active||this.$video.paused||this.$video.ended)return!1;requestAnimationFrame(()=>{1>=this.$video.readyState?this._scanFrame():this._qrEnginePromise.then(a=>d.scanImage(this.$video,this._scanRegion,a,this.$canvas)).then(this._onDecode,a=>{this._active&&(-1!==(a.message||a).indexOf("service unavailable")&&(this._qrEnginePromise=d.createQrEngine()),
this._onDecodeError(a))}).then(()=>this._scanFrame())})}_onDecodeError(a){a!==d.NO_QR_CODE_FOUND&&console.log(a)}_getCameraStream(a,b=!1){let c=[{width:{min:1024}},{width:{min:768}},{}];if(a){let f="environment"===a||"user"===a?"facingMode":"deviceId";b&&(a={exact:a});c.forEach(b=>b[f]=a)}return this._getMatchingCameraStream(c)}_getMatchingCameraStream(a){return navigator.mediaDevices&&0!==a.length?navigator.mediaDevices.getUserMedia({video:a.shift()}).catch(()=>this._getMatchingCameraStream(a)):
Promise.reject("Camera not found.")}_restartVideoStream(){let a=this._paused;return this.pause(!0).then(b=>{if(b&&!a&&this._active)return this.start()})}_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=null,c=null,f=!1){c=c||document.createElement("canvas");let d=b&&b.x?b.x:0,g=b&&b.y?b.y:0,h=
b&&b.width?b.width:a.width||a.videoWidth,l=b&&b.height?b.height:a.height||a.videoHeight;f||(f=b&&b.downScaledWidth?b.downScaledWidth:h,b=b&&b.downScaledHeight?b.downScaledHeight:l,c.width!==f&&(c.width=f),c.height!==b&&(c.height=b));b=c.getContext("2d",{alpha:!1});b.imageSmoothingEnabled=!1;b.drawImage(a,d,g,h,l,0,0,c.width,c.height);return[c,b]}static _loadImage(a){if(a instanceof HTMLCanvasElement||a instanceof HTMLVideoElement||window.ImageBitmap&&a instanceof window.ImageBitmap||window.OffscreenCanvas&&
a instanceof window.OffscreenCanvas)return Promise.resolve(a);if(a instanceof Image)return d._awaitImageLoad(a).then(()=>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;return d._awaitImageLoad(b).then(()=>{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src);return b})}return Promise.reject("Unsupported image type.")}static _awaitImageLoad(a){return new Promise((b,
c)=>{if(a.complete&&0!==a.naturalWidth)b();else{let f,d;f=()=>{a.removeEventListener("load",f);a.removeEventListener("error",d);b()};d=()=>{a.removeEventListener("load",f);a.removeEventListener("error",d);c("Image load error")};a.addEventListener("load",f);a.addEventListener("error",d)}})}static _postWorkerMessage(a,b,c){return Promise.resolve(a).then(a=>{a instanceof Worker&&a.postMessage({type:b,data:c})})}}d.DEFAULT_CANVAS_SIZE=400;d.NO_QR_CODE_FOUND="No QR code found";d.WORKER_PATH="qr-scanner-worker.min.js";
return d})
//# sourceMappingURL=qr-scanner.umd.min.js.map
File diff suppressed because one or more lines are too long
+41 -27
View File
@@ -152,17 +152,38 @@ export default class QrScanner {
/* async */
toggleFlash() {
return this._setFlash(!this._flashOn);
}
/* async */
turnFlashOff() {
return this._setFlash(false);
if (this._flashOn) {
return this.turnFlashOff();
} else {
return this.turnFlashOn();
}
}
/* async */
turnFlashOn() {
return this._setFlash(true);
if (this._flashOn) return Promise.resolve();
this._flashOn = true;
if (!this._active || this._paused) return Promise.resolve(); // flash will be turned on later on .start()
return this.hasFlash().then((hasFlash) => {
if (!hasFlash) return Promise.reject('No flash available');
// Note that the video track is guaranteed to exist at this point
return this.$video.srcObject.getVideoTracks()[0].applyConstraints({
advanced: [{ torch: true }],
});
}).catch(() => {
this._flashOn = false;
throw e;
});
}
/* async */
turnFlashOff() {
if (!this._flashOn) return;
// applyConstraints with torch: false does not work to turn the flashlight off, as a stream's torch stays
// continuously on, see https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints#torch. Therefore,
// we have to stop the stream to turn the flashlight off.
this._flashOn = false;
return this._restartVideoStream();
}
destroy() {
@@ -219,6 +240,7 @@ export default class QrScanner {
// Restart the flash if it was previously on
if (this._flashOn) {
this._flashOn = false; // force turnFlashOn to restart the flash
this.turnFlashOn().catch(() => {});
}
})
@@ -233,6 +255,7 @@ export default class QrScanner {
this._active = false;
}
/* async */
pause(stopStreamImmediately = false) {
this._paused = true;
if (!this._active) {
@@ -266,14 +289,8 @@ export default class QrScanner {
setCamera(facingModeOrDeviceId) {
if (facingModeOrDeviceId === this._preferredCamera) return Promise.resolve();
this._preferredCamera = facingModeOrDeviceId;
// Restart the scanner with the new camera which will also update the video mirror and the scan region. Note
// that we always pause the stream and not only if !this._paused as even if this._paused === true, the stream
// might still be running, as it's by default only stopped after a delay of 300ms.
const wasPaused = this._paused;
return this.pause(true).then((paused) => {
if (!paused || wasPaused || !this._active) return;
return this.start();
});
// Restart the scanner with the new camera which will also update the video mirror and the scan region.
return this._restartVideoStream();
}
/* async */
@@ -441,6 +458,7 @@ export default class QrScanner {
console.log(error);
}
/* async */
_getCameraStream(preferredCamera, exact = false) {
const constraintsToTry = [{
width: { min: 1024 }
@@ -461,6 +479,7 @@ export default class QrScanner {
return this._getMatchingCameraStream(constraintsToTry);
}
/* async */
_getMatchingCameraStream(constraintsToTry) {
if (!navigator.mediaDevices || constraintsToTry.length === 0) {
return Promise.reject('Camera not found.');
@@ -471,18 +490,13 @@ export default class QrScanner {
}
/* async */
_setFlash(on) {
this._flashOn = on;
if (on && (!this._active || this._paused)) return Promise.resolve(); // will be started later on .start
return this.hasFlash().then((hasFlash) => {
if (!hasFlash) return Promise.reject('No flash available');
// Note that the video track is guaranteed to exist at this point
return this.$video.srcObject.getVideoTracks()[0].applyConstraints({
advanced: [{ torch: on }],
});
}).catch((e) => {
this._flashOn = !on;
throw e;
_restartVideoStream() {
// Note that we always pause the stream and not only if !this._paused as even if this._paused === true, the
// stream might still be running, as it's by default only stopped after a delay of 300ms.
const wasPaused = this._paused;
return this.pause(true).then((paused) => {
if (!paused || wasPaused || !this._active) return;
return this.start();
});
}