mirror of
https://github.com/nimiq/qr-scanner.git
synced 2026-07-28 04:06:51 +00:00
Convert to typescript
This commit is contained in:
Generated
+74
@@ -59,6 +59,16 @@
|
||||
"integrity": "sha512-9oLAnygRMi8Q5QkYEU4XWK04B+nuoXoxjRvRxgjuChkLZFBja0YPSgdZ7dZtwhncLBcQe/I/E+fLuk5qxcYVJA==",
|
||||
"dev": true
|
||||
},
|
||||
"@rollup/plugin-typescript": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz",
|
||||
"integrity": "sha512-I5FpSvLbtAdwJ+naznv+B4sjXZUcIvLLceYpITAn7wAP8W0wqc5noLdGIp9HGVntNhRWXctwPYrSSFQxtl0FPA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@rollup/pluginutils": "^3.1.0",
|
||||
"resolve": "^1.17.0"
|
||||
}
|
||||
},
|
||||
"@rollup/pluginutils": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
|
||||
@@ -84,6 +94,11 @@
|
||||
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/offscreencanvas": {
|
||||
"version": "2019.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.6.4.tgz",
|
||||
"integrity": "sha512-u8SAgdZ8ROtkTF+mfZGOscl0or6BSj9A4g37e6nvxDc+YB/oDut0wHkK2PBBiC2bNR8TS0CPV+1gAk4fNisr1Q=="
|
||||
},
|
||||
"acorn": {
|
||||
"version": "8.7.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
|
||||
@@ -197,6 +212,12 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
},
|
||||
"google-closure-compiler": {
|
||||
"version": "20220104.0.0",
|
||||
"resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20220104.0.0.tgz",
|
||||
@@ -269,6 +290,15 @@
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"has": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
@@ -281,6 +311,15 @@
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-core-module": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz",
|
||||
"integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
@@ -308,6 +347,12 @@
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
|
||||
@@ -347,6 +392,17 @@
|
||||
"integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz",
|
||||
"integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-core-module": "^2.8.1",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "2.21.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.21.0.tgz",
|
||||
@@ -412,6 +468,24 @@
|
||||
"has-flag": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
|
||||
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.5.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
|
||||
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
|
||||
"dev": true
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
||||
+7
-1
@@ -42,12 +42,18 @@
|
||||
"url": "https://github.com/nimiq/qr-scanner/issues"
|
||||
},
|
||||
"homepage": "https://github.com/nimiq/qr-scanner#readme",
|
||||
"dependencies": {
|
||||
"@types/offscreencanvas": "^2019.6.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ampproject/rollup-plugin-closure-compiler": "^0.27.0",
|
||||
"@rollup/plugin-typescript": "^8.3.0",
|
||||
"acorn": "^8.7.0",
|
||||
"google-closure-compiler": "^20220104.0.0",
|
||||
"jsqr-es6": "^1.4.0-1",
|
||||
"rollup": "^2.21.0",
|
||||
"rollup-plugin-sourcemaps": "^0.6.2"
|
||||
"rollup-plugin-sourcemaps": "^0.6.2",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.5.5"
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+23
-23
@@ -1,7 +1,7 @@
|
||||
var c=c||{};c.scope={};c.ASSUME_ES5=!1;c.ASSUME_NO_NATIVE_MAP=!1;c.ASSUME_NO_NATIVE_SET=!1;c.SIMPLE_FROUND_POLYFILL=!1;c.ISOLATE_POLYFILLS=!1;c.FORCE_POLYFILL_PROMISE=!1;c.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION=!1;c.defineProperty=c.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,d){if(a==Array.prototype||a==Object.prototype)return a;a[b]=d.value;return a};
|
||||
c.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,a,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var b=0;b<a.length;++b){var d=a[b];if(d&&d.Math==Math)return d}throw Error("Cannot find global object");};c.global=c.getGlobal(this);c.IS_SYMBOL_NATIVE="function"===typeof Symbol&&"symbol"===typeof Symbol("x");c.TRUST_ES6_POLYFILLS=!c.ISOLATE_POLYFILLS||c.IS_SYMBOL_NATIVE;c.polyfills={};c.propertyToPolyfillSymbol={};
|
||||
c.POLYFILL_PREFIX="$jscp$";c.polyfill=function(a,b,d,e){b&&(c.ISOLATE_POLYFILLS?c.polyfillIsolated(a,b,d,e):c.polyfillUnisolated(a,b,d,e))};c.polyfillUnisolated=function(a,b){var d=c.global;a=a.split(".");for(var e=0;e<a.length-1;e++){var f=a[e];if(!(f in d))return;d=d[f]}a=a[a.length-1];e=d[a];b=b(e);b!=e&&null!=b&&c.defineProperty(d,a,{configurable:!0,writable:!0,value:b})};
|
||||
c.polyfillIsolated=function(a,b,d){var e=a.split(".");a=1===e.length;var f=e[0];f=!a&&f in c.polyfills?c.polyfills:c.global;for(var g=0;g<e.length-1;g++){var k=e[g];if(!(k in f))return;f=f[k]}e=e[e.length-1];d=c.IS_SYMBOL_NATIVE&&"es6"===d?f[e]:null;b=b(d);null!=b&&(a?c.defineProperty(c.polyfills,e,{configurable:!0,writable:!0,value:b}):b!==d&&(void 0===c.propertyToPolyfillSymbol[e]&&(a=1E9*Math.random()>>>0,c.propertyToPolyfillSymbol[e]=c.IS_SYMBOL_NATIVE?c.global.Symbol(e):c.POLYFILL_PREFIX+a+"$"+
|
||||
c.polyfillIsolated=function(a,b,d){var e=a.split(".");a=1===e.length;var f=e[0];f=!a&&f in c.polyfills?c.polyfills:c.global;for(var h=0;h<e.length-1;h++){var k=e[h];if(!(k in f))return;f=f[k]}e=e[e.length-1];d=c.IS_SYMBOL_NATIVE&&"es6"===d?f[e]:null;b=b(d);null!=b&&(a?c.defineProperty(c.polyfills,e,{configurable:!0,writable:!0,value:b}):b!==d&&(void 0===c.propertyToPolyfillSymbol[e]&&(a=1E9*Math.random()>>>0,c.propertyToPolyfillSymbol[e]=c.IS_SYMBOL_NATIVE?c.global.Symbol(e):c.POLYFILL_PREFIX+a+"$"+
|
||||
e),c.defineProperty(f,c.propertyToPolyfillSymbol[e],{configurable:!0,writable:!0,value:b})))};c.underscoreProtoCanBeSet=function(){var a={a:!0},b={};try{return b.__proto__=a,b.a}catch(d){}return!1};c.setPrototypeOf=c.TRUST_ES6_POLYFILLS&&"function"==typeof Object.setPrototypeOf?Object.setPrototypeOf:c.underscoreProtoCanBeSet()?function(a,b){a.__proto__=b;if(a.__proto__!==b)throw new TypeError(a+" is not extensible");return a}:null;
|
||||
c.arrayIteratorImpl=function(a){var b=0;return function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}}};c.arrayIterator=function(a){return{next:c.arrayIteratorImpl(a)}};c.makeIterator=function(a){var b="undefined"!=typeof Symbol&&Symbol.iterator&&a[Symbol.iterator];return b?b.call(a):c.arrayIterator(a)};c.generator={};c.generator.ensureIteratorResultIsObject_=function(a){if(!(a instanceof Object))throw new TypeError("Iterator result "+a+" is not an object");};
|
||||
c.generator.Context=function(){this.isRunning_=!1;this.yieldAllIterator_=null;this.yieldResult=void 0;this.nextAddress=1;this.finallyAddress_=this.catchAddress_=0;this.finallyContexts_=this.abruptCompletion_=null};c.generator.Context.prototype.start_=function(){if(this.isRunning_)throw new TypeError("Generator is already running");this.isRunning_=!0};c.generator.Context.prototype.stop_=function(){this.isRunning_=!1};
|
||||
@@ -13,28 +13,28 @@ c.generator.Context.prototype.leaveFinallyBlock=function(a,b){b=this.finallyCont
|
||||
c.generator.Context.PropertyIterator=function(a){this.object_=a;this.properties_=[];for(var b in a)this.properties_.push(b);this.properties_.reverse()};c.generator.Context.PropertyIterator.prototype.getNext=function(){for(;0<this.properties_.length;){var a=this.properties_.pop();if(a in this.object_)return a}return null};c.generator.Engine_=function(a){this.context_=new c.generator.Context;this.program_=a};
|
||||
c.generator.Engine_.prototype.next_=function(a){this.context_.start_();if(this.context_.yieldAllIterator_)return this.yieldAllStep_(this.context_.yieldAllIterator_.next,a,this.context_.next_);this.context_.next_(a);return this.nextStep_()};c.generator.Engine_.prototype.return_=function(a){this.context_.start_();var b=this.context_.yieldAllIterator_;if(b)return this.yieldAllStep_("return"in b?b["return"]:function(d){return{value:d,done:!0}},a,this.context_.return);this.context_.return(a);return this.nextStep_()};
|
||||
c.generator.Engine_.prototype.throw_=function(a){this.context_.start_();if(this.context_.yieldAllIterator_)return this.yieldAllStep_(this.context_.yieldAllIterator_["throw"],a,this.context_.next_);this.context_.throw_(a);return this.nextStep_()};
|
||||
c.generator.Engine_.prototype.yieldAllStep_=function(a,b,d){try{var e=a.call(this.context_.yieldAllIterator_,b);c.generator.ensureIteratorResultIsObject_(e);if(!e.done)return this.context_.stop_(),e;var f=e.value}catch(g){return this.context_.yieldAllIterator_=null,this.context_.throw_(g),this.nextStep_()}this.context_.yieldAllIterator_=null;d.call(this.context_,f);return this.nextStep_()};
|
||||
c.generator.Engine_.prototype.yieldAllStep_=function(a,b,d){try{var e=a.call(this.context_.yieldAllIterator_,b);c.generator.ensureIteratorResultIsObject_(e);if(!e.done)return this.context_.stop_(),e;var f=e.value}catch(h){return this.context_.yieldAllIterator_=null,this.context_.throw_(h),this.nextStep_()}this.context_.yieldAllIterator_=null;d.call(this.context_,f);return this.nextStep_()};
|
||||
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 g(k){k.done?e(k.value):Promise.resolve(k.value).then(b,d).then(g,f)}g(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 h{static hasCamera(){return c.asyncExecutePromiseGeneratorFunction(function*(){try{return!!(yield h.listCameras(!1)).length}catch(a){return!1}})}static listCameras(a=!1){return c.asyncExecutePromiseGeneratorFunction(function*(){if(!navigator.mediaDevices)return[];let b=null;if(a)try{b=yield navigator.mediaDevices.getUserMedia({audio:!1,video:!0})}catch(d){}try{return(yield navigator.mediaDevices.enumerateDevices()).filter(d=>"videoinput"===d.kind).map((d,e)=>({id:d.deviceId,label:d.label||(0===
|
||||
e?"Default Camera":`Camera ${e+1}`)}))}finally{if(b)for(let d of b.getTracks())d.stop(),b.removeTrack(d)}})}constructor(a,b,d=this._onDecodeError,e=this._calculateScanRegion,f="environment"){this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;this._legacyCanvasSize=h.DEFAULT_CANVAS_SIZE;this._preferredCamera=f;this._flashOn=this._paused=this._active=!1;"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 g=!1;a.hidden&&(a.hidden=!1,
|
||||
g=!0);document.body.contains(a)||(document.body.appendChild(a),g=!0);requestAnimationFrame(()=>{let k=window.getComputedStyle(a);"none"===k.display&&(a.style.setProperty("display","block","important"),g=!0);"visible"!==k.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=h.createQrEngine()}hasFlash(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){let b=null;try{return b=a.$video.srcObject||(yield a._getCameraStream()).stream,"torch"in b.getVideoTracks()[0].getSettings()}catch(d){return!1}finally{if(b&&b!==a.$video.srcObject){console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream");
|
||||
for(let d of b.getTracks())d.stop(),b.removeTrack(d)}}})}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._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.stop();h._postWorkerMessage(this._qrEnginePromise,"close")}start(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){if(!a._active||
|
||||
a._paused)if("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)a.$video.play();else try{let {stream:b,facingMode:d}=yield a._getCameraStream();a.$video.srcObject=b;a.$video.play();a._setVideoMirror(d);a._flashOn&&(a._flashOn=!1,a.turnFlashOn().catch(()=>{}))}catch(b){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=()=>{const e=b.$video.srcObject?b.$video.srcObject.getTracks():[];for(const f of e)f.stop(),b.$video.srcObject.removeTrack(f);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=null,d=null,e=null,f=
|
||||
!1,g=!1){const k=this;return c.asyncExecutePromiseGeneratorFunction(function*(){let n=!!d;try{let q,v;[d,q]=yield Promise.all([d||h.createQrEngine(),h._loadImage(a)]);[e,v]=k._drawToCanvas(q,b,e,f);return d instanceof Worker?(n||d.postMessage({type:"inversionMode",data:"both"}),yield new Promise((l,r)=>{let u,t,p;t=m=>{"qrResult"===m.data.type&&(d.removeEventListener("message",t),d.removeEventListener("error",p),clearTimeout(u),null!==m.data.data?l(m.data.data):r(h.NO_QR_CODE_FOUND))};p=m=>{d.removeEventListener("message",
|
||||
t);d.removeEventListener("error",p);clearTimeout(u);r("Scanner error: "+(m?m.message||m:"Unknown Error"))};d.addEventListener("message",t);d.addEventListener("error",p);u=setTimeout(()=>p("timeout"),1E4);let w=v.getImageData(0,0,e.width,e.height);d.postMessage({type:"decode",data:w},[w.data.buffer])})):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 h.NO_QR_CODE_FOUND;
|
||||
return l.rawValue}catch(l){throw`Scanner error: ${l instanceof Error?l.message:l}`;}}))()])}catch(q){if(!b||!g)throw q;return yield h.scanImage(a,null,d,e,f)}finally{n||h._postWorkerMessage(d,"close")}})}setGrayscaleWeights(a,b,d,e=!0){h._postWorkerMessage(this._qrEnginePromise,"grayscaleWeights",{red:a,green:b,blue:d,useIntegerApproximation:e})}setInversionMode(a){h._postWorkerMessage(this._qrEnginePromise,"inversionMode",a)}static createQrEngine(a=h.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 h.scanImage(a.$video,a._scanRegion,a._qrEnginePromise,a.$canvas)}catch(d){if(!a._active)return;(d.message||d).includes("service unavailable")&&(a._qrEnginePromise=
|
||||
h.createQrEngine());a._onDecodeError(d)}b&&a._onDecode(b)}a._scanFrame()})})}_onDecodeError(a){a!==h.NO_QR_CODE_FOUND&&console.log(a)}_getCameraStream(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){if(!navigator.mediaDevices)throw"Camera not found.";let b="environment"===a._preferredCamera||"user"===a._preferredCamera?"facingMode":"deviceId",d=[{width:{min:1024}},{width:{min:768}},{}],e=d.map(f=>Object.assign({},f,{[b]:{exact:a._preferredCamera}}));for(let f of[...e,...d])try{let g=
|
||||
yield navigator.mediaDevices.getUserMedia({video:f,audio:!1}),k=a._getFacingMode(g)||(f.facingMode?a._preferredCamera:"environment"===a._preferredCamera?"user":"environment");return{stream:g,facingMode:k}}catch(g){}throw"Camera not found.";})}_restartVideoStream(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){let b=a._paused;(yield a.pause(!0))&&!b&&a._active&&(yield a.start())})}_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,d=null,e=!1){d=d||document.createElement("canvas");let f=b&&b.x?b.x:0,g=b&&b.y?b.y:0,k=b&&b.width?b.width:a.width||a.videoWidth,n=b&&b.height?b.height:a.height||a.videoHeight;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,g,k,n,0,0,d.width,d.height);return[d,b]}static _loadImage(a){return c.asyncExecutePromiseGeneratorFunction(function*(){if(a instanceof HTMLCanvasElement||a instanceof HTMLVideoElement||window.ImageBitmap&&a instanceof window.ImageBitmap||window.OffscreenCanvas&&a instanceof window.OffscreenCanvas)return a;if(a instanceof Image)return yield h._awaitImageLoad(a),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;try{return yield h._awaitImageLoad(b),b}finally{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src)}}else throw"Unsupported image type.";})}static _awaitImageLoad(a){return c.asyncExecutePromiseGeneratorFunction(function*(){a.complete&&0!==a.naturalWidth||(yield new Promise((b,d)=>{let e,f;e=()=>{a.removeEventListener("load",e);a.removeEventListener("error",f);b()};f=()=>{a.removeEventListener("load",e);a.removeEventListener("error",
|
||||
f);d("Image load error")};a.addEventListener("load",e);a.addEventListener("error",f)}))})}static _postWorkerMessage(a,b,d){return c.asyncExecutePromiseGeneratorFunction(function*(){let e=yield a;e instanceof Worker&&e.postMessage({type:b,data:d})})}}h.DEFAULT_CANVAS_SIZE=400;h.NO_QR_CODE_FOUND="No QR code found";h.WORKER_PATH="qr-scanner-worker.min.js";export default h
|
||||
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._flashOn=this._paused=this._active=!1;d=d||this._onDecodeError;e=e||this._calculateScanRegion;if(a.srcObject&&!(a.srcObject instanceof MediaStream))throw"Only MediaStreams are supported as video source.";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;if(a)try{b=yield navigator.mediaDevices.getUserMedia({audio:!1,
|
||||
video:!0})}catch(d){}try{return(yield navigator.mediaDevices.enumerateDevices()).filter(d=>"videoinput"===d.kind).map((d,e)=>({id:d.deviceId,label:d.label||(0===e?"Default Camera":`Camera ${e+1}`)}))}finally{if(b)for(let d of b.getTracks())d.stop(),b.removeTrack(d)}})}hasFlash(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){let b;try{return b=a.$video.srcObject||(yield a._getCameraStream()).stream,"torch"in b.getVideoTracks()[0].getSettings()}catch(d){return!1}finally{if(b&&
|
||||
b!==a.$video.srcObject){console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream");for(let d of b.getTracks())d.stop(),b.removeTrack(d)}}})}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._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.stop();g._postWorkerMessage(this._qrEnginePromise,
|
||||
"close")}start(){const a=this;return c.asyncExecutePromiseGeneratorFunction(function*(){if(!a._active||a._paused)if("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)a.$video.play();else try{let {stream:b,facingMode:d}=yield a._getCameraStream();a.$video.srcObject=b;a.$video.play();a._setVideoMirror(d);a._flashOn&&(a._flashOn=!1,a.turnFlashOn().catch(()=>{}))}catch(b){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=()=>{const e=b.$video.srcObject?b.$video.srcObject.getTracks():[];for(const f of e)f.stop(),b.$video.srcObject.removeTrack(f);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())})}_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
|
||||
//# sourceMappingURL=qr-scanner.legacy.min.js.map
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+18
-18
@@ -1,20 +1,20 @@
|
||||
class e{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=null;if(a)try{b=await navigator.mediaDevices.getUserMedia({audio:!1,video:!0})}catch(c){}try{return(await navigator.mediaDevices.enumerateDevices()).filter(c=>"videoinput"===c.kind).map((c,d)=>({id:c.deviceId,label:c.label||(0===d?"Default Camera":`Camera ${d+1}`)}))}finally{if(b)for(let c of b.getTracks())c.stop(),b.removeTrack(c)}}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 k=window.getComputedStyle(a);"none"===k.display&&(a.style.setProperty("display","block",
|
||||
"important"),g=!0);"visible"!==k.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()}async hasFlash(){let a=
|
||||
null;try{return a=this.$video.srcObject||(await this._getCameraStream()).stream,"torch"in a.getVideoTracks()[0].getSettings()}catch(b){return!1}finally{if(a&&a!==this.$video.srcObject){console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream");for(let b of a.getTracks())b.stop(),a.removeTrack(b)}}}isFlashOn(){return this._flashOn}async toggleFlash(){this._flashOn?await this.turnFlashOff():await this.turnFlashOn()}async turnFlashOn(){if(!this._flashOn&&
|
||||
(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.stop();e._postWorkerMessage(this._qrEnginePromise,"close")}async start(){if(!this._active||this._paused)if("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)this.$video.play();else try{let {stream:a,facingMode:b}=await this._getCameraStream();this.$video.srcObject=a;this.$video.play();this._setVideoMirror(b);this._flashOn&&
|
||||
(this._flashOn=!1,this.turnFlashOn().catch(()=>{}))}catch(a){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=()=>{const c=this.$video.srcObject?this.$video.srcObject.getTracks():[];for(const d of c)d.stop(),this.$video.srcObject.removeTrack(d);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=null,c=null,d=null,f=!1,g=!1){let k=!!c;try{let l,t;[c,l]=await Promise.all([c||e.createQrEngine(),e._loadImage(a)]);[d,t]=this._drawToCanvas(l,b,d,f);return c instanceof Worker?(k||c.postMessage({type:"inversionMode",data:"both"}),await new Promise((h,p)=>{let r,q,n;q=m=>{"qrResult"===m.data.type&&(c.removeEventListener("message",q),c.removeEventListener("error",n),clearTimeout(r),null!==m.data.data?h(m.data.data):
|
||||
p(e.NO_QR_CODE_FOUND))};n=m=>{c.removeEventListener("message",q);c.removeEventListener("error",n);clearTimeout(r);p("Scanner error: "+(m?m.message||m:"Unknown Error"))};c.addEventListener("message",q);c.addEventListener("error",n);r=setTimeout(()=>n("timeout"),1E4);let u=t.getImageData(0,0,d.width,d.height);c.postMessage({type:"decode",data:u},[u.data.buffer])})):await Promise.race([new Promise((h,p)=>window.setTimeout(()=>p("Scanner error: timeout"),1E4)),(async()=>{try{let [h]=await c.detect(d);
|
||||
if(!h)throw e.NO_QR_CODE_FOUND;return h.rawValue}catch(h){throw`Scanner error: ${h instanceof Error?h.message:h}`;}})()])}catch(l){if(!b||!g)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"===this._preferredCamera||"user"===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}),g=this._getFacingMode(f)||
|
||||
(d.facingMode?this._preferredCamera:"environment"===this._preferredCamera?"user":"environment");return{stream:f,facingMode:g}}catch(f){}throw"Camera not found.";}async _restartVideoStream(){let a=this._paused;await this.pause(!0)&&!a&&this._active&&await 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 f=b&&b.x?b.x:0,g=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,f,g,k,l,0,0,c.width,c.height);return[c,b]}static async _loadImage(a){if(a instanceof HTMLCanvasElement||
|
||||
a instanceof HTMLVideoElement||window.ImageBitmap&&a instanceof window.ImageBitmap||window.OffscreenCanvas&&a instanceof window.OffscreenCanvas)return a;if(a instanceof Image)return await e._awaitImageLoad(a),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;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;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 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";
|
||||
class e{constructor(a,b,c,d,g="environment"){this._legacyCanvasSize=e.DEFAULT_CANVAS_SIZE;this._flashOn=this._paused=this._active=!1;c=c||this._onDecodeError;d=d||this._calculateScanRegion;if(a.srcObject&&!(a.srcObject instanceof MediaStream))throw"Only MediaStreams are supported as video source.";this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;this._preferredCamera=g;"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;if(a)try{b=await navigator.mediaDevices.getUserMedia({audio:!1,video:!0})}catch(c){}try{return(await navigator.mediaDevices.enumerateDevices()).filter(c=>"videoinput"===
|
||||
c.kind).map((c,d)=>({id:c.deviceId,label:c.label||(0===d?"Default Camera":`Camera ${d+1}`)}))}finally{if(b)for(let c of b.getTracks())c.stop(),b.removeTrack(c)}}async hasFlash(){let a;try{return a=this.$video.srcObject||(await this._getCameraStream()).stream,"torch"in a.getVideoTracks()[0].getSettings()}catch(b){return!1}finally{if(a&&a!==this.$video.srcObject){console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream");for(let b of a.getTracks())b.stop(),
|
||||
a.removeTrack(b)}}}isFlashOn(){return this._flashOn}async toggleFlash(){this._flashOn?await this.turnFlashOff():await this.turnFlashOn()}async turnFlashOn(){if(!this._flashOn&&(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.stop();e._postWorkerMessage(this._qrEnginePromise,"close")}async start(){if(!this._active||this._paused)if("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)this.$video.play();else try{let {stream:a,
|
||||
facingMode:b}=await this._getCameraStream();this.$video.srcObject=a;this.$video.play();this._setVideoMirror(b);this._flashOn&&(this._flashOn=!1,this.turnFlashOn().catch(()=>{}))}catch(a){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=()=>{const c=this.$video.srcObject?this.$video.srcObject.getTracks():[];for(const d of c)d.stop(),this.$video.srcObject.removeTrack(d);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,g=!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,g);if(c instanceof Worker){let f=c;k||f.postMessage({type:"inversionMode",data:"both"});return await new Promise((q,u)=>{let r,p,n;p=m=>{"qrResult"===m.data.type&&
|
||||
(f.removeEventListener("message",p),f.removeEventListener("error",n),clearTimeout(r),null!==m.data.data?q(m.data.data):u(e.NO_QR_CODE_FOUND))};n=m=>{f.removeEventListener("message",p);f.removeEventListener("error",n);clearTimeout(r);u("Scanner error: "+(m?m.message||m:"Unknown Error"))};f.addEventListener("message",p);f.addEventListener("error",n);r=setTimeout(()=>n("timeout"),1E4);let v=t.getImageData(0,0,d.width,d.height);f.postMessage({type:"decode",data:v},[v.data.buffer])})}return await Promise.race([new Promise((f,
|
||||
q)=>window.setTimeout(()=>q("Scanner error: timeout"),1E4)),(async()=>{try{let [f]=await c.detect(d);if(!f)throw e.NO_QR_CODE_FOUND;return f.rawValue}catch(f){throw`Scanner error: ${f.message||f}`;}})()])}catch(l){if(!b||!h)throw l;return await e.scanImage(a,null,c,d,g)}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 g=await navigator.mediaDevices.getUserMedia({video:d,
|
||||
audio:!1}),h=this._getFacingMode(g)||(d.facingMode?this._preferredCamera:"environment"===this._preferredCamera?"user":"environment");return{stream:g,facingMode:h}}catch(g){}throw"Camera not found.";}async _restartVideoStream(){let a=this._paused;await this.pause(!0)&&!a&&this._active&&await 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,c,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.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,g,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=g=>{a.removeEventListener("load",d);a.removeEventListener("error",d);g 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";export default e
|
||||
//# sourceMappingURL=qr-scanner.min.js.map
|
||||
|
||||
File diff suppressed because one or more lines are too long
Vendored
+19
-19
@@ -1,20 +1,20 @@
|
||||
'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{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=null;if(a)try{b=await navigator.mediaDevices.getUserMedia({audio:!1,video:!0})}catch(c){}try{return(await navigator.mediaDevices.enumerateDevices()).filter(c=>
|
||||
"videoinput"===c.kind).map((c,d)=>({id:c.deviceId,label:c.label||(0===d?"Default Camera":`Camera ${d+1}`)}))}finally{if(b)for(let c of b.getTracks())c.stop(),b.removeTrack(c)}}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 k=window.getComputedStyle(a);"none"===k.display&&(a.style.setProperty("display","block","important"),g=!0);"visible"!==k.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()}async hasFlash(){let a=null;try{return a=this.$video.srcObject||(await this._getCameraStream()).stream,"torch"in a.getVideoTracks()[0].getSettings()}catch(b){return!1}finally{if(a&&a!==this.$video.srcObject){console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream");for(let b of a.getTracks())b.stop(),a.removeTrack(b)}}}isFlashOn(){return this._flashOn}async toggleFlash(){this._flashOn?
|
||||
await this.turnFlashOff():await this.turnFlashOn()}async turnFlashOn(){if(!this._flashOn&&(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.stop();e._postWorkerMessage(this._qrEnginePromise,"close")}async start(){if(!this._active||this._paused)if("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)this.$video.play();else try{let {stream:a,facingMode:b}=await this._getCameraStream();this.$video.srcObject=
|
||||
a;this.$video.play();this._setVideoMirror(b);this._flashOn&&(this._flashOn=!1,this.turnFlashOn().catch(()=>{}))}catch(a){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=()=>{const c=this.$video.srcObject?this.$video.srcObject.getTracks():[];for(const d of c)d.stop(),this.$video.srcObject.removeTrack(d);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=null,c=null,d=null,f=!1,g=!1){let k=!!c;try{let l,t;[c,l]=await Promise.all([c||e.createQrEngine(),e._loadImage(a)]);[d,t]=this._drawToCanvas(l,b,d,f);return c instanceof Worker?(k||c.postMessage({type:"inversionMode",data:"both"}),await new Promise((h,p)=>{let r,q,n;q=m=>{"qrResult"===m.data.type&&(c.removeEventListener("message",q),c.removeEventListener("error",
|
||||
n),clearTimeout(r),null!==m.data.data?h(m.data.data):p(e.NO_QR_CODE_FOUND))};n=m=>{c.removeEventListener("message",q);c.removeEventListener("error",n);clearTimeout(r);p("Scanner error: "+(m?m.message||m:"Unknown Error"))};c.addEventListener("message",q);c.addEventListener("error",n);r=setTimeout(()=>n("timeout"),1E4);let u=t.getImageData(0,0,d.width,d.height);c.postMessage({type:"decode",data:u},[u.data.buffer])})):await Promise.race([new Promise((h,p)=>window.setTimeout(()=>p("Scanner error: timeout"),
|
||||
1E4)),(async()=>{try{let [h]=await c.detect(d);if(!h)throw e.NO_QR_CODE_FOUND;return h.rawValue}catch(h){throw`Scanner error: ${h instanceof Error?h.message:h}`;}})()])}catch(l){if(!b||!g)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"===this._preferredCamera||"user"===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}),g=this._getFacingMode(f)||(d.facingMode?this._preferredCamera:"environment"===this._preferredCamera?"user":"environment");return{stream:f,facingMode:g}}catch(f){}throw"Camera not found.";}async _restartVideoStream(){let a=this._paused;await this.pause(!0)&&!a&&this._active&&await 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 f=b&&b.x?b.x:0,g=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,f,g,k,l,0,0,c.width,c.height);return[c,b]}static async _loadImage(a){if(a instanceof
|
||||
HTMLCanvasElement||a instanceof HTMLVideoElement||window.ImageBitmap&&a instanceof window.ImageBitmap||window.OffscreenCanvas&&a instanceof window.OffscreenCanvas)return a;if(a instanceof Image)return await e._awaitImageLoad(a),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;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;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 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,g="environment"){this._legacyCanvasSize=e.DEFAULT_CANVAS_SIZE;this._flashOn=this._paused=this._active=!1;c=c||this._onDecodeError;d=d||this._calculateScanRegion;if(a.srcObject&&!(a.srcObject instanceof MediaStream))throw"Only MediaStreams are supported as video source.";
|
||||
this.$video=a;this.$canvas=document.createElement("canvas");this._onDecode=b;this._preferredCamera=g;"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;if(a)try{b=await navigator.mediaDevices.getUserMedia({audio:!1,video:!0})}catch(c){}try{return(await navigator.mediaDevices.enumerateDevices()).filter(c=>"videoinput"===c.kind).map((c,d)=>({id:c.deviceId,label:c.label||(0===d?"Default Camera":`Camera ${d+1}`)}))}finally{if(b)for(let c of b.getTracks())c.stop(),b.removeTrack(c)}}async hasFlash(){let a;try{return a=this.$video.srcObject||(await this._getCameraStream()).stream,"torch"in a.getVideoTracks()[0].getSettings()}catch(b){return!1}finally{if(a&&
|
||||
a!==this.$video.srcObject){console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream");for(let b of a.getTracks())b.stop(),a.removeTrack(b)}}}isFlashOn(){return this._flashOn}async toggleFlash(){this._flashOn?await this.turnFlashOff():await this.turnFlashOn()}async turnFlashOn(){if(!this._flashOn&&(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.stop();e._postWorkerMessage(this._qrEnginePromise,"close")}async start(){if(!this._active||this._paused)if("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)this.$video.play();else try{let {stream:a,facingMode:b}=await this._getCameraStream();this.$video.srcObject=a;this.$video.play();this._setVideoMirror(b);this._flashOn&&(this._flashOn=!1,this.turnFlashOn().catch(()=>{}))}catch(a){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=()=>{const c=this.$video.srcObject?this.$video.srcObject.getTracks():
|
||||
[];for(const d of c)d.stop(),this.$video.srcObject.removeTrack(d);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,g=!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,g);if(c instanceof Worker){let f=c;k||f.postMessage({type:"inversionMode",
|
||||
data:"both"});return await new Promise((q,u)=>{let r,p,n;p=m=>{"qrResult"===m.data.type&&(f.removeEventListener("message",p),f.removeEventListener("error",n),clearTimeout(r),null!==m.data.data?q(m.data.data):u(e.NO_QR_CODE_FOUND))};n=m=>{f.removeEventListener("message",p);f.removeEventListener("error",n);clearTimeout(r);u("Scanner error: "+(m?m.message||m:"Unknown Error"))};f.addEventListener("message",p);f.addEventListener("error",n);r=setTimeout(()=>n("timeout"),1E4);let v=t.getImageData(0,0,d.width,
|
||||
d.height);f.postMessage({type:"decode",data:v},[v.data.buffer])})}return await Promise.race([new Promise((f,q)=>window.setTimeout(()=>q("Scanner error: timeout"),1E4)),(async()=>{try{let [f]=await c.detect(d);if(!f)throw e.NO_QR_CODE_FOUND;return f.rawValue}catch(f){throw`Scanner error: ${f.message||f}`;}})()])}catch(l){if(!b||!h)throw l;return await e.scanImage(a,null,c,d,g)}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 g=await navigator.mediaDevices.getUserMedia({video:d,audio:!1}),h=this._getFacingMode(g)||(d.facingMode?this._preferredCamera:"environment"===this._preferredCamera?"user":"environment");return{stream:g,facingMode:h}}catch(g){}throw"Camera not found.";}async _restartVideoStream(){let a=this._paused;await this.pause(!0)&&!a&&this._active&&await 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,c,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.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,g,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=g=>{a.removeEventListener("load",d);a.removeEventListener("error",d);g 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
+11
-3
@@ -1,9 +1,10 @@
|
||||
import sourcemaps from 'rollup-plugin-sourcemaps';
|
||||
import typescript from '@rollup/plugin-typescript'; // config is a combination of tsconfig.json and overrides here
|
||||
import closureCompiler from '@ampproject/rollup-plugin-closure-compiler';
|
||||
|
||||
export default [{
|
||||
// library
|
||||
input: 'src/qr-scanner.js',
|
||||
input: 'src/qr-scanner.ts',
|
||||
output: [{
|
||||
file: 'qr-scanner.min.js',
|
||||
format: 'esm',
|
||||
@@ -17,6 +18,9 @@ export default [{
|
||||
sourcemap: true,
|
||||
}],
|
||||
plugins: [
|
||||
typescript({
|
||||
target: 'ES2017',
|
||||
}),
|
||||
closureCompiler({
|
||||
language_in: 'ECMASCRIPT_2017',
|
||||
language_out: 'ECMASCRIPT_2017',
|
||||
@@ -25,7 +29,7 @@ export default [{
|
||||
]
|
||||
}, {
|
||||
// library legacy build
|
||||
input: 'src/qr-scanner.js',
|
||||
input: 'src/qr-scanner.ts',
|
||||
output: [{
|
||||
file: 'qr-scanner.legacy.min.js',
|
||||
format: 'esm',
|
||||
@@ -33,6 +37,9 @@ export default [{
|
||||
sourcemap: true,
|
||||
}],
|
||||
plugins: [
|
||||
typescript({
|
||||
target: 'ES2017',
|
||||
}),
|
||||
closureCompiler({
|
||||
language_in: 'ECMASCRIPT_2017',
|
||||
language_out: 'ECMASCRIPT6',
|
||||
@@ -41,7 +48,7 @@ export default [{
|
||||
]
|
||||
}, {
|
||||
// worker
|
||||
input: 'src/worker.js',
|
||||
input: 'src/worker.ts',
|
||||
output: {
|
||||
file: 'qr-scanner-worker.min.js',
|
||||
format: 'iife',
|
||||
@@ -49,6 +56,7 @@ export default [{
|
||||
sourcemap: true,
|
||||
},
|
||||
plugins: [
|
||||
typescript(),
|
||||
sourcemaps(),
|
||||
closureCompiler({
|
||||
//compilation_level: 'ADVANCED',
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
export default class QrScanner {
|
||||
static async hasCamera() {
|
||||
static readonly DEFAULT_CANVAS_SIZE = 400;
|
||||
static readonly NO_QR_CODE_FOUND = 'No QR code found';
|
||||
static WORKER_PATH = 'qr-scanner-worker.min.js';
|
||||
|
||||
static async hasCamera(): Promise<boolean> {
|
||||
try {
|
||||
return !!(await QrScanner.listCameras(false)).length;
|
||||
} catch (e) {
|
||||
@@ -7,14 +11,14 @@ export default class QrScanner {
|
||||
}
|
||||
}
|
||||
|
||||
static async listCameras(requestLabels = false) {
|
||||
static async listCameras(requestLabels = false): Promise<Array<QrScanner.Camera>> {
|
||||
if (!navigator.mediaDevices) return [];
|
||||
|
||||
// Note that enumerateDevices can always be called and does not prompt the user for permission.
|
||||
// However, enumerateDevices only includes device labels if served via https and an active media stream exists
|
||||
// or permission to access the camera was given. Therefore, ask for camera permission by opening a stream, if
|
||||
// labels were requested.
|
||||
let openedStream = null;
|
||||
let openedStream: MediaStream | undefined;
|
||||
if (requestLabels) {
|
||||
try {
|
||||
openedStream = await navigator.mediaDevices.getUserMedia({ audio: false, video: true });
|
||||
@@ -40,21 +44,52 @@ export default class QrScanner {
|
||||
}
|
||||
}
|
||||
|
||||
$video: Omit<HTMLVideoElement, 'srcObject'> & { srcObject: MediaStream | null }; // we only set MediaStreams as src
|
||||
$canvas: HTMLCanvasElement;
|
||||
private readonly _onDecode: (result: string) => void;
|
||||
private _preferredCamera: QrScanner.FacingMode | QrScanner.DeviceId;
|
||||
private _scanRegion: QrScanner.ScanRegion;
|
||||
private _legacyCanvasSize: number = QrScanner.DEFAULT_CANVAS_SIZE;
|
||||
private _qrEnginePromise: Promise<Worker | BarcodeDetector>
|
||||
private _active: boolean = false;
|
||||
private _paused: boolean = false;
|
||||
private _flashOn: boolean = false;
|
||||
|
||||
constructor(
|
||||
video,
|
||||
onDecode,
|
||||
canvasSizeOrOnDecodeError = this._onDecodeError,
|
||||
canvasSizeOrCalculateScanRegion = this._calculateScanRegion,
|
||||
preferredCamera = 'environment'
|
||||
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,
|
||||
);
|
||||
/** @deprecated */
|
||||
constructor(video: HTMLVideoElement, onDecode: (result: string) => void, canvasSize?: number);
|
||||
constructor(
|
||||
video: HTMLVideoElement,
|
||||
onDecode: (result: string) => void,
|
||||
canvasSizeOrOnDecodeError?: number | ((error: Error | string) => void),
|
||||
canvasSizeOrCalculateScanRegion?: number | ((video: HTMLVideoElement) => QrScanner.ScanRegion),
|
||||
preferredCamera: QrScanner.FacingMode | QrScanner.DeviceId = 'environment',
|
||||
) {
|
||||
this.$video = video;
|
||||
canvasSizeOrOnDecodeError = canvasSizeOrOnDecodeError || this._onDecodeError;
|
||||
canvasSizeOrCalculateScanRegion = canvasSizeOrCalculateScanRegion || this._calculateScanRegion;
|
||||
|
||||
if (video.srcObject && !(video.srcObject instanceof MediaStream)) {
|
||||
throw 'Only MediaStreams are supported as video source.';
|
||||
}
|
||||
|
||||
this.$video = video as QrScanner['$video'];
|
||||
this.$canvas = document.createElement('canvas');
|
||||
this._onDecode = onDecode;
|
||||
this._legacyCanvasSize = QrScanner.DEFAULT_CANVAS_SIZE;
|
||||
this._preferredCamera = preferredCamera;
|
||||
this._active = false;
|
||||
this._paused = false;
|
||||
this._flashOn = false;
|
||||
|
||||
if (typeof canvasSizeOrOnDecodeError === 'number') {
|
||||
// legacy function signature where the third argument is the canvas size
|
||||
@@ -80,9 +115,11 @@ export default class QrScanner {
|
||||
this._onLoadedMetaData = this._onLoadedMetaData.bind(this);
|
||||
this._onVisibilityChange = this._onVisibilityChange.bind(this);
|
||||
|
||||
// @ts-ignore
|
||||
video.disablePictureInPicture = true;
|
||||
// Allow inline playback on iPhone instead of requiring full screen playback,
|
||||
// see https://webkit.org/blog/6784/new-video-policies-for-ios/
|
||||
// @ts-ignore
|
||||
video.playsInline = true;
|
||||
// Allow play() on iPhone without requiring a user gesture. Should not really be needed as camera stream
|
||||
// includes no audio, but just to be safe.
|
||||
@@ -113,9 +150,9 @@ export default class QrScanner {
|
||||
if (shouldHideVideo) {
|
||||
// Hide the video in a way that doesn't cause Safari to stop the playback.
|
||||
console.warn('QrScanner has overwritten the video hiding style to avoid Safari stopping the playback.');
|
||||
video.style.opacity = 0;
|
||||
video.style.width = 0;
|
||||
video.style.height = 0;
|
||||
video.style.opacity = '0';
|
||||
video.style.width = '0';
|
||||
video.style.height = '0';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -126,8 +163,8 @@ export default class QrScanner {
|
||||
this._qrEnginePromise = QrScanner.createQrEngine();
|
||||
}
|
||||
|
||||
async hasFlash() {
|
||||
let stream = null;
|
||||
async hasFlash(): Promise<boolean> {
|
||||
let stream: MediaStream | undefined;
|
||||
try {
|
||||
stream = this.$video.srcObject || (await this._getCameraStream()).stream;
|
||||
return 'torch' in stream.getVideoTracks()[0].getSettings();
|
||||
@@ -146,11 +183,11 @@ export default class QrScanner {
|
||||
}
|
||||
}
|
||||
|
||||
isFlashOn() {
|
||||
isFlashOn(): boolean {
|
||||
return this._flashOn;
|
||||
}
|
||||
|
||||
async toggleFlash() {
|
||||
async toggleFlash(): Promise<void> {
|
||||
if (this._flashOn) {
|
||||
await this.turnFlashOff();
|
||||
} else {
|
||||
@@ -158,14 +195,15 @@ export default class QrScanner {
|
||||
}
|
||||
}
|
||||
|
||||
async turnFlashOn() {
|
||||
async turnFlashOn(): Promise<void> {
|
||||
if (this._flashOn) return;
|
||||
this._flashOn = true;
|
||||
if (!this._active || this._paused) return; // flash will be turned on later on .start()
|
||||
try {
|
||||
if (!await this.hasFlash()) throw 'No flash available';
|
||||
// Note that the video track is guaranteed to exist at this point
|
||||
await this.$video.srcObject.getVideoTracks()[0].applyConstraints({
|
||||
await this.$video.srcObject!.getVideoTracks()[0].applyConstraints({
|
||||
// @ts-ignore: constraint 'torch' is unknown to ts
|
||||
advanced: [{ torch: true }],
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -174,7 +212,7 @@ export default class QrScanner {
|
||||
}
|
||||
}
|
||||
|
||||
async turnFlashOff() {
|
||||
async turnFlashOff(): Promise<void> {
|
||||
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,
|
||||
@@ -183,7 +221,7 @@ export default class QrScanner {
|
||||
await this._restartVideoStream();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
destroy(): void {
|
||||
this.$video.removeEventListener('loadedmetadata', this._onLoadedMetaData);
|
||||
this.$video.removeEventListener('play', this._onPlay);
|
||||
document.removeEventListener('visibilitychange', this._onVisibilityChange);
|
||||
@@ -192,7 +230,7 @@ export default class QrScanner {
|
||||
QrScanner._postWorkerMessage(this._qrEnginePromise, 'close');
|
||||
}
|
||||
|
||||
async start() {
|
||||
async start(): Promise<void> {
|
||||
if (this._active && !this._paused) return;
|
||||
|
||||
if (window.location.protocol !== 'https:') {
|
||||
@@ -226,12 +264,12 @@ export default class QrScanner {
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
stop(): void {
|
||||
this.pause();
|
||||
this._active = false;
|
||||
}
|
||||
|
||||
async pause(stopStreamImmediately = false) {
|
||||
async pause(stopStreamImmediately = false): Promise<boolean> {
|
||||
this._paused = true;
|
||||
if (!this._active) return true;
|
||||
this.$video.pause();
|
||||
@@ -240,7 +278,7 @@ export default class QrScanner {
|
||||
const tracks = this.$video.srcObject ? this.$video.srcObject.getTracks() : [];
|
||||
for (const track of tracks) {
|
||||
track.stop(); // note that this will also automatically turn the flashlight off
|
||||
this.$video.srcObject.removeTrack(track);
|
||||
this.$video.srcObject!.removeTrack(track);
|
||||
}
|
||||
this.$video.srcObject = null;
|
||||
};
|
||||
@@ -256,7 +294,7 @@ export default class QrScanner {
|
||||
return true;
|
||||
}
|
||||
|
||||
async setCamera(facingModeOrDeviceId) {
|
||||
async setCamera(facingModeOrDeviceId: QrScanner.FacingMode | QrScanner.DeviceId): Promise<void> {
|
||||
if (facingModeOrDeviceId === this._preferredCamera) return;
|
||||
this._preferredCamera = facingModeOrDeviceId;
|
||||
// Restart the scanner with the new camera which will also update the video mirror and the scan region.
|
||||
@@ -264,37 +302,42 @@ export default class QrScanner {
|
||||
}
|
||||
|
||||
static async scanImage(
|
||||
imageOrFileOrUrl,
|
||||
scanRegion = null,
|
||||
qrEngine = null,
|
||||
canvas = null,
|
||||
disallowCanvasResizing = false,
|
||||
alsoTryWithoutScanRegion = false
|
||||
) {
|
||||
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 = false,
|
||||
alsoTryWithoutScanRegion: boolean = false,
|
||||
): Promise<string> {
|
||||
const gotExternalEngine = !!qrEngine;
|
||||
|
||||
try {
|
||||
let image;
|
||||
let canvasContext;
|
||||
let image: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap
|
||||
| SVGImageElement;
|
||||
let canvasContext: CanvasRenderingContext2D;
|
||||
[qrEngine, image] = await Promise.all([
|
||||
qrEngine || QrScanner.createQrEngine(),
|
||||
QrScanner._loadImage(imageOrFileOrUrl),
|
||||
QrScanner._loadImage(imageOrFileOrBlobOrUrl),
|
||||
]);
|
||||
[canvas, canvasContext] = this._drawToCanvas(image, scanRegion, canvas, disallowCanvasResizing);
|
||||
[canvas, canvasContext] = QrScanner._drawToCanvas(image, scanRegion, canvas, disallowCanvasResizing);
|
||||
|
||||
if (qrEngine instanceof Worker) {
|
||||
const qrEngineWorker = qrEngine; // for ts to know that it's still a worker later in the event listeners
|
||||
if (!gotExternalEngine) {
|
||||
// Enable scanning of inverted color qr codes. Not using _postWorkerMessage as it's async
|
||||
qrEngine.postMessage({ type: 'inversionMode', data: 'both' });
|
||||
qrEngineWorker.postMessage({ type: 'inversionMode', data: 'both' });
|
||||
}
|
||||
return await new Promise((resolve, reject) => {
|
||||
let timeout, onMessage, onError;
|
||||
onMessage = event => {
|
||||
let timeout: number;
|
||||
let onMessage: (event: MessageEvent) => void;
|
||||
let onError: (error: ErrorEvent | string) => void;
|
||||
onMessage = (event: MessageEvent) => {
|
||||
if (event.data.type !== 'qrResult') {
|
||||
return;
|
||||
}
|
||||
qrEngine.removeEventListener('message', onMessage);
|
||||
qrEngine.removeEventListener('error', onError);
|
||||
qrEngineWorker.removeEventListener('message', onMessage);
|
||||
qrEngineWorker.removeEventListener('error', onError);
|
||||
clearTimeout(timeout);
|
||||
if (event.data.data !== null) {
|
||||
resolve(event.data.data);
|
||||
@@ -302,47 +345,50 @@ export default class QrScanner {
|
||||
reject(QrScanner.NO_QR_CODE_FOUND);
|
||||
}
|
||||
};
|
||||
onError = (e) => {
|
||||
qrEngine.removeEventListener('message', onMessage);
|
||||
qrEngine.removeEventListener('error', onError);
|
||||
onError = (error: ErrorEvent | string) => {
|
||||
qrEngineWorker.removeEventListener('message', onMessage);
|
||||
qrEngineWorker.removeEventListener('error', onError);
|
||||
clearTimeout(timeout);
|
||||
const errorMessage = !e ? 'Unknown Error' : (e.message || e);
|
||||
const errorMessage = !error ? 'Unknown Error' : ((error as ErrorEvent).message || error);
|
||||
reject('Scanner error: ' + errorMessage);
|
||||
};
|
||||
qrEngine.addEventListener('message', onMessage);
|
||||
qrEngine.addEventListener('error', onError);
|
||||
qrEngineWorker.addEventListener('message', onMessage);
|
||||
qrEngineWorker.addEventListener('error', onError);
|
||||
timeout = setTimeout(() => onError('timeout'), 10000);
|
||||
const imageData = canvasContext.getImageData(0, 0, canvas.width, canvas.height);
|
||||
qrEngine.postMessage({
|
||||
const imageData = canvasContext.getImageData(0, 0, canvas!.width, canvas!.height);
|
||||
qrEngineWorker.postMessage({
|
||||
type: 'decode',
|
||||
data: imageData
|
||||
}, [imageData.data.buffer]);
|
||||
});
|
||||
} else {
|
||||
return await Promise.race([
|
||||
new Promise((resolve, reject) => window.setTimeout(() => reject('Scanner error: timeout'), 10000)),
|
||||
(async () => {
|
||||
new Promise<string>((resolve, reject) => window.setTimeout(
|
||||
() => reject('Scanner error: timeout'),
|
||||
10000,
|
||||
)),
|
||||
(async (): Promise<string> => {
|
||||
try {
|
||||
const [scanResult] = await qrEngine.detect(canvas);
|
||||
const [scanResult] = await qrEngine.detect(canvas!);
|
||||
if (!scanResult) throw QrScanner.NO_QR_CODE_FOUND;
|
||||
return scanResult.rawValue;
|
||||
} catch (e) {
|
||||
throw `Scanner error: ${e instanceof Error ? e.message : e}`;
|
||||
throw `Scanner error: ${(e as Error).message || e}`;
|
||||
}
|
||||
})(),
|
||||
]);
|
||||
}
|
||||
} catch (e) {
|
||||
if (!scanRegion || !alsoTryWithoutScanRegion) throw e;
|
||||
return await QrScanner.scanImage(imageOrFileOrUrl, null, qrEngine, canvas, disallowCanvasResizing);
|
||||
return await QrScanner.scanImage(imageOrFileOrBlobOrUrl, null, qrEngine, canvas, disallowCanvasResizing);
|
||||
} finally {
|
||||
if (!gotExternalEngine) {
|
||||
QrScanner._postWorkerMessage(qrEngine, 'close');
|
||||
QrScanner._postWorkerMessage(qrEngine!, 'close');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setGrayscaleWeights(red, green, blue, useIntegerApproximation = true) {
|
||||
setGrayscaleWeights(red: number, green: number, blue: number, useIntegerApproximation: boolean = true): void {
|
||||
// Note that for the native BarcodeDecoder, this is a no-op. However, the native implementations work also
|
||||
// well with colored qr codes.
|
||||
QrScanner._postWorkerMessage(
|
||||
@@ -352,29 +398,29 @@ export default class QrScanner {
|
||||
);
|
||||
}
|
||||
|
||||
setInversionMode(inversionMode) {
|
||||
setInversionMode(inversionMode: QrScanner.InversionMode): void {
|
||||
// Note that for the native BarcodeDecoder, this is a no-op. However, the native implementations scan normal
|
||||
// and inverted qr codes by default
|
||||
QrScanner._postWorkerMessage(this._qrEnginePromise, 'inversionMode', inversionMode);
|
||||
}
|
||||
|
||||
static async createQrEngine(workerPath = QrScanner.WORKER_PATH) {
|
||||
static async createQrEngine(workerPath: string = QrScanner.WORKER_PATH): Promise<Worker | BarcodeDetector> {
|
||||
const hasNativeBarcodeDetector = 'BarcodeDetector' in window && BarcodeDetector.getSupportedFormats
|
||||
? (await BarcodeDetector.getSupportedFormats()).includes('qr_code')
|
||||
: false;
|
||||
return hasNativeBarcodeDetector ? new BarcodeDetector({ formats: ['qr_code'] }) : new Worker(workerPath);
|
||||
}
|
||||
|
||||
_onPlay() {
|
||||
private _onPlay(): void {
|
||||
this._scanRegion = this._calculateScanRegion(this.$video);
|
||||
this._scanFrame();
|
||||
}
|
||||
|
||||
_onLoadedMetaData() {
|
||||
private _onLoadedMetaData(): void {
|
||||
this._scanRegion = this._calculateScanRegion(this.$video);
|
||||
}
|
||||
|
||||
_onVisibilityChange() {
|
||||
private _onVisibilityChange(): void {
|
||||
if (document.hidden) {
|
||||
this.pause();
|
||||
} else if (this._active) {
|
||||
@@ -382,7 +428,7 @@ export default class QrScanner {
|
||||
}
|
||||
}
|
||||
|
||||
_calculateScanRegion(video) {
|
||||
private _calculateScanRegion(video: HTMLVideoElement): QrScanner.ScanRegion {
|
||||
// Default scan region calculation. Note that this can be overwritten in the constructor.
|
||||
const smallestDimension = Math.min(video.videoWidth, video.videoHeight);
|
||||
const scanRegionSize = Math.round(2 / 3 * smallestDimension);
|
||||
@@ -396,7 +442,7 @@ export default class QrScanner {
|
||||
};
|
||||
}
|
||||
|
||||
_scanFrame() {
|
||||
private _scanFrame(): void {
|
||||
if (!this._active || this.$video.paused || this.$video.ended) return;
|
||||
// using requestAnimationFrame to avoid scanning if tab is in background
|
||||
requestAnimationFrame(async () => {
|
||||
@@ -409,22 +455,22 @@ export default class QrScanner {
|
||||
return;
|
||||
}
|
||||
|
||||
let result;
|
||||
let result: string | undefined;
|
||||
try {
|
||||
result = await QrScanner.scanImage(
|
||||
this.$video,
|
||||
this._scanRegion,
|
||||
this._qrEnginePromise,
|
||||
this.$canvas
|
||||
this.$canvas,
|
||||
);
|
||||
} catch (error) {
|
||||
if (!this._active) return;
|
||||
const errorMessage = error.message || error;
|
||||
const errorMessage = (error as Error).message || error as string;
|
||||
if (errorMessage.includes('service unavailable')) {
|
||||
// When the native BarcodeDetector crashed, create a new one
|
||||
this._qrEnginePromise = QrScanner.createQrEngine();
|
||||
}
|
||||
this._onDecodeError(error);
|
||||
this._onDecodeError(error as Error | string);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
@@ -435,19 +481,19 @@ export default class QrScanner {
|
||||
});
|
||||
}
|
||||
|
||||
_onDecodeError(error) {
|
||||
private _onDecodeError(error: Error | string): void {
|
||||
// default error handler; can be overwritten in the constructor
|
||||
if (error === QrScanner.NO_QR_CODE_FOUND) return;
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
async _getCameraStream() {
|
||||
private async _getCameraStream(): Promise<{ stream: MediaStream, facingMode: QrScanner.FacingMode }> {
|
||||
if (!navigator.mediaDevices) throw 'Camera not found.';
|
||||
|
||||
const preferenceType = this._preferredCamera === 'environment' || this._preferredCamera === 'user'
|
||||
const preferenceType = /^(environment|user)$/.test(this._preferredCamera)
|
||||
? 'facingMode'
|
||||
: 'deviceId';
|
||||
const constraintsWithoutCamera = [{
|
||||
const constraintsWithoutCamera: Array<MediaTrackConstraints> = [{
|
||||
width: { min: 1024 }
|
||||
}, {
|
||||
width: { min: 768 }
|
||||
@@ -464,7 +510,7 @@ export default class QrScanner {
|
||||
// mode, even for exact facingMode constraints.
|
||||
const facingMode = this._getFacingMode(stream)
|
||||
|| (constraints.facingMode
|
||||
? this._preferredCamera // _preferredCamera is a facing mode and we are able to fulfill it
|
||||
? this._preferredCamera as QrScanner.FacingMode // a facing mode we were able to fulfill
|
||||
: (this._preferredCamera === 'environment'
|
||||
? 'user' // switch as _preferredCamera was environment but we are not able to fulfill it
|
||||
: 'environment' // switch from unfulfilled user facingMode or default to environment
|
||||
@@ -477,7 +523,7 @@ export default class QrScanner {
|
||||
throw 'Camera not found.';
|
||||
}
|
||||
|
||||
async _restartVideoStream() {
|
||||
private async _restartVideoStream(): Promise<void> {
|
||||
// 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;
|
||||
@@ -486,13 +532,13 @@ export default class QrScanner {
|
||||
await this.start();
|
||||
}
|
||||
|
||||
_setVideoMirror(facingMode) {
|
||||
private _setVideoMirror(facingMode: QrScanner.FacingMode): void {
|
||||
// in user facing mode mirror the video to make it easier for the user to position the QR code
|
||||
const scaleFactor = facingMode==='user'? -1 : 1;
|
||||
const scaleFactor = facingMode === 'user'? -1 : 1;
|
||||
this.$video.style.transform = 'scaleX(' + scaleFactor + ')';
|
||||
}
|
||||
|
||||
_getFacingMode(videoStream) {
|
||||
private _getFacingMode(videoStream: MediaStream): QrScanner.FacingMode | null {
|
||||
const videoTrack = videoStream.getVideoTracks()[0];
|
||||
if (!videoTrack) return null; // unknown
|
||||
// inspired by https://github.com/JodusNodus/react-qr-reader/blob/master/src/getDeviceId.js#L13
|
||||
@@ -503,12 +549,22 @@ export default class QrScanner {
|
||||
: null; // unknown
|
||||
}
|
||||
|
||||
static _drawToCanvas(image, scanRegion=null, canvas=null, disallowCanvasResizing=false) {
|
||||
private static _drawToCanvas(
|
||||
image: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap
|
||||
| SVGImageElement,
|
||||
scanRegion?: QrScanner.ScanRegion | null,
|
||||
canvas?: HTMLCanvasElement | null,
|
||||
disallowCanvasResizing= false,
|
||||
): [HTMLCanvasElement, CanvasRenderingContext2D] {
|
||||
canvas = canvas || document.createElement('canvas');
|
||||
const scanRegionX = scanRegion && scanRegion.x? scanRegion.x : 0;
|
||||
const scanRegionY = scanRegion && scanRegion.y? scanRegion.y : 0;
|
||||
const scanRegionWidth = scanRegion && scanRegion.width? scanRegion.width : image.width || image.videoWidth;
|
||||
const scanRegionHeight = scanRegion && scanRegion.height? scanRegion.height : image.height || image.videoHeight;
|
||||
const scanRegionX = scanRegion && scanRegion.x ? scanRegion.x : 0;
|
||||
const scanRegionY = scanRegion && scanRegion.y ? scanRegion.y : 0;
|
||||
const scanRegionWidth = scanRegion && scanRegion.width
|
||||
? scanRegion.width
|
||||
: (image as HTMLVideoElement).videoWidth || image.width as number;
|
||||
const scanRegionHeight = scanRegion && scanRegion.height
|
||||
? scanRegion.height
|
||||
: (image as HTMLVideoElement).videoHeight || image.height as number;
|
||||
|
||||
if (!disallowCanvasResizing) {
|
||||
const canvasWidth = scanRegion && scanRegion.downScaledWidth
|
||||
@@ -527,31 +583,37 @@ export default class QrScanner {
|
||||
}
|
||||
}
|
||||
|
||||
const context = canvas.getContext('2d', { alpha: false });
|
||||
const context = canvas.getContext('2d', { alpha: false })!;
|
||||
context.imageSmoothingEnabled = false; // gives less blurry images
|
||||
context.drawImage(
|
||||
image,
|
||||
scanRegionX, scanRegionY, scanRegionWidth, scanRegionHeight,
|
||||
0, 0, canvas.width, canvas.height
|
||||
0, 0, canvas.width, canvas.height,
|
||||
);
|
||||
return [canvas, context];
|
||||
}
|
||||
|
||||
static async _loadImage(imageOrFileOrBlobOrUrl) {
|
||||
if (imageOrFileOrBlobOrUrl instanceof HTMLCanvasElement || imageOrFileOrBlobOrUrl instanceof HTMLVideoElement
|
||||
|| window.ImageBitmap && imageOrFileOrBlobOrUrl instanceof window.ImageBitmap
|
||||
|| window.OffscreenCanvas && imageOrFileOrBlobOrUrl instanceof window.OffscreenCanvas) {
|
||||
return imageOrFileOrBlobOrUrl;
|
||||
} else if (imageOrFileOrBlobOrUrl instanceof Image) {
|
||||
private static async _loadImage(
|
||||
imageOrFileOrBlobOrUrl: HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap
|
||||
| SVGImageElement | File | Blob | URL | String,
|
||||
): Promise<HTMLImageElement | HTMLVideoElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap
|
||||
| SVGImageElement > {
|
||||
if (imageOrFileOrBlobOrUrl instanceof Image) {
|
||||
await QrScanner._awaitImageLoad(imageOrFileOrBlobOrUrl);
|
||||
return imageOrFileOrBlobOrUrl;
|
||||
} else if (imageOrFileOrBlobOrUrl instanceof HTMLVideoElement
|
||||
|| imageOrFileOrBlobOrUrl instanceof HTMLCanvasElement
|
||||
|| imageOrFileOrBlobOrUrl instanceof SVGImageElement
|
||||
|| 'OffscreenCanvas' in window && imageOrFileOrBlobOrUrl instanceof OffscreenCanvas
|
||||
|| 'ImageBitmap' in window && imageOrFileOrBlobOrUrl instanceof ImageBitmap) {
|
||||
return imageOrFileOrBlobOrUrl;
|
||||
} else if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob
|
||||
|| imageOrFileOrBlobOrUrl instanceof URL || typeof(imageOrFileOrBlobOrUrl)==='string') {
|
||||
|| imageOrFileOrBlobOrUrl instanceof URL || typeof imageOrFileOrBlobOrUrl === 'string') {
|
||||
const image = new Image();
|
||||
if (imageOrFileOrBlobOrUrl instanceof File || imageOrFileOrBlobOrUrl instanceof Blob) {
|
||||
image.src = URL.createObjectURL(imageOrFileOrBlobOrUrl);
|
||||
} else {
|
||||
image.src = imageOrFileOrBlobOrUrl;
|
||||
image.src = imageOrFileOrBlobOrUrl.toString();
|
||||
}
|
||||
try {
|
||||
await QrScanner._awaitImageLoad(image);
|
||||
@@ -566,31 +628,59 @@ export default class QrScanner {
|
||||
}
|
||||
}
|
||||
|
||||
static async _awaitImageLoad(image) {
|
||||
private static async _awaitImageLoad(image: HTMLImageElement): Promise<void> {
|
||||
if (image.complete && image.naturalWidth !== 0) return; // already loaded
|
||||
await new Promise((resolve, reject) => {
|
||||
let onLoad, onError;
|
||||
onLoad = () => {
|
||||
image.removeEventListener('load', onLoad);
|
||||
image.removeEventListener('error', onError);
|
||||
resolve();
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const listener = (event: ErrorEvent | Event) => {
|
||||
image.removeEventListener('load', listener);
|
||||
image.removeEventListener('error', listener);
|
||||
if (event instanceof ErrorEvent) {
|
||||
reject('Image load error');
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
onError = () => {
|
||||
image.removeEventListener('load', onLoad);
|
||||
image.removeEventListener('error', onError);
|
||||
reject('Image load error');
|
||||
};
|
||||
image.addEventListener('load', onLoad);
|
||||
image.addEventListener('error', onError);
|
||||
image.addEventListener('load', listener);
|
||||
image.addEventListener('error', listener);
|
||||
});
|
||||
}
|
||||
|
||||
static async _postWorkerMessage(qrEngineOrQrEnginePromise, type, data) {
|
||||
private static async _postWorkerMessage(
|
||||
qrEngineOrQrEnginePromise: Worker | BarcodeDetector | Promise<Worker | BarcodeDetector>,
|
||||
type: string,
|
||||
data?: any,
|
||||
): Promise<void> {
|
||||
const qrEngine = await qrEngineOrQrEnginePromise;
|
||||
if (!(qrEngine instanceof Worker)) return;
|
||||
qrEngine.postMessage({ type, data });
|
||||
}
|
||||
}
|
||||
QrScanner.DEFAULT_CANVAS_SIZE = 400;
|
||||
QrScanner.NO_QR_CODE_FOUND = 'No QR code found';
|
||||
QrScanner.WORKER_PATH = 'qr-scanner-worker.min.js';
|
||||
|
||||
declare namespace QrScanner {
|
||||
export interface ScanRegion {
|
||||
x?: number;
|
||||
y?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
downScaledWidth?: number;
|
||||
downScaledHeight?: number;
|
||||
}
|
||||
|
||||
export type FacingMode = 'environment' | 'user';
|
||||
export type DeviceId = string;
|
||||
|
||||
export interface Camera {
|
||||
id: DeviceId;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export type InversionMode = 'original' | 'invert' | 'both';
|
||||
}
|
||||
|
||||
// simplified from https://wicg.github.io/shape-detection-api/#barcode-detection-api
|
||||
declare class BarcodeDetector {
|
||||
constructor(options?: { formats: string[] });
|
||||
static getSupportedFormats(): Promise<string[]>;
|
||||
detect(image: ImageBitmapSource): Promise<Array<{ rawValue: string }>>;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
// @ts-ignore jsqr-es6 does not provide types currently
|
||||
import jsQR from '../node_modules/jsqr-es6/dist/jsQR.js';
|
||||
|
||||
let inversionAttempts = 'dontInvert';
|
||||
let grayscaleWeights = {
|
||||
type GrayscaleWeights = {
|
||||
red: number,
|
||||
green: number,
|
||||
blue: number,
|
||||
useIntegerApproximation: boolean,
|
||||
};
|
||||
|
||||
let inversionAttempts: 'dontInvert' | 'onlyInvert' | 'attemptBoth' = 'dontInvert';
|
||||
let grayscaleWeights: GrayscaleWeights = {
|
||||
// weights for quick luma integer approximation (https://en.wikipedia.org/wiki/YUV#Full_swing_for_BT.601)
|
||||
red: 77,
|
||||
green: 150,
|
||||
@@ -30,7 +38,7 @@ self.onmessage = event => {
|
||||
}
|
||||
};
|
||||
|
||||
function decode(data) {
|
||||
function decode(data: { data: Uint8ClampedArray, width: number, height: number }): void {
|
||||
const rgbaData = data['data'];
|
||||
const width = data['width'];
|
||||
const height = data['height'];
|
||||
@@ -38,13 +46,13 @@ function decode(data) {
|
||||
inversionAttempts: inversionAttempts,
|
||||
greyScaleWeights: grayscaleWeights,
|
||||
});
|
||||
self.postMessage({
|
||||
(self as unknown as Worker).postMessage({
|
||||
type: 'qrResult',
|
||||
data: result? result.data : null,
|
||||
});
|
||||
}
|
||||
|
||||
function setGrayscaleWeights(data) {
|
||||
function setGrayscaleWeights(data: GrayscaleWeights) {
|
||||
// update grayscaleWeights in a closure compiler compatible fashion
|
||||
grayscaleWeights.red = data['red'];
|
||||
grayscaleWeights.green = data['green'];
|
||||
@@ -52,7 +60,7 @@ function setGrayscaleWeights(data) {
|
||||
grayscaleWeights.useIntegerApproximation = data['useIntegerApproximation'];
|
||||
}
|
||||
|
||||
function setInversionMode(inversionMode) {
|
||||
function setInversionMode(inversionMode: 'original' | 'invert' | 'both') {
|
||||
switch (inversionMode) {
|
||||
case 'original':
|
||||
inversionAttempts = 'dontInvert';
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noEmitOnError": true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user