vue-qrcode-reader
Advanced tools
Comparing version 1.4.0 to 1.4.1
module.exports = { | ||
root: true, | ||
parser: 'babel-eslint', | ||
parser: "babel-eslint", | ||
parserOptions: { | ||
sourceType: 'module' | ||
sourceType: "module" | ||
}, | ||
// https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style | ||
extends: 'standard', | ||
extends: "prettier", | ||
// required to lint *.vue files | ||
plugins: [ | ||
'html' | ||
], | ||
plugins: ["html"], | ||
env: { | ||
browser: true, | ||
}, | ||
browser: true | ||
} | ||
// add your custom rules here | ||
'rules': { | ||
// allow paren-less arrow functions | ||
'arrow-parens': 0, | ||
// allow async-await | ||
'generator-star-spacing': 0, | ||
// allow debugger during development | ||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, | ||
// trailing comma | ||
'comma-dangle': ['error', 'always-multiline'], | ||
} | ||
} | ||
// 'rules': { | ||
// // allow paren-less arrow functions | ||
// 'arrow-parens': 0, | ||
// // allow async-await | ||
// 'generator-star-spacing': 0, | ||
// // allow debugger during development | ||
// 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, | ||
// // trailing comma | ||
// 'comma-dangle': ['error', 'always-multiline'], | ||
// } | ||
}; |
@@ -1,46 +0,49 @@ | ||
var webpack = require('webpack') | ||
var ExtractTextPlugin = require('extract-text-webpack-plugin') | ||
var webpack = require("webpack"); | ||
var ExtractTextPlugin = require("extract-text-webpack-plugin"); | ||
var outputFile = 'vue-qrcode-reader' | ||
var globalName = 'VueQrcodeReader' | ||
var outputFile = "vue-qrcode-reader"; | ||
var globalName = "VueQrcodeReader"; | ||
var config = require('../package.json') | ||
var config = require("../package.json"); | ||
module.exports = { | ||
entry: './src/index.js', | ||
entry: "./src/index.js", | ||
module: { | ||
rules: [ | ||
{ | ||
enforce: 'pre', | ||
enforce: "pre", | ||
test: /\.(js|vue)$/, | ||
loader: 'eslint-loader', | ||
exclude: /node_modules/, | ||
loader: "eslint-loader", | ||
exclude: /node_modules/ | ||
}, | ||
{ | ||
test: /worker\.js$/, | ||
loader: "worker-loader", | ||
options: { | ||
inline: true, | ||
fallback: false | ||
} | ||
}, | ||
{ | ||
test: /.js$/, | ||
use: 'babel-loader', | ||
exclude: /node_modules/, | ||
use: "babel-loader", | ||
exclude: /node_modules/ | ||
}, | ||
{ | ||
test: /\.vue$/, | ||
loader: 'vue-loader', | ||
loader: "vue-loader", | ||
options: { | ||
loaders: { | ||
css: ExtractTextPlugin.extract({ use: { | ||
loader: 'css-loader', | ||
options: { minimize: true }, | ||
}}), | ||
sass: ExtractTextPlugin.extract('css-loader!sass-loader'), | ||
scss: ExtractTextPlugin.extract('css-loader!sass-loader'), | ||
}, | ||
}, | ||
}, | ||
], | ||
css: ExtractTextPlugin.extract({ | ||
use: { | ||
loader: "css-loader", | ||
options: { minimize: true } | ||
} | ||
}) | ||
} | ||
} | ||
} | ||
] | ||
}, | ||
plugins: [ | ||
new webpack.DefinePlugin({ | ||
'VERSION': JSON.stringify(config.version), | ||
}), | ||
new ExtractTextPlugin(outputFile + '.css'), | ||
], | ||
} | ||
plugins: [new ExtractTextPlugin(outputFile + ".css")] | ||
}; |
{ | ||
"name": "vue-qrcode-reader", | ||
"description": "A set of Vue.js components for detecting and decoding QR codes.", | ||
"version": "1.4.0", | ||
"version": "1.4.1", | ||
"author": { | ||
@@ -27,6 +27,3 @@ "name": "Niklas Gruhn", | ||
"build:common": "cross-env NODE_ENV=production webpack --config config/webpack.config.common.js --progress --hide-modules", | ||
"unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", | ||
"e2e": "node test/e2e/runner.js", | ||
"test": "npm run unit && npm run e2e", | ||
"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs" | ||
"lint": "eslint --ext .js,.vue src" | ||
}, | ||
@@ -41,2 +38,13 @@ "repository": { | ||
"homepage": "https://github.com/gruhn/vue-qrcode-reader#readme", | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "lint-staged" | ||
} | ||
}, | ||
"lint-staged": { | ||
"*.{js,json,css,md,vue}": [ | ||
"prettier --write", | ||
"git add" | ||
] | ||
}, | ||
"devDependencies": { | ||
@@ -50,4 +58,2 @@ "babel-core": "^6.0.0", | ||
"babel-preset-stage-0": "^6.16.0", | ||
"chai": "^3.5.0", | ||
"chromedriver": "^2.27.2", | ||
"cross-env": "^3.1.3", | ||
@@ -57,2 +63,3 @@ "cross-spawn": "^5.0.1", | ||
"eslint": "^3.12.1", | ||
"eslint-config-prettier": "^4.1.0", | ||
"eslint-config-standard": "^6.2.1", | ||
@@ -64,24 +71,10 @@ "eslint-loader": "^1.6.1", | ||
"extract-text-webpack-plugin": "^2.0.0-beta.4", | ||
"husky": "^1.3.1", | ||
"inject-loader": "^3.0.0", | ||
"karma": "^1.7.1", | ||
"karma-coverage": "^1.1.1", | ||
"karma-mocha": "^1.3.0", | ||
"karma-phantomjs-launcher": "^1.0.2", | ||
"karma-phantomjs-shim": "^1.4.0", | ||
"karma-sinon-chai": "^1.3.1", | ||
"karma-sourcemap-loader": "^0.3.7", | ||
"karma-spec-reporter": "0.0.31", | ||
"karma-webpack": "^2.0.2", | ||
"mocha": "^3.2.0", | ||
"nightwatch": "^0.9.12", | ||
"node-sass": "^4.0.0", | ||
"phantomjs-prebuilt": "^2.1.14", | ||
"sass-loader": "^4.1.0", | ||
"selenium-server": "^3.0.1", | ||
"lint-staged": "^8.1.4", | ||
"prettier": "1.16.4", | ||
"semantic-release": "^15.9.9", | ||
"sinon": "^2.1.0", | ||
"sinon-chai": "^2.8.0", | ||
"vue-loader": "^10.0.0", | ||
"vue-template-compiler": "^2.1.6", | ||
"webpack": "^2.1.0-beta.28", | ||
"webpack": "^3.0.0", | ||
"webpack-merge": "^1.1.2", | ||
@@ -88,0 +81,0 @@ "worker-loader": "^2.0.0" |
@@ -1,7 +0,7 @@ | ||
<p align="center"> | ||
<p align="center"> | ||
<img src="https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/logo.png" alt="Logo" width="240" height="240" style="max-width: 100%;"> | ||
<br> | ||
<br> | ||
<a href="https://vuejs.org/"> | ||
@@ -14,7 +14,7 @@ <img src="https://img.shields.io/badge/vue-2.x-brightgreen.svg" alt="for Vue.js 2"> | ||
</a> | ||
<a href="https://travis-ci.org/gruhn/vue-qrcode-reader"> | ||
<img src="https://travis-ci.org/gruhn/vue-qrcode-reader.svg?branch=master" alt="Travis CI: build status"> | ||
</a> | ||
<br> | ||
@@ -31,21 +31,25 @@ | ||
</a> | ||
<br> | ||
<a href="https://github.com/semantic-release/semantic-release"> | ||
<img src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg" alt="uses semantic release"> | ||
</a> | ||
<a href="https://github.com/prettier/prettier"> | ||
<img src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square" alt="code style: prettier"> | ||
</a> | ||
<br> | ||
<a href="https://bundlephobia.com/result?p=vue-qrcode-reader"> | ||
<img src="https://badgen.net/bundlephobia/minzip/vue-qrcode-reader" alt="size minified + gzipped"> | ||
</a> | ||
<a href="https://www.npmjs.com/package/vue-qrcode-reader"> | ||
<img src="https://img.shields.io/npm/v/vue-qrcode-reader.svg" alt="npm current version"> | ||
</a> | ||
<br> | ||
<a href="https://github.com/semantic-release/semantic-release"> | ||
<img src="https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg" alt="uses semantic release"> | ||
</a> | ||
<br> | ||
<br> | ||
<a href="https://gruhn.github.io/vue-qrcode-reader/api/QrcodeStream.html">documentation</a> | | ||
@@ -57,5 +61,5 @@ <a href="https://gruhn.github.io/vue-qrcode-reader/demos/DecodeAll.html">live demos</a> | ||
* :movie_camera: `QrcodeStream` accesses the device camera and continuously scans incoming frames. | ||
* :put_litter_in_its_place: `QrcodeDropZone` renders to an empty region where you can drag-and-drop images to be decoded. | ||
* :open_file_folder: `QrcodeCapture` is a classic file upload field, instantly scanning all files you select. | ||
- :movie_camera: `QrcodeStream` accesses the device camera and continuously scans incoming frames. | ||
- :put_litter_in_its_place: `QrcodeDropZone` renders to an empty region where you can drag-and-drop images to be decoded. | ||
- :open_file_folder: `QrcodeCapture` is a classic file upload field, instantly scanning all files you select. | ||
@@ -67,2 +71,3 @@ All components are responsive. Beyond that, close to zero styling. Make them fit your layout. Usage is simple and straight forward: | ||
``` | ||
```js | ||
@@ -75,2 +80,3 @@ methods: { | ||
``` | ||
### Screenshots | ||
@@ -91,11 +97,11 @@ | ||
| ![Internet Explorer](https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/ie_32x32.png) | ![Edge](https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/edge_32x32.png) | ![Firefox](https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/firefox_32x32.png) | ![Chrome](https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/chrome_32x32.png) | ![Safari](https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/safari_32x32.png) | | ||
|:--:|:---:|:---:|:---:|:---:| | ||
| No | Yes | Yes | Yes | 11+ | | ||
| :---------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------: | | ||
| No | Yes | Yes | Yes | 11+ | | ||
* Chrome requires [HTTPS or localhost](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins) (see _Troubleshooting_ for help) | ||
* Safari also requires HTTPS **even** on localhost (see [#48](../../issues/48)) | ||
* on iOS it **only** works with Safari | ||
* *Chrome for iOS*, *Firefox for iOS* and so on are not supported (see [#29](../../issues/29)) | ||
* even web apps added to home screen are not supported (see [#76](../../issues/76)) | ||
- Chrome requires [HTTPS or localhost](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins) (see _Troubleshooting_ for help) | ||
- Safari also requires HTTPS **even** on localhost (see [#48](../../issues/48)) | ||
- on iOS it **only** works with Safari | ||
- _Chrome for iOS_, _Firefox for iOS_ and so on are not supported (see [#29](../../issues/29)) | ||
- even web apps added to home screen are not supported (see [#76](../../issues/76)) | ||
#### `QrcodeDropZone` and `QrcodeCapture` | ||
@@ -106,16 +112,16 @@ | ||
| ![Internet Explorer](https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/ie_32x32.png) | ![Edge](https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/edge_32x32.png) | ![Firefox](https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/firefox_32x32.png) | ![Chrome](https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/chrome_32x32.png) | ![Safari](https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/safari_32x32.png) | | ||
|:--:|:---:|:---:|:---:|:---:| | ||
| 10+ | Yes | Yes | Yes | Yes | | ||
| :---------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------: | | ||
| 10+ | Yes | Yes | Yes | Yes | | ||
* Drag-and-drop is not supported on mobile | ||
- Drag-and-drop is not supported on mobile | ||
# Troubleshooting | ||
* I don't see the camera when using `QrcodeStream`. | ||
* Check if it works on the demo page. Especially the [Decode All](https://gruhn.github.io/vue-qrcode-reader/demos/DecodeAll.html) demo, since it renders error messages. If you see errors, consult the docs to understand their meaning. | ||
* Demo works but not locally: Listen for the `init` event to investigate errors. | ||
* Demo doesn't work: Carefully review the Browser Support section above. Maybe your device is just not supported. | ||
* I'm running a dev server on localhost. How to test on my mobile device without HTTPS? | ||
* If your setup is Desktop Chrome + Android Chrome, use [Remote Debugging](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/) which allows your Android device to [access your local server as localhost](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/local-server). | ||
* Otherwise use a reverse proxy like [ngrok](https://ngrok.com/) or [serveo](https://serveo.net/) to temporarily make your local server publicly available with HTTPS. | ||
- I don't see the camera when using `QrcodeStream`. | ||
- Check if it works on the demo page. Especially the [Decode All](https://gruhn.github.io/vue-qrcode-reader/demos/DecodeAll.html) demo, since it renders error messages. If you see errors, consult the docs to understand their meaning. | ||
- Demo works but not locally: Listen for the `init` event to investigate errors. | ||
- Demo doesn't work: Carefully review the Browser Support section above. Maybe your device is just not supported. | ||
- I'm running a dev server on localhost. How to test on my mobile device without HTTPS? | ||
- If your setup is Desktop Chrome + Android Chrome, use [Remote Debugging](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/) which allows your Android device to [access your local server as localhost](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/local-server). | ||
- Otherwise use a reverse proxy like [ngrok](https://ngrok.com/) or [serveo](https://serveo.net/) to temporarily make your local server publicly available with HTTPS. | ||
@@ -127,2 +133,3 @@ # Installation | ||
``` | ||
or with Yarn: | ||
@@ -156,6 +163,6 @@ | ||
```javascript | ||
import Vue from 'vue' | ||
import VueQrcodeReader from 'vue-qrcode-reader' | ||
import Vue from "vue"; | ||
import VueQrcodeReader from "vue-qrcode-reader"; | ||
Vue.use(VueQrcodeReader) | ||
Vue.use(VueQrcodeReader); | ||
``` | ||
@@ -170,5 +177,6 @@ | ||
Besides Vue you need to include the following CSS and JS file: | ||
* `<link href="`[vue-qrcode-reader.css](https://unpkg.com/vue-qrcode-reader/dist/vue-qrcode-reader.css)`" rel="stylesheet">` | ||
* `<script src="`[vue-qrcode-reader.browser.js](https://unpkg.com/vue-qrcode-reader/dist/vue-qrcode-reader.browser.js)`"></script>` | ||
- `<link href="`[vue-qrcode-reader.css](https://unpkg.com/vue-qrcode-reader/dist/vue-qrcode-reader.css)`" rel="stylesheet">` | ||
- `<script src="`[vue-qrcode-reader.browser.js](https://unpkg.com/vue-qrcode-reader/dist/vue-qrcode-reader.browser.js)`"></script>` | ||
The global variable `window.VueQrcodeReader` should now be available. | ||
@@ -179,3 +187,3 @@ | ||
```javascript | ||
Vue.use(VueQrcodeReader) | ||
Vue.use(VueQrcodeReader); | ||
``` | ||
@@ -196,1 +204,9 @@ | ||
``` | ||
# Acknowledgements | ||
Tested with | ||
<a href="https://browserstack.com"> | ||
<img width="180" src="https://raw.githubusercontent.com/gruhn/vue-qrcode-reader/master/.github/browserstack-logo.png" alt="BrowserStack Logo"> | ||
</a> |
/* DEPRECATED: QrcodeReader renamed to QrcodeStream */ | ||
import QrcodeReader from './components/QrcodeReader.vue' | ||
import QrcodeReader from "./components/QrcodeReader.vue"; | ||
import QrcodeStream from './components/QrcodeStream.vue' | ||
import QrcodeCapture from './components/QrcodeCapture.vue' | ||
import QrcodeDropZone from './components/QrcodeDropZone.vue' | ||
import QrcodeStream from "./components/QrcodeStream.vue"; | ||
import QrcodeCapture from "./components/QrcodeCapture.vue"; | ||
import QrcodeDropZone from "./components/QrcodeDropZone.vue"; | ||
// Install the components | ||
export function install (Vue) { | ||
export function install(Vue) { | ||
/* DEPRECATED: QrcodeReader renamed to QrcodeStream */ | ||
Vue.component('qrcode-reader', QrcodeReader) | ||
Vue.component("qrcode-reader", QrcodeReader); | ||
Vue.component('qrcode-stream', QrcodeStream) | ||
Vue.component('qrcode-capture', QrcodeCapture) | ||
Vue.component('qrcode-drop-zone', QrcodeDropZone) | ||
Vue.component("qrcode-stream", QrcodeStream); | ||
Vue.component("qrcode-capture", QrcodeCapture); | ||
Vue.component("qrcode-drop-zone", QrcodeDropZone); | ||
} | ||
@@ -20,7 +20,7 @@ | ||
export { | ||
QrcodeReader, /* DEPRECATED: QrcodeReader renamed to QrcodeStream */ | ||
QrcodeReader /* DEPRECATED: QrcodeReader renamed to QrcodeStream */, | ||
QrcodeStream, | ||
QrcodeCapture, | ||
QrcodeDropZone, | ||
} | ||
QrcodeDropZone | ||
}; | ||
@@ -31,15 +31,15 @@ /* -- Plugin definition & Auto-install -- */ | ||
// Plugin | ||
const plugin = { install } | ||
const plugin = { install }; | ||
export default plugin | ||
export default plugin; | ||
// Auto-install | ||
let GlobalVue = null | ||
if (typeof window !== 'undefined') { | ||
GlobalVue = window.Vue | ||
} else if (typeof global !== 'undefined') { | ||
GlobalVue = global.Vue | ||
let GlobalVue = null; | ||
if (typeof window !== "undefined") { | ||
GlobalVue = window.Vue; | ||
} else if (typeof global !== "undefined") { | ||
GlobalVue = global.Vue; | ||
} | ||
if (GlobalVue) { | ||
GlobalVue.use(plugin) | ||
GlobalVue.use(plugin); | ||
} |
@@ -1,47 +0,43 @@ | ||
import { StreamApiNotSupportedError } from './errors.js' | ||
import { imageDataFromVideo } from './image-data.js' | ||
import { hasFired } from './promisify.js' | ||
import { StreamApiNotSupportedError } from "./errors.js"; | ||
import { imageDataFromVideo } from "./image-data.js"; | ||
import { hasFired } from "./promisify.js"; | ||
class Camera { | ||
constructor (videoEl, stream) { | ||
this.videoEl = videoEl | ||
this.stream = stream | ||
constructor(videoEl, stream) { | ||
this.videoEl = videoEl; | ||
this.stream = stream; | ||
} | ||
stop () { | ||
this.stream.getTracks().forEach( | ||
track => track.stop() | ||
) | ||
stop() { | ||
this.stream.getTracks().forEach(track => track.stop()); | ||
} | ||
captureFrame () { | ||
return imageDataFromVideo(this.videoEl) | ||
captureFrame() { | ||
return imageDataFromVideo(this.videoEl); | ||
} | ||
} | ||
export default async function (constraints, videoEl) { | ||
export default async function(constraints, videoEl) { | ||
if (!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)) { | ||
throw new StreamApiNotSupportedError() | ||
throw new StreamApiNotSupportedError(); | ||
} | ||
const stream = await navigator.mediaDevices.getUserMedia(constraints) | ||
const streamLoaded = hasFired(videoEl, 'loadeddata', 'error') | ||
const stream = await navigator.mediaDevices.getUserMedia(constraints); | ||
const streamLoaded = hasFired(videoEl, "loadeddata"); | ||
if (videoEl.srcObject !== undefined) { | ||
videoEl.srcObject = stream | ||
videoEl.srcObject = stream; | ||
} else if (videoEl.mozSrcObject !== undefined) { | ||
videoEl.mozSrcObject = stream | ||
videoEl.mozSrcObject = stream; | ||
} else if (window.URL.createObjectURL) { | ||
videoEl.src = window.URL.createObjectURL(stream) | ||
videoEl.src = window.URL.createObjectURL(stream); | ||
} else if (window.webkitURL) { | ||
videoEl.src = window.webkitURL.createObjectURL(stream) | ||
videoEl.src = window.webkitURL.createObjectURL(stream); | ||
} else { | ||
videoEl.src = stream | ||
videoEl.src = stream; | ||
} | ||
await streamLoaded | ||
await streamLoaded; | ||
return new Camera(videoEl, stream) | ||
return new Camera(videoEl, stream); | ||
} |
@@ -1,7 +0,6 @@ | ||
export class DropImageFetchError extends Error { | ||
constructor () { | ||
super('can\'t process cross-origin image') | ||
constructor() { | ||
super("can't process cross-origin image"); | ||
this.name = 'DropImageFetchError' | ||
this.name = "DropImageFetchError"; | ||
} | ||
@@ -11,6 +10,6 @@ } | ||
export class DropImageDecodeError extends Error { | ||
constructor () { | ||
super('drag-and-dropped file is not of type image and can\'t be decoded') | ||
constructor() { | ||
super("drag-and-dropped file is not of type image and can't be decoded"); | ||
this.name = 'DropImageDecodeError' | ||
this.name = "DropImageDecodeError"; | ||
} | ||
@@ -20,7 +19,7 @@ } | ||
export class StreamApiNotSupportedError extends Error { | ||
constructor () { | ||
super('this browser has no Stream API support') | ||
constructor() { | ||
super("this browser has no Stream API support"); | ||
this.name = 'StreamApiNotSupportedError' | ||
this.name = "StreamApiNotSupportedError"; | ||
} | ||
} |
@@ -1,62 +0,66 @@ | ||
import { DropImageFetchError, DropImageDecodeError } from './errors.js' | ||
import { hasFired } from './promisify.js' | ||
import { DropImageFetchError, DropImageDecodeError } from "./errors.js"; | ||
import { hasFired } from "./promisify.js"; | ||
const canvas = document.createElement('canvas') | ||
const canvasCtx = canvas.getContext('2d') | ||
const canvas = document.createElement("canvas"); | ||
const canvasCtx = canvas.getContext("2d"); | ||
canvas.width = 1920 | ||
canvas.height = 1080 | ||
canvas.width = 1920; | ||
canvas.height = 1080; | ||
function imageDataFromCanvas (canvasImageSource, width, height) { | ||
const scalingRatio = Math.min(1, canvas.width / width, canvas.height / height) | ||
const widthScaled = scalingRatio * width | ||
const heightScaled = scalingRatio * height | ||
function imageDataFromCanvas(canvasImageSource, width, height) { | ||
const scalingRatio = Math.min( | ||
1, | ||
canvas.width / width, | ||
canvas.height / height | ||
); | ||
const widthScaled = scalingRatio * width; | ||
const heightScaled = scalingRatio * height; | ||
canvasCtx.drawImage(canvasImageSource, 0, 0, widthScaled, heightScaled) | ||
canvasCtx.drawImage(canvasImageSource, 0, 0, widthScaled, heightScaled); | ||
return canvasCtx.getImageData(0, 0, widthScaled, heightScaled) | ||
return canvasCtx.getImageData(0, 0, widthScaled, heightScaled); | ||
} | ||
export function imageDataFromImage (imageElement) { | ||
const width = imageElement.naturalWidth | ||
const height = imageElement.naturalHeight | ||
export function imageDataFromImage(imageElement) { | ||
const width = imageElement.naturalWidth; | ||
const height = imageElement.naturalHeight; | ||
return imageDataFromCanvas(imageElement, width, height) | ||
return imageDataFromCanvas(imageElement, width, height); | ||
} | ||
export function imageDataFromVideo (videoElement) { | ||
const width = videoElement.videoWidth | ||
const height = videoElement.videoHeight | ||
export function imageDataFromVideo(videoElement) { | ||
const width = videoElement.videoWidth; | ||
const height = videoElement.videoHeight; | ||
return imageDataFromCanvas(videoElement, width, height) | ||
return imageDataFromCanvas(videoElement, width, height); | ||
} | ||
export async function imageDataFromUrl (url) { | ||
if (url.startsWith('http') && url.includes(location.host) === false) { | ||
throw new DropImageFetchError() | ||
export async function imageDataFromUrl(url) { | ||
if (url.startsWith("http") && url.includes(location.host) === false) { | ||
throw new DropImageFetchError(); | ||
} | ||
const image = document.createElement('img') | ||
const imageLoaded = hasFired(image, 'load') | ||
const image = document.createElement("img"); | ||
const imageLoaded = hasFired(image, "load"); | ||
image.src = url | ||
image.src = url; | ||
await imageLoaded | ||
await imageLoaded; | ||
return imageDataFromImage(image) | ||
return imageDataFromImage(image); | ||
} | ||
export async function imageDataFromFile (file) { | ||
export async function imageDataFromFile(file) { | ||
if (/image.*/.test(file.type)) { | ||
const reader = new FileReader() | ||
const readerLoaded = hasFired(reader, 'load') | ||
const reader = new FileReader(); | ||
const readerLoaded = hasFired(reader, "load"); | ||
reader.readAsDataURL(file) | ||
reader.readAsDataURL(file); | ||
const dataURL = (await readerLoaded).target.result | ||
const dataURL = (await readerLoaded).target.result; | ||
return imageDataFromUrl(dataURL) | ||
return imageDataFromUrl(dataURL); | ||
} else { | ||
throw new DropImageDecodeError() | ||
throw new DropImageDecodeError(); | ||
} | ||
} |
@@ -0,10 +1,18 @@ | ||
export function hasFired(eventTarget, successEvent, errorEvent = "error") { | ||
let $resolve, $reject; | ||
export function hasFired (element, successEvent, errorEvent) { | ||
return new Promise((resolve, reject) => { | ||
element.addEventListener(successEvent, resolve, { once: true }) | ||
const promise = new Promise((resolve, reject) => { | ||
$resolve = resolve; | ||
$reject = reject; | ||
}); | ||
if (errorEvent !== undefined) { | ||
element.addEventListener(errorEvent, reject, { once: true }) | ||
} | ||
}) | ||
eventTarget.addEventListener(successEvent, $resolve); | ||
eventTarget.addEventListener(errorEvent, $reject); | ||
promise.finally(() => { | ||
eventTarget.removeEventListener(successEvent, $resolve); | ||
eventTarget.removeEventListener(errorEvent, $reject); | ||
}); | ||
return promise; | ||
} |
@@ -1,16 +0,15 @@ | ||
import 'webrtc-adapter' | ||
import Worker from 'worker-loader?inline=true&fallback=false!./worker.js' | ||
import "webrtc-adapter"; | ||
import { hasFired } from "./promisify.js"; | ||
import Worker from "./worker.js"; | ||
export function scan (imageData) { | ||
const worker = new Worker() | ||
export async function scan(imageData) { | ||
const worker = new Worker(); | ||
return new Promise(resolve => { | ||
worker.onmessage = event => { | ||
resolve(event.data) | ||
worker.postMessage(imageData, [imageData.data.buffer]); | ||
worker.terminate() | ||
} | ||
const event = await hasFired(worker, "message"); | ||
worker.postMessage(imageData, [imageData.data.buffer]) | ||
}) | ||
worker.terminate(); | ||
return event.data; | ||
} | ||
@@ -22,62 +21,58 @@ | ||
*/ | ||
export function keepScanning (camera, options) { | ||
const { | ||
detectHandler, | ||
locateHandler, | ||
minDelay, | ||
} = options | ||
export function keepScanning(camera, options) { | ||
const { detectHandler, locateHandler, minDelay } = options; | ||
let contentBefore = null | ||
let locationBefore = null | ||
let lastScanned = performance.now() | ||
let contentBefore = null; | ||
let locationBefore = null; | ||
let lastScanned = performance.now(); | ||
const worker = new Worker() | ||
const worker = new Worker(); | ||
// If worker can't process frames fast enough, memory will quickly full up. | ||
// Make sure to process only one frame at a time. | ||
let workerBusy = false | ||
let shouldContinue = true | ||
let workerBusy = false; | ||
let shouldContinue = true; | ||
worker.onmessage = event => { | ||
workerBusy = false | ||
workerBusy = false; | ||
const { content, location } = event.data | ||
const { content, location } = event.data; | ||
if (content !== null && content !== contentBefore) { | ||
detectHandler(event.data) | ||
detectHandler(event.data); | ||
} | ||
if (location !== locationBefore) { | ||
locateHandler(location) | ||
locateHandler(location); | ||
} | ||
contentBefore = content || contentBefore | ||
locationBefore = location | ||
} | ||
contentBefore = content || contentBefore; | ||
locationBefore = location; | ||
}; | ||
const processFrame = timeNow => { | ||
if (shouldContinue) { | ||
window.requestAnimationFrame(processFrame) | ||
window.requestAnimationFrame(processFrame); | ||
if (timeNow - lastScanned >= minDelay) { | ||
lastScanned = timeNow | ||
lastScanned = timeNow; | ||
if (workerBusy === false) { | ||
workerBusy = true | ||
workerBusy = true; | ||
const imageData = camera.captureFrame() | ||
const imageData = camera.captureFrame(); | ||
worker.postMessage(imageData, [imageData.data.buffer]) | ||
worker.postMessage(imageData, [imageData.data.buffer]); | ||
} | ||
} | ||
} else { | ||
worker.terminate() | ||
worker.terminate(); | ||
} | ||
} | ||
}; | ||
processFrame() | ||
processFrame(); | ||
return () => { | ||
shouldContinue = false | ||
} | ||
shouldContinue = false; | ||
}; | ||
} |
@@ -1,4 +0,3 @@ | ||
export function thinSquare ({ color }) { | ||
return function (location, ctx) { | ||
export function thinSquare({ color }) { | ||
return function(location, ctx) { | ||
const { | ||
@@ -8,17 +7,17 @@ topLeftCorner, | ||
bottomLeftCorner, | ||
bottomRightCorner, | ||
} = location | ||
bottomRightCorner | ||
} = location; | ||
ctx.strokeStyle = 'red' | ||
ctx.strokeStyle = color; | ||
ctx.beginPath() | ||
ctx.moveTo(topLeftCorner.x, topLeftCorner.y) | ||
ctx.lineTo(bottomLeftCorner.x, bottomLeftCorner.y) | ||
ctx.lineTo(bottomRightCorner.x, bottomRightCorner.y) | ||
ctx.lineTo(topRightCorner.x, topRightCorner.y) | ||
ctx.lineTo(topLeftCorner.x, topLeftCorner.y) | ||
ctx.closePath() | ||
ctx.beginPath(); | ||
ctx.moveTo(topLeftCorner.x, topLeftCorner.y); | ||
ctx.lineTo(bottomLeftCorner.x, bottomLeftCorner.y); | ||
ctx.lineTo(bottomRightCorner.x, bottomRightCorner.y); | ||
ctx.lineTo(topRightCorner.x, topRightCorner.y); | ||
ctx.lineTo(topLeftCorner.x, topLeftCorner.y); | ||
ctx.closePath(); | ||
ctx.stroke() | ||
} | ||
ctx.stroke(); | ||
}; | ||
} |
@@ -1,23 +0,19 @@ | ||
import jsQR from 'jsqr' | ||
import jsQR from "jsqr"; | ||
self.addEventListener('message', function (event) { | ||
const imageData = event.data | ||
self.addEventListener("message", function(event) { | ||
const imageData = event.data; | ||
const result = jsQR( | ||
imageData.data, | ||
imageData.width, | ||
imageData.height | ||
) | ||
const result = jsQR(imageData.data, imageData.width, imageData.height); | ||
let content = null | ||
let location = null | ||
let content = null; | ||
let location = null; | ||
if (result !== null) { | ||
content = result.data | ||
location = result.location | ||
content = result.data; | ||
location = result.location; | ||
} | ||
const message = { content, location, imageData } | ||
const message = { content, location, imageData }; | ||
self.postMessage(message, [imageData.data.buffer]) | ||
}) | ||
self.postMessage(message, [imageData.data.buffer]); | ||
}); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
28
201
1
799330
30
383
6