Compare commits

..

No commits in common. 'master' and 'v0.10.4' have entirely different histories.

@ -24,12 +24,7 @@
"objectLiteralComputedProperties": true "objectLiteralComputedProperties": true
}, },
"globals": { "globals": {
"ENV": true, "ENV": true
"beforeEach": true,
"describe": true,
"it": true,
"expect": true,
"sinon": true
}, },
"rules": { "rules": {
"no-unused-expressions": 1, "no-unused-expressions": 1,

1
.gitignore vendored

@ -4,4 +4,3 @@ coverage/
.project .project
_site/ _site/
.idea/ .idea/
.vscode/

@ -1,26 +1,22 @@
quaggaJS quaggaJS
======== ========
[![Join the chat at https://gitter.im/quaggaJS/Lobby](https://badges.gitter.im/quaggaJS/Lobby.svg)](https://gitter.im/quaggaJS/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - [Changelog](#changelog) (2016-04-19)
- [Changelog](#changelog) (2017-06-07)
- [Browser Support](#browser-support) - [Browser Support](#browser-support)
- [Installing](#installing) - [Installing](#installing)
- [Getting Started](#gettingstarted) - [Getting Started](#gettingstarted)
- [API](#api) - [API](#api)
- [Configuration](#configobject) - [Configuration](#configobject)
- [Tips & Tricks](#tipsandtricks)
- [Sponsors](#sponsors)
## What is QuaggaJS? ## What is QuaggaJS?
QuaggaJS is a barcode-scanner entirely written in JavaScript supporting real- QuaggaJS is a barcode-scanner entirely written in JavaScript supporting real-
time localization and decoding of various types of barcodes such as __EAN__, time localization and decoding of various types of barcodes such as __EAN__,
__CODE 128__, __CODE 39__, __EAN 8__, __UPC-A__, __UPC-C__, __I2of5__, __CODE 128__, __CODE 39__, __EAN 8__, __UPC-A__, __UPC-C__, __I2of5__ and
__2of5__, __CODE 93__ and __CODABAR__. The library is also capable of using __CODABAR__. The library is also capable of using `getUserMedia` to get direct
`getUserMedia` to get direct access to the user's camera stream. Although the access to the user's camera stream. Although the code relies on heavy image-
code relies on heavy image-processing even recent smartphones are capable of processing even recent smartphones are capable of locating and decoding
locating and decoding barcodes in real-time. barcodes in real-time.
Try some [examples](https://serratus.github.io/quaggaJS/examples) and check out Try some [examples](https://serratus.github.io/quaggaJS/examples) and check out
the blog post ([How barcode-localization works in QuaggaJS][oberhofer_co_how]) the blog post ([How barcode-localization works in QuaggaJS][oberhofer_co_how])
@ -64,35 +60,6 @@ The following APIs need to be implemented in your browser:
In addition to the APIs mentioned above: In addition to the APIs mentioned above:
- [MediaDevices](http://caniuse.com/#feat=stream) - [MediaDevices](http://caniuse.com/#feat=stream)
__Important:__ Accessing `getUserMedia` requires a secure origin in most
browsers, meaning that `http://` can only be used on `localhost`. All other
hostnames need to be served via `https://`. You can find more information in the
[Chrome M47 WebRTC Release Notes](https://groups.google.com/forum/#!topic/discuss-webrtc/sq5CVmY69sc).
### Feature-detection of getUserMedia
Every browser seems to differently implement the `mediaDevices.getUserMedia`
API. Therefore it's highly recommended to include
[webrtc-adapter](https://github.com/webrtc/adapter) in your project.
Here's how you can test your browser's capabilities:
```javascript
if (navigator.mediaDevices && typeof navigator.mediaDevices.getUserMedia === 'function') {
// safely access `navigator.mediaDevices.getUserMedia`
}
```
The above condition evaluates to:
| Browser | result |
| ------------- |:-------:|
| Edge | `true` |
| Chrome | `true` |
| Firefox | `true` |
| IE 11 | `false` |
| Safari iOS | `true` |
## <a name="installing">Installing</a> ## <a name="installing">Installing</a>
QuaggaJS can be installed using __npm__, __bower__, or by including it with QuaggaJS can be installed using __npm__, __bower__, or by including it with
@ -447,8 +414,6 @@ barcodes which should be decoded during the session. Possible values are:
- upc_reader - upc_reader
- upc_e_reader - upc_e_reader
- i2of5_reader - i2of5_reader
- 2of5_reader
- code_93_reader
Why are not all types activated by default? Simply because one should Why are not all types activated by default? Simply because one should
explicitly define the set of barcodes for their use-case. More decoders means explicitly define the set of barcodes for their use-case. More decoders means
@ -465,35 +430,6 @@ individual box.
The remaining properties `drawBoundingBox`, `showFrequency`, `drawScanline` and The remaining properties `drawBoundingBox`, `showFrequency`, `drawScanline` and
`showPattern` are mostly of interest during debugging and visualization. `showPattern` are mostly of interest during debugging and visualization.
#### <a name="ean_extended">Enabling extended EAN</a>
The default setting for `ean_reader` is not capable of reading extensions such
as [EAN-2](https://en.wikipedia.org/wiki/EAN_2) or
[EAN-5](https://en.wikipedia.org/wiki/EAN_5). In order to activate those
supplements you have to provide them in the configuration as followed:
```javascript
decoder: {
readers: [{
format: "ean_reader",
config: {
supplements: [
'ean_5_reader', 'ean_2_reader'
]
}
}]
}
```
Beware that the order of the `supplements` matters in such that the reader stops
decoding when the first supplement was found. So if you are interested in EAN-2
and EAN-5 extensions, use the order depicted above.
It's important to mention that, if supplements are supplied, regular EAN-13
codes cannot be read any more with the same reader. If you want to read EAN-13
with and without extensions you have to add another `ean_reader` reader to the
configuration.
### locator ### locator
The `locator` config is only relevant if the `locate` flag is set to `true`. The `locator` config is only relevant if the `locate` flag is set to `true`.
@ -573,7 +509,7 @@ that node does not support web-workers out of the box. Therefore the config
property `numOfWorkers` must be explicitly set to `0`. property `numOfWorkers` must be explicitly set to `0`.
```javascript ```javascript
var Quagga = require('quagga').default; var Quagga = require('quagga');
Quagga.decodeSingle({ Quagga.decodeSingle({
src: "image-abc-123.jpg", src: "image-abc-123.jpg",
@ -593,36 +529,6 @@ Quagga.decodeSingle({
}); });
``` ```
## <a name="tipsandtricks">Tips & Tricks</a>
A growing collection of tips & tricks to improve the various aspects of Quagga.
### Barcodes too small?
Barcodes too far away from the camera, or a lens too close to the object
result in poor recognition rates and Quagga might respond with a lot of
false-positives.
Starting in Chrome 59 you can now make use of `capabilities` and directly
control the zoom of the camera. Head over to the
[web-cam demo](https://serratus.github.io/quaggaJS/examples/live_w_locator.html)
and check out the __Zoom__ feature.
You can read more about those `capabilities` in
[Let's light a torch and explore MediaStreamTrack's capabilities](https://www.oberhofer.co/mediastreamtrack-and-its-capabilities)
### Video too dark?
Dark environments usually result in noisy images and therefore mess with the
recognition logic.
Since Chrome 59 you can turn on/off the __Torch__ of our device and vastly
improve the quality of the images. Head over to the
[web-cam demo](https://serratus.github.io/quaggaJS/examples/live_w_locator.html)
and check out the __Torch__ feature.
To find out more about this feature [read on](https://www.oberhofer.co/mediastreamtrack-and-its-capabilities).
## Tests ## Tests
Unit Tests can be run with [Karma][karmaUrl] and written using Unit Tests can be run with [Karma][karmaUrl] and written using
@ -699,56 +605,8 @@ calling ``decodeSingle`` with the same configuration as used during recording
. In order to reproduce the exact same result, you have to make sure to turn . In order to reproduce the exact same result, you have to make sure to turn
on the ``singleChannel`` flag in the configuration when using ``decodeSingle``. on the ``singleChannel`` flag in the configuration when using ``decodeSingle``.
## <a name="sponsors">Sponsors</a>
- [Maintenance Connection Canada (Asset Pro Solutions Inc.](http://maintenanceconnection.ca/)
## <a name="changelog">Changelog</a> ## <a name="changelog">Changelog</a>
### 2017-06-07
- Improvements
- added `muted` and `playsinline` to `<video/>` to make it work for Safari 11
Beta (even iOS)
- Fixes
- Fixed [example/live_w_locator.js](https://github.com/serratus/quaggaJS/blob/master/example/live_w_locator.js)
### 2017-06-06
- Features
- Support for Standard 2of5 barcodes (See
[\#194](https://github.com/serratus/quaggaJS/issues/194))
- Support for Code 93 barcodes (See
[\#194](https://github.com/serratus/quaggaJS/issues/195))
- Exposing `Quagga.CameraAccess.getActiveTrack()` to get access to the
currently used `MediaStreamTrack`
- Example can be viewed here: [example/live_w_locator.js](https://github.com/serratus/quaggaJS/blob/master/example/live_w_locator.js) and a [demo](https://serratus.github.io/quaggaJS/examples/live_w_locator.html)
Take a look at the release-notes (
[0.12.0](https://github.com/serratus/quaggaJS/releases/tag/v0.12.0))
### 2017-01-08
- Improvements
- Exposing `CameraAccess` module to get access to methods like
`enumerateVideoDevices` and `getActiveStreamLabel`
(see `example/live_w_locator`)
- Update to webpack 2.2 (API is still unstable)
### 2016-10-03
- Fixes
- Fixed `facingMode` issue with Chrome >= 53 (see [#128](https://github.com/serratus/quaggaJS/issues/128))
### 2016-08-15
- Features
- Proper handling of EXIF orientation when using `Quagga.decodeSingle`
(see [#121](https://github.com/serratus/quaggaJS/issues/121))
### 2016-04-24
- Features
- EAN-13 extended codes can now be decoded (See
[\#71](https://github.com/serratus/quaggaJS/issues/71))
Take a look at the release-notes (
[0.11.0](https://github.com/serratus/quaggaJS/releases/tag/v0.11.0))
### 2016-04-19 ### 2016-04-19
- Improvements - Improvements
- Reducing false-positives for Code 128 barcodes ( - Reducing false-positives for Code 128 barcodes (

@ -1,6 +1,6 @@
{ {
"name": "quagga", "name": "quagga",
"version": "0.12.1", "version": "0.6.14",
"description": "An advanced barcode-scanner written in JavaScript", "description": "An advanced barcode-scanner written in JavaScript",
"main": "dist/quagga.js", "main": "dist/quagga.js",
"ignore": [ "ignore": [

23286
dist/quagga.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
@import url("https://fonts.googleapis.com/css?family=Ubuntu:400,700|Cabin+Condensed:400,600"); @import url("http://fonts.googleapis.com/css?family=Ubuntu:400,700|Cabin+Condensed:400,600");

@ -1,109 +1,79 @@
@charset "UTF-8"; @charset "UTF-8";
@import url("https://fonts.googleapis.com/css?family=Ubuntu:400,700|Cabin+Condensed:400,600"); /* usual styles */
/* LESS - http://lesscss.org style sheet */
body { /* Palette color codes */
background-color: #FFF; /* Palette URL: http://paletton.com/#uid=31g0q0kHZAviRSkrHLOGomVNzac */
margin: 0px; /* Feel free to copy&paste color codes to your application */
font-family: Ubuntu, sans-serif; /* MIXINS */
color: #1e1e1e; /* As hex codes */
font-weight: normal; /* Main Primary color */
padding-top: 0; /* Main Secondary color (1) */
} /* Main Secondary color (2) */
/* As RGBa codes */
h1, h2, h3, h4 { /* Main Primary color */
font-family: "Cabin Condensed", sans-serif; /* Main Secondary color (1) */
} /* Main Secondary color (2) */
/* Generated by Paletton.com ├é┬® 2002-2014 */
header { /* http://paletton.com */
background: #FFC600; @import url("http://fonts.googleapis.com/css?family=Ubuntu:400,700|Cabin+Condensed:400,600");
padding: 1em; /* line 1, ../sass/_viewport.scss */
}
header .headline {
max-width: 640px;
margin: 0 auto;
}
header .headline h1 {
color: #FFDD69;
font-size: 3em;
margin-bottom: 0;
}
header .headline h2 {
margin-top: 0.2em;
}
footer {
background: #0A4DB7;
color: #6C9CE8;
padding: 1em 2em 2em;
}
#container {
width: 640px;
margin: 20px auto;
padding: 10px;
}
#interactive.viewport { #interactive.viewport {
width: 640px; width: 640px;
height: 480px; height: 480px;
} }
/* line 6, ../sass/_viewport.scss */
#interactive.viewport canvas, video { #interactive.viewport canvas, video {
float: left; float: left;
width: 640px; width: 640px;
height: 480px; height: 480px;
} }
/* line 10, ../sass/_viewport.scss */
#interactive.viewport canvas.drawingBuffer, video.drawingBuffer { #interactive.viewport canvas.drawingBuffer, video.drawingBuffer {
margin-left: -640px; margin-left: -640px;
} }
/* line 16, ../sass/_viewport.scss */
.controls fieldset { .controls fieldset {
border: none; border: none;
margin: 0;
padding: 0;
} }
/* line 19, ../sass/_viewport.scss */
.controls .input-group { .controls .input-group {
float: left; float: left;
} }
/* line 21, ../sass/_viewport.scss */
.controls .input-group input, .controls .input-group button { .controls .input-group input, .controls .input-group button {
display: block; display: block;
} }
/* line 25, ../sass/_viewport.scss */
.controls .reader-config-group { .controls .reader-config-group {
float: right; float: right;
} }
/* line 28, ../sass/_viewport.scss */
.controls .reader-config-group label { .controls .reader-config-group label {
display: block; display: block;
} }
/* line 30, ../sass/_viewport.scss */
.controls .reader-config-group label span { .controls .reader-config-group label span {
width: 9rem; width: 11rem;
display: inline-block; display: inline-block;
text-align: right; text-align: right;
} }
/* line 37, ../sass/_viewport.scss */
.controls:after { .controls:after {
content: ''; content: '';
display: block; display: block;
clear: both; clear: both;
} }
/* line 44, ../sass/_viewport.scss */
#result_strip { #result_strip {
margin: 10px 0; margin: 10px 0;
border-top: 1px solid #EEE; border-top: 1px solid #EEE;
border-bottom: 1px solid #EEE; border-bottom: 1px solid #EEE;
padding: 10px 0; padding: 10px 0;
} }
/* line 50, ../sass/_viewport.scss */
#result_strip > ul { #result_strip > ul {
padding: 0; padding: 0;
margin: 0; margin: 0;
@ -113,41 +83,41 @@ footer {
overflow-y: hidden; overflow-y: hidden;
white-space: nowrap; white-space: nowrap;
} }
/* line 59, ../sass/_viewport.scss */
#result_strip > ul > li { #result_strip > ul > li {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
width: 160px; width: 160px;
} }
/* line 63, ../sass/_viewport.scss */
#result_strip > ul > li .thumbnail { #result_strip > ul > li .thumbnail {
padding: 5px; padding: 5px;
margin: 4px; margin: 4px;
border: 1px dashed #CCC; border: 1px dashed #CCC;
} }
/* line 68, ../sass/_viewport.scss */
#result_strip > ul > li .thumbnail img { #result_strip > ul > li .thumbnail img {
max-width: 140px; max-width: 140px;
} }
/* line 71, ../sass/_viewport.scss */
#result_strip > ul > li .thumbnail .caption { #result_strip > ul > li .thumbnail .caption {
white-space: normal; white-space: normal;
} }
/* line 73, ../sass/_viewport.scss */
#result_strip > ul > li .thumbnail .caption h4 { #result_strip > ul > li .thumbnail .caption h4 {
text-align: center; text-align: center;
word-wrap: break-word; word-wrap: break-word;
height: 40px; height: 40px;
margin: 0px; margin: 0px;
} }
/* line 83, ../sass/_viewport.scss */
#result_strip > ul:after { #result_strip > ul:after {
content: ""; content: "";
display: table; display: table;
clear: both; clear: both;
} }
/* line 7, ../sass/_overlay.scss */
.scanner-overlay { .scanner-overlay {
display: none; display: none;
width: 640px; width: 640px;
@ -163,21 +133,21 @@ footer {
-webkit-box-shadow: #333333 0px 4px 10px; -webkit-box-shadow: #333333 0px 4px 10px;
box-shadow: #333333 0px 4px 10px; box-shadow: #333333 0px 4px 10px;
} }
/* line 20, ../sass/_overlay.scss */
.scanner-overlay > .header { .scanner-overlay > .header {
position: relative; position: relative;
margin-bottom: 14px; margin-bottom: 14px;
} }
/* line 23, ../sass/_overlay.scss */
.scanner-overlay > .header h4, .scanner-overlay > .header .close { .scanner-overlay > .header h4, .scanner-overlay > .header .close {
line-height: 16px; line-height: 16px;
} }
/* line 26, ../sass/_overlay.scss */
.scanner-overlay > .header h4 { .scanner-overlay > .header h4 {
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
} }
/* line 30, ../sass/_overlay.scss */
.scanner-overlay > .header .close { .scanner-overlay > .header .close {
position: absolute; position: absolute;
right: 0px; right: 0px;
@ -190,7 +160,7 @@ footer {
cursor: pointer; cursor: pointer;
} }
/* line 1, ../sass/_icons.scss */
i.icon-24-scan { i.icon-24-scan {
width: 24px; width: 24px;
height: 24px; height: 24px;
@ -202,8 +172,11 @@ i.icon-24-scan {
vertical-align: text-top; vertical-align: text-top;
} }
@media (min-width: 604px) and (max-width: 1024px) {
/* tablet styles */
}
@media (max-width: 603px) { @media (max-width: 603px) {
/* line 2, ../sass/phone/_core.scss */
#container { #container {
width: 300px; width: 300px;
margin: 10px auto; margin: 10px auto;
@ -211,58 +184,46 @@ i.icon-24-scan {
-webkit-box-shadow: none; -webkit-box-shadow: none;
box-shadow: none; box-shadow: none;
} }
/* line 9, ../sass/phone/_core.scss */
#container form.voucher-form input.voucher-code { #container form.voucher-form input.voucher-code {
width: 180px; width: 180px;
} }
} }
@media (max-width: 603px) { @media (max-width: 603px) {
/* line 5, ../sass/phone/_viewport.scss */
.reader-config-group {
width: 100%;
}
.reader-config-group label > span {
width: 50%;
}
.reader-config-group label > select, .reader-config-group label > input {
max-width: calc(50% - 2px);
}
#interactive.viewport { #interactive.viewport {
width: 300px; width: 300px;
height: 300px; height: 300px;
overflow: hidden; overflow: hidden;
} }
/* line 11, ../sass/phone/_viewport.scss */
#interactive.viewport canvas, video { #interactive.viewport canvas, video {
margin-top: -50px; margin-top: -50px;
width: 300px; width: 300px;
height: 400px; height: 400px;
} }
/* line 15, ../sass/phone/_viewport.scss */
#interactive.viewport canvas.drawingBuffer, video.drawingBuffer { #interactive.viewport canvas.drawingBuffer, video.drawingBuffer {
margin-left: -300px; margin-left: -300px;
} }
/* line 20, ../sass/phone/_viewport.scss */
#result_strip { #result_strip {
margin-top: 5px; margin-top: 5px;
padding-top: 5px; padding-top: 5px;
} }
/* line 24, ../sass/phone/_viewport.scss */
#result_strip ul.thumbnails > li { #result_strip ul.thumbnails > li {
width: 150px; width: 150px;
} }
/* line 27, ../sass/phone/_viewport.scss */
#result_strip ul.thumbnails > li .thumbnail .imgWrapper { #result_strip ul.thumbnails > li .thumbnail .imgWrapper {
width: 130px; width: 130px;
height: 130px; height: 130px;
overflow: hidden; overflow: hidden;
} }
/* line 31, ../sass/phone/_viewport.scss */
#result_strip ul.thumbnails > li .thumbnail .imgWrapper img { #result_strip ul.thumbnails > li .thumbnail .imgWrapper img {
margin-top: -25px; margin-top: -25px;
width: 130px; width: 130px;
@ -270,7 +231,7 @@ i.icon-24-scan {
} }
} }
@media (max-width: 603px) { @media (max-width: 603px) {
/* line 8, ../sass/phone/_overlay.scss */
.overlay.scanner { .overlay.scanner {
width: 640px; width: 640px;
height: 510px; height: 510px;
@ -282,17 +243,66 @@ i.icon-24-scan {
-webkit-box-shadow: none; -webkit-box-shadow: none;
box-shadow: none; box-shadow: none;
} }
/* line 17, ../sass/phone/_overlay.scss */
.overlay.scanner > .header { .overlay.scanner > .header {
margin-bottom: 14px; margin-bottom: 14px;
} }
/* line 19, ../sass/phone/_overlay.scss */
.overlay.scanner > .header h4, .overlay.scanner > .header .close { .overlay.scanner > .header h4, .overlay.scanner > .header .close {
line-height: 16px; line-height: 16px;
} }
/* line 22, ../sass/phone/_overlay.scss */
.overlay.scanner > .header .close { .overlay.scanner > .header .close {
height: 16px; height: 16px;
width: 16px; width: 16px;
} }
} }
/* line 15, ../sass/styles.scss */
body {
background-color: #FFF;
margin: 0px;
font-family: Ubuntu, sans-serif;
color: #1e1e1e;
font-weight: normal;
padding-top: 0;
}
/* line 24, ../sass/styles.scss */
h1, h2, h3, h4 {
font-family: "Cabin Condensed", sans-serif;
}
/* line 28, ../sass/styles.scss */
header {
background: #FFC600;
padding: 1em;
}
/* line 31, ../sass/styles.scss */
header .headline {
width: 640px;
margin: 0 auto;
}
/* line 34, ../sass/styles.scss */
header .headline h1 {
color: #FFDD69;
font-size: 3em;
margin-bottom: 0;
}
/* line 39, ../sass/styles.scss */
header .headline h2 {
margin-top: 0.2em;
}
/* line 45, ../sass/styles.scss */
footer {
background: #0A4DB7;
color: #6C9CE8;
padding: 1em 2em 2em;
}
/* line 51, ../sass/styles.scss */
#container {
width: 640px;
margin: 20px auto;
padding: 10px;
}

@ -35,7 +35,7 @@
<div class="controls"> <div class="controls">
<fieldset class="input-group"> <fieldset class="input-group">
<input type="file" accept="image/*" capture="camera"/> <input type="file" capture/>
<button>Rerun</button> <button>Rerun</button>
</fieldset> </fieldset>
<fieldset class="reader-config-group"> <fieldset class="reader-config-group">
@ -46,22 +46,19 @@
<option value="code_39">Code 39</option> <option value="code_39">Code 39</option>
<option value="code_39_vin">Code 39 VIN</option> <option value="code_39_vin">Code 39 VIN</option>
<option value="ean">EAN</option> <option value="ean">EAN</option>
<option value="ean_extended">EAN-extended</option>
<option value="ean_8">EAN-8</option> <option value="ean_8">EAN-8</option>
<option value="upc">UPC</option> <option value="upc">UPC</option>
<option value="upc_e">UPC-E</option> <option value="upc_e">UPC-E</option>
<option value="codabar">Codabar</option> <option value="codabar">Codabar</option>
<option value="i2of5">Interleaved 2 of 5</option> <option value="i2of5">ITF</option>
<option value="2of5">Standard 2 of 5</option>
<option value="code_93">Code 93</option>
</select> </select>
</label> </label>
<label> <label>
<span>Resolution (long side)</span> <span>Resolution (long side)</span>
<select name="input-stream_size"> <select name="input-stream_size">
<option value="320">320px</option> <option value="320">320px</option>
<option value="640">640px</option> <option selected="selected" value="640">640px</option>
<option selected="selected" value="800">800px</option> <option value="800">800px</option>
<option value="1280">1280px</option> <option value="1280">1280px</option>
<option value="1600">1600px</option> <option value="1600">1600px</option>
<option value="1920">1920px</option> <option value="1920">1920px</option>

@ -82,37 +82,21 @@ $(function() {
}, },
decoder: { decoder: {
readers: function(value) { readers: function(value) {
if (value === 'ean_extended') { return [value + "_reader"];
return [{
format: "ean_reader",
config: {
supplements: [
'ean_5_reader', 'ean_2_reader'
]
}
}];
}
return [{
format: value + "_reader",
config: {}
}];
} }
} }
}, },
state: { state: {
inputStream: { inputStream: {
size: 800, size: 640,
singleChannel: false singleChannel: false
}, },
locator: { locator: {
patchSize: "medium", patchSize: "large",
halfSample: true halfSample: false
}, },
decoder: { decoder: {
readers: [{ readers: ["code_128_reader"]
format: "code_128_reader",
config: {}
}]
}, },
locate: true, locate: true,
src: null src: null

@ -8,7 +8,7 @@
<meta name="description" content="" /> <meta name="description" content="" />
<meta name="author" content="Christoph Oberhofer" /> <meta name="author" content="Christoph Oberhofer" />
<meta name="viewport" content="width=device-width; initial-scale=1.0; user-scalable=no" /> <meta name="viewport" content="width=device-width; initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="css/styles.css" /> <link rel="stylesheet" type="text/css" href="css/styles.css" />
</head> </head>
@ -38,18 +38,15 @@
<option value="code_39">Code 39</option> <option value="code_39">Code 39</option>
<option value="code_39_vin">Code 39 VIN</option> <option value="code_39_vin">Code 39 VIN</option>
<option value="ean">EAN</option> <option value="ean">EAN</option>
<option value="ean_extended">EAN-extended</option>
<option value="ean_8">EAN-8</option> <option value="ean_8">EAN-8</option>
<option value="upc">UPC</option> <option value="upc">UPC</option>
<option value="upc_e">UPC-E</option> <option value="upc_e">UPC-E</option>
<option value="codabar">Codabar</option> <option value="codabar">Codabar</option>
<option value="i2of5">Interleaved 2 of 5</option> <option value="i2of5">ITF</option>
<option value="2of5">Standard 2 of 5</option>
<option value="code_93">Code 93</option>
</select> </select>
</label> </label>
<label> <label>
<span>Resolution (width)</span> <span>Resolution (long side)</span>
<select name="input-stream_constraints"> <select name="input-stream_constraints">
<option value="320x240">320px</option> <option value="320x240">320px</option>
<option selected="selected" value="640x480">640px</option> <option selected="selected" value="640x480">640px</option>
@ -83,19 +80,6 @@
<option value="8">8</option> <option value="8">8</option>
</select> </select>
</label> </label>
<label>
<span>Camera</span>
<select name="input-stream_constraints" id="deviceSelection">
</select>
</label>
<label style="display: none">
<span>Zoom</span>
<select name="settings_zoom"></select>
</label>
<label style="display: none">
<span>Torch</span>
<input type="checkbox" name="settings_torch" />
</label>
</fieldset> </fieldset>
</div> </div>
<div id="result_strip"> <div id="result_strip">
@ -106,12 +90,11 @@
</section> </section>
<footer> <footer>
<p> <p>
&copy; Made with ❤️ by Christoph Oberhofer &copy; Copyright by Christoph Oberhofer
</p> </p>
</footer> </footer>
<script src="vendor/jquery-1.9.0.min.js" type="text/javascript"></script> <script src="vendor/jquery-1.9.0.min.js" type="text/javascript"></script>
<script src="//webrtc.github.io/adapter/adapter-latest.js" type="text/javascript"></script>
<script src="../dist/quagga.js" type="text/javascript"></script> <script src="../dist/quagga.js" type="text/javascript"></script>
<script src="live_w_locator.js" type="text/javascript"></script> <script src="live_w_locator.js" type="text/javascript"></script>
</body> </body>

@ -2,19 +2,7 @@ $(function() {
var resultCollector = Quagga.ResultCollector.create({ var resultCollector = Quagga.ResultCollector.create({
capture: true, capture: true,
capacity: 20, capacity: 20,
blacklist: [{ blacklist: [{code: "2167361334", format: "i2of5"}],
code: "WIWV8ETQZ1", format: "code_93"
}, {
code: "EH3C-%GU23RK3", format: "code_93"
}, {
code: "O308SIHQOXN5SA/PJ", format: "code_93"
}, {
code: "DG7Q$TV8JQ/EN", format: "code_93"
}, {
code: "VOFD1DB5A.1F6QU", format: "code_93"
}, {
code: "4SO64P4X8 U4YUU1T-", format: "code_93"
}],
filter: function(codeResult) { filter: function(codeResult) {
// only store results which match this constraint // only store results which match this constraint
// e.g.: codeResult // e.g.: codeResult
@ -22,91 +10,24 @@ $(function() {
} }
}); });
var App = { var App = {
init: function() { init : function() {
var self = this; var self = this;
Quagga.init(this.state, function(err) { Quagga.init(this.state, function(err) {
if (err) { if (err) {
return self.handleError(err); return self.handleError(err);
} }
//Quagga.registerResultCollector(resultCollector); Quagga.registerResultCollector(resultCollector);
App.attachListeners(); App.attachListeners();
App.checkCapabilities();
Quagga.start(); Quagga.start();
}); });
}, },
handleError: function(err) { handleError: function(err) {
console.log(err); console.log(err);
}, },
checkCapabilities: function() {
var track = Quagga.CameraAccess.getActiveTrack();
var capabilities = {};
if (typeof track.getCapabilities === 'function') {
capabilities = track.getCapabilities();
}
this.applySettingsVisibility('zoom', capabilities.zoom);
this.applySettingsVisibility('torch', capabilities.torch);
},
updateOptionsForMediaRange: function(node, range) {
console.log('updateOptionsForMediaRange', node, range);
var NUM_STEPS = 6;
var stepSize = (range.max - range.min) / NUM_STEPS;
var option;
var value;
while (node.firstChild) {
node.removeChild(node.firstChild);
}
for (var i = 0; i <= NUM_STEPS; i++) {
value = range.min + (stepSize * i);
option = document.createElement('option');
option.value = value;
option.innerHTML = value;
node.appendChild(option);
}
},
applySettingsVisibility: function(setting, capability) {
// depending on type of capability
if (typeof capability === 'boolean') {
var node = document.querySelector('input[name="settings_' + setting + '"]');
if (node) {
node.parentNode.style.display = capability ? 'block' : 'none';
}
return;
}
if (window.MediaSettingsRange && capability instanceof window.MediaSettingsRange) {
var node = document.querySelector('select[name="settings_' + setting + '"]');
if (node) {
this.updateOptionsForMediaRange(node, capability);
node.parentNode.style.display = 'block';
}
return;
}
},
initCameraSelection: function(){
var streamLabel = Quagga.CameraAccess.getActiveStreamLabel();
return Quagga.CameraAccess.enumerateVideoDevices()
.then(function(devices) {
function pruneText(text) {
return text.length > 30 ? text.substr(0, 30) : text;
}
var $deviceSelection = document.getElementById("deviceSelection");
while ($deviceSelection.firstChild) {
$deviceSelection.removeChild($deviceSelection.firstChild);
}
devices.forEach(function(device) {
var $option = document.createElement("option");
$option.value = device.deviceId || device.id;
$option.appendChild(document.createTextNode(pruneText(device.label || device.deviceId || device.id)));
$option.selected = streamLabel === device.label;
$deviceSelection.appendChild($option);
});
});
},
attachListeners: function() { attachListeners: function() {
var self = this; var self = this;
self.initCameraSelection();
$(".controls").on("click", "button.stop", function(e) { $(".controls").on("click", "button.stop", function(e) {
e.preventDefault(); e.preventDefault();
Quagga.stop(); Quagga.stop();
@ -143,11 +64,7 @@ $(function() {
return parts.reduce(function(o, key, i) { return parts.reduce(function(o, key, i) {
if (setter && (i + 1) === depth) { if (setter && (i + 1) === depth) {
if (typeof o[key] === "object" && typeof val === "object") { o[key] = val;
Object.assign(o[key], val);
} else {
o[key] = val;
}
} }
return key in o ? o[key] : {}; return key in o ? o[key] : {};
}, obj); }, obj);
@ -161,17 +78,6 @@ $(function() {
$(".controls").off("click", "button.stop"); $(".controls").off("click", "button.stop");
$(".controls .reader-config-group").off("change", "input, select"); $(".controls .reader-config-group").off("change", "input, select");
}, },
applySetting: function(setting, value) {
var track = Quagga.CameraAccess.getActiveTrack();
if (track && typeof track.getCapabilities === 'function') {
switch (setting) {
case 'zoom':
return track.applyConstraints({advanced: [{zoom: parseFloat(value)}]});
case 'torch':
return track.applyConstraints({advanced: [{torch: !!value}]});
}
}
},
setState: function(path, value) { setState: function(path, value) {
var self = this; var self = this;
@ -179,10 +85,6 @@ $(function() {
value = self._accessByPath(self.inputMapper, path)(value); value = self._accessByPath(self.inputMapper, path)(value);
} }
if (path.startsWith('settings.')) {
var setting = path.substring(9);
return self.applySetting(setting, value);
}
self._accessByPath(self.state, path, value); self._accessByPath(self.state, path, value);
console.log(JSON.stringify(self.state)); console.log(JSON.stringify(self.state));
@ -193,16 +95,11 @@ $(function() {
inputMapper: { inputMapper: {
inputStream: { inputStream: {
constraints: function(value){ constraints: function(value){
if (/^(\d+)x(\d+)$/.test(value)) { var values = value.split('x');
var values = value.split('x');
return {
width: {min: parseInt(values[0])},
height: {min: parseInt(values[1])}
};
}
return { return {
deviceId: value width: parseInt(values[0]),
}; height: parseInt(values[1])
}
} }
}, },
numOfWorkers: function(value) { numOfWorkers: function(value) {
@ -210,20 +107,7 @@ $(function() {
}, },
decoder: { decoder: {
readers: function(value) { readers: function(value) {
if (value === 'ean_extended') { return [value + "_reader"];
return [{
format: "ean_reader",
config: {
supplements: [
'ean_5_reader', 'ean_2_reader'
]
}
}];
}
return [{
format: value + "_reader",
config: {}
}];
} }
} }
}, },
@ -231,23 +115,18 @@ $(function() {
inputStream: { inputStream: {
type : "LiveStream", type : "LiveStream",
constraints: { constraints: {
width: {min: 640}, width: 640,
height: {min: 480}, height: 480,
facingMode: "environment", facingMode: "environment"
aspectRatio: {min: 1, max: 2}
} }
}, },
locator: { locator: {
patchSize: "medium", patchSize: "medium",
halfSample: true halfSample: true
}, },
numOfWorkers: 2, numOfWorkers: 4,
frequency: 10,
decoder: { decoder: {
readers : [{ readers : [ "code_128_reader"]
format: "code_128_reader",
config: {}
}]
}, },
locate: true locate: true
}, },

@ -1,27 +0,0 @@
var Quagga = require('../lib/quagga').default;
var buffer = require('fs').readFileSync('../test/fixtures/code_128/image-001.jpg');
decode(buffer);
function decode(buff){
Quagga.decodeSingle({
src: buff,
numOfWorkers: 0,
inputStream: {
mime: "image/jpeg",
size: 800,
area: {
top: "10%",
right: "5%",
left: "5%",
bottom: "10%"
}
}
}, function(result) {
if (result.codeResult) {
console.log("result", result.codeResult.code);
} else {
console.log("not detected");
}
});
}

@ -0,0 +1,10 @@
i.icon-24-scan{
width: 24px;
height: 24px;
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzFFMjMzNTBFNjcwMTFFMkIzMERGOUMzMzEzM0E1QUMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzFFMjMzNTFFNjcwMTFFMkIzMERGOUMzMzEzM0E1QUMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDMUUyMzM0RUU2NzAxMUUyQjMwREY5QzMzMTMzQTVBQyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDMUUyMzM0RkU2NzAxMUUyQjMwREY5QzMzMTMzQTVBQyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PtQr90wAAAUuSURBVHjanFVLbFRVGP7ua97T9DGPthbamAYYBNSMVbBpjCliWWGIEBMWsnDJxkh8RDeEDW5MDGticMmGBWnSlRSCwgLFNkqmmrRIqzjTznTazkxn5s7c6/efzm0G0Jhwkj/nP+d/nv91tIWFBTQaDQWapkGW67p4ltUub5qmAi0UCqF/a/U2m81tpmddotwwDGSz2dzi4uKSaOucnJycGhsbe1XXdQiIIcdxEAgEtgXq9brySHCht79UXi/8QheawN27d385fPjwuEl6XyKR6LdtW7t06RLK5TKOHj2K/fv3Q87Dw8OYn5/HiRMnMDs7i5mZGQwODiqlPp8PuVwO6XRaOXb16lXl1OnTp5FMJvtosF8M+MWLarWqGJaWlpBKpRRcu3YN4+PjmJ6exsTEhDJw5coVjI6OKgPhcBiZTAbxeBx+vx+XL19Gd3c3Tp48Ka9zqDYgBlTQxYNgMIhIJKLCILkQb+TZsgvdsiyFi+feWRR7oRNZyanQtvW2V4DEUUBiK2eJpeDirSyhCe7F2QPh8fiEp72i9PbsC5G52DbiKZA771yr1dTuGfJ4PQNPFoAyQNR1aNEmsS5eyB3PgjeooMZd2AWvNmzYci/Gea7TeFOcI93jV/K67noGmi4vdRI9gPSDeMLSdKUBZZczlWm1rTtHjLZ24d+WER2tc8N1m+Y+ID74wx0zGYvhg9UNrJdtHJyZRdQfwPsrq9g99xsGlgsYmr6BNzO/IVwsYfjBQ6XYz6JI/72MV366B5/lw0elOkJWGUM3bmKtWjXSLuLaBWhnPnnp0FfoiFi4+TMfVAb2poBkDLjO845uYLEAjL4ALGWBP5YAOsP4AJYBFDaB1HOSVWD2PuV95H2RdV93Lv74/cf6p6Zxq/h6OofeOPJBC39JtONdwOAAViOs4p4OFGTf0Uc8iiyrr9YdQrUnDLsngrVOC0jQib44HlF2RafRZBz1Qy+vfhgK3NJZBlrm+LEm9qWwzFgLU7Ozg0JxZP06jQSRpQ7EerAWDSt6PuhHPmChEAog56fCLvJT5hHTm3OZkz3DyLx7XNWTGEA1GkV14gjWgwbW0ESVjYRwCOuai03L5E7OUBAV4kXSS4auoGIaKOma4m8EA5R1sMEGLh95C+XuLph0WJWpxepYYLtfT0RRgY1KgNODY6BoaChRuEhDCIZQYseuki5KN6hcQHiq7OZNv4/Zq2O6P4Lfkwn46vZjjaYZrIpvWbpzjLErrc4xUGE4avRedpYJalRcIl5hQius/SrPm9xrNOQYJhao6BvNUeWqtY8KaWuNjHOFAr7mM9f4NA4UbKysoUJ8PV9UzVOx6wxDDWUOxnK1pmCD07fOMAvtIsM3l89Dl3HRGhVma9AZMqjOnz2LQqWCxs6dqr3T7x1DTzKJaG8SekcHhg4cgI/56uKdlKnBV/WndqN3YAB/7tyBd3oT6GBIOzs7kc/nDfFdDFT5bS73cp06dQoaPa/Rw/rtO/resTHxxE2m9rCrbSR27UJCcMf1BpiA5rAAGgdfc868fUR1sMwj0cm9Iu9IctweisViB3hhKTHDcHc5jv/LspbyaZrR1OD82/fIlOkuB9LnEWRmDX2TsddUPg3D5gvuc0je0rZaD5EW6G3yjS+A3eeBEWq3XW/Abw1HhUspXADufQb86oW7tZytkYCN//3hHwBvDALPi8EnSOYK8DAOfCc2h4aGcO7cuafkzampqf9UripH12/DtOZbx8ciVGzYy5OO40o25ascGRl5Ssc/AgwAjW3JwqIUjSYAAAAASUVORK5CYII=");
display: inline-block;
background-repeat: no-repeat;
line-height: 24px;
margin-top: 1px;
vertical-align: text-top;
}

@ -0,0 +1,48 @@
$overlayWidth: $videoWidth;
$overlayHeadline: 16px;
$overlayHeadlineMargin: 14px;
$overlayPadding: 20px;
$overlayHeight: $videoHeight + $overlayHeadlineMargin + $overlayHeadline;
.scanner-overlay {
display: none;
width: $overlayWidth;
height: $overlayHeight;
position: absolute;
padding: $overlayPadding;
top: 50%;
margin-top: -($overlayHeight)/2 - $overlayPadding;
left: 50%;
margin-left: -($overlayWidth + 2*$overlayPadding)/2;
background-color: $overlayBackground;
@include box-shadow(#333333 0px 4px 10px);
& > .header{
position: relative;
margin-bottom: $overlayHeadlineMargin;
h4, .close{
line-height: $overlayHeadline;
}
h4{
margin: 0px;
padding: 0px;
}
.close{
position: absolute;
right: 0px;
top: 0px;
height: $overlayHeadline;
width: $overlayHeadline;
text-align: center;
font-weight: bold;
font-size: 14px;
cursor: pointer;
}
}
& > .body{
}
}

@ -0,0 +1,3 @@
@import "phone/core";
@import "phone/viewport";
@import "phone/overlay";

@ -0,0 +1,4 @@
@include tablet {
/* tablet styles */
}

@ -0,0 +1,17 @@
$tablet_upper: 1024px;
$phone_upper: 603px;
@mixin phone {
@media (max-width: $phone_upper) {
@content;
}
} // @mixin phone
@mixin tablet {
@media (min-width: $phone_upper+1) and (max-width: $tablet_upper) {
@content;
}
} // @mixin tablet

@ -0,0 +1,7 @@
$background_color: #FFF;
$overlayBackground: #FFF;
$videoWidth: 640px;
$videoHeight: 480px;
$text-font: Ubuntu, sans-serif;
$header-font: 'Cabin Condensed', sans-serif;

@ -0,0 +1,89 @@
#interactive.viewport{
width: $videoWidth;
height: $videoHeight;
}
#interactive.viewport canvas, video{
float: left;
width: $videoWidth;
height: $videoHeight;
&.drawingBuffer{
margin-left: -$videoWidth;
}
}
.controls {
fieldset {
border: none;
}
.input-group {
float: left;
input, button {
display: block;
}
}
.reader-config-group {
float: right;
label {
display: block;
span {
width: 11rem;
display: inline-block;
text-align: right;
}
}
}
&:after {
content:'';
display: block;
clear: both;
}
}
#result_strip{
margin: 10px 0;
border-top: 1px solid #EEE;
border-bottom: 1px solid #EEE;
padding: 10px 0;
& > ul {
padding: 0;
margin: 0;
list-style-type: none;
width: auto;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
& > li{
display: inline-block;
vertical-align: middle;
width: $videoWidth/4;
.thumbnail{
padding: 5px;
margin: 4px;
border: 1px dashed #CCC;
img{
max-width: $videoWidth/4 - 20px;
}
.caption{
white-space: normal;
h4{
text-align: center;
word-wrap: break-word;
height: 40px;
margin: 0px;
}
}
}
}
&:after{
content: "";
display: table;
clear: both;
}
}
}

@ -0,0 +1,55 @@
/* LESS - http://lesscss.org style sheet */
/* Palette color codes */
/* Palette URL: http://paletton.com/#uid=31g0q0kHZAviRSkrHLOGomVNzac */
/* Feel free to copy&paste color codes to your application */
/* MIXINS */
/* As hex codes */
$color-primary-0: #FFC600; /* Main Primary color */
$color-primary-1: #FFDD69;
$color-primary-2: #FFCE22;
$color-primary-3: #B78E00;
$color-primary-4: #513F00;
$color-secondary-1-0: #0A4DB7; /* Main Secondary color (1) */
$color-secondary-1-1: #6C9CE8;
$color-secondary-1-2: #2E6FD6;
$color-secondary-1-3: #073379;
$color-secondary-1-4: #021636;
$color-secondary-2-0: #5809BB; /* Main Secondary color (2) */
$color-secondary-2-1: #A36BE9;
$color-secondary-2-2: #782DD8;
$color-secondary-2-3: #3A077C;
$color-secondary-2-4: #190237;
/* As RGBa codes */
$rgba-primary-0: rgba(255,198, 0,1); /* Main Primary color */
$rgba-primary-1: rgba(255,221,105,1);
$rgba-primary-2: rgba(255,206, 34,1);
$rgba-primary-3: rgba(183,142, 0,1);
$rgba-primary-4: rgba( 81, 63, 0,1);
$rgba-secondary-1-0: rgba( 10, 77,183,1); /* Main Secondary color (1) */
$rgba-secondary-1-1: rgba(108,156,232,1);
$rgba-secondary-1-2: rgba( 46,111,214,1);
$rgba-secondary-1-3: rgba( 7, 51,121,1);
$rgba-secondary-1-4: rgba( 2, 22, 54,1);
$rgba-secondary-2-0: rgba( 88, 9,187,1); /* Main Secondary color (2) */
$rgba-secondary-2-1: rgba(163,107,233,1);
$rgba-secondary-2-2: rgba(120, 45,216,1);
$rgba-secondary-2-3: rgba( 58, 7,124,1);
$rgba-secondary-2-4: rgba( 25, 2, 55,1);
/* Generated by Paletton.com © 2002-2014 */
/* http://paletton.com */

@ -0,0 +1 @@
@import url('http://fonts.googleapis.com/css?family=Ubuntu:400,700|Cabin+Condensed:400,600');

@ -0,0 +1,15 @@
@include phone {
#container{
width: 300px;
margin: 10px auto;
@include box-shadow(none);
form.voucher-form{
input{
&.voucher-code{
width: 180px;
}
}
}
}
}

@ -0,0 +1,28 @@
@include phone {
$overlayWidth: $videoWidth;
$overlayHeadline: 16px;
$overlayHeadlineMargin: 14px;
$overlayPadding: 20px;
$overlayHeight: $videoHeight + $overlayHeadlineMargin + $overlayHeadline;
.overlay.scanner {
width: $overlayWidth;
height: $overlayHeight;
padding: $overlayPadding;
margin-top: -($overlayHeight)/2 - $overlayPadding;
margin-left: -($overlayWidth + 2*$overlayPadding)/2;
background-color: $overlayBackground;
@include box-shadow(none);
& > .header{
margin-bottom: $overlayHeadlineMargin;
h4, .close{
line-height: $overlayHeadline;
}
.close{
height: $overlayHeadline;
width: $overlayHeadline;
}
}
}
}

@ -0,0 +1,42 @@
@include phone {
$videoWidth: 300px;
$videoHeight: 400px;
#interactive.viewport{
width: $videoWidth;
height: $videoWidth;
overflow: hidden;
}
#interactive.viewport canvas, video{
margin-top: ($videoWidth - $videoHeight)/2;
width: $videoWidth;
height: $videoHeight;
&.drawingBuffer{
margin-left: -$videoWidth;
}
}
#result_strip{
margin-top: 5px;
padding-top: 5px;
ul.thumbnails{
& > li{
width: $videoWidth/2;
.thumbnail{
.imgWrapper{
width: $videoWidth/2 - 20px;
height: $videoWidth/2 - 20px;
overflow: hidden;
img{
margin-top: (($videoWidth/2 - 20px) - ($videoHeight/2 - 20px))/2;
width: $videoWidth/2 - 20px;
height: $videoHeight/2 - 20px;
}
}
}
}
}
}
}

@ -0,0 +1,14 @@
@import "compass/css3";
@import "variables";
@import "utility";
/* usual styles */
@import "fonts";
@import "viewport";
@import "overlay";
@import "icons";
@import "tablet";
@import "phone";

@ -0,0 +1,56 @@
@import "compass/css3";
@import "variables";
@import "utility";
/* usual styles */
@import "colors";
@import "fonts";
@import "viewport";
@import "overlay";
@import "icons";
@import "tablet";
@import "phone";
body{
background-color: #FFF;
margin: 0px;
font-family: $text-font;
color: #1e1e1e;
font-weight: normal;
padding-top: 0;
}
h1,h2,h3,h4 {
font-family: $header-font;
}
header {
background: $color-primary-0;
padding: 1em;
.headline {
width: 640px;
margin: 0 auto;
h1 {
color: $color-primary-1;
font-size: 3em;
margin-bottom: 0;
}
h2 {
margin-top: 0.2em;
}
}
}
footer {
background: $color-secondary-1-0;
color: $color-secondary-1-1;
padding: 1em 2em 2em;
}
#container{
width: 640px;
margin: 20px auto;
padding: 10px;
}

@ -39,15 +39,11 @@
<option value="code_39">Code 39</option> <option value="code_39">Code 39</option>
<option value="code_39_vin">Code 39 VIN</option> <option value="code_39_vin">Code 39 VIN</option>
<option value="ean">EAN</option> <option value="ean">EAN</option>
<option value="ean_extended">EAN-extended</option>
<option value="ean_8">EAN-8</option> <option value="ean_8">EAN-8</option>
<option value="upc">UPC</option> <option value="upc">UPC</option>
<option value="upc_e">UPC-E</option> <option value="upc_e">UPC-E</option>
<option value="codabar">Codabar</option> <option value="codabar">Codabar</option>
<option value="i2of5">I2of5</option> <option value="i2of5">I2of5</option>
<option value="i2of5">Interleaved 2 of 5</option>
<option value="2of5">Standard 2 of 5</option>
<option value="code_93">Code 93</option>
</select> </select>
</fieldset> </fieldset>
</div> </div>

@ -1,7 +1,7 @@
$(function() { $(function() {
var App = { var App = {
init: function() { init: function() {
var config = this.config[this.state.decoder.readers[0].format] || this.config.default; var config = this.config[this.state.decoder.readers[0]] || this.config.default;
config = $.extend(true, {}, config, this.state); config = $.extend(true, {}, config, this.state);
Quagga.init(config, function() { Quagga.init(config, function() {
App.attachListeners(); App.attachListeners();
@ -95,20 +95,7 @@ $(function() {
inputMapper: { inputMapper: {
decoder: { decoder: {
readers: function(value) { readers: function(value) {
if (value === 'ean_extended') { return [value + "_reader"];
return [{
format: "ean_reader",
config: {
supplements: [
'ean_5_reader', 'ean_2_reader'
]
}
}];
}
return [{
format: value + "_reader",
config: {}
}];
} }
}, },
inputStream: { inputStream: {
@ -122,10 +109,7 @@ $(function() {
src: "../test/fixtures/code_128/" src: "../test/fixtures/code_128/"
}, },
decoder : { decoder : {
readers : [{ readers : ["code_128_reader"]
format: "code_128_reader",
config: {}
}]
} }
} }
}; };

@ -14,9 +14,6 @@ module.exports = function(config) {
'test/test-main-integration.js': ['webpack'] 'test/test-main-integration.js': ['webpack']
}, },
webpack: { webpack: {
entry: [
'./src/quagga.js'
],
module: { module: {
loaders: [{ loaders: [{
test: /\.jsx?$/, test: /\.jsx?$/,
@ -27,7 +24,6 @@ module.exports = function(config) {
resolve: { resolve: {
modules: [ modules: [
path.resolve('./src/input/'), path.resolve('./src/input/'),
path.resolve('./src/common/'),
'node_modules' 'node_modules'
] ]
}, },
@ -40,6 +36,7 @@ module.exports = function(config) {
plugins: [ plugins: [
'karma-chrome-launcher', 'karma-chrome-launcher',
'karma-mocha', 'karma-mocha',
'karma-requirejs',
'karma-chai', 'karma-chai',
'karma-sinon', 'karma-sinon',
'karma-sinon-chai', 'karma-sinon-chai',
@ -48,7 +45,7 @@ module.exports = function(config) {
reporters: ['progress'], reporters: ['progress'],
port: 9876, port: 9876,
colors: true, colors: true,
logLevel: config.LOG_INFO, // LOG_DEBUG logLevel: config.LOG_INFO,
autoWatch: true, autoWatch: true,
browsers: ['Chrome'], browsers: ['Chrome'],
singleRun: false singleRun: false

@ -13,9 +13,6 @@ module.exports = function(config) {
'test/test-main.js': ['webpack'] 'test/test-main.js': ['webpack']
}, },
webpack: { webpack: {
entry: [
'./src/quagga.js'
],
module: { module: {
loaders: [{ loaders: [{
test: /\.jsx?$/, test: /\.jsx?$/,
@ -26,13 +23,12 @@ module.exports = function(config) {
}, { }, {
test: /\.js$/, test: /\.js$/,
include: path.resolve('src'), include: path.resolve('src'),
loader: 'babel-istanbul-loader' loader: 'babel-istanbul'
}] }]
}, },
resolve: { resolve: {
modules: [ modules: [
path.resolve('./src/input/'), path.resolve('./src/input/'),
path.resolve('./test/mocks/'),
'node_modules' 'node_modules'
] ]
}, },

@ -24,7 +24,7 @@ InputStream.createImageStream = function() {
function loadImages() { function loadImages() {
loaded = false; loaded = false;
GetPixels(baseUrl, _config.mime, function(err, pixels) { GetPixels(baseUrl, function(err, pixels) {
if (err) { if (err) {
console.log(err); console.log(err);
exit(1); exit(1);

File diff suppressed because one or more lines are too long

@ -1,25 +1,22 @@
{ {
"name": "quagga", "name": "quagga",
"version": "0.12.1", "version": "0.10.4",
"description": "An advanced barcode-scanner written in JavaScript", "description": "An advanced barcode-scanner written in JavaScript",
"main": "lib/quagga.js", "main": "lib/quagga.js",
"typings": "type-definitions/quagga.d.ts",
"browser": "dist/quagga.min.js", "browser": "dist/quagga.min.js",
"typings": "type-definitions/quagga.d.ts",
"devDependencies": { "devDependencies": {
"async": "^1.4.2", "async": "^1.4.2",
"babel-cli": "^6.5.1", "babel-cli": "^6.5.1",
"babel-core": "^6.21.0", "babel-core": "^6.7.4",
"babel-eslint": "^7.1.1", "babel-eslint": "^6.0.0",
"babel-istanbul": "^0.8.0",
"babel-istanbul-loader": "^0.1.0", "babel-istanbul-loader": "^0.1.0",
"babel-loader": "^6.2.10", "babel-loader": "^6.2.4",
"babel-plugin-add-module-exports": "^0.2.1", "babel-plugin-add-module-exports": "^0.1.2",
"babel-plugin-check-es2015-constants": "^6.3.13", "babel-plugin-check-es2015-constants": "^6.3.13",
"babel-plugin-lodash": "^3.2.11", "babel-plugin-lodash": "^2.2.1",
"babel-plugin-transform-es2015-arrow-functions": "^6.3.13", "babel-plugin-transform-es2015-arrow-functions": "^6.3.13",
"babel-plugin-transform-es2015-block-scoped-functions": "^6.3.13", "babel-plugin-transform-es2015-block-scoped-functions": "^6.3.13",
"babel-plugin-transform-es2015-block-scoping": "^6.21.0", "babel-plugin-transform-es2015-block-scoping": "^6.3.13",
"babel-plugin-transform-es2015-classes": "^6.3.13", "babel-plugin-transform-es2015-classes": "^6.3.13",
"babel-plugin-transform-es2015-computed-properties": "^6.3.13", "babel-plugin-transform-es2015-computed-properties": "^6.3.13",
"babel-plugin-transform-es2015-destructuring": "^6.3.13", "babel-plugin-transform-es2015-destructuring": "^6.3.13",
@ -28,7 +25,7 @@
"babel-plugin-transform-es2015-literals": "^6.3.13", "babel-plugin-transform-es2015-literals": "^6.3.13",
"babel-plugin-transform-es2015-modules-commonjs": "^6.3.13", "babel-plugin-transform-es2015-modules-commonjs": "^6.3.13",
"babel-plugin-transform-es2015-object-super": "^6.3.13", "babel-plugin-transform-es2015-object-super": "^6.3.13",
"babel-plugin-transform-es2015-parameters": "^6.21.0", "babel-plugin-transform-es2015-parameters": "^6.3.13",
"babel-plugin-transform-es2015-shorthand-properties": "^6.3.13", "babel-plugin-transform-es2015-shorthand-properties": "^6.3.13",
"babel-plugin-transform-es2015-spread": "^6.3.13", "babel-plugin-transform-es2015-spread": "^6.3.13",
"babel-plugin-transform-es2015-sticky-regex": "^6.3.13", "babel-plugin-transform-es2015-sticky-regex": "^6.3.13",
@ -36,34 +33,34 @@
"babel-plugin-transform-es2015-typeof-symbol": "^6.3.13", "babel-plugin-transform-es2015-typeof-symbol": "^6.3.13",
"babel-plugin-transform-es2015-unicode-regex": "^6.3.13", "babel-plugin-transform-es2015-unicode-regex": "^6.3.13",
"babel-plugin-transform-object-rest-spread": "^6.5.0", "babel-plugin-transform-object-rest-spread": "^6.5.0",
"babel-plugin-transform-regenerator": "^6.21.0", "babel-plugin-transform-regenerator": "^6.3.13",
"chai": "^3.4.1", "chai": "^3.4.1",
"core-js": "^2.4.1", "core-js": "^1.2.1",
"cross-env": "^3.1.4", "cross-env": "^1.0.7",
"eslint": "^1.10.3", "eslint": "^1.10.3",
"grunt": "^0.4.5", "grunt": "^0.4.5",
"grunt-cli": "^0.1.13", "grunt-cli": "^0.1.13",
"grunt-contrib-nodeunit": "^0.4.1", "grunt-contrib-nodeunit": "^0.4.1",
"grunt-karma": "^2.0.0", "grunt-karma": "^0.12.1",
"isparta-loader": "^2.0.0", "isparta-loader": "^2.0.0",
"karma": "^1.3.0", "karma": "^0.13.9",
"karma-chai": "0.1.0", "karma-chai": "0.1.0",
"karma-chrome-launcher": "^2.0.0", "karma-chrome-launcher": "^0.2.0",
"karma-coverage": "^1.1.1", "karma-coverage": "^0.5.2",
"karma-firefox-launcher": "^0.1.7", "karma-firefox-launcher": "^0.1.7",
"karma-mocha": "~0.2.0", "karma-mocha": "~0.2.0",
"karma-phantomjs-launcher": "^0.2.1", "karma-phantomjs-launcher": "^0.2.1",
"karma-sinon": "^1.0.4", "karma-sinon": "^1.0.4",
"karma-sinon-chai": "^1.1.0", "karma-sinon-chai": "^1.1.0",
"karma-source-map-support": "^1.1.0", "karma-source-map-support": "^1.1.0",
"karma-webpack": "^1.8.1", "karma-webpack": "^1.7.0",
"lolex": "^1.4.0", "lolex": "^1.4.0",
"mocha": "^2.3.2", "mocha": "^2.3.2",
"phantomjs": "^1.9.18", "phantomjs": "^1.9.18",
"sinon": "^1.17.7", "sinon": "^1.16.1",
"sinon-chai": "^2.8.0", "sinon-chai": "^2.8.0",
"webpack": "^2.2.1", "webpack": "^2.1.0-beta.4",
"webpack-sources": "^0.1.4" "webpack-sources": "^0.1.1"
}, },
"directories": { "directories": {
"doc": "doc" "doc": "doc"
@ -108,8 +105,9 @@
"gl-mat2": "^1.0.0", "gl-mat2": "^1.0.0",
"gl-vec2": "^1.0.0", "gl-vec2": "^1.0.0",
"gl-vec3": "^1.0.3", "gl-vec3": "^1.0.3",
"lodash": "^4.17.4", "lodash": "^4.6.1",
"ndarray": "^1.0.18", "ndarray": "^1.0.18",
"ndarray-linear-interpolate": "^1.0.0" "ndarray-linear-interpolate": "^1.0.0",
"webrtc-adapter": "^1.0.6"
} }
} }

@ -1,33 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQC9ORap3LvRegtrhRc8dLdH9Bp2QokcKEsWbtvyhtjisRRm2slK
A6Q11McB/YTb7oImFfNaCX+7vdM1oVXVLJ0ekQaNljXG5Dy7DXEcT1V6gpN4xmZJ
8f/KZ45VBINN0Ha74L7nS4kgImh5yvNolNr4IdlSjGf09kciFy8S3kPlGQIDAQAB
AoGAYDlaxBCC1liY3Bl3IoA7//QrTL4zGUWIQaUoZmGag1UHifJycBf/9nv4o5N3
b5wPRSzebofsE93JPTmI+3nPf62k5rS2xOo8swwOZc+f5/v0EnUNixD7P0jBiLVR
B8kbMvNdNn33HuynW1/MSBFE0cfeDH2i8SVl+Z+fHYIUW10CQQD0yWB8xeM8AxYB
/ZZWClem6gf1lQAYLzid3x51pkLqRFpX+rG251cSBUouE+kVO14j2xrBqCyyOwNu
17eazy3DAkEAxeQdWP9b11ihKOf/kjXiczltLnBsotn6K9EEAe0QuH/6iXLm86mL
ZiQe+TrY1GWbK3ns0sfXgNJ2aeaRkeZn8wJAWF5WedTKisCmckOEwTzslbJI+0w2
A4UQkFWa3mgOIhpY7wfunhP35+aG+AlyDJspChKwHxdCQ3lwbNRtUPLYFwJBAK8G
9QIbUbLlPB1/HOfH6xM4rp3NZ/idzQxmISJG+GwHHaPmUekfgyEDP7X2W4N4nsbU
XyeLA8t32q4N9aDS5gsCQDHqhsXqnY6e4IEZrvf90l2V1PpnTKfEl/F5wye3g69G
JN57scVUBHP/KKoyfge0fytWiQN/56KvWH+G5+N/JyA=
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIC2zCCAkSgAwIBAgIJALUDN95Or7XlMA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV
BAYTAkFUMQ8wDQYDVQQIEwZTdHlyaWExDTALBgNVBAcTBEdyYXoxETAPBgNVBAoT
CFF1YWdnYUpTMREwDwYDVQQDEwhxdWFnZ2FqczAeFw0xNzAxMDgxNjI5MjhaFw0x
ODAxMDgxNjI5MjhaMFMxCzAJBgNVBAYTAkFUMQ8wDQYDVQQIEwZTdHlyaWExDTAL
BgNVBAcTBEdyYXoxETAPBgNVBAoTCFF1YWdnYUpTMREwDwYDVQQDEwhxdWFnZ2Fq
czCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvTkWqdy70XoLa4UXPHS3R/Qa
dkKJHChLFm7b8obY4rEUZtrJSgOkNdTHAf2E2+6CJhXzWgl/u73TNaFV1SydHpEG
jZY1xuQ8uw1xHE9VeoKTeMZmSfH/ymeOVQSDTdB2u+C+50uJICJoecrzaJTa+CHZ
Uoxn9PZHIhcvEt5D5RkCAwEAAaOBtjCBszAdBgNVHQ4EFgQUYm5+uJVOOGiYa+Vx
2o++VHyWkwIwgYMGA1UdIwR8MHqAFGJufriVTjhomGvlcdqPvlR8lpMCoVekVTBT
MQswCQYDVQQGEwJBVDEPMA0GA1UECBMGU3R5cmlhMQ0wCwYDVQQHEwRHcmF6MREw
DwYDVQQKEwhRdWFnZ2FKUzERMA8GA1UEAxMIcXVhZ2dhanOCCQC1AzfeTq+15TAM
BgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBACyzC/CKL1mgTuNgFDuUf6u+
YMnqlc9wcnEaFuvXnkSh6fT+qMZm188C/tlZwcWTrGmoCM0K6mX1TpHOjm8vbeXZ
diezAVGIVN3VoHqm6yJldI2rgFI9r5BfwAWYC8XNjqnT3U6cm4k8iC7jmLC+dT9r
Ysx2ucAF6lNHayekRmNq
-----END CERTIFICATE-----

@ -1,30 +0,0 @@
# taken from http://www.piware.de/2011/01/creating-an-https-server-in-python/
# generate server.xml with the following command:
# openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes
# run as follows:
# python simple-https-server.py
# then in your browser, visit:
# https://localhost:4443
import BaseHTTPServer, SimpleHTTPServer
import ssl
import sys, getopt
host = 'localhost'
port = 4443
try:
opts, args = getopt.getopt(sys.argv[1:],"",["host=", "port="])
except getopt.GetoptError:
print 'simple-https-server.py --host <host> --port <port>'
sys.exit(2)
for opt, arg in opts:
if opt in ("--host"):
host = arg
elif opt in ("--port"):
port = int(arg)
print 'host is ', host
print 'port is ', port
httpd = BaseHTTPServer.HTTPServer((host, port), SimpleHTTPServer.SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket (httpd.socket, certfile='./server.pem', server_side=True)
httpd.serve_forever()

@ -488,7 +488,7 @@ export function grayAndHalfSampleFromCanvasData(canvasData, size, outArray) {
while (bottomRowIdx < endIdx) { while (bottomRowIdx < endIdx) {
for ( i = 0; i < outWidth; i++) { for ( i = 0; i < outWidth; i++) {
outArray[outImgIdx] = ( outArray[outImgIdx] = Math.floor((
(0.299 * canvasData[topRowIdx * 4 + 0] + (0.299 * canvasData[topRowIdx * 4 + 0] +
0.587 * canvasData[topRowIdx * 4 + 1] + 0.587 * canvasData[topRowIdx * 4 + 1] +
0.114 * canvasData[topRowIdx * 4 + 2]) + 0.114 * canvasData[topRowIdx * 4 + 2]) +
@ -500,7 +500,7 @@ export function grayAndHalfSampleFromCanvasData(canvasData, size, outArray) {
0.114 * canvasData[(bottomRowIdx) * 4 + 2]) + 0.114 * canvasData[(bottomRowIdx) * 4 + 2]) +
(0.299 * canvasData[(bottomRowIdx + 1) * 4 + 0] + (0.299 * canvasData[(bottomRowIdx + 1) * 4 + 0] +
0.587 * canvasData[(bottomRowIdx + 1) * 4 + 1] + 0.587 * canvasData[(bottomRowIdx + 1) * 4 + 1] +
0.114 * canvasData[(bottomRowIdx + 1) * 4 + 2])) / 4; 0.114 * canvasData[(bottomRowIdx + 1) * 4 + 2])) / 4);
outImgIdx++; outImgIdx++;
topRowIdx = topRowIdx + 2; topRowIdx = topRowIdx + 2;
bottomRowIdx = bottomRowIdx + 2; bottomRowIdx = bottomRowIdx + 2;
@ -521,8 +521,8 @@ export function computeGray(imageData, outArray, config) {
} }
} else { } else {
for (i = 0; i < l; i++) { for (i = 0; i < l; i++) {
outArray[i] = outArray[i] = Math.floor(
0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2]; 0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2]);
} }
} }
}; };

@ -50,21 +50,9 @@ export default (function() {
var event = getEvent(eventName), var event = getEvent(eventName),
subscribers = event.subscribers; subscribers = event.subscribers;
// Publish one-time subscriptions
subscribers.filter(function(subscriber) {
return !!subscriber.once;
}).forEach((subscriber) => {
publishSubscription(subscriber, data);
});
// remove them from the subscriber
event.subscribers = subscribers.filter(function(subscriber) { event.subscribers = subscribers.filter(function(subscriber) {
return !subscriber.once;
});
// publish the rest
event.subscribers.forEach((subscriber) => {
publishSubscription(subscriber, data); publishSubscription(subscriber, data);
return !subscriber.once;
}); });
}, },
once: function(event, callback, async) { once: function(event, callback, async) {

@ -2,7 +2,7 @@ export default {
drawRect: function(pos, size, ctx, style){ drawRect: function(pos, size, ctx, style){
ctx.strokeStyle = style.color; ctx.strokeStyle = style.color;
ctx.fillStyle = style.color; ctx.fillStyle = style.color;
ctx.lineWidth = style.lineWidth || 1; ctx.lineWidth = 1;
ctx.beginPath(); ctx.beginPath();
ctx.strokeRect(pos.x, pos.y, size.x, size.y); ctx.strokeRect(pos.x, pos.y, size.x, size.y);
}, },

@ -1,17 +0,0 @@
export function enumerateDevices() {
if (navigator.mediaDevices
&& typeof navigator.mediaDevices.enumerateDevices === 'function') {
return navigator.mediaDevices.enumerateDevices();
}
return Promise.reject(new Error('enumerateDevices is not defined'));
};
export function getUserMedia(constraints) {
if (navigator.mediaDevices
&& typeof navigator.mediaDevices.getUserMedia === 'function') {
return navigator.mediaDevices
.getUserMedia(constraints);
}
return Promise.reject(new Error('getUserMedia is not defined'));
}

@ -24,28 +24,3 @@ Math.imul = Math.imul || function(a, b) {
// the final |0 converts the unsigned value into a signed value // the final |0 converts the unsigned value into a signed value
return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0); return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0);
}; };
if (typeof Object.assign !== 'function') {
Object.assign = function(target) { // .length of function is 2
'use strict';
if (target === null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource !== null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
};
}

@ -7,27 +7,19 @@ import Code39VINReader from '../reader/code_39_vin_reader';
import CodabarReader from '../reader/codabar_reader'; import CodabarReader from '../reader/codabar_reader';
import UPCReader from '../reader/upc_reader'; import UPCReader from '../reader/upc_reader';
import EAN8Reader from '../reader/ean_8_reader'; import EAN8Reader from '../reader/ean_8_reader';
import EAN2Reader from '../reader/ean_2_reader';
import EAN5Reader from '../reader/ean_5_reader';
import UPCEReader from '../reader/upc_e_reader'; import UPCEReader from '../reader/upc_e_reader';
import I2of5Reader from '../reader/i2of5_reader'; import I2of5Reader from '../reader/i2of5_reader';
import TwoOfFiveReader from '../reader/2of5_reader';
import Code93Reader from '../reader/code_93_reader';
const READERS = { const READERS = {
code_128_reader: Code128Reader, code_128_reader: Code128Reader,
ean_reader: EANReader, ean_reader: EANReader,
ean_5_reader: EAN5Reader,
ean_2_reader: EAN2Reader,
ean_8_reader: EAN8Reader, ean_8_reader: EAN8Reader,
code_39_reader: Code39Reader, code_39_reader: Code39Reader,
code_39_vin_reader: Code39VINReader, code_39_vin_reader: Code39VINReader,
codabar_reader: CodabarReader, codabar_reader: CodabarReader,
upc_reader: UPCReader, upc_reader: UPCReader,
upc_e_reader: UPCEReader, upc_e_reader: UPCEReader,
i2of5_reader: I2of5Reader, i2of5_reader: I2of5Reader
'2of5_reader': TwoOfFiveReader,
code_93_reader: Code93Reader
}; };
export default { export default {
create: function(config, inputImageWrapper) { create: function(config, inputImageWrapper) {
@ -82,8 +74,7 @@ export default {
function initReaders() { function initReaders() {
config.readers.forEach(function(readerConfig) { config.readers.forEach(function(readerConfig) {
var reader, var reader,
configuration = {}, configuration = {};
supplements = [];
if (typeof readerConfig === 'object') { if (typeof readerConfig === 'object') {
reader = readerConfig.format; reader = readerConfig.format;
@ -94,13 +85,7 @@ export default {
if (ENV.development) { if (ENV.development) {
console.log("Before registering reader: ", reader); console.log("Before registering reader: ", reader);
} }
if (configuration.supplements) { _barcodeReaders.push(new READERS[reader](configuration));
supplements = configuration
.supplements.map((supplement) => {
return new READERS[supplement]();
});
}
_barcodeReaders.push(new READERS[reader](configuration, supplements));
}); });
if (ENV.development) { if (ENV.development) {
console.log("Registered Readers: " + _barcodeReaders console.log("Registered Readers: " + _barcodeReaders

@ -1,12 +1,7 @@
import {omit, pick} from 'lodash'; import {merge, pick} from 'lodash';
import {getUserMedia, enumerateDevices} from 'mediaDevices';
const facingMatching = { var streamRef,
"user": /front/i, loadedDataHandler;
"environment": /back/i
};
var streamRef;
function waitForVideo(video) { function waitForVideo(video) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -14,7 +9,7 @@ function waitForVideo(video) {
function checkVideo() { function checkVideo() {
if (attempts > 0) { if (attempts > 0) {
if (video.videoWidth > 10 && video.videoHeight > 10) { if (video.videoWidth > 0 && video.videoHeight > 0) {
if (ENV.development) { if (ENV.development) {
console.log(video.videoWidth + "px x " + video.videoHeight + "px"); console.log(video.videoWidth + "px x " + video.videoHeight + "px");
} }
@ -38,18 +33,15 @@ function waitForVideo(video) {
* @param {Object} video * @param {Object} video
*/ */
function initCamera(video, constraints) { function initCamera(video, constraints) {
return getUserMedia(constraints) return navigator.mediaDevices.getUserMedia(constraints)
.then((stream) => { .then((stream) => {
return new Promise((resolve) => { return new Promise((resolve, reject) => {
streamRef = stream; streamRef = stream;
video.setAttribute("autoplay", true); video.src = window.URL.createObjectURL(stream);
video.setAttribute('muted', true); video.onloadedmetadata = (e) => {
video.setAttribute('playsinline', true);
video.srcObject = stream;
video.addEventListener('loadedmetadata', () => {
video.play(); video.play();
resolve(); resolve();
}); };
}); });
}) })
.then(waitForVideo.bind(null, video)); .then(waitForVideo.bind(null, video));
@ -59,43 +51,46 @@ function deprecatedConstraints(videoConstraints) {
const normalized = pick(videoConstraints, ["width", "height", "facingMode", const normalized = pick(videoConstraints, ["width", "height", "facingMode",
"aspectRatio", "deviceId"]); "aspectRatio", "deviceId"]);
if (typeof videoConstraints.minAspectRatio !== 'undefined' && if (typeof videoConstraints["minAspectRatio"] !== 'undefined' &&
videoConstraints.minAspectRatio > 0) { videoConstraints["minAspectRatio"] > 0) {
normalized.aspectRatio = videoConstraints.minAspectRatio; normalized["aspectRatio"] = videoConstraints["minAspectRatio"];
console.log("WARNING: Constraint 'minAspectRatio' is deprecated; Use 'aspectRatio' instead"); console.log("WARNING: Constraint 'minAspectRatio' is deprecated; Use 'aspectRatio' instead");
} }
if (typeof videoConstraints.facing !== 'undefined') { if (typeof videoConstraints["facing"] !== 'undefined') {
normalized.facingMode = videoConstraints.facing; normalized["facingMode"] = videoConstraints["facing"];
console.log("WARNING: Constraint 'facing' is deprecated. Use 'facingMode' instead'"); console.log("WARNING: Constraint 'facing' is deprecated. Use 'facingMode' instead'");
} }
return normalized; return normalized;
} }
export function pickConstraints(videoConstraints) { function applyCameraFacing(facing, constraints) {
const normalizedConstraints = { if (typeof constraints.video.deviceId !== 'undefined' || !facing){
audio: false, return Promise.resolve(constraints);
video: deprecatedConstraints(videoConstraints)
};
if (normalizedConstraints.video.deviceId
&& normalizedConstraints.video.facingMode) {
delete normalizedConstraints.video.facingMode;
} }
return Promise.resolve(normalizedConstraints); if ( typeof MediaStreamTrack !== 'undefined' &&
} typeof MediaStreamTrack.getSources !== 'undefined') {
return new Promise((resolve, reject) => {
function enumerateVideoDevices() { MediaStreamTrack.getSources((sourceInfos) => {
return enumerateDevices() const videoSource = sourceInfos.filter((sourceInfo) => (
.then(devices => devices.filter(device => device.kind === 'videoinput')); sourceInfo.kind === "video" && sourceInfo.facing === facing
))[0];
if (videoSource) {
return resolve(merge({}, constraints,
{video: {deviceId: videoSource.id}}));
}
return resolve(constraints);
});
});
}
return Promise.resolve(merge({}, constraints, {video: {facingMode: facing}}));
} }
function getActiveTrack() { function pickConstraints(videoConstraints) {
if (streamRef) { const constraints = {
const tracks = streamRef.getVideoTracks(); audio: false,
if (tracks && tracks.length) { video: deprecatedConstraints(videoConstraints)
return tracks[0]; };
} return applyCameraFacing(constraints.video.facingMode, constraints);
}
} }
export default { export default {
@ -109,11 +104,5 @@ export default {
tracks[0].stop(); tracks[0].stop();
} }
streamRef = null; streamRef = null;
}, }
enumerateVideoDevices,
getActiveStreamLabel: function() {
const track = getActiveTrack();
return track ? track.label : '';
},
getActiveTrack
}; };

@ -1,146 +0,0 @@
// Scraped from https://github.com/exif-js/exif-js
const ExifTags = {0x0112: "orientation"};
export const AvailableTags = Object.keys(ExifTags).map(key => ExifTags[key]);
export function findTagsInObjectURL(src, tags = AvailableTags) {
if (/^blob\:/i.test(src)) {
return objectURLToBlob(src)
.then(readToBuffer)
.then(buffer => findTagsInBuffer(buffer, tags));
}
return Promise.resolve(null);
}
export function base64ToArrayBuffer(dataUrl) {
const base64 = dataUrl.replace(/^data\:([^\;]+)\;base64,/gmi, ''),
binary = atob(base64),
len = binary.length,
buffer = new ArrayBuffer(len),
view = new Uint8Array(buffer);
for (let i = 0; i < len; i++) {
view[i] = binary.charCodeAt(i);
}
return buffer;
}
function readToBuffer(blob) {
return new Promise(resolve => {
const fileReader = new FileReader();
fileReader.onload = function(e) {
return resolve(e.target.result);
};
fileReader.readAsArrayBuffer(blob);
});
}
function objectURLToBlob(url) {
return new Promise((resolve, reject) => {
const http = new XMLHttpRequest();
http.open("GET", url, true);
http.responseType = "blob";
http.onreadystatechange = function () {
if (http.readyState === XMLHttpRequest.DONE && (http.status === 200 || http.status === 0)) {
resolve(this.response);
}
};
http.onerror = reject;
http.send();
});
}
export function findTagsInBuffer(file, selectedTags = AvailableTags) {
const dataView = new DataView(file),
length = file.byteLength,
exifTags = selectedTags.reduce((result, selectedTag) => {
const exifTag = Object.keys(ExifTags).filter(tag => ExifTags[tag] === selectedTag)[0];
if (exifTag) {
result[exifTag] = selectedTag;
}
return result;
}, {});
let offset = 2,
marker;
if ((dataView.getUint8(0) !== 0xFF) || (dataView.getUint8(1) !== 0xD8)) {
return false;
}
while (offset < length) {
if (dataView.getUint8(offset) !== 0xFF) {
return false;
}
marker = dataView.getUint8(offset + 1);
if (marker === 0xE1) {
return readEXIFData(dataView, offset + 4, exifTags);
} else {
offset += 2 + dataView.getUint16(offset + 2);
}
}
}
function readEXIFData(file, start, exifTags) {
if (getStringFromBuffer(file, start, 4) !== "Exif") {
return false;
}
const tiffOffset = start + 6;
let bigEnd,
tags;
if (file.getUint16(tiffOffset) === 0x4949) {
bigEnd = false;
} else if (file.getUint16(tiffOffset) === 0x4D4D) {
bigEnd = true;
} else {
return false;
}
if (file.getUint16(tiffOffset + 2, !bigEnd) !== 0x002A) {
return false;
}
const firstIFDOffset = file.getUint32(tiffOffset + 4, !bigEnd);
if (firstIFDOffset < 0x00000008) {
return false;
}
tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, exifTags, bigEnd);
return tags;
}
function readTags(file, tiffStart, dirStart, strings, bigEnd) {
const entries = file.getUint16(dirStart, !bigEnd),
tags = {};
for (let i = 0; i < entries; i++) {
const entryOffset = dirStart + i * 12 + 2,
tag = strings[file.getUint16(entryOffset, !bigEnd)];
if (tag) {
tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
}
}
return tags;
}
function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
const type = file.getUint16(entryOffset + 2, !bigEnd),
numValues = file.getUint32(entryOffset + 4, !bigEnd);
switch (type) {
case 3:
if (numValues === 1) {
return file.getUint16(entryOffset + 8, !bigEnd);
}
}
}
function getStringFromBuffer(buffer, start, length) {
let outstr = "";
for (let n = start; n < start + length; n++) {
outstr += String.fromCharCode(buffer.getUint8(n));
}
return outstr;
}

@ -4,23 +4,6 @@ import {
computeGray computeGray
} from '../common/cv_utils'; } from '../common/cv_utils';
const TO_RADIANS = Math.PI / 180;
function adjustCanvasSize(canvas, targetSize) {
if (canvas.width !== targetSize.x) {
if (ENV.development) {
console.log("WARNING: canvas-size needs to be adjusted");
}
canvas.width = targetSize.x;
}
if (canvas.height !== targetSize.y) {
if (ENV.development) {
console.log("WARNING: canvas-size needs to be adjusted");
}
canvas.height = targetSize.y;
}
}
var FrameGrabber = {}; var FrameGrabber = {};
FrameGrabber.create = function(inputStream, canvas) { FrameGrabber.create = function(inputStream, canvas) {
@ -71,35 +54,9 @@ FrameGrabber.create = function(inputStream, canvas) {
_that.grab = function() { _that.grab = function() {
var doHalfSample = _streamConfig.halfSample, var doHalfSample = _streamConfig.halfSample,
frame = inputStream.getFrame(), frame = inputStream.getFrame(),
drawable = frame,
drawAngle = 0,
ctxData; ctxData;
if (drawable) { if (frame) {
adjustCanvasSize(_canvas, _canvasSize); _ctx.drawImage(frame, 0, 0, _canvasSize.x, _canvasSize.y);
if (_streamConfig.type === 'ImageStream') {
drawable = frame.img;
if (frame.tags && frame.tags.orientation) {
switch (frame.tags.orientation) {
case 6:
drawAngle = 90 * TO_RADIANS;
break;
case 8:
drawAngle = -90 * TO_RADIANS;
break;
}
}
}
if (drawAngle !== 0) {
_ctx.translate(_canvasSize.x / 2, _canvasSize.y / 2);
_ctx.rotate(drawAngle);
_ctx.drawImage(drawable, -_canvasSize.y / 2, -_canvasSize.x / 2, _canvasSize.y, _canvasSize.x);
_ctx.rotate(-drawAngle);
_ctx.translate(-_canvasSize.x / 2, -_canvasSize.y / 2);
} else {
_ctx.drawImage(drawable, 0, 0, _canvasSize.x, _canvasSize.y);
}
ctxData = _ctx.getImageData(_sx, _sy, _size.x, _size.y).data; ctxData = _ctx.getImageData(_sx, _sy, _size.x, _size.y).data;
if (doHalfSample){ if (doHalfSample){
grayAndHalfSampleFromCanvasData(ctxData, _size, _data); grayAndHalfSampleFromCanvasData(ctxData, _size, _data);

@ -1,5 +1,3 @@
import {findTagsInObjectURL} from './exif_helper';
var ImageLoader = {}; var ImageLoader = {};
ImageLoader.load = function(directory, callback, offset, size, sequence) { ImageLoader.load = function(directory, callback, offset, size, sequence) {
var htmlImagesSrcArray = new Array(size), var htmlImagesSrcArray = new Array(size),
@ -28,7 +26,7 @@ ImageLoader.load = function(directory, callback, offset, size, sequence) {
for (var y = 0; y < htmlImagesSrcArray.length; y++) { for (var y = 0; y < htmlImagesSrcArray.length; y++) {
var imgName = htmlImagesSrcArray[y].substr(htmlImagesSrcArray[y].lastIndexOf("/")); var imgName = htmlImagesSrcArray[y].substr(htmlImagesSrcArray[y].lastIndexOf("/"));
if (loadedImg.src.lastIndexOf(imgName) !== -1) { if (loadedImg.src.lastIndexOf(imgName) !== -1) {
htmlImagesArray[y] = {img: loadedImg}; htmlImagesArray[y] = loadedImg;
break; break;
} }
} }
@ -39,18 +37,7 @@ ImageLoader.load = function(directory, callback, offset, size, sequence) {
if (ENV.development) { if (ENV.development) {
console.log("Images loaded"); console.log("Images loaded");
} }
if (sequence === false) { callback.apply(null, [htmlImagesArray]);
findTagsInObjectURL(directory, ['orientation'])
.then(tags => {
htmlImagesArray[0].tags = tags;
callback(htmlImagesArray);
}).catch(e => {
console.log(e);
callback(htmlImagesArray);
});
} else {
callback(htmlImagesArray);
}
} }
}; };

@ -176,21 +176,8 @@ InputStream.createImageStream = function() {
loaded = false; loaded = false;
ImageLoader.load(baseUrl, function(imgs) { ImageLoader.load(baseUrl, function(imgs) {
imgArray = imgs; imgArray = imgs;
if (imgs[0].tags && imgs[0].tags.orientation) { width = imgs[0].width;
switch (imgs[0].tags.orientation) { height = imgs[0].height;
case 6:
case 8:
width = imgs[0].img.height;
height = imgs[0].img.width;
break;
default:
width = imgs[0].img.width;
height = imgs[0].img.height;
}
} else {
width = imgs[0].img.width;
height = imgs[0].img.height;
}
calculatedWidth = calculatedWidth =
_config.size ? width / height > 1 ? _config.size : Math.floor((width / height) * _config.size) : width; _config.size ? width / height > 1 ? _config.size : Math.floor((width / height) * _config.size) : width;
calculatedHeight = calculatedHeight =

@ -1,4 +1,5 @@
import TypeDefs from './common/typedefs'; // eslint-disable-line no-unused-vars import TypeDefs from './common/typedefs'; // eslint-disable-line no-unused-vars
import WebrtcAdapter from 'webrtc-adapter'; // eslint-disable-line no-unused-vars
import ImageWrapper from './common/image_wrapper'; import ImageWrapper from './common/image_wrapper';
import BarcodeLocator from './locator/barcode_locator'; import BarcodeLocator from './locator/barcode_locator';
import BarcodeDecoder from './decoder/barcode_decoder'; import BarcodeDecoder from './decoder/barcode_decoder';
@ -66,6 +67,7 @@ function initInputStream(cb) {
} }
_inputStream.setAttribute("preload", "auto"); _inputStream.setAttribute("preload", "auto");
_inputStream.setAttribute("autoplay", true);
_inputStream.setInputStream(_config.inputStream); _inputStream.setInputStream(_config.inputStream);
_inputStream.addEventListener("canrecord", canRecord.bind(undefined, cb)); _inputStream.addEventListener("canrecord", canRecord.bind(undefined, cb));
} }
@ -538,6 +540,5 @@ export default {
}, },
ImageWrapper: ImageWrapper, ImageWrapper: ImageWrapper,
ImageDebug: ImageDebug, ImageDebug: ImageDebug,
ResultCollector: ResultCollector, ResultCollector: ResultCollector
CameraAccess: CameraAccess,
}; };

@ -1,257 +0,0 @@
import BarcodeReader from './barcode_reader';
function TwoOfFiveReader(opts) {
BarcodeReader.call(this, opts);
this.barSpaceRatio = [1, 1];
}
var N = 1,
W = 3,
properties = {
START_PATTERN: {value: [W, N, W, N, N, N]},
STOP_PATTERN: {value: [W, N, N, N, W]},
CODE_PATTERN: {value: [
[N, N, W, W, N],
[W, N, N, N, W],
[N, W, N, N, W],
[W, W, N, N, N],
[N, N, W, N, W],
[W, N, W, N, N],
[N, W, W, N, N],
[N, N, N, W, W],
[W, N, N, W, N],
[N, W, N, W, N]
]},
SINGLE_CODE_ERROR: {value: 0.78, writable: true},
AVG_CODE_ERROR: {value: 0.30, writable: true},
FORMAT: {value: "2of5"}
};
const startPatternLength = properties.START_PATTERN.value.reduce((sum, val) => sum + val, 0);
TwoOfFiveReader.prototype = Object.create(BarcodeReader.prototype, properties);
TwoOfFiveReader.prototype.constructor = TwoOfFiveReader;
TwoOfFiveReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder) {
var counter = [],
self = this,
i,
counterPos = 0,
bestMatch = {
error: Number.MAX_VALUE,
code: -1,
start: 0,
end: 0
},
error,
j,
sum,
epsilon = self.AVG_CODE_ERROR;
isWhite = isWhite || false;
tryHarder = tryHarder || false;
if (!offset) {
offset = self._nextSet(self._row);
}
for ( i = 0; i < pattern.length; i++) {
counter[i] = 0;
}
for ( i = offset; i < self._row.length; i++) {
if (self._row[i] ^ isWhite) {
counter[counterPos]++;
} else {
if (counterPos === counter.length - 1) {
sum = 0;
for ( j = 0; j < counter.length; j++) {
sum += counter[j];
}
error = self._matchPattern(counter, pattern);
if (error < epsilon) {
bestMatch.error = error;
bestMatch.start = i - sum;
bestMatch.end = i;
return bestMatch;
}
if (tryHarder) {
for (j = 0; j < counter.length - 2; j++) {
counter[j] = counter[j + 2];
}
counter[counter.length - 2] = 0;
counter[counter.length - 1] = 0;
counterPos--;
} else {
return null;
}
} else {
counterPos++;
}
counter[counterPos] = 1;
isWhite = !isWhite;
}
}
return null;
};
TwoOfFiveReader.prototype._findStart = function() {
var self = this,
leadingWhitespaceStart,
offset = self._nextSet(self._row),
startInfo,
narrowBarWidth = 1;
while (!startInfo) {
startInfo = self._findPattern(self.START_PATTERN, offset, false, true);
if (!startInfo) {
return null;
}
narrowBarWidth = Math.floor((startInfo.end - startInfo.start) / startPatternLength);
leadingWhitespaceStart = startInfo.start - narrowBarWidth * 5;
if (leadingWhitespaceStart >= 0) {
if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) {
return startInfo;
}
}
offset = startInfo.end;
startInfo = null;
}
};
TwoOfFiveReader.prototype._verifyTrailingWhitespace = function(endInfo) {
var self = this,
trailingWhitespaceEnd;
trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start) / 2);
if (trailingWhitespaceEnd < self._row.length) {
if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) {
return endInfo;
}
}
return null;
};
TwoOfFiveReader.prototype._findEnd = function() {
var self = this,
endInfo,
tmp,
offset;
self._row.reverse();
offset = self._nextSet(self._row);
endInfo = self._findPattern(self.STOP_PATTERN, offset, false, true);
self._row.reverse();
if (endInfo === null) {
return null;
}
// reverse numbers
tmp = endInfo.start;
endInfo.start = self._row.length - endInfo.end;
endInfo.end = self._row.length - tmp;
return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null;
};
TwoOfFiveReader.prototype._decodeCode = function(counter) {
var j,
self = this,
sum = 0,
normalized,
error,
epsilon = self.AVG_CODE_ERROR,
code,
bestMatch = {
error: Number.MAX_VALUE,
code: -1,
start: 0,
end: 0
};
for ( j = 0; j < counter.length; j++) {
sum += counter[j];
}
for (code = 0; code < self.CODE_PATTERN.length; code++) {
error = self._matchPattern(counter, self.CODE_PATTERN[code]);
if (error < bestMatch.error) {
bestMatch.code = code;
bestMatch.error = error;
}
}
if (bestMatch.error < epsilon) {
return bestMatch;
}
};
TwoOfFiveReader.prototype._decodePayload = function(counters, result, decodedCodes) {
var i,
self = this,
pos = 0,
counterLength = counters.length,
counter = [0, 0, 0, 0, 0],
code;
while (pos < counterLength) {
for (i = 0; i < 5; i++) {
counter[i] = counters[pos] * this.barSpaceRatio[0];
pos += 2;
}
code = self._decodeCode(counter);
if (!code) {
return null;
}
result.push(code.code + "");
decodedCodes.push(code);
}
return code;
};
TwoOfFiveReader.prototype._verifyCounterLength = function(counters) {
return (counters.length % 10 === 0);
};
TwoOfFiveReader.prototype._decode = function() {
var startInfo,
endInfo,
self = this,
code,
result = [],
decodedCodes = [],
counters;
startInfo = self._findStart();
if (!startInfo) {
return null;
}
decodedCodes.push(startInfo);
endInfo = self._findEnd();
if (!endInfo) {
return null;
}
counters = self._fillCounters(startInfo.end, endInfo.start, false);
if (!self._verifyCounterLength(counters)) {
return null;
}
code = self._decodePayload(counters, result, decodedCodes);
if (!code) {
return null;
}
if (result.length < 5) {
return null;
}
decodedCodes.push(endInfo);
return {
code: result.join(""),
start: startInfo.start,
end: endInfo.end,
startInfo: startInfo,
decodedCodes: decodedCodes
};
};
export default TwoOfFiveReader;

@ -1,9 +1,6 @@
import ArrayHelper from '../common/array_helper'; function BarcodeReader(config) {
function BarcodeReader(config, supplements) {
this._row = []; this._row = [];
this.config = config || {}; this.config = config || {};
this.supplements = supplements;
return this; return this;
} }
@ -21,32 +18,15 @@ BarcodeReader.prototype._nextUnset = function(line, start) {
return line.length; return line.length;
}; };
BarcodeReader.prototype._matchPattern = function(counter, code, maxSingleError) { BarcodeReader.prototype._matchPattern = function(counter, code) {
var i, var i,
error = 0, error = 0,
singleError = 0, singleError = 0,
sum = 0, modulo = this.MODULO,
modulo = 0, maxSingleError = this.SINGLE_CODE_ERROR || 1;
barWidth,
count,
scaled;
maxSingleError = maxSingleError || this.SINGLE_CODE_ERROR || 1;
for (i = 0; i < counter.length; i++) { for (i = 0; i < counter.length; i++) {
sum += counter[i]; singleError = Math.abs(code[i] - counter[i]);
modulo += code[i];
}
if (sum < modulo) {
return Number.MAX_VALUE;
}
barWidth = sum / modulo;
maxSingleError *= barWidth;
for (i = 0; i < counter.length; i++) {
count = counter[i];
scaled = code[i] * barWidth;
singleError = Math.abs(count - scaled) / scaled;
if (singleError > maxSingleError) { if (singleError > maxSingleError) {
return Number.MAX_VALUE; return Number.MAX_VALUE;
} }
@ -67,6 +47,30 @@ BarcodeReader.prototype._nextSet = function(line, offset) {
return line.length; return line.length;
}; };
BarcodeReader.prototype._normalize = function(counter, correction) {
var i,
self = this,
sum = 0,
ratio,
numOnes = 0,
normalized = new Array(counter.length),
norm = 0,
modulo = self.MODULO;
if (correction) {
self._correct(counter, correction);
}
for (i = 0; i < counter.length; i++) {
sum += counter[i];
}
ratio = sum / (modulo - numOnes);
for (i = 0; i < counter.length; i++) {
norm = counter[i] === 1 ? counter[i] : counter[i] / ratio;
normalized[i] = norm;
}
return normalized;
};
BarcodeReader.prototype._correctBars = function(counter, correction, indices) { BarcodeReader.prototype._correctBars = function(counter, correction, indices) {
var length = indices.length, var length = indices.length,
tmp = 0; tmp = 0;
@ -197,33 +201,6 @@ BarcodeReader.prototype._fillCounters = function(offset, end, isWhite) {
return counters; return counters;
}; };
BarcodeReader.prototype._toCounters = function(start, counter) {
var self = this,
numCounters = counter.length,
end = self._row.length,
isWhite = !self._row[start],
i,
counterPos = 0;
ArrayHelper.init(counter, 0);
for ( i = start; i < end; i++) {
if (self._row[i] ^ isWhite) {
counter[counterPos]++;
} else {
counterPos++;
if (counterPos === numCounters) {
break;
} else {
counter[counterPos] = 1;
isWhite = !isWhite;
}
}
}
return counter;
};
Object.defineProperty(BarcodeReader.prototype, "FORMAT", { Object.defineProperty(BarcodeReader.prototype, "FORMAT", {
value: 'unknown', value: 'unknown',
writeable: false writeable: false

@ -13,6 +13,7 @@ var properties = {
START_CODE_B: {value: 104}, START_CODE_B: {value: 104},
START_CODE_C: {value: 105}, START_CODE_C: {value: 105},
STOP_CODE: {value: 106}, STOP_CODE: {value: 106},
MODULO: {value: 11},
CODE_PATTERN: {value: [ CODE_PATTERN: {value: [
[2, 1, 2, 2, 2, 2], [2, 1, 2, 2, 2, 2],
[2, 2, 2, 1, 2, 2], [2, 2, 2, 1, 2, 2],
@ -149,36 +150,37 @@ Code128Reader.prototype._decodeCode = function(start, correction) {
} }
}, },
code, code,
error; error,
normalized;
for ( i = offset; i < self._row.length; i++) { for ( i = offset; i < self._row.length; i++) {
if (self._row[i] ^ isWhite) { if (self._row[i] ^ isWhite) {
counter[counterPos]++; counter[counterPos]++;
} else { } else {
if (counterPos === counter.length - 1) { if (counterPos === counter.length - 1) {
if (correction) { normalized = self._normalize(counter, correction);
self._correct(counter, correction); if (normalized) {
} for (code = 0; code < self.CODE_PATTERN.length; code++) {
for (code = 0; code < self.CODE_PATTERN.length; code++) { error = self._matchPattern(normalized, self.CODE_PATTERN[code]);
error = self._matchPattern(counter, self.CODE_PATTERN[code]); if (error < bestMatch.error) {
if (error < bestMatch.error) { bestMatch.code = code;
bestMatch.code = code; bestMatch.error = error;
bestMatch.error = error; }
} }
bestMatch.end = i;
if (bestMatch.code === -1) {
return null;
}
if (self.CODE_PATTERN[bestMatch.code]) {
bestMatch.correction.bar = calculateCorrection(
self.CODE_PATTERN[bestMatch.code], normalized,
this.MODULE_INDICES.bar);
bestMatch.correction.space = calculateCorrection(
self.CODE_PATTERN[bestMatch.code], normalized,
this.MODULE_INDICES.space);
}
return bestMatch;
} }
bestMatch.end = i;
if (bestMatch.code === -1 || bestMatch.error > self.AVG_CODE_ERROR) {
return null;
}
if (self.CODE_PATTERN[bestMatch.code]) {
bestMatch.correction.bar = calculateCorrection(
self.CODE_PATTERN[bestMatch.code], counter,
this.MODULE_INDICES.bar);
bestMatch.correction.space = calculateCorrection(
self.CODE_PATTERN[bestMatch.code], counter,
this.MODULE_INDICES.space);
}
return bestMatch;
} else { } else {
counterPos++; counterPos++;
} }
@ -214,7 +216,8 @@ Code128Reader.prototype._findStart = function() {
code, code,
error, error,
j, j,
sum; sum,
normalized;
for ( i = offset; i < self._row.length; i++) { for ( i = offset; i < self._row.length; i++) {
if (self._row[i] ^ isWhite) { if (self._row[i] ^ isWhite) {
@ -225,23 +228,24 @@ Code128Reader.prototype._findStart = function() {
for ( j = 0; j < counter.length; j++) { for ( j = 0; j < counter.length; j++) {
sum += counter[j]; sum += counter[j];
} }
for (code = self.START_CODE_A; code <= self.START_CODE_C; code++) { normalized = self._normalize(counter);
error = self._matchPattern(counter, self.CODE_PATTERN[code]); if (normalized) {
if (error < bestMatch.error) { for (code = self.START_CODE_A; code <= self.START_CODE_C; code++) {
bestMatch.code = code; error = self._matchPattern(normalized, self.CODE_PATTERN[code]);
bestMatch.error = error; if (error < bestMatch.error) {
bestMatch.code = code;
bestMatch.error = error;
}
}
if (bestMatch.error < self.AVG_CODE_ERROR) {
bestMatch.start = i - sum;
bestMatch.end = i;
bestMatch.correction.bar = calculateCorrection(
self.CODE_PATTERN[code], normalized, [0, 2, 4]);
bestMatch.correction.space = calculateCorrection(
self.CODE_PATTERN[code], normalized, [1, 3, 5]);
return bestMatch;
} }
}
if (bestMatch.error < self.AVG_CODE_ERROR) {
bestMatch.start = i - sum;
bestMatch.end = i;
bestMatch.correction.bar = calculateCorrection(
self.CODE_PATTERN[bestMatch.code], counter,
this.MODULE_INDICES.bar);
bestMatch.correction.space = calculateCorrection(
self.CODE_PATTERN[bestMatch.code], counter,
this.MODULE_INDICES.space);
return bestMatch;
} }
for ( j = 0; j < 4; j++) { for ( j = 0; j < 4; j++) {

@ -20,6 +20,33 @@ var properties = {
Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties); Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties);
Code39Reader.prototype.constructor = Code39Reader; Code39Reader.prototype.constructor = Code39Reader;
Code39Reader.prototype._toCounters = function(start, counter) {
var self = this,
numCounters = counter.length,
end = self._row.length,
isWhite = !self._row[start],
i,
counterPos = 0;
ArrayHelper.init(counter, 0);
for ( i = start; i < end; i++) {
if (self._row[i] ^ isWhite) {
counter[counterPos]++;
} else {
counterPos++;
if (counterPos === numCounters) {
break;
} else {
counter[counterPos] = 1;
isWhite = !isWhite;
}
}
}
return counter;
};
Code39Reader.prototype._decode = function() { Code39Reader.prototype._decode = function() {
var self = this, var self = this,
counters = [0, 0, 0, 0, 0, 0, 0, 0, 0], counters = [0, 0, 0, 0, 0, 0, 0, 0, 0],

@ -1,251 +0,0 @@
import BarcodeReader from './barcode_reader';
import ArrayHelper from '../common/array_helper';
function Code93Reader() {
BarcodeReader.call(this);
}
const ALPHABETH_STRING = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. $/+%abcd*";
var properties = {
ALPHABETH_STRING: {value: ALPHABETH_STRING},
ALPHABET: {value: ALPHABETH_STRING.split('').map(char => char.charCodeAt(0))},
CHARACTER_ENCODINGS: {value: [
0x114, 0x148, 0x144, 0x142, 0x128, 0x124, 0x122, 0x150, 0x112, 0x10A,
0x1A8, 0x1A4, 0x1A2, 0x194, 0x192, 0x18A, 0x168, 0x164, 0x162, 0x134,
0x11A, 0x158, 0x14C, 0x146, 0x12C, 0x116, 0x1B4, 0x1B2, 0x1AC, 0x1A6,
0x196, 0x19A, 0x16C, 0x166, 0x136, 0x13A, 0x12E, 0x1D4, 0x1D2, 0x1CA,
0x16E, 0x176, 0x1AE, 0x126, 0x1DA, 0x1D6, 0x132, 0x15E
]},
ASTERISK: {value: 0x15E},
FORMAT: {value: "code_93", writeable: false}
};
Code93Reader.prototype = Object.create(BarcodeReader.prototype, properties);
Code93Reader.prototype.constructor = Code93Reader;
Code93Reader.prototype._decode = function() {
var self = this,
counters = [0, 0, 0, 0, 0, 0],
result = [],
start = self._findStart(),
decodedChar,
lastStart,
pattern,
nextStart;
if (!start) {
return null;
}
nextStart = self._nextSet(self._row, start.end);
do {
counters = self._toCounters(nextStart, counters);
pattern = self._toPattern(counters);
if (pattern < 0) {
return null;
}
decodedChar = self._patternToChar(pattern);
if (decodedChar < 0){
return null;
}
result.push(decodedChar);
lastStart = nextStart;
nextStart += ArrayHelper.sum(counters);
nextStart = self._nextSet(self._row, nextStart);
} while (decodedChar !== '*');
result.pop();
if (!result.length) {
return null;
}
if (!self._verifyEnd(lastStart, nextStart, counters)) {
return null;
}
if (!self._verifyChecksums(result)) {
return null;
}
result = result.slice(0, result.length - 2);
if ((result = self._decodeExtended(result)) === null) {
return null;
};
return {
code: result.join(""),
start: start.start,
end: nextStart,
startInfo: start,
decodedCodes: result
};
};
Code93Reader.prototype._verifyEnd = function(lastStart, nextStart) {
if (lastStart === nextStart || !this._row[nextStart]) {
return false;
}
return true;
};
Code93Reader.prototype._patternToChar = function(pattern) {
var i,
self = this;
for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) {
if (self.CHARACTER_ENCODINGS[i] === pattern) {
return String.fromCharCode(self.ALPHABET[i]);
}
}
return -1;
};
Code93Reader.prototype._toPattern = function(counters) {
const numCounters = counters.length;
let pattern = 0;
let sum = 0;
for (let i = 0; i < numCounters; i++) {
sum += counters[i];
}
for (let i = 0; i < numCounters; i++) {
let normalized = Math.round(counters[i] * 9 / sum);
if (normalized < 1 || normalized > 4) {
return -1;
}
if ((i & 1) === 0) {
for (let j = 0; j < normalized; j++) {
pattern = (pattern << 1) | 1;
}
} else {
pattern <<= normalized;
}
}
return pattern;
};
Code93Reader.prototype._findStart = function() {
var self = this,
offset = self._nextSet(self._row),
patternStart = offset,
counter = [0, 0, 0, 0, 0, 0],
counterPos = 0,
isWhite = false,
i,
j,
whiteSpaceMustStart;
for ( i = offset; i < self._row.length; i++) {
if (self._row[i] ^ isWhite) {
counter[counterPos]++;
} else {
if (counterPos === counter.length - 1) {
// find start pattern
if (self._toPattern(counter) === self.ASTERISK) {
whiteSpaceMustStart = Math.floor(Math.max(0, patternStart - ((i - patternStart) / 4)));
if (self._matchRange(whiteSpaceMustStart, patternStart, 0)) {
return {
start: patternStart,
end: i
};
}
}
patternStart += counter[0] + counter[1];
for ( j = 0; j < 4; j++) {
counter[j] = counter[j + 2];
}
counter[4] = 0;
counter[5] = 0;
counterPos--;
} else {
counterPos++;
}
counter[counterPos] = 1;
isWhite = !isWhite;
}
}
return null;
};
Code93Reader.prototype._decodeExtended = function(charArray) {
const length = charArray.length;
const result = [];
for (let i = 0; i < length; i++) {
const char = charArray[i];
if (char >= 'a' && char <= 'd') {
if (i > (length - 2)) {
return null;
}
const nextChar = charArray[++i];
const nextCharCode = nextChar.charCodeAt(0);
let decodedChar;
switch (char) {
case 'a':
if (nextChar >= 'A' && nextChar <= 'Z') {
decodedChar = String.fromCharCode(nextCharCode - 64);
} else {
return null;
}
break;
case 'b':
if (nextChar >= 'A' && nextChar <= 'E') {
decodedChar = String.fromCharCode(nextCharCode - 38);
} else if (nextChar >= 'F' && nextChar <= 'J') {
decodedChar = String.fromCharCode(nextCharCode - 11);
} else if (nextChar >= 'K' && nextChar <= 'O') {
decodedChar = String.fromCharCode(nextCharCode + 16);
} else if (nextChar >= 'P' && nextChar <= 'S') {
decodedChar = String.fromCharCode(nextCharCode + 43);
} else if (nextChar >= 'T' && nextChar <= 'Z') {
decodedChar = String.fromCharCode(127);
} else {
return null;
}
break;
case 'c':
if (nextChar >= 'A' && nextChar <= 'O') {
decodedChar = String.fromCharCode(nextCharCode - 32);
} else if (nextChar === 'Z') {
decodedChar = ':';
} else {
return null;
}
break;
case 'd':
if (nextChar >= 'A' && nextChar <= 'Z') {
decodedChar = String.fromCharCode(nextCharCode + 32);
} else {
return null;
}
break;
}
result.push(decodedChar);
} else {
result.push(char);
}
}
return result;
};
Code93Reader.prototype._verifyChecksums = function(charArray) {
return this._matchCheckChar(charArray, charArray.length - 2, 20)
&& this._matchCheckChar(charArray, charArray.length - 1, 15);
};
Code93Reader.prototype._matchCheckChar = function(charArray, index, maxWeight) {
const arrayToCheck = charArray.slice(0, index);
const length = arrayToCheck.length;
const weightedSums = arrayToCheck.reduce((sum, char, i) => {
const weight = (((i * -1) + (length - 1)) % maxWeight) + 1;
const value = this.ALPHABET.indexOf(char.charCodeAt(0));
return sum + (weight * value);
}, 0);
const checkChar = this.ALPHABET[(weightedSums % 47)];
return checkChar === charArray[index].charCodeAt(0);
};
export default Code93Reader;

@ -1,51 +0,0 @@
import EANReader from './ean_reader';
function EAN2Reader() {
EANReader.call(this);
}
var properties = {
FORMAT: {value: "ean_2", writeable: false}
};
EAN2Reader.prototype = Object.create(EANReader.prototype, properties);
EAN2Reader.prototype.constructor = EAN2Reader;
EAN2Reader.prototype.decode = function(row, start) {
this._row = row;
var counters = [0, 0, 0, 0],
codeFrequency = 0,
i = 0,
offset = start,
end = this._row.length,
code,
result = [],
decodedCodes = [];
for (i = 0; i < 2 && offset < end; i++) {
code = this._decodeCode(offset);
if (!code) {
return null;
}
decodedCodes.push(code);
result.push(code.code % 10);
if (code.code >= this.CODE_G_START) {
codeFrequency |= 1 << (1 - i);
}
if (i != 1) {
offset = this._nextSet(this._row, code.end);
offset = this._nextUnset(this._row, offset);
}
}
if (result.length != 2 || (parseInt(result.join("")) % 4) !== codeFrequency) {
return null;
}
return {
code: result.join(""),
decodedCodes,
end: code.end
};
};
export default EAN2Reader;

@ -1,84 +0,0 @@
import EANReader from './ean_reader';
function EAN5Reader() {
EANReader.call(this);
}
var properties = {
FORMAT: {value: "ean_5", writeable: false}
};
const CHECK_DIGIT_ENCODINGS = [24, 20, 18, 17, 12, 6, 3, 10, 9, 5];
EAN5Reader.prototype = Object.create(EANReader.prototype, properties);
EAN5Reader.prototype.constructor = EAN5Reader;
EAN5Reader.prototype.decode = function(row, start) {
this._row = row;
var counters = [0, 0, 0, 0],
codeFrequency = 0,
i = 0,
offset = start,
end = this._row.length,
code,
result = [],
decodedCodes = [];
for (i = 0; i < 5 && offset < end; i++) {
code = this._decodeCode(offset);
if (!code) {
return null;
}
decodedCodes.push(code);
result.push(code.code % 10);
if (code.code >= this.CODE_G_START) {
codeFrequency |= 1 << (4 - i);
}
if (i != 4) {
offset = this._nextSet(this._row, code.end);
offset = this._nextUnset(this._row, offset);
}
}
if (result.length != 5) {
return null;
}
if (extensionChecksum(result) !== determineCheckDigit(codeFrequency)) {
return null;
}
return {
code: result.join(""),
decodedCodes,
end: code.end
};
};
function determineCheckDigit(codeFrequency) {
var i;
for (i = 0; i < 10; i++) {
if (codeFrequency === CHECK_DIGIT_ENCODINGS[i]) {
return i;
}
}
return null;
}
function extensionChecksum(result) {
var length = result.length,
sum = 0,
i;
for (i = length - 2; i >= 0; i -= 2) {
sum += result[i];
}
sum *= 3;
for (i = length - 1; i >= 0; i -= 2) {
sum += result[i];
}
sum *= 3;
return sum % 10;
}
export default EAN5Reader;

@ -1,7 +1,7 @@
import EANReader from './ean_reader'; import EANReader from './ean_reader';
function EAN8Reader(opts, supplements) { function EAN8Reader() {
EANReader.call(this, opts, supplements); EANReader.call(this);
} }
var properties = { var properties = {

@ -1,27 +1,16 @@
import BarcodeReader from './barcode_reader'; import BarcodeReader from './barcode_reader';
import {merge} from 'lodash';
function EANReader(opts, supplements) { function EANReader(opts) {
opts = merge(getDefaulConfig(), opts); BarcodeReader.call(this, opts);
BarcodeReader.call(this, opts, supplements);
}
function getDefaulConfig() {
var config = {};
Object.keys(EANReader.CONFIG_KEYS).forEach(function(key) {
config[key] = EANReader.CONFIG_KEYS[key].default;
});
return config;
} }
var properties = { var properties = {
CODE_L_START: {value: 0}, CODE_L_START: {value: 0},
MODULO: {value: 7},
CODE_G_START: {value: 10}, CODE_G_START: {value: 10},
START_PATTERN: {value: [1, 1, 1]}, START_PATTERN: {value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7]},
STOP_PATTERN: {value: [1, 1, 1]}, STOP_PATTERN: {value: [1 / 3 * 7, 1 / 3 * 7, 1 / 3 * 7]},
MIDDLE_PATTERN: {value: [1, 1, 1, 1, 1]}, MIDDLE_PATTERN: {value: [1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7, 1 / 5 * 7]},
EXTENSION_START_PATTERN: {value: [1, 1, 2]},
CODE_PATTERN: {value: [ CODE_PATTERN: {value: [
[3, 2, 1, 1], [3, 2, 1, 1],
[2, 2, 2, 1], [2, 2, 2, 1],
@ -45,8 +34,8 @@ var properties = {
[2, 1, 1, 3] [2, 1, 1, 3]
]}, ]},
CODE_FREQUENCY: {value: [0, 11, 13, 14, 19, 25, 28, 21, 22, 26]}, CODE_FREQUENCY: {value: [0, 11, 13, 14, 19, 25, 28, 21, 22, 26]},
SINGLE_CODE_ERROR: {value: 0.70}, SINGLE_CODE_ERROR: {value: 0.67},
AVG_CODE_ERROR: {value: 0.48}, AVG_CODE_ERROR: {value: 0.27},
FORMAT: {value: "ean_13", writeable: false} FORMAT: {value: "ean_13", writeable: false}
}; };
@ -67,7 +56,8 @@ EANReader.prototype._decodeCode = function(start, coderange) {
end: start end: start
}, },
code, code,
error; error,
normalized;
if (!coderange) { if (!coderange) {
coderange = self.CODE_PATTERN.length; coderange = self.CODE_PATTERN.length;
@ -78,18 +68,21 @@ EANReader.prototype._decodeCode = function(start, coderange) {
counter[counterPos]++; counter[counterPos]++;
} else { } else {
if (counterPos === counter.length - 1) { if (counterPos === counter.length - 1) {
for (code = 0; code < coderange; code++) { normalized = self._normalize(counter);
error = self._matchPattern(counter, self.CODE_PATTERN[code]); if (normalized) {
if (error < bestMatch.error) { for (code = 0; code < coderange; code++) {
bestMatch.code = code; error = self._matchPattern(normalized, self.CODE_PATTERN[code]);
bestMatch.error = error; if (error < bestMatch.error) {
bestMatch.code = code;
bestMatch.error = error;
}
} }
bestMatch.end = i;
if (bestMatch.error > self.AVG_CODE_ERROR) {
return null;
}
return bestMatch;
} }
bestMatch.end = i;
if (bestMatch.error > self.AVG_CODE_ERROR) {
return null;
}
return bestMatch;
} else { } else {
counterPos++; counterPos++;
} }
@ -113,7 +106,8 @@ EANReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder,
}, },
error, error,
j, j,
sum; sum,
normalized;
if (!offset) { if (!offset) {
offset = self._nextSet(self._row); offset = self._nextSet(self._row);
@ -144,13 +138,16 @@ EANReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder,
for ( j = 0; j < counter.length; j++) { for ( j = 0; j < counter.length; j++) {
sum += counter[j]; sum += counter[j];
} }
error = self._matchPattern(counter, pattern); normalized = self._normalize(counter);
if (normalized) {
error = self._matchPattern(normalized, pattern);
if (error < epsilon) { if (error < epsilon) {
bestMatch.error = error; bestMatch.error = error;
bestMatch.start = i - sum; bestMatch.start = i - sum;
bestMatch.end = i; bestMatch.end = i;
return bestMatch; return bestMatch;
}
} }
if (tryHarder) { if (tryHarder) {
for ( j = 0; j < counter.length - 2; j++) { for ( j = 0; j < counter.length - 2; j++) {
@ -276,8 +273,7 @@ EANReader.prototype._decode = function() {
self = this, self = this,
code, code,
result = [], result = [],
decodedCodes = [], decodedCodes = [];
resultInfo = {};
startInfo = self._findStart(); startInfo = self._findStart();
if (!startInfo) { if (!startInfo) {
@ -305,62 +301,16 @@ EANReader.prototype._decode = function() {
return null; return null;
} }
if (this.supplements.length > 0) {
let ext = this._decodeExtensions(code.end);
if (!ext) {
return null;
}
let lastCode = ext.decodedCodes[ext.decodedCodes.length-1],
endInfo = {
start: lastCode.start + (((lastCode.end - lastCode.start) / 2) | 0),
end: lastCode.end
};
if(!self._verifyTrailingWhitespace(endInfo)) {
return null;
}
resultInfo = {
supplement: ext,
code: result.join("") + ext.code
}
}
return { return {
code: result.join(""), code: result.join(""),
start: startInfo.start, start: startInfo.start,
end: code.end, end: code.end,
codeset: "", codeset: "",
startInfo: startInfo, startInfo: startInfo,
decodedCodes: decodedCodes, decodedCodes: decodedCodes
...resultInfo
}; };
}; };
EANReader.prototype._decodeExtensions = function(offset) {
var i,
start = this._nextSet(this._row, offset),
startInfo = this._findPattern(this.EXTENSION_START_PATTERN, start, false, false),
result;
if (startInfo === null) {
return null;
}
for (i = 0; i < this.supplements.length; i++) {
result = this.supplements[i].decode(this._row, startInfo.end);
if (result !== null) {
return {
code: result.code,
start,
startInfo,
end: result.end,
codeset: "",
decodedCodes: result.decodedCodes
}
}
}
return null;
};
EANReader.prototype._checksum = function(result) { EANReader.prototype._checksum = function(result) {
var sum = 0, i; var sum = 0, i;
@ -374,12 +324,4 @@ EANReader.prototype._checksum = function(result) {
return sum % 10 === 0; return sum % 10 === 0;
}; };
EANReader.CONFIG_KEYS = {
supplements: {
'type': 'arrayOf(string)',
'default': [],
'description': 'Allowed extensions to be decoded (2 and/or 5)'
}
};
export default (EANReader); export default (EANReader);

@ -23,8 +23,9 @@ function getDefaulConfig() {
var N = 1, var N = 1,
W = 3, W = 3,
properties = { properties = {
START_PATTERN: {value: [N, N, N, N]}, MODULO: {value: 10},
STOP_PATTERN: {value: [N, N, W]}, START_PATTERN: {value: [N * 2.5, N * 2.5, N * 2.5, N * 2.5]},
STOP_PATTERN: {value: [N * 2, N * 2, W * 2]},
CODE_PATTERN: {value: [ CODE_PATTERN: {value: [
[N, N, W, W, N], [N, N, W, W, N],
[W, N, N, N, W], [W, N, N, N, W],
@ -109,12 +110,16 @@ I2of5Reader.prototype._findPattern = function(pattern, offset, isWhite, tryHarde
for ( j = 0; j < counter.length; j++) { for ( j = 0; j < counter.length; j++) {
sum += counter[j]; sum += counter[j];
} }
error = self._matchPattern(counter, pattern); normalized = self._normalize(counter);
if (error < epsilon) { if (normalized) {
bestMatch.error = error; error = self._matchPattern(normalized, pattern);
bestMatch.start = i - sum;
bestMatch.end = i; if (error < epsilon) {
return bestMatch; bestMatch.error = error;
bestMatch.start = i - sum;
bestMatch.end = i;
return bestMatch;
}
} }
if (tryHarder) { if (tryHarder) {
for (j = 0; j < counter.length - 2; j++) { for (j = 0; j < counter.length - 2; j++) {
@ -228,16 +233,20 @@ I2of5Reader.prototype._decodeCode = function(counter) {
for ( j = 0; j < counter.length; j++) { for ( j = 0; j < counter.length; j++) {
sum += counter[j]; sum += counter[j];
} }
for (code = 0; code < self.CODE_PATTERN.length; code++) { normalized = self._normalize(counter);
error = self._matchPattern(counter, self.CODE_PATTERN[code]); if (normalized) {
if (error < bestMatch.error) { for (code = 0; code < self.CODE_PATTERN.length; code++) {
bestMatch.code = code; error = self._matchPattern(normalized, self.CODE_PATTERN[code]);
bestMatch.error = error; if (error < bestMatch.error) {
bestMatch.code = code;
bestMatch.error = error;
}
}
if (bestMatch.error < epsilon) {
return bestMatch;
} }
} }
if (bestMatch.error < epsilon) { return null;
return bestMatch;
}
}; };
I2of5Reader.prototype._decodePayload = function(counters, result, decodedCodes) { I2of5Reader.prototype._decodePayload = function(counters, result, decodedCodes) {

@ -1,7 +1,7 @@
import EANReader from './ean_reader'; import EANReader from './ean_reader';
function UPCEReader(opts, supplements) { function UPCEReader() {
EANReader.call(this, opts, supplements); EANReader.call(this);
} }
var properties = { var properties = {

@ -1,7 +1,7 @@
import EANReader from './ean_reader'; import EANReader from './ean_reader';
function UPCReader(opts, supplements) { function UPCReader() {
EANReader.call(this, opts, supplements); EANReader.call(this);
} }
var properties = { var properties = {

@ -10,9 +10,9 @@ module.exports = function(grunt) {
grunt.registerTask('uglyasm', function() { grunt.registerTask('uglyasm', function() {
var code = fs.readFileSync('dist/quagga.js', 'utf-8'), var code = fs.readFileSync('dist/quagga.js', 'utf-8'),
minifiedCode = fs.readFileSync('dist/quagga.min.js', 'utf-8'), minifiedCode = fs.readFileSync('dist/quagga.min.js', 'utf-8'),
commentEnd = '@preserve ASM END', commentEnd = '/* @preserve ASM END */',
moduleFunctionRegex = /function\s*\((\w+,\s*\w+,\s*\w+)\)\s*\{(\n?\s*\"use strict\";?)*\n?\/\*\s*\@preserve ASM BEGIN/, moduleFunctionRegex = /function\s*\((\w+,\s*\w+,\s*\w+)\)\s*\{\s*\/\* \@preserve ASM BEGIN \*\//,
commentStartIdx = code.indexOf("@preserve ASM BEGIN"), commentStartIdx = code.indexOf("/* @preserve ASM BEGIN */"),
asmEndIdxTmp = code.indexOf(commentEnd), asmEndIdxTmp = code.indexOf(commentEnd),
asmEndIdx = code.indexOf("}", asmEndIdxTmp), asmEndIdx = code.indexOf("}", asmEndIdxTmp),
asmCodeTmp = code.substring(commentStartIdx - Math.min(500, commentStartIdx), asmCodeTmp = code.substring(commentStartIdx - Math.min(500, commentStartIdx),
@ -30,6 +30,8 @@ module.exports = function(grunt) {
.replace(/ ([+=^|&]|>+|<+) /g, '$1') // remove spaces around operators .replace(/ ([+=^|&]|>+|<+) /g, '$1') // remove spaces around operators
.replace(/[\r\n/]/g, ''); // remove new lines .replace(/[\r\n/]/g, ''); // remove new lines
grunt.log.debug(asmCodeMinified);
asmModule = moduleFunctionRegex.exec(asmCode); asmModule = moduleFunctionRegex.exec(asmCode);
if (!asmModule) { if (!asmModule) {
grunt.log.error("No ASM module found"); grunt.log.error("No ASM module found");

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

@ -27,19 +27,15 @@ describe('decodeSingle', function () {
function _runTestSet(testSet, config) { function _runTestSet(testSet, config) {
var readers = config.decoder.readers.slice(), var readers = config.decoder.readers.slice(),
format, format,
folder, folder;
suffix;
if (typeof readers[0] === 'string'){ if (typeof readers[0] === 'string'){
format = readers[0]; format = readers[0];
} else { } else {
if (readers[0].config && readers[0].config.supplements && readers[0].config.supplements.length) {
suffix = "extended";
}
format = readers[0].format; format = readers[0].format;
} }
folder = baseFolder + format.split('_').slice(0, -1).concat(suffix ? [suffix] : []).join('_') + "/"; folder = baseFolder + format.split('_').slice(0, -1).join('_') + "/";
it('should decode ' + folder + " correctly", function(done) { it('should decode ' + folder + " correctly", function(done) {
async.eachSeries(testSet, function (sample, callback) { async.eachSeries(testSet, function (sample, callback) {
@ -80,49 +76,6 @@ describe('decodeSingle', function () {
_runTestSet(testSet, config); _runTestSet(testSet, config);
}); });
describe("EAN-extended", function() {
var config = {
inputStream: {
size: 800,
singleChannel: false
},
locator: {
patchSize: "medium",
halfSample: true
},
numOfWorkers: 0,
decoder: {
readers: [{
format: "ean_reader",
config: {
supplements: [
'ean_5_reader', 'ean_2_reader'
]
}
}]
},
locate: true,
src: null
},
testSet = [
{"name": "image-001.jpg", "result": "900437801102701"},
{"name": "image-002.jpg", "result": "419871600890101"},
{"name": "image-003.jpg", "result": "419871600890101"},
{"name": "image-004.jpg", "result": "978054466825652495"},
{"name": "image-005.jpg", "result": "419664190890712"},
{"name": "image-006.jpg", "result": "412056690699101"},
{"name": "image-007.jpg", "result": "419204531290601"},
{"name": "image-008.jpg", "result": "419871600890101"},
{"name": "image-009.jpg", "result": "978054466825652495"},
{"name": "image-010.jpg", "result": "900437801102701"}
];
testSet.forEach(function(sample) {
sample.format = "ean_13";
});
_runTestSet(testSet, config);
});
describe("Code128", function() { describe("Code128", function() {
var config = { var config = {
inputStream: { inputStream: {
@ -255,14 +208,14 @@ describe('decodeSingle', function () {
describe("Codabar", function() { describe("Codabar", function() {
var config = generateConfig(), var config = generateConfig(),
testSet = [ testSet = [
//{"name": "image-001.jpg", "result": "A10/53+17-70D"}, {"name": "image-001.jpg", "result": "A10/53+17-70D"},
{"name": "image-002.jpg", "result": "B546745735B"}, {"name": "image-002.jpg", "result": "B546745735B"},
{"name": "image-003.jpg", "result": "C$399.95A"}, {"name": "image-003.jpg", "result": "C$399.95A"},
{"name": "image-004.jpg", "result": "B546745735B"}, {"name": "image-004.jpg", "result": "B546745735B"},
{"name": "image-005.jpg", "result": "C$399.95A"}, {"name": "image-005.jpg", "result": "C$399.95A"},
{"name": "image-006.jpg", "result": "B546745735B"}, {"name": "image-006.jpg", "result": "B546745735B"},
{"name": "image-007.jpg", "result": "C$399.95A"}, {"name": "image-007.jpg", "result": "C$399.95A"},
//{"name": "image-008.jpg", "result": "A16:9/4:3/3:2D"}, {"name": "image-008.jpg", "result": "A16:9/4:3/3:2D"},
{"name": "image-009.jpg", "result": "C$399.95A"}, {"name": "image-009.jpg", "result": "C$399.95A"},
{"name": "image-010.jpg", "result": "C$399.95A"} {"name": "image-010.jpg", "result": "C$399.95A"}
]; ];
@ -304,78 +257,4 @@ describe('decodeSingle', function () {
}); });
_runTestSet(testSet, config); _runTestSet(testSet, config);
}); });
describe("2of5", function() {
var config = config = {
inputStream: {
size: 800,
singleChannel: false
},
locator: {
patchSize: "medium",
halfSample: true
},
numOfWorkers: 0,
decoder: {
readers: ["2of5_reader"]
},
locate: true,
src: null
},
testSet = [
{"name": "image-001.jpg", "result": "9577149002"},
{"name": "image-002.jpg", "result": "9577149002"},
{"name": "image-003.jpg", "result": "5776158811"},
{"name": "image-004.jpg", "result": "0463381455"},
{"name": "image-005.jpg", "result": "3261594101"},
{"name": "image-006.jpg", "result": "3261594101"},
{"name": "image-007.jpg", "result": "3261594101"},
{"name": "image-008.jpg", "result": "6730705801"},
{"name": "image-009.jpg", "result": "5776158811"},
{"name": "image-010.jpg", "result": "5776158811"}
];
testSet.forEach(function(sample) {
sample.format = "2of5";
});
_runTestSet(testSet, config);
});
describe("code_93", function() {
var config = config = {
inputStream: {
size: 800,
singleChannel: false
},
locator: {
patchSize: "large",
halfSample: true
},
numOfWorkers: 0,
decoder: {
readers: ["code_93_reader"]
},
locate: true,
src: null
},
testSet = [
{"name": "image-001.jpg", "result": "WIWV8ETQZ1"},
{"name": "image-002.jpg", "result": "EH3C-%GU23RK3"},
{"name": "image-003.jpg", "result": "O308SIHQOXN5SA/PJ"},
{"name": "image-004.jpg", "result": "DG7Q$TV8JQ/EN"},
{"name": "image-005.jpg", "result": "DG7Q$TV8JQ/EN"},
{"name": "image-006.jpg", "result": "O308SIHQOXN5SA/PJ"},
{"name": "image-007.jpg", "result": "VOFD1DB5A.1F6QU"},
{"name": "image-008.jpg", "result": "WIWV8ETQZ1"},
{"name": "image-009.jpg", "result": "4SO64P4X8 U4YUU1T-"},
{"name": "image-010.jpg", "result": "4SO64P4X8 U4YUU1T-"}
];
testSet.forEach(function(sample) {
sample.format = "code_93";
});
_runTestSet(testSet, config);
});
}); });

@ -1,36 +0,0 @@
let devices = [],
stream,
_constraints,
_supported = true;
export function enumerateDevices() {
console.log("enumerateDevices!!!!");
return Promise.resolve(devices);
};
export function getUserMedia(constraints) {
console.log("getUserMedia!!!!");
_constraints = constraints;
if (_supported) {
return Promise.resolve(stream);
}
return Promise.reject(new Error("das"));
}
export function setDevices(newDevices) {
devices = [...newDevices];
}
export function setStream(newStream) {
stream = newStream;
}
export function getConstraints() {
return _constraints;
}
export function setSupported(supported) {
console.log("Supported: " + supported);
_supported = supported;
}

@ -1,157 +1,188 @@
import CameraAccess, {pickConstraints} from '../../src/input/camera_access'; import CameraAccess from '../../src/input/camera_access';
import {setDevices, setStream, getConstraints, setSupported} from 'mediaDevices';
var originalURL, var originalURL,
originalMediaStreamTrack, originalMediaStreamTrack,
video, video,
stream, stream;
devices = [];
beforeEach(function() {
var tracks = [{
stop: function() {}
}];
originalURL = window.URL;
originalMediaStreamTrack = window.MediaStreamTrack;
window.MediaStreamTrack = {};
window.URL = {
createObjectURL(stream) {
return stream;
}
};
stream = {
getVideoTracks: function() {
return tracks;
}
};
sinon.spy(tracks[0], "stop");
video = {
src: null,
addEventListener: function() {},
removeEventListener: function() {},
play: function() {},
videoWidth: 320,
videoHeight: 480
};
sinon.stub(video, "addEventListener", function(event, cb) {
cb();
});
sinon.stub(video, "play");
});
afterEach(function() {
window.URL = originalURL;
window.MediaStreamTrack = originalMediaStreamTrack;
});
describe("camera_access", () => { describe('success', function() {
beforeEach(function() { beforeEach(function() {
var tracks = [{ sinon.stub(navigator.mediaDevices, "getUserMedia", function(constraints) {
stop: function() {} return Promise.resolve(stream);
}];
originalURL = window.URL;
originalMediaStreamTrack = window.MediaStreamTrack;
window.MediaStreamTrack = {};
window.URL = {
createObjectURL(stream) {
return stream;
}
};
stream = {
getVideoTracks: function() {
return tracks;
}
};
setStream(stream);
sinon.spy(tracks[0], "stop");
video = {
srcObject: null,
addEventListener: function() {},
removeEventListener: function() {},
setAttribute: sinon.spy(),
play: function() {},
videoWidth: 320,
videoHeight: 480
};
sinon.stub(video, "addEventListener", function(event, cb) {
cb();
}); });
sinon.stub(video, "play");
}); });
afterEach(function() { afterEach(function() {
window.URL = originalURL; navigator.mediaDevices.getUserMedia.restore();
window.MediaStreamTrack = originalMediaStreamTrack;
}); });
describe('request', function () {
describe('success', function() { it('should request the camera', function (done) {
describe('request', function () { CameraAccess.request(video, {})
it('should request the camera', function (done) { .then(function () {
CameraAccess.request(video, {}) expect(navigator.mediaDevices.getUserMedia.calledOnce).to.equal(true);
.then(function () { expect(video.src).to.deep.equal(stream);
expect(video.srcObject).to.deep.equal(stream); done();
done(); })
}) window.setTimeout(() => {
}); video.onloadedmetadata();
}, 100);
it("should allow deprecated constraints to be used", (done) => {
CameraAccess.request(video, {
width: 320,
height: 240,
facing: "user",
minAspectRatio: 2,
maxAspectRatio: 100
})
.then(function () {
const constraints = getConstraints();
expect(constraints.video.width).to.equal(320);
expect(constraints.video.height).to.equal(240);
expect(constraints.video.facingMode).to.equal("user");
expect(constraints.video.aspectRatio).to.equal(2);
expect(constraints.video.facing).not.to.be.defined;
expect(constraints.video.minAspectRatio).not.to.be.defined;
expect(constraints.video.maxAspectRatio).not.to.be.defined;
done();
})
});
}); });
describe('release', function () { it("should allow deprecated constraints to be used", (done) => {
it('should release the camera', function (done) { CameraAccess.request(video, {
CameraAccess.request(video, {}) width: 320,
.then(function () { height: 240,
expect(video.srcObject).to.deep.equal(stream); facing: "user",
CameraAccess.release(); minAspectRatio: 2,
expect(video.srcObject.getVideoTracks()).to.have.length(1); maxAspectRatio: 100
expect(video.srcObject.getVideoTracks()[0].stop.calledOnce).to.equal(true); })
done(); .then(function () {
}); const call = navigator.mediaDevices.getUserMedia.getCall(0),
}); args = call.args;
expect(call).to.be.defined;
expect(args[0].video.width).to.equal(320);
expect(args[0].video.height).to.equal(240);
expect(args[0].video.facingMode).to.equal("user");
expect(args[0].video.aspectRatio).to.equal(2);
expect(args[0].video.facing).not.to.be.defined;
expect(args[0].video.minAspectRatio).not.to.be.defined;
expect(args[0].video.maxAspectRatio).not.to.be.defined;
done();
})
window.setTimeout(() => {
video.onloadedmetadata();
}, 100);
}); });
}); });
describe('failure', function() { describe('facingMode fallback in Chrome', () => {
beforeEach(() => { beforeEach(() => {
setSupported(false); window.MediaStreamTrack.getSources = (cb) => {
return cb([
{kind: "video", facing: "environment", id: "environment"},
{kind: "audio", id: "audio"},
{kind: "video", facing: "user", id: "user"}
]);
};
}); });
afterEach(() => { afterEach(() => {
setSupported(true); window.MediaStreamTrack = {};
})
it("should set deviceId in case facingMode is not supported", (done) => {
CameraAccess.request(video, {
facing: "user"
})
.then(function () {
const call = navigator.mediaDevices.getUserMedia.getCall(0),
args = call.args;
expect(call).to.be.defined;
expect(args[0].video.facingMode).not.to.be.defined;
expect(args[0].video.deviceId).to.equal("user");
done();
})
window.setTimeout(() => {
video.onloadedmetadata();
}, 100);
}); });
});
describe("permission denied", function(){ describe('release', function () {
it('should throw if getUserMedia not available', function(done) { it('should release the camera', function (done) {
CameraAccess.request(video, {}) CameraAccess.request(video, {})
.catch(function (err) { .then(function () {
expect(err).to.be.defined; expect(video.src).to.deep.equal(stream);
done(); CameraAccess.release();
}); expect(video.src.getVideoTracks()).to.have.length(1);
expect(video.src.getVideoTracks()[0].stop.calledOnce).to.equal(true);
done();
}); });
window.setTimeout(() => {
video.onloadedmetadata();
}, 100);
}); });
});
});
describe("not available", function(){ describe('failure', function() {
var originalGetUserMedia; describe("permission denied", function(){
beforeEach(function() {
it('should throw if getUserMedia not available', function(done) { sinon.stub(navigator.mediaDevices, "getUserMedia", function(constraints, success, failure) {
CameraAccess.request(video, {}) return Promise.reject(new Error());
.catch((err) => {
expect(err).to.be.defined;
done();
});
}); });
}); });
describe("pickConstraints", () => { afterEach(function() {
it("should return the given constraints if no facingMode is defined", (done) => { navigator.mediaDevices.getUserMedia.restore();
const givenConstraints = {width: 180}; });
return pickConstraints(givenConstraints).then((actualConstraints) => {
expect(actualConstraints.video).to.deep.equal(givenConstraints); it('should throw if getUserMedia not available', function(done) {
done(); CameraAccess.request(video, {})
}) .catch(function (err) {
.catch((err) => { expect(err).to.be.defined;
expect(err).to.equal(null); done();
console.log(err);
done();
});
}); });
});
});
describe("not available", function(){
var originalGetUserMedia;
beforeEach(function() {
originalGetUserMedia = navigator.mediaDevices.getUserMedia;
navigator.mediaDevices.getUserMedia = undefined;
});
afterEach(function() {
navigator.mediaDevices.getUserMedia = originalGetUserMedia;
});
it("should return the given constraints if deviceId is defined", (done) => { it('should throw if getUserMedia not available', function(done) {
const givenConstraints = {width: 180, deviceId: "4343"}; CameraAccess.request(video, {})
return pickConstraints(givenConstraints).then((actualConstraints) => { .catch((err) => {
expect(actualConstraints.video).to.deep.equal(givenConstraints); expect(err).to.be.defined;
done(); done();
})
.catch((err) => {
expect(err).to.equal(null);
console.log(err);
done();
});
}); });
}); });
}); });

@ -1,59 +0,0 @@
import {
findTagsInObjectURL,
findTagsInBuffer,
base64ToArrayBuffer
} from '../../src/input/exif_helper';
const fixtures = {
orientation: {
'none': 'data:image/jpeg;base64,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',
'6': 'data:image/jpeg;base64,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',
'3': 'data:image/jpeg;base64,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',
'8': 'data:image/jpeg;base64,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',
'1': 'data:image/jpeg;base64,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'
}
};
describe('exif_helper', () => {
describe("findTagsInObjectURL", () => {
it("should return null if the src type is not supported", (done) => {
findTagsInObjectURL("blabla").then(tags => {
expect(tags === null).to.equal(true);
done();
});
});
it("should fail for a invalid blob type", (done) => {
findTagsInObjectURL('blob:balbla')
.then(() => {})
.catch(err => {
console.log(err);
expect(typeof err !== 'undefined');
}).then(done);
});
});
describe("findTagsInBuffer", () => {
it("should result in a rotation value of 1", () => {
const result = findTagsInBuffer(base64ToArrayBuffer(fixtures.orientation['1']));
expect(result).to.deep.equal({orientation: 1});
});
it("should result in a rotation value of 6", () => {
const result = findTagsInBuffer(base64ToArrayBuffer(fixtures.orientation['6']));
expect(result).to.deep.equal({orientation: 6});
});
it("should result in a rotation value of 3", () => {
const result = findTagsInBuffer(base64ToArrayBuffer(fixtures.orientation['3']));
expect(result).to.deep.equal({orientation: 3});
});
it("should result in a rotation value of 4", () => {
const result = findTagsInBuffer(base64ToArrayBuffer(fixtures.orientation['8']));
expect(result).to.deep.equal({orientation: 8});
});
it("should return nothing if orientation is not found", () => {
const result = findTagsInBuffer(base64ToArrayBuffer(fixtures.orientation['none']));
expect(result).to.deep.equal({});
});
});
});

@ -5,102 +5,99 @@ var canvasMock,
imageSize, imageSize,
config; config;
beforeEach(function() {
describe("resultCollector", () => { imageSize = {x: 320, y: 240};
beforeEach(function() { config = {
imageSize = {x: 320, y: 240}; capture: true,
config = { capacity: 20,
capture: true, blacklist: [{code: "3574660239843", format: "ean_13"}],
capacity: 20, filter: function() {
blacklist: [{code: "3574660239843", format: "ean_13"}], return true;
filter: function() { }
return true; };
} canvasMock = {
}; getContext: function() {
canvasMock = { return {};
getContext: function() { },
return {}; toDataURL: sinon.spy(),
}, width: 0,
toDataURL: sinon.spy(), height: 0
width: 0, };
height: 0 sinon.stub(document, "createElement", function(type) {
}; if (type === "canvas") {
sinon.stub(document, "createElement", function(type) { return canvasMock;
if (type === "canvas") { }
return canvasMock;
}
});
}); });
});
afterEach(function() { afterEach(function() {
document.createElement.restore(); document.createElement.restore();
}); });
describe('create', function () { describe('create', function () {
it("should return a new collector", function() { it("should return a new collector", function() {
ResultCollector.create(config); ResultCollector.create(config);
expect(document.createElement.calledOnce).to.be.equal(true); expect(document.createElement.calledOnce).to.be.equal(true);
expect(document.createElement.getCall(0).args[0]).to.equal("canvas"); expect(document.createElement.getCall(0).args[0]).to.equal("canvas");
});
}); });
});
describe('addResult', function() { describe('addResult', function() {
beforeEach(function() { beforeEach(function() {
sinon.stub(ImageDebug, "drawImage", function() {}); sinon.stub(ImageDebug, "drawImage", function() {});
}); });
afterEach(function() { afterEach(function() {
ImageDebug.drawImage.restore(); ImageDebug.drawImage.restore();
}); });
it("should not add result if capacity is full", function(){ it("should not add result if capacity is full", function(){
config.capacity = 1; config.capacity = 1;
var collector = ResultCollector.create(config); var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {}); collector.addResult([], imageSize, {});
collector.addResult([], imageSize, {}); collector.addResult([], imageSize, {});
collector.addResult([], imageSize, {}); collector.addResult([], imageSize, {});
expect(collector.getResults()).to.have.length(1); expect(collector.getResults()).to.have.length(1);
}); });
it("should only add results which match constraints", function(){ it("should only add results which match constraints", function(){
var collector = ResultCollector.create(config), var collector = ResultCollector.create(config),
results; results;
collector.addResult([], imageSize, {code: "423423443", format: "ean_13"}); collector.addResult([], imageSize, {code: "423423443", format: "ean_13"});
collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"}); collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"});
collector.addResult([], imageSize, {code: "3574660239843", format: "code_128"}); collector.addResult([], imageSize, {code: "3574660239843", format: "code_128"});
results = collector.getResults(); results = collector.getResults();
expect(results).to.have.length(2); expect(results).to.have.length(2);
results.forEach(function(result) { results.forEach(function(result) {
expect(result).not.to.deep.equal(config.blacklist[0]); expect(result).not.to.deep.equal(config.blacklist[0]);
});
}); });
});
it("should add result if no filter is set", function() { it("should add result if no filter is set", function() {
delete config.filter; delete config.filter;
var collector = ResultCollector.create(config); var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {code: "423423443", format: "ean_13"}); collector.addResult([], imageSize, {code: "423423443", format: "ean_13"});
expect(collector.getResults()).to.have.length(1); expect(collector.getResults()).to.have.length(1);
}); });
it("should not add results if filter returns false", function() { it("should not add results if filter returns false", function() {
config.filter = () => (false); config.filter = () => (false);
var collector = ResultCollector.create(config); var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {code: "423423443", format: "ean_13"}); collector.addResult([], imageSize, {code: "423423443", format: "ean_13"});
expect(collector.getResults()).to.have.length(0); expect(collector.getResults()).to.have.length(0);
}); });
it("should add result if no blacklist is set", function() { it("should add result if no blacklist is set", function() {
delete config.blacklist; delete config.blacklist;
var collector = ResultCollector.create(config); var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"}); collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"});
expect(collector.getResults()).to.have.length(1); expect(collector.getResults()).to.have.length(1);
});
}); });
}) });

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save